-
Notifications
You must be signed in to change notification settings - Fork 134
Expand file tree
/
Copy pathfake-mcp-hanging-server.js
More file actions
146 lines (126 loc) · 3.1 KB
/
Copy pathfake-mcp-hanging-server.js
File metadata and controls
146 lines (126 loc) · 3.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// Fake MCP server for manual QA testing of hang/interrupt/timeout behavior.
//
// Exposes two tools:
// - sleep_forever: never responds (simulates a stuck MCP call)
// - ping: returns "pong" (verifies server is alive)
//
// Usage: Add to Settings → MCP with command:
// node ./scripts/fake-mcp-hanging-server.js
const readline = require("readline");
/**
* Write a JSON-RPC message to stdout.
*
* NOTE: MCP stdio transport uses newline-delimited JSON.
*/
function send(message) {
process.stdout.write(`${JSON.stringify(message)}\n`);
}
const SERVER_INFO = { name: "mux-fake-hanging-mcp", version: "0.0.0" };
const TOOLS = [
{
name: "sleep_forever",
description:
"Intentionally never responds. Use to test hang/interrupt/timeout behavior.",
inputSchema: {
type: "object",
properties: {},
additionalProperties: false,
},
},
{
name: "ping",
description: "Returns pong. Use to verify server connectivity.",
inputSchema: {
type: "object",
properties: {},
additionalProperties: false,
},
},
];
const rl = readline.createInterface({ input: process.stdin, crlfDelay: Infinity });
rl.on("line", (line) => {
const trimmed = line.trim();
if (trimmed.length === 0) return;
let message;
try {
message = JSON.parse(trimmed);
} catch {
return;
}
if (message?.jsonrpc !== "2.0") return;
// Notifications have no id; ignore.
if (message.id === undefined) {
return;
}
const id = message.id;
try {
switch (message.method) {
case "initialize": {
const protocolVersion = message.params?.protocolVersion ?? "2024-11-05";
send({
jsonrpc: "2.0",
id,
result: {
protocolVersion,
capabilities: { tools: {} },
serverInfo: SERVER_INFO,
},
});
return;
}
case "tools/list": {
send({ jsonrpc: "2.0", id, result: { tools: TOOLS } });
return;
}
case "tools/call": {
const toolName = message.params?.name;
if (toolName === "sleep_forever") {
// Intentionally never responds — simulates a wedged MCP server.
return;
}
if (toolName === "ping") {
send({
jsonrpc: "2.0",
id,
result: {
content: [{ type: "text", text: "pong" }],
},
});
return;
}
send({
jsonrpc: "2.0",
id,
error: { code: -32601, message: `Unknown tool: ${toolName}` },
});
return;
}
default: {
send({
jsonrpc: "2.0",
id,
error: { code: -32601, message: `Method not found: ${message.method}` },
});
return;
}
}
} catch (error) {
send({
jsonrpc: "2.0",
id,
error: {
code: -32603,
message: error instanceof Error ? error.message : String(error),
},
});
}
});
rl.on("close", () => {
process.exit(0);
});
process.on("SIGTERM", () => {
rl.close();
});
process.on("SIGINT", () => {
rl.close();
});