Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions dev-with-mitm.sh
Original file line number Diff line number Diff line change
@@ -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 changes: 31 additions & 0 deletions dev-with-mitmweb.sh
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions src/modules/mcp/handlers/shttp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -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"
Expand All @@ -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"
Expand Down
30 changes: 17 additions & 13 deletions src/modules/mcp/services/mcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@
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;

Check failure on line 28 in src/modules/mcp/services/mcp.ts

View workflow job for this annotation

GitHub Actions / build

Property 'toJSONSchema' does not exist on type 'typeof import("/home/runner/work/example-remote-server/example-remote-server/node_modules/zod/v3/external")'.
};

/* Input schemas for tools implemented in this server */
Expand Down Expand Up @@ -413,40 +414,40 @@
{
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,
Expand Down Expand Up @@ -524,9 +525,12 @@
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}` },
],
};
}
Expand Down
Loading