@modelcontextprotocol/sdk 1.9.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +284 -28
- package/dist/cjs/client/index.d.ts.map +1 -1
- package/dist/cjs/client/index.js +5 -0
- package/dist/cjs/client/index.js.map +1 -1
- package/dist/cjs/client/streamableHttp.d.ts +124 -0
- package/dist/cjs/client/streamableHttp.d.ts.map +1 -0
- package/dist/cjs/client/streamableHttp.js +353 -0
- package/dist/cjs/client/streamableHttp.js.map +1 -0
- package/dist/cjs/examples/client/simpleStreamableHttp.d.ts +2 -0
- package/dist/cjs/examples/client/simpleStreamableHttp.d.ts.map +1 -0
- package/dist/cjs/examples/client/simpleStreamableHttp.js +448 -0
- package/dist/cjs/examples/client/simpleStreamableHttp.js.map +1 -0
- package/dist/cjs/examples/client/streamableHttpWithSseFallbackClient.d.ts +2 -0
- package/dist/cjs/examples/client/streamableHttpWithSseFallbackClient.d.ts.map +1 -0
- package/dist/cjs/examples/client/streamableHttpWithSseFallbackClient.js +168 -0
- package/dist/cjs/examples/client/streamableHttpWithSseFallbackClient.js.map +1 -0
- package/dist/cjs/examples/server/jsonResponseStreamableHttp.d.ts +2 -0
- package/dist/cjs/examples/server/jsonResponseStreamableHttp.d.ts.map +1 -0
- package/dist/cjs/examples/server/jsonResponseStreamableHttp.js +139 -0
- package/dist/cjs/examples/server/jsonResponseStreamableHttp.js.map +1 -0
- package/dist/cjs/examples/server/simpleSseServer.d.ts +2 -0
- package/dist/cjs/examples/server/simpleSseServer.d.ts.map +1 -0
- package/dist/cjs/examples/server/simpleSseServer.js +148 -0
- package/dist/cjs/examples/server/simpleSseServer.js.map +1 -0
- package/dist/cjs/examples/server/simpleStreamableHttp.d.ts +2 -0
- package/dist/cjs/examples/server/simpleStreamableHttp.d.ts.map +1 -0
- package/dist/cjs/examples/server/simpleStreamableHttp.js +250 -0
- package/dist/cjs/examples/server/simpleStreamableHttp.js.map +1 -0
- package/dist/cjs/examples/server/sseAndStreamableHttpCompatibleServer.d.ts +2 -0
- package/dist/cjs/examples/server/sseAndStreamableHttpCompatibleServer.d.ts.map +1 -0
- package/dist/cjs/examples/server/sseAndStreamableHttpCompatibleServer.js +229 -0
- package/dist/cjs/examples/server/sseAndStreamableHttpCompatibleServer.js.map +1 -0
- package/dist/cjs/examples/server/standaloneSseWithGetStreamableHttp.d.ts +2 -0
- package/dist/cjs/examples/server/standaloneSseWithGetStreamableHttp.d.ts.map +1 -0
- package/dist/cjs/examples/server/standaloneSseWithGetStreamableHttp.js +112 -0
- package/dist/cjs/examples/server/standaloneSseWithGetStreamableHttp.js.map +1 -0
- package/dist/cjs/examples/shared/inMemoryEventStore.d.ts +31 -0
- package/dist/cjs/examples/shared/inMemoryEventStore.d.ts.map +1 -0
- package/dist/cjs/examples/shared/inMemoryEventStore.js +69 -0
- package/dist/cjs/examples/shared/inMemoryEventStore.js.map +1 -0
- package/dist/cjs/inMemory.d.ts +13 -3
- package/dist/cjs/inMemory.d.ts.map +1 -1
- package/dist/cjs/inMemory.js +9 -7
- package/dist/cjs/inMemory.js.map +1 -1
- package/dist/cjs/server/auth/types.d.ts +5 -0
- package/dist/cjs/server/auth/types.d.ts.map +1 -1
- package/dist/cjs/server/mcp.d.ts +100 -19
- package/dist/cjs/server/mcp.d.ts.map +1 -1
- package/dist/cjs/server/mcp.js +154 -12
- package/dist/cjs/server/mcp.js.map +1 -1
- package/dist/cjs/server/sse.d.ts +10 -3
- package/dist/cjs/server/sse.d.ts.map +1 -1
- package/dist/cjs/server/sse.js +13 -4
- package/dist/cjs/server/sse.js.map +1 -1
- package/dist/cjs/server/streamableHttp.d.ts +146 -0
- package/dist/cjs/server/streamableHttp.d.ts.map +1 -0
- package/dist/cjs/server/streamableHttp.js +538 -0
- package/dist/cjs/server/streamableHttp.js.map +1 -0
- package/dist/cjs/shared/protocol.d.ts +31 -5
- package/dist/cjs/shared/protocol.d.ts.map +1 -1
- package/dist/cjs/shared/protocol.js +23 -15
- package/dist/cjs/shared/protocol.js.map +1 -1
- package/dist/cjs/shared/transport.d.ts +32 -3
- package/dist/cjs/shared/transport.d.ts.map +1 -1
- package/dist/cjs/types.d.ts +6 -0
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/cjs/types.js +14 -2
- package/dist/cjs/types.js.map +1 -1
- package/dist/esm/client/index.d.ts.map +1 -1
- package/dist/esm/client/index.js +5 -0
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/client/streamableHttp.d.ts +124 -0
- package/dist/esm/client/streamableHttp.d.ts.map +1 -0
- package/dist/esm/client/streamableHttp.js +348 -0
- package/dist/esm/client/streamableHttp.js.map +1 -0
- package/dist/esm/examples/client/simpleStreamableHttp.d.ts +2 -0
- package/dist/esm/examples/client/simpleStreamableHttp.d.ts.map +1 -0
- package/dist/esm/examples/client/simpleStreamableHttp.js +446 -0
- package/dist/esm/examples/client/simpleStreamableHttp.js.map +1 -0
- package/dist/esm/examples/client/streamableHttpWithSseFallbackClient.d.ts +2 -0
- package/dist/esm/examples/client/streamableHttpWithSseFallbackClient.d.ts.map +1 -0
- package/dist/esm/examples/client/streamableHttpWithSseFallbackClient.js +166 -0
- package/dist/esm/examples/client/streamableHttpWithSseFallbackClient.js.map +1 -0
- package/dist/esm/examples/server/jsonResponseStreamableHttp.d.ts +2 -0
- package/dist/esm/examples/server/jsonResponseStreamableHttp.d.ts.map +1 -0
- package/dist/esm/examples/server/jsonResponseStreamableHttp.js +134 -0
- package/dist/esm/examples/server/jsonResponseStreamableHttp.js.map +1 -0
- package/dist/esm/examples/server/simpleSseServer.d.ts +2 -0
- package/dist/esm/examples/server/simpleSseServer.d.ts.map +1 -0
- package/dist/esm/examples/server/simpleSseServer.js +143 -0
- package/dist/esm/examples/server/simpleSseServer.js.map +1 -0
- package/dist/esm/examples/server/simpleStreamableHttp.d.ts +2 -0
- package/dist/esm/examples/server/simpleStreamableHttp.d.ts.map +1 -0
- package/dist/esm/examples/server/simpleStreamableHttp.js +245 -0
- package/dist/esm/examples/server/simpleStreamableHttp.js.map +1 -0
- package/dist/esm/examples/server/sseAndStreamableHttpCompatibleServer.d.ts +2 -0
- package/dist/esm/examples/server/sseAndStreamableHttpCompatibleServer.d.ts.map +1 -0
- package/dist/esm/examples/server/sseAndStreamableHttpCompatibleServer.js +224 -0
- package/dist/esm/examples/server/sseAndStreamableHttpCompatibleServer.js.map +1 -0
- package/dist/esm/examples/server/standaloneSseWithGetStreamableHttp.d.ts +2 -0
- package/dist/esm/examples/server/standaloneSseWithGetStreamableHttp.d.ts.map +1 -0
- package/dist/esm/examples/server/standaloneSseWithGetStreamableHttp.js +107 -0
- package/dist/esm/examples/server/standaloneSseWithGetStreamableHttp.js.map +1 -0
- package/dist/esm/examples/shared/inMemoryEventStore.d.ts +31 -0
- package/dist/esm/examples/shared/inMemoryEventStore.d.ts.map +1 -0
- package/dist/esm/examples/shared/inMemoryEventStore.js +65 -0
- package/dist/esm/examples/shared/inMemoryEventStore.js.map +1 -0
- package/dist/esm/inMemory.d.ts +13 -3
- package/dist/esm/inMemory.d.ts.map +1 -1
- package/dist/esm/inMemory.js +9 -7
- package/dist/esm/inMemory.js.map +1 -1
- package/dist/esm/server/auth/types.d.ts +5 -0
- package/dist/esm/server/auth/types.d.ts.map +1 -1
- package/dist/esm/server/mcp.d.ts +100 -19
- package/dist/esm/server/mcp.d.ts.map +1 -1
- package/dist/esm/server/mcp.js +154 -12
- package/dist/esm/server/mcp.js.map +1 -1
- package/dist/esm/server/sse.d.ts +10 -3
- package/dist/esm/server/sse.d.ts.map +1 -1
- package/dist/esm/server/sse.js +13 -4
- package/dist/esm/server/sse.js.map +1 -1
- package/dist/esm/server/streamableHttp.d.ts +146 -0
- package/dist/esm/server/streamableHttp.d.ts.map +1 -0
- package/dist/esm/server/streamableHttp.js +531 -0
- package/dist/esm/server/streamableHttp.js.map +1 -0
- package/dist/esm/shared/protocol.d.ts +31 -5
- package/dist/esm/shared/protocol.d.ts.map +1 -1
- package/dist/esm/shared/protocol.js +24 -16
- package/dist/esm/shared/protocol.js.map +1 -1
- package/dist/esm/shared/transport.d.ts +32 -3
- package/dist/esm/shared/transport.d.ts.map +1 -1
- package/dist/esm/types.d.ts +6 -0
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/types.js +6 -0
- package/dist/esm/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const express_1 = __importDefault(require("express"));
|
|
7
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
const mcp_js_1 = require("../../server/mcp.js");
|
|
9
|
+
const streamableHttp_js_1 = require("../../server/streamableHttp.js");
|
|
10
|
+
const sse_js_1 = require("../../server/sse.js");
|
|
11
|
+
const zod_1 = require("zod");
|
|
12
|
+
const types_js_1 = require("../../types.js");
|
|
13
|
+
const inMemoryEventStore_js_1 = require("../shared/inMemoryEventStore.js");
|
|
14
|
+
/**
|
|
15
|
+
* This example server demonstrates backwards compatibility with both:
|
|
16
|
+
* 1. The deprecated HTTP+SSE transport (protocol version 2024-11-05)
|
|
17
|
+
* 2. The Streamable HTTP transport (protocol version 2025-03-26)
|
|
18
|
+
*
|
|
19
|
+
* It maintains a single MCP server instance but exposes two transport options:
|
|
20
|
+
* - /mcp: The new Streamable HTTP endpoint (supports GET/POST/DELETE)
|
|
21
|
+
* - /sse: The deprecated SSE endpoint for older clients (GET to establish stream)
|
|
22
|
+
* - /messages: The deprecated POST endpoint for older clients (POST to send messages)
|
|
23
|
+
*/
|
|
24
|
+
const server = new mcp_js_1.McpServer({
|
|
25
|
+
name: 'backwards-compatible-server',
|
|
26
|
+
version: '1.0.0',
|
|
27
|
+
}, { capabilities: { logging: {} } });
|
|
28
|
+
// Register a simple tool that sends notifications over time
|
|
29
|
+
server.tool('start-notification-stream', 'Starts sending periodic notifications for testing resumability', {
|
|
30
|
+
interval: zod_1.z.number().describe('Interval in milliseconds between notifications').default(100),
|
|
31
|
+
count: zod_1.z.number().describe('Number of notifications to send (0 for 100)').default(50),
|
|
32
|
+
}, async ({ interval, count }, { sendNotification }) => {
|
|
33
|
+
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
34
|
+
let counter = 0;
|
|
35
|
+
while (count === 0 || counter < count) {
|
|
36
|
+
counter++;
|
|
37
|
+
try {
|
|
38
|
+
await sendNotification({
|
|
39
|
+
method: "notifications/message",
|
|
40
|
+
params: {
|
|
41
|
+
level: "info",
|
|
42
|
+
data: `Periodic notification #${counter} at ${new Date().toISOString()}`
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
console.error("Error sending notification:", error);
|
|
48
|
+
}
|
|
49
|
+
// Wait for the specified interval
|
|
50
|
+
await sleep(interval);
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
content: [
|
|
54
|
+
{
|
|
55
|
+
type: 'text',
|
|
56
|
+
text: `Started sending periodic notifications every ${interval}ms`,
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
// Create Express application
|
|
62
|
+
const app = (0, express_1.default)();
|
|
63
|
+
app.use(express_1.default.json());
|
|
64
|
+
// Store transports by session ID
|
|
65
|
+
const transports = {};
|
|
66
|
+
//=============================================================================
|
|
67
|
+
// STREAMABLE HTTP TRANSPORT (PROTOCOL VERSION 2025-03-26)
|
|
68
|
+
//=============================================================================
|
|
69
|
+
// Handle all MCP Streamable HTTP requests (GET, POST, DELETE) on a single endpoint
|
|
70
|
+
app.all('/mcp', async (req, res) => {
|
|
71
|
+
console.log(`Received ${req.method} request to /mcp`);
|
|
72
|
+
try {
|
|
73
|
+
// Check for existing session ID
|
|
74
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
75
|
+
let transport;
|
|
76
|
+
if (sessionId && transports[sessionId]) {
|
|
77
|
+
// Check if the transport is of the correct type
|
|
78
|
+
const existingTransport = transports[sessionId];
|
|
79
|
+
if (existingTransport instanceof streamableHttp_js_1.StreamableHTTPServerTransport) {
|
|
80
|
+
// Reuse existing transport
|
|
81
|
+
transport = existingTransport;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// Transport exists but is not a StreamableHTTPServerTransport (could be SSEServerTransport)
|
|
85
|
+
res.status(400).json({
|
|
86
|
+
jsonrpc: '2.0',
|
|
87
|
+
error: {
|
|
88
|
+
code: -32000,
|
|
89
|
+
message: 'Bad Request: Session exists but uses a different transport protocol',
|
|
90
|
+
},
|
|
91
|
+
id: null,
|
|
92
|
+
});
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else if (!sessionId && req.method === 'POST' && (0, types_js_1.isInitializeRequest)(req.body)) {
|
|
97
|
+
const eventStore = new inMemoryEventStore_js_1.InMemoryEventStore();
|
|
98
|
+
transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
|
|
99
|
+
sessionIdGenerator: () => (0, node_crypto_1.randomUUID)(),
|
|
100
|
+
eventStore, // Enable resumability
|
|
101
|
+
onsessioninitialized: (sessionId) => {
|
|
102
|
+
// Store the transport by session ID when session is initialized
|
|
103
|
+
console.log(`StreamableHTTP session initialized with ID: ${sessionId}`);
|
|
104
|
+
transports[sessionId] = transport;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
// Set up onclose handler to clean up transport when closed
|
|
108
|
+
transport.onclose = () => {
|
|
109
|
+
const sid = transport.sessionId;
|
|
110
|
+
if (sid && transports[sid]) {
|
|
111
|
+
console.log(`Transport closed for session ${sid}, removing from transports map`);
|
|
112
|
+
delete transports[sid];
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
// Connect the transport to the MCP server
|
|
116
|
+
await server.connect(transport);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// Invalid request - no session ID or not initialization request
|
|
120
|
+
res.status(400).json({
|
|
121
|
+
jsonrpc: '2.0',
|
|
122
|
+
error: {
|
|
123
|
+
code: -32000,
|
|
124
|
+
message: 'Bad Request: No valid session ID provided',
|
|
125
|
+
},
|
|
126
|
+
id: null,
|
|
127
|
+
});
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// Handle the request with the transport
|
|
131
|
+
await transport.handleRequest(req, res, req.body);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
console.error('Error handling MCP request:', error);
|
|
135
|
+
if (!res.headersSent) {
|
|
136
|
+
res.status(500).json({
|
|
137
|
+
jsonrpc: '2.0',
|
|
138
|
+
error: {
|
|
139
|
+
code: -32603,
|
|
140
|
+
message: 'Internal server error',
|
|
141
|
+
},
|
|
142
|
+
id: null,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
//=============================================================================
|
|
148
|
+
// DEPRECATED HTTP+SSE TRANSPORT (PROTOCOL VERSION 2024-11-05)
|
|
149
|
+
//=============================================================================
|
|
150
|
+
app.get('/sse', async (req, res) => {
|
|
151
|
+
console.log('Received GET request to /sse (deprecated SSE transport)');
|
|
152
|
+
const transport = new sse_js_1.SSEServerTransport('/messages', res);
|
|
153
|
+
transports[transport.sessionId] = transport;
|
|
154
|
+
res.on("close", () => {
|
|
155
|
+
delete transports[transport.sessionId];
|
|
156
|
+
});
|
|
157
|
+
await server.connect(transport);
|
|
158
|
+
});
|
|
159
|
+
app.post("/messages", async (req, res) => {
|
|
160
|
+
const sessionId = req.query.sessionId;
|
|
161
|
+
let transport;
|
|
162
|
+
const existingTransport = transports[sessionId];
|
|
163
|
+
if (existingTransport instanceof sse_js_1.SSEServerTransport) {
|
|
164
|
+
// Reuse existing transport
|
|
165
|
+
transport = existingTransport;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
// Transport exists but is not a SSEServerTransport (could be StreamableHTTPServerTransport)
|
|
169
|
+
res.status(400).json({
|
|
170
|
+
jsonrpc: '2.0',
|
|
171
|
+
error: {
|
|
172
|
+
code: -32000,
|
|
173
|
+
message: 'Bad Request: Session exists but uses a different transport protocol',
|
|
174
|
+
},
|
|
175
|
+
id: null,
|
|
176
|
+
});
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (transport) {
|
|
180
|
+
await transport.handlePostMessage(req, res);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
res.status(400).send('No transport found for sessionId');
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
// Start the server
|
|
187
|
+
const PORT = 3000;
|
|
188
|
+
app.listen(PORT, () => {
|
|
189
|
+
console.log(`Backwards compatible MCP server listening on port ${PORT}`);
|
|
190
|
+
console.log(`
|
|
191
|
+
==============================================
|
|
192
|
+
SUPPORTED TRANSPORT OPTIONS:
|
|
193
|
+
|
|
194
|
+
1. Streamable Http(Protocol version: 2025-03-26)
|
|
195
|
+
Endpoint: /mcp
|
|
196
|
+
Methods: GET, POST, DELETE
|
|
197
|
+
Usage:
|
|
198
|
+
- Initialize with POST to /mcp
|
|
199
|
+
- Establish SSE stream with GET to /mcp
|
|
200
|
+
- Send requests with POST to /mcp
|
|
201
|
+
- Terminate session with DELETE to /mcp
|
|
202
|
+
|
|
203
|
+
2. Http + SSE (Protocol version: 2024-11-05)
|
|
204
|
+
Endpoints: /sse (GET) and /messages (POST)
|
|
205
|
+
Usage:
|
|
206
|
+
- Establish SSE stream with GET to /sse
|
|
207
|
+
- Send requests with POST to /messages?sessionId=<id>
|
|
208
|
+
==============================================
|
|
209
|
+
`);
|
|
210
|
+
});
|
|
211
|
+
// Handle server shutdown
|
|
212
|
+
process.on('SIGINT', async () => {
|
|
213
|
+
console.log('Shutting down server...');
|
|
214
|
+
// Close all active transports to properly clean up resources
|
|
215
|
+
for (const sessionId in transports) {
|
|
216
|
+
try {
|
|
217
|
+
console.log(`Closing transport for session ${sessionId}`);
|
|
218
|
+
await transports[sessionId].close();
|
|
219
|
+
delete transports[sessionId];
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
console.error(`Error closing transport for session ${sessionId}:`, error);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
await server.close();
|
|
226
|
+
console.log('Server shutdown complete');
|
|
227
|
+
process.exit(0);
|
|
228
|
+
});
|
|
229
|
+
//# sourceMappingURL=sseAndStreamableHttpCompatibleServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sseAndStreamableHttpCompatibleServer.js","sourceRoot":"","sources":["../../../../src/examples/server/sseAndStreamableHttpCompatibleServer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standaloneSseWithGetStreamableHttp.d.ts","sourceRoot":"","sources":["../../../../src/examples/server/standaloneSseWithGetStreamableHttp.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const express_1 = __importDefault(require("express"));
|
|
7
|
+
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
const mcp_js_1 = require("../../server/mcp.js");
|
|
9
|
+
const streamableHttp_js_1 = require("../../server/streamableHttp.js");
|
|
10
|
+
const types_js_1 = require("../../types.js");
|
|
11
|
+
// Create an MCP server with implementation details
|
|
12
|
+
const server = new mcp_js_1.McpServer({
|
|
13
|
+
name: 'resource-list-changed-notification-server',
|
|
14
|
+
version: '1.0.0',
|
|
15
|
+
});
|
|
16
|
+
// Store transports by session ID to send notifications
|
|
17
|
+
const transports = {};
|
|
18
|
+
const addResource = (name, content) => {
|
|
19
|
+
const uri = `https://mcp-example.com/dynamic/${encodeURIComponent(name)}`;
|
|
20
|
+
server.resource(name, uri, { mimeType: 'text/plain', description: `Dynamic resource: ${name}` }, async () => {
|
|
21
|
+
return {
|
|
22
|
+
contents: [{ uri, text: content }],
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
addResource('example-resource', 'Initial content for example-resource');
|
|
27
|
+
const resourceChangeInterval = setInterval(() => {
|
|
28
|
+
const name = (0, node_crypto_1.randomUUID)();
|
|
29
|
+
addResource(name, `Content for ${name}`);
|
|
30
|
+
}, 5000); // Change resources every 5 seconds for testing
|
|
31
|
+
const app = (0, express_1.default)();
|
|
32
|
+
app.use(express_1.default.json());
|
|
33
|
+
app.post('/mcp', async (req, res) => {
|
|
34
|
+
console.log('Received MCP request:', req.body);
|
|
35
|
+
try {
|
|
36
|
+
// Check for existing session ID
|
|
37
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
38
|
+
let transport;
|
|
39
|
+
if (sessionId && transports[sessionId]) {
|
|
40
|
+
// Reuse existing transport
|
|
41
|
+
transport = transports[sessionId];
|
|
42
|
+
}
|
|
43
|
+
else if (!sessionId && (0, types_js_1.isInitializeRequest)(req.body)) {
|
|
44
|
+
// New initialization request
|
|
45
|
+
transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
|
|
46
|
+
sessionIdGenerator: () => (0, node_crypto_1.randomUUID)(),
|
|
47
|
+
onsessioninitialized: (sessionId) => {
|
|
48
|
+
// Store the transport by session ID when session is initialized
|
|
49
|
+
// This avoids race conditions where requests might come in before the session is stored
|
|
50
|
+
console.log(`Session initialized with ID: ${sessionId}`);
|
|
51
|
+
transports[sessionId] = transport;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
// Connect the transport to the MCP server
|
|
55
|
+
await server.connect(transport);
|
|
56
|
+
// Handle the request - the onsessioninitialized callback will store the transport
|
|
57
|
+
await transport.handleRequest(req, res, req.body);
|
|
58
|
+
return; // Already handled
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
// Invalid request - no session ID or not initialization request
|
|
62
|
+
res.status(400).json({
|
|
63
|
+
jsonrpc: '2.0',
|
|
64
|
+
error: {
|
|
65
|
+
code: -32000,
|
|
66
|
+
message: 'Bad Request: No valid session ID provided',
|
|
67
|
+
},
|
|
68
|
+
id: null,
|
|
69
|
+
});
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
// Handle the request with existing transport
|
|
73
|
+
await transport.handleRequest(req, res, req.body);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
console.error('Error handling MCP request:', error);
|
|
77
|
+
if (!res.headersSent) {
|
|
78
|
+
res.status(500).json({
|
|
79
|
+
jsonrpc: '2.0',
|
|
80
|
+
error: {
|
|
81
|
+
code: -32603,
|
|
82
|
+
message: 'Internal server error',
|
|
83
|
+
},
|
|
84
|
+
id: null,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
// Handle GET requests for SSE streams (now using built-in support from StreamableHTTP)
|
|
90
|
+
app.get('/mcp', async (req, res) => {
|
|
91
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
92
|
+
if (!sessionId || !transports[sessionId]) {
|
|
93
|
+
res.status(400).send('Invalid or missing session ID');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
console.log(`Establishing SSE stream for session ${sessionId}`);
|
|
97
|
+
const transport = transports[sessionId];
|
|
98
|
+
await transport.handleRequest(req, res);
|
|
99
|
+
});
|
|
100
|
+
// Start the server
|
|
101
|
+
const PORT = 3000;
|
|
102
|
+
app.listen(PORT, () => {
|
|
103
|
+
console.log(`Server listening on port ${PORT}`);
|
|
104
|
+
});
|
|
105
|
+
// Handle server shutdown
|
|
106
|
+
process.on('SIGINT', async () => {
|
|
107
|
+
console.log('Shutting down server...');
|
|
108
|
+
clearInterval(resourceChangeInterval);
|
|
109
|
+
await server.close();
|
|
110
|
+
process.exit(0);
|
|
111
|
+
});
|
|
112
|
+
//# sourceMappingURL=standaloneSseWithGetStreamableHttp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standaloneSseWithGetStreamableHttp.js","sourceRoot":"","sources":["../../../../src/examples/server/standaloneSseWithGetStreamableHttp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { JSONRPCMessage } from '../../types.js';
|
|
2
|
+
import { EventStore } from '../../server/streamableHttp.js';
|
|
3
|
+
/**
|
|
4
|
+
* Simple in-memory implementation of the EventStore interface for resumability
|
|
5
|
+
* This is primarily intended for examples and testing, not for production use
|
|
6
|
+
* where a persistent storage solution would be more appropriate.
|
|
7
|
+
*/
|
|
8
|
+
export declare class InMemoryEventStore implements EventStore {
|
|
9
|
+
private events;
|
|
10
|
+
/**
|
|
11
|
+
* Generates a unique event ID for a given stream ID
|
|
12
|
+
*/
|
|
13
|
+
private generateEventId;
|
|
14
|
+
/**
|
|
15
|
+
* Extracts the stream ID from an event ID
|
|
16
|
+
*/
|
|
17
|
+
private getStreamIdFromEventId;
|
|
18
|
+
/**
|
|
19
|
+
* Stores an event with a generated event ID
|
|
20
|
+
* Implements EventStore.storeEvent
|
|
21
|
+
*/
|
|
22
|
+
storeEvent(streamId: string, message: JSONRPCMessage): Promise<string>;
|
|
23
|
+
/**
|
|
24
|
+
* Replays events that occurred after a specific event ID
|
|
25
|
+
* Implements EventStore.replayEventsAfter
|
|
26
|
+
*/
|
|
27
|
+
replayEventsAfter(lastEventId: string, { send }: {
|
|
28
|
+
send: (eventId: string, message: JSONRPCMessage) => Promise<void>;
|
|
29
|
+
}): Promise<string>;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=inMemoryEventStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inMemoryEventStore.d.ts","sourceRoot":"","sources":["../../../../src/examples/shared/inMemoryEventStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAE5D;;;;GAIG;AACH,qBAAa,kBAAmB,YAAW,UAAU;IACnD,OAAO,CAAC,MAAM,CAAyE;IAEvF;;OAEG;IACH,OAAO,CAAC,eAAe;IAIvB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAK9B;;;OAGG;IACG,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IAM5E;;;OAGG;IACG,iBAAiB,CAAC,WAAW,EAAE,MAAM,EACzC,EAAE,IAAI,EAAE,EAAE;QAAE,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,GAC9E,OAAO,CAAC,MAAM,CAAC;CAkCnB"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InMemoryEventStore = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Simple in-memory implementation of the EventStore interface for resumability
|
|
6
|
+
* This is primarily intended for examples and testing, not for production use
|
|
7
|
+
* where a persistent storage solution would be more appropriate.
|
|
8
|
+
*/
|
|
9
|
+
class InMemoryEventStore {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.events = new Map();
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Generates a unique event ID for a given stream ID
|
|
15
|
+
*/
|
|
16
|
+
generateEventId(streamId) {
|
|
17
|
+
return `${streamId}_${Date.now()}_${Math.random().toString(36).substring(2, 10)}`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Extracts the stream ID from an event ID
|
|
21
|
+
*/
|
|
22
|
+
getStreamIdFromEventId(eventId) {
|
|
23
|
+
const parts = eventId.split('_');
|
|
24
|
+
return parts.length > 0 ? parts[0] : '';
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Stores an event with a generated event ID
|
|
28
|
+
* Implements EventStore.storeEvent
|
|
29
|
+
*/
|
|
30
|
+
async storeEvent(streamId, message) {
|
|
31
|
+
const eventId = this.generateEventId(streamId);
|
|
32
|
+
this.events.set(eventId, { streamId, message });
|
|
33
|
+
return eventId;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Replays events that occurred after a specific event ID
|
|
37
|
+
* Implements EventStore.replayEventsAfter
|
|
38
|
+
*/
|
|
39
|
+
async replayEventsAfter(lastEventId, { send }) {
|
|
40
|
+
if (!lastEventId || !this.events.has(lastEventId)) {
|
|
41
|
+
return '';
|
|
42
|
+
}
|
|
43
|
+
// Extract the stream ID from the event ID
|
|
44
|
+
const streamId = this.getStreamIdFromEventId(lastEventId);
|
|
45
|
+
if (!streamId) {
|
|
46
|
+
return '';
|
|
47
|
+
}
|
|
48
|
+
let foundLastEvent = false;
|
|
49
|
+
// Sort events by eventId for chronological ordering
|
|
50
|
+
const sortedEvents = [...this.events.entries()].sort((a, b) => a[0].localeCompare(b[0]));
|
|
51
|
+
for (const [eventId, { streamId: eventStreamId, message }] of sortedEvents) {
|
|
52
|
+
// Only include events from the same stream
|
|
53
|
+
if (eventStreamId !== streamId) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
// Start sending events after we find the lastEventId
|
|
57
|
+
if (eventId === lastEventId) {
|
|
58
|
+
foundLastEvent = true;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (foundLastEvent) {
|
|
62
|
+
await send(eventId, message);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return streamId;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.InMemoryEventStore = InMemoryEventStore;
|
|
69
|
+
//# sourceMappingURL=inMemoryEventStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inMemoryEventStore.js","sourceRoot":"","sources":["../../../../src/examples/shared/inMemoryEventStore.ts"],"names":[],"mappings":";;;AAGA;;;;GAIG;AACH,MAAa,kBAAkB;IAA/B;QACU,WAAM,GAA+D,IAAI,GAAG,EAAE,CAAC;IAmEzF,CAAC;IAjEC;;OAEG;IACK,eAAe,CAAC,QAAgB;QACtC,OAAO,GAAG,QAAQ,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IACpF,CAAC;IAED;;OAEG;IACK,sBAAsB,CAAC,OAAe;QAC5C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACjC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1C,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,UAAU,CAAC,QAAgB,EAAE,OAAuB;QACxD,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QAChD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,iBAAiB,CAAC,WAAmB,EACzC,EAAE,IAAI,EAAyE;QAE/E,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;YAClD,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,0CAA0C;QAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,sBAAsB,CAAC,WAAW,CAAC,CAAC;QAC1D,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,IAAI,cAAc,GAAG,KAAK,CAAC;QAE3B,oDAAoD;QACpD,MAAM,YAAY,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAEzF,KAAK,MAAM,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,IAAI,YAAY,EAAE,CAAC;YAC3E,2CAA2C;YAC3C,IAAI,aAAa,KAAK,QAAQ,EAAE,CAAC;gBAC/B,SAAS;YACX,CAAC;YAED,qDAAqD;YACrD,IAAI,OAAO,KAAK,WAAW,EAAE,CAAC;gBAC5B,cAAc,GAAG,IAAI,CAAC;gBACtB,SAAS;YACX,CAAC;YAED,IAAI,cAAc,EAAE,CAAC;gBACnB,MAAM,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF;AApED,gDAoEC"}
|
package/dist/cjs/inMemory.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Transport } from "./shared/transport.js";
|
|
2
|
-
import { JSONRPCMessage } from "./types.js";
|
|
2
|
+
import { JSONRPCMessage, RequestId } from "./types.js";
|
|
3
|
+
import { AuthInfo } from "./server/auth/types.js";
|
|
3
4
|
/**
|
|
4
5
|
* In-memory transport for creating clients and servers that talk to each other within the same process.
|
|
5
6
|
*/
|
|
@@ -8,7 +9,9 @@ export declare class InMemoryTransport implements Transport {
|
|
|
8
9
|
private _messageQueue;
|
|
9
10
|
onclose?: () => void;
|
|
10
11
|
onerror?: (error: Error) => void;
|
|
11
|
-
onmessage?: (message: JSONRPCMessage
|
|
12
|
+
onmessage?: (message: JSONRPCMessage, extra?: {
|
|
13
|
+
authInfo?: AuthInfo;
|
|
14
|
+
}) => void;
|
|
12
15
|
sessionId?: string;
|
|
13
16
|
/**
|
|
14
17
|
* Creates a pair of linked in-memory transports that can communicate with each other. One should be passed to a Client and one to a Server.
|
|
@@ -16,6 +19,13 @@ export declare class InMemoryTransport implements Transport {
|
|
|
16
19
|
static createLinkedPair(): [InMemoryTransport, InMemoryTransport];
|
|
17
20
|
start(): Promise<void>;
|
|
18
21
|
close(): Promise<void>;
|
|
19
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Sends a message with optional auth info.
|
|
24
|
+
* This is useful for testing authentication scenarios.
|
|
25
|
+
*/
|
|
26
|
+
send(message: JSONRPCMessage, options?: {
|
|
27
|
+
relatedRequestId?: RequestId;
|
|
28
|
+
authInfo?: AuthInfo;
|
|
29
|
+
}): Promise<void>;
|
|
20
30
|
}
|
|
21
31
|
//# sourceMappingURL=inMemory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inMemory.d.ts","sourceRoot":"","sources":["../../src/inMemory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"inMemory.d.ts","sourceRoot":"","sources":["../../src/inMemory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAOlD;;GAEG;AACH,qBAAa,iBAAkB,YAAW,SAAS;IACjD,OAAO,CAAC,eAAe,CAAC,CAAoB;IAC5C,OAAO,CAAC,aAAa,CAAuB;IAE5C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IACjC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,QAAQ,CAAA;KAAE,KAAK,IAAI,CAAC;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,MAAM,CAAC,gBAAgB,IAAI,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;IAQ3D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAO5B;;;OAGG;IACG,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,SAAS,CAAC;QAAC,QAAQ,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAWpH"}
|
package/dist/cjs/inMemory.js
CHANGED
|
@@ -22,10 +22,8 @@ class InMemoryTransport {
|
|
|
22
22
|
var _a;
|
|
23
23
|
// Process any messages that were queued before start was called
|
|
24
24
|
while (this._messageQueue.length > 0) {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
(_a = this.onmessage) === null || _a === void 0 ? void 0 : _a.call(this, message);
|
|
28
|
-
}
|
|
25
|
+
const queuedMessage = this._messageQueue.shift();
|
|
26
|
+
(_a = this.onmessage) === null || _a === void 0 ? void 0 : _a.call(this, queuedMessage.message, queuedMessage.extra);
|
|
29
27
|
}
|
|
30
28
|
}
|
|
31
29
|
async close() {
|
|
@@ -35,15 +33,19 @@ class InMemoryTransport {
|
|
|
35
33
|
await (other === null || other === void 0 ? void 0 : other.close());
|
|
36
34
|
(_a = this.onclose) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
37
35
|
}
|
|
38
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Sends a message with optional auth info.
|
|
38
|
+
* This is useful for testing authentication scenarios.
|
|
39
|
+
*/
|
|
40
|
+
async send(message, options) {
|
|
39
41
|
if (!this._otherTransport) {
|
|
40
42
|
throw new Error("Not connected");
|
|
41
43
|
}
|
|
42
44
|
if (this._otherTransport.onmessage) {
|
|
43
|
-
this._otherTransport.onmessage(message);
|
|
45
|
+
this._otherTransport.onmessage(message, { authInfo: options === null || options === void 0 ? void 0 : options.authInfo });
|
|
44
46
|
}
|
|
45
47
|
else {
|
|
46
|
-
this._otherTransport._messageQueue.push(message);
|
|
48
|
+
this._otherTransport._messageQueue.push({ message, extra: { authInfo: options === null || options === void 0 ? void 0 : options.authInfo } });
|
|
47
49
|
}
|
|
48
50
|
}
|
|
49
51
|
}
|
package/dist/cjs/inMemory.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inMemory.js","sourceRoot":"","sources":["../../src/inMemory.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"inMemory.js","sourceRoot":"","sources":["../../src/inMemory.ts"],"names":[],"mappings":";;;AASA;;GAEG;AACH,MAAa,iBAAiB;IAA9B;QAEU,kBAAa,GAAoB,EAAE,CAAC;IAgD9C,CAAC;IAzCC;;OAEG;IACH,MAAM,CAAC,gBAAgB;QACrB,MAAM,eAAe,GAAG,IAAI,iBAAiB,EAAE,CAAC;QAChD,MAAM,eAAe,GAAG,IAAI,iBAAiB,EAAE,CAAC;QAChD,eAAe,CAAC,eAAe,GAAG,eAAe,CAAC;QAClD,eAAe,CAAC,eAAe,GAAG,eAAe,CAAC;QAClD,OAAO,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,KAAK;;QACT,gEAAgE;QAChE,OAAO,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrC,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,EAAG,CAAC;YAClD,MAAA,IAAI,CAAC,SAAS,qDAAG,aAAa,CAAC,OAAO,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK;;QACT,MAAM,KAAK,GAAG,IAAI,CAAC,eAAe,CAAC;QACnC,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;QACjC,MAAM,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,KAAK,EAAE,CAAA,CAAC;QACrB,MAAA,IAAI,CAAC,OAAO,oDAAI,CAAC;IACnB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,IAAI,CAAC,OAAuB,EAAE,OAA+D;QACjG,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;QACnC,CAAC;QAED,IAAI,IAAI,CAAC,eAAe,CAAC,SAAS,EAAE,CAAC;YACnC,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,QAAQ,EAAE,CAAC,CAAC;QAC3E,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;QAC/F,CAAC;IACH,CAAC;CACF;AAlDD,8CAkDC"}
|
|
@@ -18,5 +18,10 @@ export interface AuthInfo {
|
|
|
18
18
|
* When the token expires (in seconds since epoch).
|
|
19
19
|
*/
|
|
20
20
|
expiresAt?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Additional data associated with the token.
|
|
23
|
+
* This field should be used for any additional data that needs to be attached to the auth info.
|
|
24
|
+
*/
|
|
25
|
+
extra?: Record<string, unknown>;
|
|
21
26
|
}
|
|
22
27
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/server/auth/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;IAEjB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/server/auth/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;IAEjB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;MAGE;IACF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC"}
|