Add index.js monitoring and fix capturing server error output

This commit is contained in:
Saoud Rizwan
2024-12-10 14:26:25 -08:00
parent 61311e3f41
commit 1319105bb4
6 changed files with 237 additions and 201 deletions

View File

@@ -267,86 +267,46 @@ MCP SERVERS
The Model Context Protocol (MCP) enables communication between the system and locally running MCP servers that provide additional capabilities through a standardized protocol. Each server can offer tools and resources that extend your capabilities.
When a server is connected, you can:
1. Use the server's tools via the use_mcp_tool tool:
<use_mcp_tool>
<server_name>server name here</server_name>
<tool_name>tool name here</tool_name>
<arguments>
{
"param1": "value1",
"param2": "value2"
}
</arguments>
</use_mcp_tool>
2. Access the server's resources via the access_mcp_resource tool:
<access_mcp_resource>
<server_name>server name here</server_name>
<uri>resource URI here</uri>
</access_mcp_resource>
# Guidelines for MCP Usage
- Use one MCP operation per message and wait for confirmation before proceeding
- Handle any errors returned from MCP operations gracefully
# Connected MCP Servers
When a server is connected, you can use the server's tools via the \`use_mcp_tool\` tool, and access the server's resources via the \`access_mcp_resource\` tool.
${
mcpHub.getServers().length > 0
? `${mcpHub
.getServers()
.filter((server) => server.status === "connected")
.map((server) => {
const tools =
server.tools
?.map((tool) => {
const schemaStr = tool.inputSchema
? ` Input Schema:
const tools = server.tools
?.map((tool) => {
const schemaStr = tool.inputSchema
? ` Input Schema:
${JSON.stringify(tool.inputSchema, null, 2).split("\n").join("\n ")}`
: ""
: ""
return `- ${tool.name}: ${tool.description || "No description provided"}\n${schemaStr}`
})
.join("\n\n") || "No tools available"
return `- ${tool.name}: ${tool.description}\n${schemaStr}`
})
.join("\n\n")
const templates = server.resourceTemplates?.length
? server.resourceTemplates
.map(
(template) =>
`- ${template.uriTemplate} (${template.name}): ${template.description || "No description provided"}`,
)
.join("\n")
: "No resource templates available"
const templates = server.resourceTemplates
?.map((template) => `- ${template.uriTemplate} (${template.name}): ${template.description}`)
.join("\n")
const resources = server.resources?.length
? server.resources
.map(
(resource) =>
`- ${resource.uri} (${resource.name}): ${resource.description || "No description provided"}`,
)
.join("\n")
: "No resources available"
const resources = server.resources
?.map((resource) => `- ${resource.uri} (${resource.name}): ${resource.description}`)
.join("\n")
const config = JSON.parse(server.config)
return `## ${server.name} (\`${config.command}${config.args && Array.isArray(config.args) ? ` ${config.args.join(" ")}` : ""}\`)
### Available Tools
${tools}
### Available Resources
#### Resource Templates
${templates}
#### Direct Resources
${resources}
`
return (
`## ${server.name} (\`${config.command}${config.args && Array.isArray(config.args) ? ` ${config.args.join(" ")}` : ""}\`)` +
(tools ? `\n\n### Available Tools\n${tools}` : "") +
(templates ? `\n\n### Resource Templates\n${templates}` : "") +
(resources ? `\n\n### Direct Resources\n${resources}` : "")
)
})
.join("\n\n")}`
: "No MCP servers currently connected."
: "(No MCP servers currently connected)"
}
## Creating an MCP Server
@@ -355,7 +315,7 @@ The user may ask you something along the lines of "add a tool" that does some fu
When creating MCP servers, it's important to understand that they operate in a non-interactive environment. The server cannot initiate OAuth flows, open browser windows, or prompt for user input during runtime. All credentials and authentication tokens must be provided upfront through environment variables in the MCP settings configuration. For example, Spotify's API uses OAuth to get a refresh token for the user, but the MCP server cannot initiate this flow. While you can walk the user through obtaining an application client ID and secret, you may have to create a separate one-time setup script (like get-refresh-token.js) that captures and logs the final piece of the puzzle: the user's refresh token (i.e. you might run the script using execute_command which would open a browser for authentication, and then log the refresh token so that you can see it in the command output for you to use in the MCP settings configuration).
Unless the user specifies otherwise, new MCP servers should be created in: ${mcpHub.getMcpServersPath()}
Unless the user specifies otherwise, new MCP servers should be created in: ${await mcpHub.getMcpServersPath()}
### Example MCP Server
@@ -363,9 +323,10 @@ For example, if the user wanted to give you the ability to retrieve weather info
The following examples demonstrate how to build an MCP server that provides weather data functionality. While this example shows how to implement resources, resource templates, and tools, in practice you should prefer using tools since they are more flexible and can handle dynamic parameters. The resource and resource template implementations are included here mainly for demonstration purposes of the different MCP capabilities, but a real weather server would likely just expose tools for fetching weather data. (The following steps are for macOS)
1. Use the \`create-typescript-server\` tool to bootstrap a new project:
1. Use the \`create-typescript-server\` tool to bootstrap a new project in the default MCP servers directory:
\`\`\`bash
cd ${await mcpHub.getMcpServersPath()}
npx @modelcontextprotocol/create-server weather-server
cd weather-server
# Install dependencies
@@ -382,8 +343,7 @@ weather-server/
"type": "module", // added by default, uses ES module syntax (import/export) rather than CommonJS (require/module.exports) (Important to know if you create additional scripts in this server repository like a get-refresh-token.js script)
"scripts": {
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
// The MCP Inspector is an interactive developer tool for testing and debugging MCP servers. It launches a web server that allows the user to interact with the server and test its capabilities. (You could also pass arguments or use with \`uv\` e.g., \`npx @modelcontextprotocol/inspector uvx <package-name> <args>\`)
"inspector": "npx @modelcontextprotocol/inspector build/index.js"
"inspector": "npx @modelcontextprotocol/inspector build/index.js" // The MCP Inspector is an interactive developer tool for testing and debugging MCP servers. It launches a web server that allows the user to interact with the server and test its capabilities. (You could also pass arguments or use with \`uv\` e.g., \`npx @modelcontextprotocol/inspector uvx <package-name> <args>\`)
}
...
}
@@ -450,7 +410,6 @@ class WeatherServer {
}
);
// Configure axios with defaults
this.axiosInstance = axios.create({
baseURL: 'http://api.openweathermap.org/data/2.5',
params: {
@@ -459,24 +418,18 @@ class WeatherServer {
},
});
this.setupHandlers();
this.setupErrorHandling();
}
private setupErrorHandling() {
// Setup handlers
this.setupResourceHandlers();
this.setupToolHandlers();
// Error handling
this.server.onerror = (error) => console.error('[MCP Error]', error);
process.on('SIGINT', async () => {
await this.server.close();
process.exit(0);
});
}
private setupHandlers() {
this.setupResourceHandlers();
this.setupToolHandlers();
}
// MCP Resources represent any kind of UTF-8 encoded data that an MCP server wants to make available to clients, such as database records, API responses, log files, and more. Servers define direct resources with a static URI or dynamic resources with a URI template that follows the format \`[protocol]://[host]/[path]\`.
private setupResourceHandlers() {
this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({
@@ -656,17 +609,17 @@ const server = new WeatherServer();
server.run().catch(console.error);
\`\`\`
(This is just an example, but you may use different dependencies, break this up into multiple files for example put the types in a separate types.ts file, etc.)
(Remember: This is just an exampleyou may use different dependencies, break the implementation up into multiple files, etc.)
3. Build and compile the executablejavascript file
3. Build and compile the executable JavaScript file
\`\`\`bash
npm run build
\`\`\`
4. Whenever you need an environment variable such as an API key to configure the MCP server, walk the user through the process of getting the key. For example they may need to create an account and go to a developer dashboard to generate the key. Provide step by step instructions and markdown formatted links to make it easy for the user to retrieve the necessary information. Then use the ask_followup_question tool to ask the user for the key, in this case the OpenWeather API key.
4. Whenever you need an environment variable such as an API key to configure the MCP server, walk the user through the process of getting the key. For example, they may need to create an account and go to a developer dashboard to generate the key. Provide step-by-step instructions and URLs to make it easy for the user to retrieve the necessary information. Then use the ask_followup_question tool to ask the user for the key, in this case the OpenWeather API key.
5. Install the MCP Server by adding the MCP server configuration to the settings file located at '${mcpHub.getMcpSettingsFilePath()}'. The settings file may have other MCP servers already configured, so you would read it first and then add your new server to the existing \`mcpServers\` object.
5. Install the MCP Server by adding the MCP server configuration to the settings file located at '${await mcpHub.getMcpSettingsFilePath()}'. The settings file may have other MCP servers already configured, so you would read it first and then add your new server to the existing \`mcpServers\` object.
\`\`\`json
{
@@ -683,73 +636,11 @@ npm run build
}
\`\`\`
(Note: the user may also ask you to install the MCP server to the Claude desktop app, in which case you would modify \`~/Library/Application\ Support/Claude/claude_desktop_config.json\` on macOS for example. It follows the same format of a top level \`mcpServers\` object and each server having \`command\`, \`args\`, and/or \`env\`)
(Note: the user may also ask you to install the MCP server to the Claude desktop app, in which case you would modify \`~/Library/Application\ Support/Claude/claude_desktop_config.json\` on macOS for example. It follows the same format of a top level \`mcpServers\` object.)
6. After you have edited the MCP settings configuration file, the system will automatically run all the servers and expose the available tools and resources in the system prompt.
7. Now that you have access to these new tools and resources, you may suggest ways the user can command you to invoke them - for example, with this new weather tool now available, you can invite the user to ask "what's the weather in San Francisco?" or "get a forecast for my upcoming trip to New York".
### Python Implementation
If the user requests it or there is a good reason to use Python over TypeScript for building an MCP server, you would follow the same core concepts as the TypeScript version, with these key differences:
1. Initial setup uses \`uv\` instead of \`npm\`:
\`\`\`bash
# Create and setup project
uvx create-mcp-server --path weather-server # if uv is not installed, you may guide the user to install it using https://docs.astral.sh/uv/getting-started/installation
cd weather-server
uv add httpx python-dotenv
\`\`\`
2. Project structure differences:
\`\`\`
weather-server/
└── src/
└── weather-server/
├── __init__.py # Contains main() entry point
└── server.py # Main server implementation
\`\`\`
3. Key implementation differences:
\`\`\`python:src/weather-server/server.py
from mcp.server import Server
from mcp.types import Resource, Tool, TextContent
from pydantic import AnyUrl
# Use AnyUrl instead of string for URI validation
@app.list_resources()
async def list_resources() -> list[Resource]:
uri = AnyUrl(f"weather://San Francisco/current")
return [Resource(uri=uri, ...)]
# Async handlers instead of Promise-based
@app.read_resource()
async def read_resource(uri: AnyUrl) -> str:
# ... implementation ...
# Use httpx instead of axios
async with httpx.AsyncClient() as client:
response = await client.get(url, params=params)
\`\`\`
4. Configuration file uses \`uv\` instead of \`node\`:
\`\`\`json:
{
"mcpServers": {
"weather": {
"command": "uv",
"args": ["--directory", "/path/to/weather-server", "run", "weather-service"],
"env": {
"OPENWEATHER_API_KEY": "your-api-key"
}
}
}
}
\`\`\`
8. Now that you have access to these new tools and resources, you may suggest ways the user can command you to invoke them - for example, with this new weather tool now available, you can invite the user to ask "what's the weather in San Francisco?"
## Editing MCP Servers
@@ -760,14 +651,14 @@ The user may ask to add tools or resources to an existing MCP server (listed und
.join(", ") || "(None running currently)"
}), or may more generally ask to add functionality that may make sense to add to an existing local MCP server rather than creating a new one. This would be possible if you can locate the MCP server repository on the user's system by looking at the server arguments for a filepath.
If you edit a Connected MCP server, you will need to guide the user to restart the server manually for any changes to take effect. They would need to:
"1. Open the MCP Servers view by selecting the server icon in the menu bar
2. Select the server they want to restart
3. Click the 'Restart Server' button"
However some MCP servers may be running from installed packages rather than a local repository, in which case it may make more sense to create a new MCP server.
# MCP Servers Are Not Always Necessary
The user may not always request the use or creation of MCP servers. Instead, they might provide tasks that can be completed with existing tools. While using the MCP SDK to extend your capabilities can be useful, it's important to understand that this is just one specialized type of task you can accomplish. You should only implement MCP servers when the user explicitly requests it (e.g., "add a tool that...").
Remember: The MCP documentation provided above is to help you understand and work with existing MCP servers or create new ones when requested by the user. You already have access to tools and capabilities that can be used to accomplish a wide range of tasks.
====
CAPABILITIES