tangled
alpha
login
or
join now
graham.systems
/
cistern
3
fork
atom
Encrypted, ephemeral, private memos on atproto
3
fork
atom
overview
issues
pulls
pipelines
feat(mcp): implement sessions
graham.systems
4 months ago
6dd67986
de3bda94
verified
This commit was signed with the committer's
known signature
.
graham.systems
SSH Key Fingerprint:
SHA256:Fvaam8TgCBeBlr/Fo7eA6VGAIAWmzjwUqUTw5o6anWA=
+48
-9
2 changed files
expand all
collapse all
unified
split
packages
mcp
index.ts
server.ts
+47
-8
packages/mcp/index.ts
···
22
22
const server = createServer();
23
23
24
24
if (!args.http) {
25
25
-
logger.info("starting in stdio server");
25
25
+
logger.info("starting in stdio mode");
26
26
27
27
const transport = new StdioServerTransport();
28
28
await server.connect(transport);
29
29
} else {
30
30
-
logger.info("starting in streamable HTTP server");
30
30
+
logger.info("starting in streamable HTTP mode");
31
31
32
32
-
const transport = new StreamableHTTPServerTransport({
33
33
-
sessionIdGenerator: crypto.randomUUID,
34
34
-
});
32
32
+
const sessions: Map<string, StreamableHTTPServerTransport> = new Map();
35
33
36
34
Deno.serve(
37
35
{
···
42
40
},
43
41
onError(error) {
44
42
logger.error(
45
45
-
"unexpected route error: {error?.message}",
46
46
-
error as Record<string, unknown>,
43
43
+
"unexpected route error: {error}",
44
44
+
{ error },
47
45
);
48
46
49
47
return new Response(null, { status: 500 });
···
61
59
return new Response(null, { status: 404 });
62
60
}
63
61
62
62
+
const sessionId = request.headers.get("mcp-session-id");
63
63
+
let transport: StreamableHTTPServerTransport;
64
64
+
65
65
+
if (sessionId && sessions.has(sessionId)) {
66
66
+
logger.info("{method} resuming session {sessionId}", {
67
67
+
sessionId,
68
68
+
method: request.method,
69
69
+
});
70
70
+
71
71
+
transport = sessions.get(sessionId)!;
72
72
+
} else if (
73
73
+
request.method !== "POST" && !sessions.has(sessionId ?? "")
74
74
+
) {
75
75
+
logger.error("{method} has invalid session {sessionId}", {
76
76
+
sessionId,
77
77
+
method: request.method,
78
78
+
});
79
79
+
80
80
+
return Response.json({ error: "invalid or missing session" }, {
81
81
+
status: 401,
82
82
+
});
83
83
+
} else {
84
84
+
const sessionId = crypto.randomUUID();
85
85
+
86
86
+
logger.info("opening new session {sessionId}", { sessionId });
87
87
+
88
88
+
transport = new StreamableHTTPServerTransport({
89
89
+
sessionIdGenerator: () => sessionId as string,
90
90
+
});
91
91
+
92
92
+
transport.onclose = () => {
93
93
+
logger.info("session {sessionId} closed, cleaning up", {
94
94
+
sessionId,
95
95
+
});
96
96
+
sessions.delete(sessionId);
97
97
+
};
98
98
+
99
99
+
sessions.set(sessionId, transport);
100
100
+
101
101
+
await server.connect(transport);
102
102
+
}
103
103
+
64
104
const { req, res } = toReqRes(request);
65
105
66
66
-
await server.connect(transport);
67
106
await transport.handleRequest(req, res);
68
107
69
108
return await toFetchResponse(res);
+1
-1
packages/mcp/server.ts
···
14
14
{
15
15
title: "Addition Tool",
16
16
description: "Add two numbers",
17
17
-
inputSchema: { a: z.string(), b: z.string() },
17
17
+
inputSchema: { a: z.number(), b: z.number() },
18
18
outputSchema: { result: z.number() },
19
19
},
20
20
({ a, b }) => {