Skip to content

Commit 5d14409

Browse files
authored
Merge branch 'main' into feature/add-plane-mcp
2 parents 787d78d + 23e3595 commit 5d14409

12 files changed

Lines changed: 332 additions & 39 deletions

File tree

README.md

Lines changed: 33 additions & 9 deletions
Large diffs are not rendered by default.

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/everything/README.md

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ The server sends random-leveled log messages every 15 seconds, e.g.:
126126
}
127127
```
128128

129-
## Usage with Claude Desktop
129+
## Usage with Claude Desktop (uses [stdio Transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#stdio))
130130

131131
Add to your `claude_desktop_config.json`:
132132

@@ -172,3 +172,46 @@ Optionally, you can add it to a file called `.vscode/mcp.json` in your workspace
172172
}
173173
}
174174
```
175+
176+
## Running from source with [HTTP+SSE Transport](https://modelcontextprotocol.io/specification/2024-11-05/basic/transports#http-with-sse) (deprecated as of [2025-03-26](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports))
177+
178+
```shell
179+
cd src/everything
180+
npm install
181+
npm run start:sse
182+
```
183+
184+
## Run from source with [Streamable HTTP Transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http)
185+
186+
```shell
187+
cd src/everything
188+
npm install
189+
npm run start:streamableHttp
190+
```
191+
192+
## Running as an installed package
193+
### Install
194+
```shell
195+
npm install -g @modelcontextprotocol/server-everything@latest
196+
````
197+
198+
### Run the default (stdio) server
199+
```shell
200+
npx @modelcontextprotocol/server-everything
201+
```
202+
203+
### Or specify stdio explicitly
204+
```shell
205+
npx @modelcontextprotocol/server-everything stdio
206+
```
207+
208+
### Run the SSE server
209+
```shell
210+
npx @modelcontextprotocol/server-everything sse
211+
```
212+
213+
### Run the streamable HTTP server
214+
```shell
215+
npx @modelcontextprotocol/server-everything streamableHttp
216+
```
217+

src/everything/index.ts

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,37 @@
11
#!/usr/bin/env node
22

3-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4-
import { createServer } from "./everything.js";
3+
// Parse command line arguments first
4+
const args = process.argv.slice(2);
5+
const scriptName = args[0] || 'stdio';
56

6-
async function main() {
7-
const transport = new StdioServerTransport();
8-
const { server, cleanup } = createServer();
9-
10-
await server.connect(transport);
11-
12-
// Cleanup on exit
13-
process.on("SIGINT", async () => {
14-
await cleanup();
15-
await server.close();
16-
process.exit(0);
17-
});
7+
async function run() {
8+
try {
9+
// Dynamically import only the requested module to prevent all modules from initializing
10+
switch (scriptName) {
11+
case 'stdio':
12+
// Import and run the default server
13+
await import('./stdio.js');
14+
break;
15+
case 'sse':
16+
// Import and run the SSE server
17+
await import('./sse.js');
18+
break;
19+
case 'streamableHttp':
20+
// Import and run the streamable HTTP server
21+
await import('./streamableHttp.js');
22+
break;
23+
default:
24+
console.error(`Unknown script: ${scriptName}`);
25+
console.log('Available scripts:');
26+
console.log('- stdio');
27+
console.log('- sse');
28+
console.log('- streamableHttp');
29+
process.exit(1);
30+
}
31+
} catch (error) {
32+
console.error('Error running script:', error);
33+
process.exit(1);
34+
}
1835
}
1936

20-
main().catch((error) => {
21-
console.error("Server error:", error);
22-
process.exit(1);
23-
});
37+
run();

src/everything/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@
1818
"prepare": "npm run build",
1919
"watch": "tsc --watch",
2020
"start": "node dist/index.js",
21-
"start:sse": "node dist/sse.js"
21+
"start:sse": "node dist/sse.js",
22+
"start:streamableHttp": "node dist/streamableHttp.js"
2223
},
2324
"dependencies": {
24-
"@modelcontextprotocol/sdk": "^1.9.0",
25+
"@modelcontextprotocol/sdk": "^1.11.0",
2526
"express": "^4.21.1",
2627
"zod": "^3.23.8",
2728
"zod-to-json-schema": "^3.23.5"

src/everything/sse.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
22
import express from "express";
33
import { createServer } from "./everything.js";
44

5+
console.error('Starting SSE server...');
6+
57
const app = express();
68

79
const { server, cleanup } = createServer();

src/everything/stdio.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/usr/bin/env node
2+
3+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4+
import { createServer } from "./everything.js";
5+
6+
console.error('Starting default (STDIO) server...');
7+
8+
async function main() {
9+
const transport = new StdioServerTransport();
10+
const {server, cleanup} = createServer();
11+
12+
await server.connect(transport);
13+
14+
// Cleanup on exit
15+
process.on("SIGINT", async () => {
16+
await cleanup();
17+
await server.close();
18+
process.exit(0);
19+
});
20+
}
21+
22+
main().catch((error) => {
23+
console.error("Server error:", error);
24+
process.exit(1);
25+
});
26+

src/everything/streamableHttp.ts

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
2+
import { InMemoryEventStore } from '@modelcontextprotocol/sdk/examples/shared/inMemoryEventStore.js';
3+
import express, { Request, Response } from "express";
4+
import { createServer } from "./everything.js";
5+
import { randomUUID } from 'node:crypto';
6+
7+
console.error('Starting Streamable HTTP server...');
8+
9+
const app = express();
10+
11+
const { server, cleanup } = createServer();
12+
13+
const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};
14+
15+
app.post('/mcp', async (req: Request, res: Response) => {
16+
console.log('Received MCP POST request');
17+
try {
18+
// Check for existing session ID
19+
const sessionId = req.headers['mcp-session-id'] as string | undefined;
20+
let transport: StreamableHTTPServerTransport;
21+
22+
if (sessionId && transports[sessionId]) {
23+
// Reuse existing transport
24+
transport = transports[sessionId];
25+
} else if (!sessionId) {
26+
// New initialization request
27+
const eventStore = new InMemoryEventStore();
28+
transport = new StreamableHTTPServerTransport({
29+
sessionIdGenerator: () => randomUUID(),
30+
eventStore, // Enable resumability
31+
onsessioninitialized: (sessionId) => {
32+
// Store the transport by session ID when session is initialized
33+
// This avoids race conditions where requests might come in before the session is stored
34+
console.log(`Session initialized with ID: ${sessionId}`);
35+
transports[sessionId] = transport;
36+
}
37+
});
38+
39+
// Set up onclose handler to clean up transport when closed
40+
transport.onclose = () => {
41+
const sid = transport.sessionId;
42+
if (sid && transports[sid]) {
43+
console.log(`Transport closed for session ${sid}, removing from transports map`);
44+
delete transports[sid];
45+
}
46+
};
47+
48+
// Connect the transport to the MCP server BEFORE handling the request
49+
// so responses can flow back through the same transport
50+
await server.connect(transport);
51+
52+
await transport.handleRequest(req, res);
53+
return; // Already handled
54+
} else {
55+
// Invalid request - no session ID or not initialization request
56+
res.status(400).json({
57+
jsonrpc: '2.0',
58+
error: {
59+
code: -32000,
60+
message: 'Bad Request: No valid session ID provided',
61+
},
62+
id: req?.body?.id,
63+
});
64+
return;
65+
}
66+
67+
// Handle the request with existing transport - no need to reconnect
68+
// The existing transport is already connected to the server
69+
await transport.handleRequest(req, res);
70+
} catch (error) {
71+
console.error('Error handling MCP request:', error);
72+
if (!res.headersSent) {
73+
res.status(500).json({
74+
jsonrpc: '2.0',
75+
error: {
76+
code: -32603,
77+
message: 'Internal server error',
78+
},
79+
id: req?.body?.id,
80+
});
81+
return;
82+
}
83+
}
84+
});
85+
86+
// Handle GET requests for SSE streams (using built-in support from StreamableHTTP)
87+
app.get('/mcp', async (req: Request, res: Response) => {
88+
console.log('Received MCP GET request');
89+
const sessionId = req.headers['mcp-session-id'] as string | undefined;
90+
if (!sessionId || !transports[sessionId]) {
91+
res.status(400).json({
92+
jsonrpc: '2.0',
93+
error: {
94+
code: -32000,
95+
message: 'Bad Request: No valid session ID provided',
96+
},
97+
id: req?.body?.id,
98+
});
99+
return;
100+
}
101+
102+
// Check for Last-Event-ID header for resumability
103+
const lastEventId = req.headers['last-event-id'] as string | undefined;
104+
if (lastEventId) {
105+
console.log(`Client reconnecting with Last-Event-ID: ${lastEventId}`);
106+
} else {
107+
console.log(`Establishing new SSE stream for session ${sessionId}`);
108+
}
109+
110+
const transport = transports[sessionId];
111+
await transport.handleRequest(req, res);
112+
});
113+
114+
// Handle DELETE requests for session termination (according to MCP spec)
115+
app.delete('/mcp', async (req: Request, res: Response) => {
116+
const sessionId = req.headers['mcp-session-id'] as string | undefined;
117+
if (!sessionId || !transports[sessionId]) {
118+
res.status(400).json({
119+
jsonrpc: '2.0',
120+
error: {
121+
code: -32000,
122+
message: 'Bad Request: No valid session ID provided',
123+
},
124+
id: req?.body?.id,
125+
});
126+
return;
127+
}
128+
129+
console.log(`Received session termination request for session ${sessionId}`);
130+
131+
try {
132+
const transport = transports[sessionId];
133+
await transport.handleRequest(req, res);
134+
} catch (error) {
135+
console.error('Error handling session termination:', error);
136+
if (!res.headersSent) {
137+
res.status(500).json({
138+
jsonrpc: '2.0',
139+
error: {
140+
code: -32603,
141+
message: 'Error handling session termination',
142+
},
143+
id: req?.body?.id,
144+
});
145+
return;
146+
}
147+
}
148+
});
149+
150+
// Start the server
151+
const PORT = process.env.PORT || 3001;
152+
app.listen(PORT, () => {
153+
console.log(`MCP Streamable HTTP Server listening on port ${PORT}`);
154+
});
155+
156+
// Handle server shutdown
157+
process.on('SIGINT', async () => {
158+
console.log('Shutting down server...');
159+
160+
// Close all active transports to properly clean up resources
161+
for (const sessionId in transports) {
162+
try {
163+
console.log(`Closing transport for session ${sessionId}`);
164+
await transports[sessionId].close();
165+
delete transports[sessionId];
166+
} catch (error) {
167+
console.error(`Error closing transport for session ${sessionId}:`, error);
168+
}
169+
}
170+
await cleanup();
171+
await server.close();
172+
console.log('Server shutdown complete');
173+
process.exit(0);
174+
});

src/gdrive/README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,10 @@ Once authenticated, you can use the server in your app's server configuration:
8484
"args": [
8585
"-y",
8686
"@modelcontextprotocol/server-gdrive"
87-
]
87+
],
88+
"env": {
89+
"GDRIVE_CREDENTIALS_PATH": "/path/to/.gdrive-server-credentials.json"
90+
}
8891
}
8992
}
9093
}

src/puppeteer/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
FROM node:22-bookworm-slim
22

3-
ENV DEBIAN_FRONTEND noninteractive
3+
ENV DEBIAN_FRONTEND=noninteractive
44

55
# for arm64 support we need to install chromium provided by debian
66
# npm ERR! The chromium binary is not available for arm64.
77
# https://github.com/puppeteer/puppeteer/issues/7740
88

9-
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true
9+
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
1010
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
1111

1212
RUN apt-get update && \

0 commit comments

Comments
 (0)