Add dev scripts for mitmproxy debugging and fix JSON-RPC error responses
- Add dev-with-mitm.sh for TUI-based traffic inspection - Add dev-with-mitmweb.sh for web UI-based traffic inspection - Fix missing 'id' field in JSON-RPC error responses (required by spec) - Migrate from zod-to-json-schema to Zod v4's native z.toJSONSchema() The mitmproxy scripts help developers debug MCP traffic by running the server through a reverse proxy that intercepts all client requests. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
a02624c8b5
commit
07300e8453
4 changed files with 83 additions and 13 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
|
||||||
|
|
@ -67,6 +67,7 @@ export async function handleStreamableHTTP(req: Request, res: Response) {
|
||||||
});
|
});
|
||||||
res.status(401).json({
|
res.status(401).json({
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
|
"id": null,
|
||||||
"error": {
|
"error": {
|
||||||
"code": -32002,
|
"code": -32002,
|
||||||
"message": "User ID required"
|
"message": "User ID required"
|
||||||
|
|
@ -87,6 +88,7 @@ export async function handleStreamableHTTP(req: Request, res: Response) {
|
||||||
});
|
});
|
||||||
res.status(401).json({
|
res.status(401).json({
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
|
"id": null,
|
||||||
"error": {
|
"error": {
|
||||||
"code": -32001,
|
"code": -32001,
|
||||||
"message": "Session not found or access denied"
|
"message": "Session not found or access denied"
|
||||||
|
|
@ -148,6 +150,7 @@ export async function handleStreamableHTTP(req: Request, res: Response) {
|
||||||
});
|
});
|
||||||
res.status(400).json({
|
res.status(400).json({
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
|
"id": null,
|
||||||
"error": {
|
"error": {
|
||||||
"code": -32600,
|
"code": -32600,
|
||||||
"message": "Invalid request method for existing session"
|
"message": "Invalid request method for existing session"
|
||||||
|
|
@ -167,6 +170,7 @@ export async function handleStreamableHTTP(req: Request, res: Response) {
|
||||||
if (!res.headersSent) {
|
if (!res.headersSent) {
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
|
"id": null,
|
||||||
"error": {
|
"error": {
|
||||||
"code": -32603,
|
"code": -32603,
|
||||||
"message": "Internal error during request processing"
|
"message": "Internal error during request processing"
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,13 @@ import {
|
||||||
UnsubscribeRequestSchema,
|
UnsubscribeRequestSchema,
|
||||||
} from "@modelcontextprotocol/sdk/types.js";
|
} from "@modelcontextprotocol/sdk/types.js";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
||||||
|
|
||||||
type ToolInput = {
|
type ToolInput = Tool["inputSchema"];
|
||||||
type: "object";
|
|
||||||
properties?: Record<string, unknown>;
|
// Helper to convert Zod schema to JSON schema using Zod v4's native support
|
||||||
required?: string[];
|
// 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 */
|
/* Input schemas for tools implemented in this server */
|
||||||
|
|
@ -413,40 +414,40 @@ export const createMcpServer = (): McpServerWrapper => {
|
||||||
{
|
{
|
||||||
name: ToolName.ECHO,
|
name: ToolName.ECHO,
|
||||||
description: "Echoes back the input",
|
description: "Echoes back the input",
|
||||||
inputSchema: zodToJsonSchema(EchoSchema) as ToolInput,
|
inputSchema: toJsonSchema(EchoSchema),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ToolName.ADD,
|
name: ToolName.ADD,
|
||||||
description: "Adds two numbers",
|
description: "Adds two numbers",
|
||||||
inputSchema: zodToJsonSchema(AddSchema) as ToolInput,
|
inputSchema: toJsonSchema(AddSchema),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ToolName.LONG_RUNNING_OPERATION,
|
name: ToolName.LONG_RUNNING_OPERATION,
|
||||||
description:
|
description:
|
||||||
"Demonstrates a long running operation with progress updates",
|
"Demonstrates a long running operation with progress updates",
|
||||||
inputSchema: zodToJsonSchema(LongRunningOperationSchema) as ToolInput,
|
inputSchema: toJsonSchema(LongRunningOperationSchema),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ToolName.SAMPLE_LLM,
|
name: ToolName.SAMPLE_LLM,
|
||||||
description: "Samples from an LLM using MCP's sampling feature",
|
description: "Samples from an LLM using MCP's sampling feature",
|
||||||
inputSchema: zodToJsonSchema(SampleLLMSchema) as ToolInput,
|
inputSchema: toJsonSchema(SampleLLMSchema),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ToolName.GET_TINY_IMAGE,
|
name: ToolName.GET_TINY_IMAGE,
|
||||||
description: "Returns the MCP_TINY_IMAGE",
|
description: "Returns the MCP_TINY_IMAGE",
|
||||||
inputSchema: zodToJsonSchema(GetTinyImageSchema) as ToolInput,
|
inputSchema: toJsonSchema(GetTinyImageSchema),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ToolName.ANNOTATED_MESSAGE,
|
name: ToolName.ANNOTATED_MESSAGE,
|
||||||
description:
|
description:
|
||||||
"Demonstrates how annotations can be used to provide metadata about content",
|
"Demonstrates how annotations can be used to provide metadata about content",
|
||||||
inputSchema: zodToJsonSchema(AnnotatedMessageSchema) as ToolInput,
|
inputSchema: toJsonSchema(AnnotatedMessageSchema),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ToolName.GET_RESOURCE_REFERENCE,
|
name: ToolName.GET_RESOURCE_REFERENCE,
|
||||||
description:
|
description:
|
||||||
"Returns a resource reference that can be used by MCP clients",
|
"Returns a resource reference that can be used by MCP clients",
|
||||||
inputSchema: zodToJsonSchema(GetResourceReferenceSchema) as ToolInput,
|
inputSchema: toJsonSchema(GetResourceReferenceSchema),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: ToolName.ELICIT_INPUTS,
|
name: ToolName.ELICIT_INPUTS,
|
||||||
|
|
@ -524,9 +525,12 @@ export const createMcpServer = (): McpServerWrapper => {
|
||||||
ToolName.SAMPLE_LLM,
|
ToolName.SAMPLE_LLM,
|
||||||
maxTokens
|
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 {
|
return {
|
||||||
content: [
|
content: [
|
||||||
{ type: "text", text: `LLM sampling result: ${result.content.text}` },
|
{ type: "text", text: `LLM sampling result: ${textContent}` },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue