Merge pull request #23 from modelcontextprotocol/jerome/dev-scripts-and-fixes
Add dev scripts for mitmproxy debugging and fix JSON-RPC error responses
This commit is contained in:
commit
d87c1262cc
6 changed files with 848 additions and 1211 deletions
31
dev-with-mitm.sh
Executable file
31
dev-with-mitm.sh
Executable file
|
|
@ -0,0 +1,31 @@
|
|||
#!/bin/bash
|
||||
# Easy script to run MCP server with mitmproxy intercepting client traffic
|
||||
|
||||
echo "🚀 Starting MCP server with mitmproxy..."
|
||||
echo ""
|
||||
echo "This will:"
|
||||
echo " 1. Start your MCP server on port 3232"
|
||||
echo " 2. Start mitmproxy reverse proxy on port 8080"
|
||||
echo " 3. Intercept all client → server traffic"
|
||||
echo ""
|
||||
echo "📡 Connect your MCP client to: http://localhost:8080/mcp"
|
||||
echo "🔍 View traffic in mitmproxy TUI"
|
||||
echo ""
|
||||
echo "Press Ctrl+C to stop both processes"
|
||||
echo ""
|
||||
|
||||
# Trap Ctrl+C to kill both processes
|
||||
trap 'kill 0' EXIT
|
||||
|
||||
# Start the MCP server in background
|
||||
npm run dev &
|
||||
SERVER_PID=$!
|
||||
|
||||
# Give server time to start
|
||||
sleep 3
|
||||
|
||||
# Start mitmproxy in reverse proxy mode (foreground so we see the TUI)
|
||||
mitmproxy --mode reverse:http://localhost:3232 --listen-port 8080
|
||||
|
||||
# This line won't be reached until mitmproxy exits
|
||||
wait
|
||||
31
dev-with-mitmweb.sh
Executable file
31
dev-with-mitmweb.sh
Executable file
|
|
@ -0,0 +1,31 @@
|
|||
#!/bin/bash
|
||||
# Easy script to run MCP server with mitmproxy web interface
|
||||
|
||||
echo "🚀 Starting MCP server with mitmproxy web UI..."
|
||||
echo ""
|
||||
echo "This will:"
|
||||
echo " 1. Start your MCP server on port 3232"
|
||||
echo " 2. Start mitmproxy with web UI on port 8081"
|
||||
echo " 3. Proxy client traffic through port 8080"
|
||||
echo ""
|
||||
echo "📡 Connect your MCP client to: http://localhost:8080/mcp"
|
||||
echo "🌐 View traffic in browser at: http://localhost:8081"
|
||||
echo ""
|
||||
echo "Press Ctrl+C to stop both processes"
|
||||
echo ""
|
||||
|
||||
# Trap Ctrl+C to kill both processes
|
||||
trap 'kill 0' EXIT
|
||||
|
||||
# Start the MCP server in background
|
||||
npm run dev &
|
||||
SERVER_PID=$!
|
||||
|
||||
# Give server time to start
|
||||
sleep 3
|
||||
|
||||
# Start mitmweb in reverse proxy mode
|
||||
mitmweb --mode reverse:http://localhost:3232 --listen-port 8080 --web-port 8081
|
||||
|
||||
# This line won't be reached until mitmweb exits
|
||||
wait
|
||||
1991
package-lock.json
generated
1991
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -22,7 +22,7 @@
|
|||
"clean": "rm -rf dist"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.15.1",
|
||||
"@modelcontextprotocol/sdk": "^1.24.2",
|
||||
"@redis/client": "^1.6.0",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.7",
|
||||
|
|
@ -53,4 +53,4 @@
|
|||
},
|
||||
"author": "",
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ export async function handleStreamableHTTP(req: Request, res: Response) {
|
|||
});
|
||||
res.status(401).json({
|
||||
"jsonrpc": "2.0",
|
||||
"id": null,
|
||||
"error": {
|
||||
"code": -32002,
|
||||
"message": "User ID required"
|
||||
|
|
@ -87,6 +88,7 @@ export async function handleStreamableHTTP(req: Request, res: Response) {
|
|||
});
|
||||
res.status(401).json({
|
||||
"jsonrpc": "2.0",
|
||||
"id": null,
|
||||
"error": {
|
||||
"code": -32001,
|
||||
"message": "Session not found or access denied"
|
||||
|
|
@ -148,6 +150,7 @@ export async function handleStreamableHTTP(req: Request, res: Response) {
|
|||
});
|
||||
res.status(400).json({
|
||||
"jsonrpc": "2.0",
|
||||
"id": null,
|
||||
"error": {
|
||||
"code": -32600,
|
||||
"message": "Invalid request method for existing session"
|
||||
|
|
@ -167,6 +170,7 @@ export async function handleStreamableHTTP(req: Request, res: Response) {
|
|||
if (!res.headersSent) {
|
||||
res.status(500).json({
|
||||
"jsonrpc": "2.0",
|
||||
"id": null,
|
||||
"error": {
|
||||
"code": -32603,
|
||||
"message": "Internal error during request processing"
|
||||
|
|
|
|||
|
|
@ -19,12 +19,13 @@ import {
|
|||
UnsubscribeRequestSchema,
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
import { z } from "zod";
|
||||
import { zodToJsonSchema } from "zod-to-json-schema";
|
||||
|
||||
type ToolInput = {
|
||||
type: "object";
|
||||
properties?: Record<string, unknown>;
|
||||
required?: string[];
|
||||
type ToolInput = Tool["inputSchema"];
|
||||
|
||||
// Helper to convert Zod schema to JSON schema using Zod v4's native support
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const toJsonSchema = (schema: z.ZodType<any>): ToolInput => {
|
||||
return z.toJSONSchema(schema) as ToolInput;
|
||||
};
|
||||
|
||||
/* Input schemas for tools implemented in this server */
|
||||
|
|
@ -413,40 +414,40 @@ export const createMcpServer = (): McpServerWrapper => {
|
|||
{
|
||||
name: ToolName.ECHO,
|
||||
description: "Echoes back the input",
|
||||
inputSchema: zodToJsonSchema(EchoSchema) as ToolInput,
|
||||
inputSchema: toJsonSchema(EchoSchema),
|
||||
},
|
||||
{
|
||||
name: ToolName.ADD,
|
||||
description: "Adds two numbers",
|
||||
inputSchema: zodToJsonSchema(AddSchema) as ToolInput,
|
||||
inputSchema: toJsonSchema(AddSchema),
|
||||
},
|
||||
{
|
||||
name: ToolName.LONG_RUNNING_OPERATION,
|
||||
description:
|
||||
"Demonstrates a long running operation with progress updates",
|
||||
inputSchema: zodToJsonSchema(LongRunningOperationSchema) as ToolInput,
|
||||
inputSchema: toJsonSchema(LongRunningOperationSchema),
|
||||
},
|
||||
{
|
||||
name: ToolName.SAMPLE_LLM,
|
||||
description: "Samples from an LLM using MCP's sampling feature",
|
||||
inputSchema: zodToJsonSchema(SampleLLMSchema) as ToolInput,
|
||||
inputSchema: toJsonSchema(SampleLLMSchema),
|
||||
},
|
||||
{
|
||||
name: ToolName.GET_TINY_IMAGE,
|
||||
description: "Returns the MCP_TINY_IMAGE",
|
||||
inputSchema: zodToJsonSchema(GetTinyImageSchema) as ToolInput,
|
||||
inputSchema: toJsonSchema(GetTinyImageSchema),
|
||||
},
|
||||
{
|
||||
name: ToolName.ANNOTATED_MESSAGE,
|
||||
description:
|
||||
"Demonstrates how annotations can be used to provide metadata about content",
|
||||
inputSchema: zodToJsonSchema(AnnotatedMessageSchema) as ToolInput,
|
||||
inputSchema: toJsonSchema(AnnotatedMessageSchema),
|
||||
},
|
||||
{
|
||||
name: ToolName.GET_RESOURCE_REFERENCE,
|
||||
description:
|
||||
"Returns a resource reference that can be used by MCP clients",
|
||||
inputSchema: zodToJsonSchema(GetResourceReferenceSchema) as ToolInput,
|
||||
inputSchema: toJsonSchema(GetResourceReferenceSchema),
|
||||
},
|
||||
{
|
||||
name: ToolName.ELICIT_INPUTS,
|
||||
|
|
@ -524,9 +525,12 @@ export const createMcpServer = (): McpServerWrapper => {
|
|||
ToolName.SAMPLE_LLM,
|
||||
maxTokens
|
||||
);
|
||||
const contentArray = Array.isArray(result.content) ? result.content : [result.content];
|
||||
const firstContent = contentArray[0];
|
||||
const textContent = firstContent && "text" in firstContent ? firstContent.text : JSON.stringify(result.content);
|
||||
return {
|
||||
content: [
|
||||
{ type: "text", text: `LLM sampling result: ${result.content.text}` },
|
||||
{ type: "text", text: `LLM sampling result: ${textContent}` },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue