Model Context Protocol (MCP): Difference between revisions
Line 95: | Line 95: | ||
If all goes well, when you restart Claude it will show<br> | If all goes well, when you restart Claude it will show<br> | ||
[[File:Claude1.png]]<br> | [[File:Claude1.png]]<br> | ||
=Writing a Server (Typescript)= | |||
Well always a challenge but they will never win. The challenge is to avoid standard out. First the server. | |||
<syntaxhighlight lang="ts"> | |||
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js"; | |||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; | |||
import { z } from "zod"; | |||
const server = new McpServer({ | |||
name: "Weather Service", | |||
version: "1.0.0", | |||
}); | |||
// Add an addition tool | |||
server.tool("add", | |||
{ a: z.number(), b: z.number() }, | |||
async ({ a, b }) => ({ | |||
content: [{ type: "text", text: String(a + b) }] | |||
}) | |||
); | |||
// Add a dynamic greeting resource | |||
server.resource( | |||
"greeting", | |||
new ResourceTemplate("greeting://{name}", { list: undefined }), | |||
async (uri, { name }) => ({ | |||
contents: [{ | |||
uri: uri.href, | |||
text: `Hello, ${name}!` | |||
}] | |||
}) | |||
); | |||
// Add a tool to get the weather | |||
server.tool("getWeather", { | |||
word: z.string() | |||
}, | |||
async ({ word }) => { | |||
return { | |||
content: [ | |||
{ | |||
type: "text", | |||
text: `The weather is ${word} nice today` | |||
} | |||
] | |||
}; | |||
} | |||
); | |||
const transport = new StdioServerTransport(); | |||
await server.connect(transport); | |||
</syntaxhighlight> | |||
All went well and you can test this and other MCPs with the mcp inspector | |||
<syntaxhighlight lang="bash"> | |||
npx @modelcontextprotocol/inspector | |||
</syntaxhighlight> | |||
And all goes well. But adding it to Claude proved an hour of fun. To ensure I have not issues I made a shell script to cd to the working directory and run. But this fell over with | |||
<syntaxhighlight lang="text"> | |||
[error | |||
] Unexpected end of JSON input { | |||
"context": "connection", | |||
"stack": "SyntaxError: Unexpected end of JSON input\n at JSON.parse (<anonymous>)\n at EPe (/usr/lib/claude-desktop/app.asar/.vite/build/index.js:82:189)\n at SPe.readMessage (/usr/lib/claude-desktop/app.asar/.vite/build/index.js:82:115)\n at TPe.processReadBuffer (/usr/lib/claude-desktop/app.asar/.vite/build/index.js:83:1842)\n at Socket.<anonymous> (/usr/lib/claude-desktop/app.asar/.vite/build/index.js:83:1523)\n at Socket.emit (node:events:518:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)\n at Pipe.onStreamRead (node:internal/stream_base_commons:189:23)" | |||
} | |||
2025-03-22T03: 29: 54.581Z [add | |||
] [error | |||
] Unexpected token '>', | |||
"> start" is not valid JSON { | |||
"context": "connection", | |||
"stack": "SyntaxError: Unexpected token '>', \"> start\" is not valid JSON\n at JSON.parse (<anonymous>)\n at EPe (/usr/lib/claude-desktop/app.asar/.vite/build/index.js:82:189)\n at SPe.readMessage (/usr/lib/claude-desktop/app.asar/.vite/build/index.js:82:115)\n at TPe.processReadBuffer (/usr/lib/claude-desktop/app.asar/.vite/build/index.js:83:1842)\n at Socket.<anonymous> (/usr/lib/claude-desktop/app.asar/.vite/build/index.js:83:1523)\n at Socket.emit (node:events:518:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)\n at Pipe.onStreamRead (node:internal/stream_base_commons:189:23)" | |||
} | |||
</syntaxhighlight> | |||
This is caused by npm run start going to standard out. If you want to run node you can do this. | |||
<syntaxhighlight lang="json"> | |||
{ | |||
"mcpServers": { | |||
"add": { | |||
"command": "node", | |||
"args": [ | |||
"/home/iwiseman/dev/projects/mcpServer/tsMcpTest/dist/app.js" | |||
] | |||
}, | |||
... | |||
</syntaxhighlight> |
Revision as of 04:14, 22 March 2025
Introduction
This is my first foray into MCPs which works
Clients
- Claude
- Claude Code
- Cursor
- Windsurf
Servers
Example server site is awesome-mcp-servers
Installing Claude on Ubuntu 24.04
This was reasonably painless. Goto here. When installing the instructions are
git clone https://github.com/aaddrick/claude-desktop-debian.git
cd claude-desktop-debian
# Build the package
sudo ./build-deb.sh
sudo dpkg -i ./build/electron-app/claude-desktop_0.8.0_amd64.deb
This all goes well except the version of claude is now 0.8.1 and the script in the repository is 0.8.0. Search and replace the build-deb.sh and then it fails to launch because of sandbox issues which can be fixed, in my case, with
sudo chmod 4755 /usr/local/lib/node_modules/electron/dist/chrome-sandbox
Writing a Server (Python)
To demonstrate how easy this is Matthew Berman. There is just this code and a config
from mcp.server.fastmcp import FastMCP
import time
import signal
import sys
# Handle SIGINT (Ctrl+C) gracefully
def signal_handler(sig, frame):
print("Shutting down server gracefully...")
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
# Create an MCP server with increased timeout
mcp = FastMCP(
name="count-r",
host="127.0.0.1",
port=5000,
# Add this to make the server more resilient
timeout=30 # Increase timeout to 30 seconds
)
# Define our tool
@mcp.tool()
def count_r(word: str) -> int:
"""Count the number of 'r' letters in a given word."""
try:
# Add robust error handling
if not isinstance(word, str):
return 0
return word.lower().count("r")
except Exception as e:
# Return 0 on any error
return 0
if __name__ == "__main__":
try:
print("Starting MCP server 'count-r' on 127.0.0.1:5000")
# Use this approach to keep the server running
mcp.run()
except Exception as e:
print(f"Error: {e}")
# Sleep before exiting to give time for error logs
time.sleep(5)
Build an executable as you cannot just use pip3 or pipx to install globally.
pip3 install pyinstaller
pyinstaller --onefile main.py
This produces a file in the dist directory.
Then we change the config for claude, making sure the command is appropriate to your setup. This file can be found in the following directory. ~/.config/Claude/claude_desktop_config.json
{
"mcpServers": {
"count-r": {
"command": "/home/iwiseman/dev/projects/mcpServer/pyFastMCPTest/dist/main",
"args": [
""
],
"host": "127.0.0.1",
"port": 8080,
"timeout": 30000
}
}
}
If all goes well, when you restart Claude it will show
Writing a Server (Typescript)
Well always a challenge but they will never win. The challenge is to avoid standard out. First the server.
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "Weather Service",
version: "1.0.0",
});
// Add an addition tool
server.tool("add",
{ a: z.number(), b: z.number() },
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }]
})
);
// Add a dynamic greeting resource
server.resource(
"greeting",
new ResourceTemplate("greeting://{name}", { list: undefined }),
async (uri, { name }) => ({
contents: [{
uri: uri.href,
text: `Hello, ${name}!`
}]
})
);
// Add a tool to get the weather
server.tool("getWeather", {
word: z.string()
},
async ({ word }) => {
return {
content: [
{
type: "text",
text: `The weather is ${word} nice today`
}
]
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
All went well and you can test this and other MCPs with the mcp inspector
npx @modelcontextprotocol/inspector
And all goes well. But adding it to Claude proved an hour of fun. To ensure I have not issues I made a shell script to cd to the working directory and run. But this fell over with
[error
] Unexpected end of JSON input {
"context": "connection",
"stack": "SyntaxError: Unexpected end of JSON input\n at JSON.parse (<anonymous>)\n at EPe (/usr/lib/claude-desktop/app.asar/.vite/build/index.js:82:189)\n at SPe.readMessage (/usr/lib/claude-desktop/app.asar/.vite/build/index.js:82:115)\n at TPe.processReadBuffer (/usr/lib/claude-desktop/app.asar/.vite/build/index.js:83:1842)\n at Socket.<anonymous> (/usr/lib/claude-desktop/app.asar/.vite/build/index.js:83:1523)\n at Socket.emit (node:events:518:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)\n at Pipe.onStreamRead (node:internal/stream_base_commons:189:23)"
}
2025-03-22T03: 29: 54.581Z [add
] [error
] Unexpected token '>',
"> start" is not valid JSON {
"context": "connection",
"stack": "SyntaxError: Unexpected token '>', \"> start\" is not valid JSON\n at JSON.parse (<anonymous>)\n at EPe (/usr/lib/claude-desktop/app.asar/.vite/build/index.js:82:189)\n at SPe.readMessage (/usr/lib/claude-desktop/app.asar/.vite/build/index.js:82:115)\n at TPe.processReadBuffer (/usr/lib/claude-desktop/app.asar/.vite/build/index.js:83:1842)\n at Socket.<anonymous> (/usr/lib/claude-desktop/app.asar/.vite/build/index.js:83:1523)\n at Socket.emit (node:events:518:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)\n at Pipe.onStreamRead (node:internal/stream_base_commons:189:23)"
}
This is caused by npm run start going to standard out. If you want to run node you can do this.
{
"mcpServers": {
"add": {
"command": "node",
"args": [
"/home/iwiseman/dev/projects/mcpServer/tsMcpTest/dist/app.js"
]
},
...