Skip to content

Commit 07300e8

Browse files
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 <[email protected]>
1 parent a02624c commit 07300e8

File tree

4 files changed

+83
-13
lines changed

4 files changed

+83
-13
lines changed

dev-with-mitm.sh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/bin/bash
2+
# Easy script to run MCP server with mitmproxy intercepting client traffic
3+
4+
echo "🚀 Starting MCP server with mitmproxy..."
5+
echo ""
6+
echo "This will:"
7+
echo " 1. Start your MCP server on port 3232"
8+
echo " 2. Start mitmproxy reverse proxy on port 8080"
9+
echo " 3. Intercept all client → server traffic"
10+
echo ""
11+
echo "📡 Connect your MCP client to: http://localhost:8080/mcp"
12+
echo "🔍 View traffic in mitmproxy TUI"
13+
echo ""
14+
echo "Press Ctrl+C to stop both processes"
15+
echo ""
16+
17+
# Trap Ctrl+C to kill both processes
18+
trap 'kill 0' EXIT
19+
20+
# Start the MCP server in background
21+
npm run dev &
22+
SERVER_PID=$!
23+
24+
# Give server time to start
25+
sleep 3
26+
27+
# Start mitmproxy in reverse proxy mode (foreground so we see the TUI)
28+
mitmproxy --mode reverse:http://localhost:3232 --listen-port 8080
29+
30+
# This line won't be reached until mitmproxy exits
31+
wait

dev-with-mitmweb.sh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/bin/bash
2+
# Easy script to run MCP server with mitmproxy web interface
3+
4+
echo "🚀 Starting MCP server with mitmproxy web UI..."
5+
echo ""
6+
echo "This will:"
7+
echo " 1. Start your MCP server on port 3232"
8+
echo " 2. Start mitmproxy with web UI on port 8081"
9+
echo " 3. Proxy client traffic through port 8080"
10+
echo ""
11+
echo "📡 Connect your MCP client to: http://localhost:8080/mcp"
12+
echo "🌐 View traffic in browser at: http://localhost:8081"
13+
echo ""
14+
echo "Press Ctrl+C to stop both processes"
15+
echo ""
16+
17+
# Trap Ctrl+C to kill both processes
18+
trap 'kill 0' EXIT
19+
20+
# Start the MCP server in background
21+
npm run dev &
22+
SERVER_PID=$!
23+
24+
# Give server time to start
25+
sleep 3
26+
27+
# Start mitmweb in reverse proxy mode
28+
mitmweb --mode reverse:http://localhost:3232 --listen-port 8080 --web-port 8081
29+
30+
# This line won't be reached until mitmweb exits
31+
wait

src/modules/mcp/handlers/shttp.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export async function handleStreamableHTTP(req: Request, res: Response) {
6767
});
6868
res.status(401).json({
6969
"jsonrpc": "2.0",
70+
"id": null,
7071
"error": {
7172
"code": -32002,
7273
"message": "User ID required"
@@ -87,6 +88,7 @@ export async function handleStreamableHTTP(req: Request, res: Response) {
8788
});
8889
res.status(401).json({
8990
"jsonrpc": "2.0",
91+
"id": null,
9092
"error": {
9193
"code": -32001,
9294
"message": "Session not found or access denied"
@@ -148,6 +150,7 @@ export async function handleStreamableHTTP(req: Request, res: Response) {
148150
});
149151
res.status(400).json({
150152
"jsonrpc": "2.0",
153+
"id": null,
151154
"error": {
152155
"code": -32600,
153156
"message": "Invalid request method for existing session"
@@ -167,6 +170,7 @@ export async function handleStreamableHTTP(req: Request, res: Response) {
167170
if (!res.headersSent) {
168171
res.status(500).json({
169172
"jsonrpc": "2.0",
173+
"id": null,
170174
"error": {
171175
"code": -32603,
172176
"message": "Internal error during request processing"

src/modules/mcp/services/mcp.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@ import {
1919
UnsubscribeRequestSchema,
2020
} from "@modelcontextprotocol/sdk/types.js";
2121
import { z } from "zod";
22-
import { zodToJsonSchema } from "zod-to-json-schema";
2322

24-
type ToolInput = {
25-
type: "object";
26-
properties?: Record<string, unknown>;
27-
required?: string[];
23+
type ToolInput = Tool["inputSchema"];
24+
25+
// Helper to convert Zod schema to JSON schema using Zod v4's native support
26+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
27+
const toJsonSchema = (schema: z.ZodType<any>): ToolInput => {
28+
return z.toJSONSchema(schema) as ToolInput;
2829
};
2930

3031
/* Input schemas for tools implemented in this server */
@@ -413,40 +414,40 @@ export const createMcpServer = (): McpServerWrapper => {
413414
{
414415
name: ToolName.ECHO,
415416
description: "Echoes back the input",
416-
inputSchema: zodToJsonSchema(EchoSchema) as ToolInput,
417+
inputSchema: toJsonSchema(EchoSchema),
417418
},
418419
{
419420
name: ToolName.ADD,
420421
description: "Adds two numbers",
421-
inputSchema: zodToJsonSchema(AddSchema) as ToolInput,
422+
inputSchema: toJsonSchema(AddSchema),
422423
},
423424
{
424425
name: ToolName.LONG_RUNNING_OPERATION,
425426
description:
426427
"Demonstrates a long running operation with progress updates",
427-
inputSchema: zodToJsonSchema(LongRunningOperationSchema) as ToolInput,
428+
inputSchema: toJsonSchema(LongRunningOperationSchema),
428429
},
429430
{
430431
name: ToolName.SAMPLE_LLM,
431432
description: "Samples from an LLM using MCP's sampling feature",
432-
inputSchema: zodToJsonSchema(SampleLLMSchema) as ToolInput,
433+
inputSchema: toJsonSchema(SampleLLMSchema),
433434
},
434435
{
435436
name: ToolName.GET_TINY_IMAGE,
436437
description: "Returns the MCP_TINY_IMAGE",
437-
inputSchema: zodToJsonSchema(GetTinyImageSchema) as ToolInput,
438+
inputSchema: toJsonSchema(GetTinyImageSchema),
438439
},
439440
{
440441
name: ToolName.ANNOTATED_MESSAGE,
441442
description:
442443
"Demonstrates how annotations can be used to provide metadata about content",
443-
inputSchema: zodToJsonSchema(AnnotatedMessageSchema) as ToolInput,
444+
inputSchema: toJsonSchema(AnnotatedMessageSchema),
444445
},
445446
{
446447
name: ToolName.GET_RESOURCE_REFERENCE,
447448
description:
448449
"Returns a resource reference that can be used by MCP clients",
449-
inputSchema: zodToJsonSchema(GetResourceReferenceSchema) as ToolInput,
450+
inputSchema: toJsonSchema(GetResourceReferenceSchema),
450451
},
451452
{
452453
name: ToolName.ELICIT_INPUTS,
@@ -524,9 +525,12 @@ export const createMcpServer = (): McpServerWrapper => {
524525
ToolName.SAMPLE_LLM,
525526
maxTokens
526527
);
528+
const contentArray = Array.isArray(result.content) ? result.content : [result.content];
529+
const firstContent = contentArray[0];
530+
const textContent = firstContent && "text" in firstContent ? firstContent.text : JSON.stringify(result.content);
527531
return {
528532
content: [
529-
{ type: "text", text: `LLM sampling result: ${result.content.text}` },
533+
{ type: "text", text: `LLM sampling result: ${textContent}` },
530534
],
531535
};
532536
}

0 commit comments

Comments
 (0)