docs: update custom extensions tutorial (#6388)

This commit is contained in:
dianed-square
2026-01-07 16:49:52 -08:00
committed by GitHub
parent 78a9b5b085
commit 01da90c9b3
2 changed files with 91 additions and 44 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 KiB

After

Width:  |  Height:  |  Size: 171 KiB

@@ -4,6 +4,8 @@ description: Create your own custom MCP Server to use as a goose extension
--- ---
import { PanelLeft } from 'lucide-react'; import { PanelLeft } from 'lucide-react';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
# Building Custom Extensions with goose # Building Custom Extensions with goose
@@ -12,17 +14,24 @@ goose allows you to extend its functionality by creating your own custom extensi
In this guide, we build an MCP server using the [Python SDK for MCP][mcp-python]. Well demonstrate how to create an MCP server that reads Wikipedia articles and converts them to Markdown, integrate it as an extension in goose. You can follow a similar process to develop your own custom extensions for goose. In this guide, we build an MCP server using the [Python SDK for MCP][mcp-python]. Well demonstrate how to create an MCP server that reads Wikipedia articles and converts them to Markdown, integrate it as an extension in goose. You can follow a similar process to develop your own custom extensions for goose.
You can checkout other examples in this [MCP servers repository][mcp-servers]. MCP SDKs are also available in [Typescript][mcp-typescript] and [Kotlin][mcp-kotlin]. You can check out other example servers in the [MCP servers repository][mcp-servers]. MCP SDKs are also available for other common languages, such as [Typescript][mcp-typescript] and [Kotlin][mcp-kotlin].
:::info :::info
goose supports Tools, Resources, and Prompts from the [Model Context Protocol](https://modelcontextprotocol.io/). See [`mcp_client.rs`](https://github.com/block/goose/blob/main/crates/goose/src/agents/mcp_client.rs) for the supported protocol version and client capabilities.
goose currently supports Tools and Resources for [MCP Server features](https://spec.modelcontextprotocol.io/specification/2024-11-05/server/).
We will be adding support for MCP Prompts soon.
::: :::
--- ---
## Prerequisites
Before you begin, ensure you have the following installed on your system:
- **Python 3.13 or higher** - Required for the MCP server
- **[uv](https://docs.astral.sh/uv/)** - Python package manager used in this tutorial
- **Node.js and npm** - Only required if you want to use the MCP Inspector development tool in [Step 4](#step-4-test-your-mcp-server).
---
## Step 1: Initialize Your Project ## Step 1: Initialize Your Project
The first step is to create a new project using [uv][uv-docs]. We will name our project `mcp-wiki`. The first step is to create a new project using [uv][uv-docs]. We will name our project `mcp-wiki`.
@@ -44,13 +53,12 @@ Your project directory structure should look like this:
. .
├── README.md ├── README.md
├── pyproject.toml ├── pyproject.toml
── src ── src
└── mcp_wiki └── mcp_wiki
├── __init__.py # Primary CLI entry point ├── __init__.py # Primary CLI entry point
├── __main__.py # To enable running as a Python module ├── __main__.py # To enable running as a Python module
├── py.typed # Indicates the package supports type hints ├── py.typed # Indicates the package supports type hints
└── server.py # Your MCP server code (tool, resources, prompts) └── server.py # Your MCP server code (tool, resources, prompts)
└── uv.lock
``` ```
--- ---
@@ -72,6 +80,7 @@ import requests
from requests.exceptions import RequestException from requests.exceptions import RequestException
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from html2text import html2text from html2text import html2text
from urllib.parse import urlparse
from mcp.server.fastmcp import FastMCP from mcp.server.fastmcp import FastMCP
from mcp.shared.exceptions import McpError from mcp.shared.exceptions import McpError
@@ -93,12 +102,24 @@ def read_wikipedia_article(url: str) -> str:
if not url.startswith("http"): if not url.startswith("http"):
raise ValueError("URL must start with http or https.") raise ValueError("URL must start with http or https.")
response = requests.get(url, timeout=10) # SSRF protection: only allow Wikipedia domains
parsed = urlparse(url)
hostname = parsed.netloc.lower()
# Allow wikipedia.org or *.wikipedia.org subdomains only
if hostname != 'wikipedia.org' and not hostname.endswith('.wikipedia.org'):
raise ValueError(f"Only Wikipedia URLs are allowed. Got: {parsed.netloc}")
# Add User-Agent header to avoid 403 from Wikipedia
headers = {
'User-Agent': 'MCP-Wiki/1.0 (Educational purposes; Python requests)'
}
response = requests.get(url, headers=headers, timeout=10)
if response.status_code != 200: if response.status_code != 200:
raise McpError( raise McpError(
ErrorData( ErrorData(
INTERNAL_ERROR, code=INTERNAL_ERROR,
f"Failed to retrieve the article. HTTP status code: {response.status_code}" message=f"Failed to retrieve the article. HTTP status code: {response.status_code}"
) )
) )
@@ -107,8 +128,8 @@ def read_wikipedia_article(url: str) -> str:
if not content_div: if not content_div:
raise McpError( raise McpError(
ErrorData( ErrorData(
INVALID_PARAMS, code=INVALID_PARAMS,
"Could not find the main content on the provided Wikipedia URL." message="Could not find the main content on the provided Wikipedia URL."
) )
) )
@@ -117,11 +138,11 @@ def read_wikipedia_article(url: str) -> str:
return markdown_text return markdown_text
except ValueError as e: except ValueError as e:
raise McpError(ErrorData(INVALID_PARAMS, str(e))) from e raise McpError(ErrorData(code=INVALID_PARAMS, message=str(e))) from e
except RequestException as e: except RequestException as e:
raise McpError(ErrorData(INTERNAL_ERROR, f"Request error: {str(e)}")) from e raise McpError(ErrorData(code=INTERNAL_ERROR, message=f"Request error: {str(e)}")) from e
except Exception as e: except Exception as e:
raise McpError(ErrorData(INTERNAL_ERROR, f"Unexpected error: {str(e)}")) from e raise McpError(ErrorData(code=INTERNAL_ERROR, message=f"Unexpected error: {str(e)}")) from e
``` ```
### `__init__.py` ### `__init__.py`
@@ -164,9 +185,9 @@ description = "MCP Server for Wikipedia"
readme = "README.md" readme = "README.md"
requires-python = ">=3.13" requires-python = ">=3.13"
dependencies = [ dependencies = [
"beautifulsoup4>=4.12.3", "beautifulsoup4>=4.14.0",
"html2text>=2024.2.26", "html2text>=2025.4.15",
"mcp[cli]>=1.2.0", "mcp[cli]>=1.25.0",
"requests>=2.32.3", "requests>=2.32.3",
] ]
@@ -182,7 +203,13 @@ build-backend = "hatchling.build"
## Step 4: Test Your MCP Server ## Step 4: Test Your MCP Server
### Using MCP Inspector Verify that your MCP server is running in the MCP Inspector (a browser-based development tool) or the server CLI.
<Tabs>
<TabItem value="ui" label="In MCP Inspector" default>
:::info
MCP Inspector requires Node.js and npm installed on your computer.
:::
1. Setup the project environment: 1. Setup the project environment:
@@ -196,34 +223,42 @@ build-backend = "hatchling.build"
source .venv/bin/activate source .venv/bin/activate
``` ```
3. Run your server in development mode: 3. Run your server in development mode:
```bash ```bash
mcp dev src/mcp_wiki/server.py mcp dev src/mcp_wiki/server.py
``` ```
MCP Inspector should open automatically in your browser. On first run, you'll be prompted to install `@modelcontextprotocol/inspector`.
4. Go to `http://localhost:5173` in your browser to open the MCP Inspector UI. 4. Test the tool:
1. Click `Connect` to initialize your MCP server
2. On the `Tools` tab, click `List Tools` and click the `read_wikipedia_article` tool
3. Enter `https://en.wikipedia.org/wiki/Bangladesh` for the URL and click `Run Tool`
5. In the UI, you can click "Connect" to initialize your MCP server. Then click on "Tools" tab > "List Tools" and you should see the `read_wikipedia_article` tool. [![MCP Inspector UI](../assets/guides/custom-extension-mcp-inspector.png)](../assets/guides/custom-extension-mcp-inspector.png)
Then you can try to call the `read_wikipedia_article` tool with URL set to "https://en.wikipedia.org/wiki/Bangladesh" and click "Run Tool".
![MCP Inspector UI](../assets/guides/custom-extension-mcp-inspector.png) </TabItem>
<TabItem value="cli" label="In the CLI">
1. Setup the project environment:
### Testing the CLI ```bash
uv sync
```
1. Install your project locally: 2. Activate your virtual environment:
```bash
source .venv/bin/activate
```
3. Install your project locally:
```bash ```bash
uv pip install . uv pip install .
``` ```
2. Check the executable in your virtual environment: 4. Verify the CLI:
```bash
ls .venv/bin/ # Verify your CLI is available
```
3. Test the CLI:
```bash ```bash
mcp-wiki --help mcp-wiki --help
@@ -240,6 +275,8 @@ build-backend = "hatchling.build"
options: options:
-h, --help show this help message and exit -h, --help show this help message and exit
``` ```
</TabItem>
</Tabs>
--- ---
@@ -247,11 +284,17 @@ build-backend = "hatchling.build"
To add your MCP server as an extension in goose: To add your MCP server as an extension in goose:
1. Click the <PanelLeft className="inline" size={16} /> button in the top-left to open the sidebar 1. Build the extension binary:
2. Click `Extensions` in the sidebar
3. Set the `Type` to `STDIO` ```bash
4. Provide a name and description for your extension uv pip install .
5. In the `Command` field, provide the absolute path to your executable: ```
2. Open goose Desktop and click the <PanelLeft className="inline" size={16} /> button in the top-left to open the sidebar
3. Click `Extensions` in the sidebar
4. Set the `Type` to `STDIO`
5. Provide a name and description for your extension
6. In the `Command` field, provide the absolute path to your executable:
```plaintext ```plaintext
uv run /full/path/to/mcp-wiki/.venv/bin/mcp-wiki uv run /full/path/to/mcp-wiki/.venv/bin/mcp-wiki
``` ```
@@ -261,7 +304,11 @@ To add your MCP server as an extension in goose:
uv run /Users/smohammed/Development/mcp/mcp-wiki/.venv/bin/mcp-wiki uv run /Users/smohammed/Development/mcp/mcp-wiki/.venv/bin/mcp-wiki
``` ```
For the purposes on this guide, we'll run the local version. Alternatively, you can publish your package to PyPI. Once published, the server can be run directly using `uvx`. For example: :::tip Rebuild binary after changes
To see any changes you make to your MCP server code after integrating with goose, re-run `uv pip install .` and then restart goose Desktop.
:::
For the purposes of this guide, we'll run the local version. Alternatively, you can publish your package to PyPI. Once published, the server can be run directly using `uvx`. For example:
``` ```
uvx mcp-wiki uvx mcp-wiki