@mcp-ts/sdk 1.3.2 → 1.3.3
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 +405 -406
- package/dist/adapters/agui-adapter.d.mts +1 -1
- package/dist/adapters/agui-adapter.d.ts +1 -1
- package/dist/adapters/agui-middleware.d.mts +1 -1
- package/dist/adapters/agui-middleware.d.ts +1 -1
- package/dist/adapters/ai-adapter.d.mts +1 -1
- package/dist/adapters/ai-adapter.d.ts +1 -1
- package/dist/adapters/langchain-adapter.d.mts +1 -1
- package/dist/adapters/langchain-adapter.d.ts +1 -1
- package/dist/adapters/mastra-adapter.d.mts +1 -1
- package/dist/adapters/mastra-adapter.d.ts +1 -1
- package/dist/client/index.d.mts +8 -64
- package/dist/client/index.d.ts +8 -64
- package/dist/client/index.js +91 -173
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs +91 -173
- package/dist/client/index.mjs.map +1 -1
- package/dist/client/react.d.mts +12 -2
- package/dist/client/react.d.ts +12 -2
- package/dist/client/react.js +119 -182
- package/dist/client/react.js.map +1 -1
- package/dist/client/react.mjs +119 -182
- package/dist/client/react.mjs.map +1 -1
- package/dist/client/vue.d.mts +24 -4
- package/dist/client/vue.d.ts +24 -4
- package/dist/client/vue.js +121 -182
- package/dist/client/vue.js.map +1 -1
- package/dist/client/vue.mjs +121 -182
- package/dist/client/vue.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +215 -250
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +215 -250
- package/dist/index.mjs.map +1 -1
- package/dist/{multi-session-client-B1DBx5yR.d.mts → multi-session-client-DzjmT7FX.d.mts} +1 -0
- package/dist/{multi-session-client-DyFzyJUx.d.ts → multi-session-client-FAFpUzZ4.d.ts} +1 -0
- package/dist/server/index.d.mts +16 -21
- package/dist/server/index.d.ts +16 -21
- package/dist/server/index.js +124 -77
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +124 -77
- package/dist/server/index.mjs.map +1 -1
- package/dist/shared/index.d.mts +2 -2
- package/dist/shared/index.d.ts +2 -2
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/index.mjs.map +1 -1
- package/dist/{types-PjM1W07s.d.mts → types-CW6lghof.d.mts} +5 -0
- package/dist/{types-PjM1W07s.d.ts → types-CW6lghof.d.ts} +5 -0
- package/package.json +1 -1
- package/src/client/core/sse-client.ts +354 -493
- package/src/client/react/use-mcp.ts +75 -23
- package/src/client/vue/use-mcp.ts +111 -48
- package/src/server/handlers/nextjs-handler.ts +207 -217
- package/src/server/handlers/sse-handler.ts +10 -0
- package/src/server/mcp/oauth-client.ts +41 -32
- package/src/server/storage/types.ts +12 -5
- package/src/shared/types.ts +5 -0
package/dist/server/index.mjs
CHANGED
|
@@ -1232,7 +1232,8 @@ var MCPClient = class _MCPClient {
|
|
|
1232
1232
|
serverUrl: this.serverUrl,
|
|
1233
1233
|
callbackUrl: this.callbackUrl,
|
|
1234
1234
|
transportType: this.transportType || "streamable_http",
|
|
1235
|
-
createdAt: this.createdAt
|
|
1235
|
+
createdAt: this.createdAt,
|
|
1236
|
+
active: false
|
|
1236
1237
|
}, Math.floor(STATE_EXPIRATION_MS / 1e3));
|
|
1237
1238
|
}
|
|
1238
1239
|
}
|
|
@@ -1240,9 +1241,10 @@ var MCPClient = class _MCPClient {
|
|
|
1240
1241
|
* Saves current session state to storage
|
|
1241
1242
|
* Creates new session if it doesn't exist, updates if it does
|
|
1242
1243
|
* @param ttl - Time-to-live in seconds (defaults to 12hr for connected sessions)
|
|
1244
|
+
* @param active - Session status marker used to avoid unnecessary TTL rewrites
|
|
1243
1245
|
* @private
|
|
1244
1246
|
*/
|
|
1245
|
-
async saveSession(ttl = SESSION_TTL_SECONDS) {
|
|
1247
|
+
async saveSession(ttl = SESSION_TTL_SECONDS, active = true) {
|
|
1246
1248
|
if (!this.sessionId || !this.serverId || !this.serverUrl || !this.callbackUrl) {
|
|
1247
1249
|
return;
|
|
1248
1250
|
}
|
|
@@ -1254,7 +1256,8 @@ var MCPClient = class _MCPClient {
|
|
|
1254
1256
|
serverUrl: this.serverUrl,
|
|
1255
1257
|
callbackUrl: this.callbackUrl,
|
|
1256
1258
|
transportType: this.transportType || "streamable_http",
|
|
1257
|
-
createdAt: this.createdAt || Date.now()
|
|
1259
|
+
createdAt: this.createdAt || Date.now(),
|
|
1260
|
+
active
|
|
1258
1261
|
};
|
|
1259
1262
|
const existingSession = await storage.getSession(this.identity, this.sessionId);
|
|
1260
1263
|
if (existingSession) {
|
|
@@ -1328,15 +1331,17 @@ var MCPClient = class _MCPClient {
|
|
|
1328
1331
|
this.emitStateChange("CONNECTED");
|
|
1329
1332
|
this.emitProgress("Connected successfully");
|
|
1330
1333
|
const existingSession = await storage.getSession(this.identity, this.sessionId);
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
+
const needsTransportUpdate = !existingSession || existingSession.transportType !== this.transportType;
|
|
1335
|
+
const needsTtlPromotion = !existingSession || existingSession.active !== true;
|
|
1336
|
+
if (needsTransportUpdate || needsTtlPromotion) {
|
|
1337
|
+
console.log(`[MCPClient] Saving session ${this.sessionId} with 12hr TTL (connect success)`);
|
|
1338
|
+
await this.saveSession(SESSION_TTL_SECONDS, true);
|
|
1334
1339
|
}
|
|
1335
1340
|
} catch (error) {
|
|
1336
1341
|
if (error instanceof UnauthorizedError$1 || error instanceof Error && error.message.toLowerCase().includes("unauthorized")) {
|
|
1337
1342
|
this.emitStateChange("AUTHENTICATING");
|
|
1338
1343
|
console.log(`[MCPClient] Saving session ${this.sessionId} with 10min TTL (OAuth pending)`);
|
|
1339
|
-
await this.saveSession(Math.floor(STATE_EXPIRATION_MS / 1e3));
|
|
1344
|
+
await this.saveSession(Math.floor(STATE_EXPIRATION_MS / 1e3), false);
|
|
1340
1345
|
let authUrl = "";
|
|
1341
1346
|
if (this.oauthProvider) {
|
|
1342
1347
|
authUrl = this.oauthProvider.authUrl || "";
|
|
@@ -1414,7 +1419,7 @@ var MCPClient = class _MCPClient {
|
|
|
1414
1419
|
await this.client.connect(this.transport);
|
|
1415
1420
|
this.emitStateChange("CONNECTED");
|
|
1416
1421
|
console.log(`[MCPClient] Updating session ${this.sessionId} to 12hr TTL (OAuth complete)`);
|
|
1417
|
-
await this.saveSession(SESSION_TTL_SECONDS);
|
|
1422
|
+
await this.saveSession(SESSION_TTL_SECONDS, true);
|
|
1418
1423
|
return;
|
|
1419
1424
|
} catch (error) {
|
|
1420
1425
|
lastError = error;
|
|
@@ -2079,7 +2084,8 @@ var SSEConnectionManager = class {
|
|
|
2079
2084
|
serverName: s.serverName,
|
|
2080
2085
|
serverUrl: s.serverUrl,
|
|
2081
2086
|
transport: s.transportType,
|
|
2082
|
-
createdAt: s.createdAt
|
|
2087
|
+
createdAt: s.createdAt,
|
|
2088
|
+
active: s.active !== false
|
|
2083
2089
|
}))
|
|
2084
2090
|
};
|
|
2085
2091
|
}
|
|
@@ -2094,6 +2100,13 @@ var SSEConnectionManager = class {
|
|
|
2094
2100
|
(s) => s.serverId === serverId || s.serverUrl === serverUrl
|
|
2095
2101
|
);
|
|
2096
2102
|
if (duplicate) {
|
|
2103
|
+
if (duplicate.active === false) {
|
|
2104
|
+
await this.restoreSession({ sessionId: duplicate.sessionId });
|
|
2105
|
+
return {
|
|
2106
|
+
sessionId: duplicate.sessionId,
|
|
2107
|
+
success: true
|
|
2108
|
+
};
|
|
2109
|
+
}
|
|
2097
2110
|
throw new Error(`Connection already exists for server: ${duplicate.serverUrl || duplicate.serverId} (${duplicate.serverName})`);
|
|
2098
2111
|
}
|
|
2099
2112
|
const sessionId = await storage.generateSessionId();
|
|
@@ -2419,7 +2432,9 @@ function writeSSEEvent(res, event, data) {
|
|
|
2419
2432
|
}
|
|
2420
2433
|
|
|
2421
2434
|
// src/server/handlers/nextjs-handler.ts
|
|
2422
|
-
|
|
2435
|
+
function isRpcResponseEvent(event) {
|
|
2436
|
+
return "id" in event && ("result" in event || "error" in event);
|
|
2437
|
+
}
|
|
2423
2438
|
function createNextMcpHandler(options = {}) {
|
|
2424
2439
|
const {
|
|
2425
2440
|
getIdentity = (request) => new URL(request.url).searchParams.get("identity"),
|
|
@@ -2432,72 +2447,29 @@ function createNextMcpHandler(options = {}) {
|
|
|
2432
2447
|
clientDefaults,
|
|
2433
2448
|
getClientMetadata
|
|
2434
2449
|
} = options;
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
const stream = new TransformStream();
|
|
2446
|
-
const writer = stream.writable.getWriter();
|
|
2447
|
-
const encoder = new TextEncoder();
|
|
2448
|
-
const sendSSE = (event, data) => {
|
|
2449
|
-
const message = `event: ${event}
|
|
2450
|
-
data: ${JSON.stringify(data)}
|
|
2451
|
-
|
|
2452
|
-
`;
|
|
2453
|
-
writer.write(encoder.encode(message)).catch(() => {
|
|
2454
|
-
});
|
|
2455
|
-
};
|
|
2456
|
-
const previousManager = managers.get(identity);
|
|
2457
|
-
if (previousManager) {
|
|
2458
|
-
previousManager.dispose();
|
|
2459
|
-
}
|
|
2460
|
-
const resolvedClientMetadata = getClientMetadata ? await getClientMetadata(request) : clientDefaults;
|
|
2461
|
-
const manager = new SSEConnectionManager(
|
|
2450
|
+
const toManagerOptions = (identity, resolvedClientMetadata) => ({
|
|
2451
|
+
identity,
|
|
2452
|
+
heartbeatInterval,
|
|
2453
|
+
clientDefaults: resolvedClientMetadata
|
|
2454
|
+
});
|
|
2455
|
+
async function resolveClientMetadata(request) {
|
|
2456
|
+
return getClientMetadata ? await getClientMetadata(request) : clientDefaults;
|
|
2457
|
+
}
|
|
2458
|
+
async function GET() {
|
|
2459
|
+
return Response.json(
|
|
2462
2460
|
{
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
// Pass resolved metadata
|
|
2467
|
-
},
|
|
2468
|
-
(event) => {
|
|
2469
|
-
if ("id" in event) {
|
|
2470
|
-
sendSSE("rpc-response", event);
|
|
2471
|
-
} else if ("type" in event && "sessionId" in event) {
|
|
2472
|
-
sendSSE("connection", event);
|
|
2473
|
-
} else {
|
|
2474
|
-
sendSSE("observability", event);
|
|
2461
|
+
error: {
|
|
2462
|
+
code: "METHOD_NOT_ALLOWED",
|
|
2463
|
+
message: "Use POST /api/mcp. For streaming use Accept: text/event-stream."
|
|
2475
2464
|
}
|
|
2476
|
-
}
|
|
2465
|
+
},
|
|
2466
|
+
{ status: 405 }
|
|
2477
2467
|
);
|
|
2478
|
-
managers.set(identity, manager);
|
|
2479
|
-
sendSSE("connected", { timestamp: Date.now() });
|
|
2480
|
-
const abortController = new AbortController();
|
|
2481
|
-
request.signal?.addEventListener("abort", () => {
|
|
2482
|
-
manager.dispose();
|
|
2483
|
-
managers.delete(identity);
|
|
2484
|
-
writer.close().catch(() => {
|
|
2485
|
-
});
|
|
2486
|
-
abortController.abort();
|
|
2487
|
-
});
|
|
2488
|
-
return new Response(stream.readable, {
|
|
2489
|
-
status: 200,
|
|
2490
|
-
headers: {
|
|
2491
|
-
"Content-Type": "text/event-stream",
|
|
2492
|
-
"Cache-Control": "no-cache, no-transform",
|
|
2493
|
-
"Connection": "keep-alive",
|
|
2494
|
-
"X-Accel-Buffering": "no"
|
|
2495
|
-
}
|
|
2496
|
-
});
|
|
2497
2468
|
}
|
|
2498
2469
|
async function POST(request) {
|
|
2499
2470
|
const identity = getIdentity(request);
|
|
2500
2471
|
const authToken = getAuthToken(request);
|
|
2472
|
+
const acceptsEventStream = (request.headers.get("accept") || "").toLowerCase().includes("text/event-stream");
|
|
2501
2473
|
if (!identity) {
|
|
2502
2474
|
return Response.json({ error: { code: "MISSING_IDENTITY", message: "Missing identity" } }, { status: 400 });
|
|
2503
2475
|
}
|
|
@@ -2505,28 +2477,103 @@ data: ${JSON.stringify(data)}
|
|
|
2505
2477
|
if (!isAuthorized) {
|
|
2506
2478
|
return Response.json({ error: { code: "UNAUTHORIZED", message: "Unauthorized" } }, { status: 401 });
|
|
2507
2479
|
}
|
|
2480
|
+
let rawBody = "";
|
|
2508
2481
|
try {
|
|
2509
|
-
|
|
2510
|
-
const
|
|
2511
|
-
if (!
|
|
2482
|
+
rawBody = await request.text();
|
|
2483
|
+
const body = rawBody ? JSON.parse(rawBody) : null;
|
|
2484
|
+
if (!body || typeof body !== "object") {
|
|
2512
2485
|
return Response.json(
|
|
2513
2486
|
{
|
|
2514
2487
|
error: {
|
|
2515
|
-
code: "
|
|
2516
|
-
message: "
|
|
2488
|
+
code: "INVALID_REQUEST",
|
|
2489
|
+
message: "Invalid JSON-RPC request body"
|
|
2517
2490
|
}
|
|
2518
2491
|
},
|
|
2519
2492
|
{ status: 400 }
|
|
2520
2493
|
);
|
|
2521
2494
|
}
|
|
2522
|
-
const
|
|
2523
|
-
|
|
2495
|
+
const resolvedClientMetadata = await resolveClientMetadata(request);
|
|
2496
|
+
if (!acceptsEventStream) {
|
|
2497
|
+
const manager2 = new SSEConnectionManager(
|
|
2498
|
+
toManagerOptions(identity, resolvedClientMetadata),
|
|
2499
|
+
() => {
|
|
2500
|
+
}
|
|
2501
|
+
);
|
|
2502
|
+
try {
|
|
2503
|
+
const response = await manager2.handleRequest(body);
|
|
2504
|
+
return Response.json(response);
|
|
2505
|
+
} finally {
|
|
2506
|
+
manager2.dispose();
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
const stream = new TransformStream();
|
|
2510
|
+
const writer = stream.writable.getWriter();
|
|
2511
|
+
const encoder = new TextEncoder();
|
|
2512
|
+
let streamWritable = true;
|
|
2513
|
+
const sendSSE = (event, data) => {
|
|
2514
|
+
if (!streamWritable) return;
|
|
2515
|
+
const message = `event: ${event}
|
|
2516
|
+
data: ${JSON.stringify(data)}
|
|
2517
|
+
|
|
2518
|
+
`;
|
|
2519
|
+
writer.write(encoder.encode(message)).catch(() => {
|
|
2520
|
+
streamWritable = false;
|
|
2521
|
+
});
|
|
2522
|
+
};
|
|
2523
|
+
const manager = new SSEConnectionManager(
|
|
2524
|
+
toManagerOptions(identity, resolvedClientMetadata),
|
|
2525
|
+
(event) => {
|
|
2526
|
+
if (isRpcResponseEvent(event)) {
|
|
2527
|
+
sendSSE("rpc-response", event);
|
|
2528
|
+
} else if ("type" in event && "sessionId" in event) {
|
|
2529
|
+
sendSSE("connection", event);
|
|
2530
|
+
} else {
|
|
2531
|
+
sendSSE("observability", event);
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
);
|
|
2535
|
+
sendSSE("connected", { timestamp: Date.now() });
|
|
2536
|
+
void (async () => {
|
|
2537
|
+
try {
|
|
2538
|
+
await manager.handleRequest(body);
|
|
2539
|
+
} catch (error) {
|
|
2540
|
+
const err = error instanceof Error ? error : new Error("Unknown error");
|
|
2541
|
+
sendSSE("rpc-response", {
|
|
2542
|
+
id: body.id || "unknown",
|
|
2543
|
+
error: {
|
|
2544
|
+
code: "EXECUTION_ERROR",
|
|
2545
|
+
message: err.message
|
|
2546
|
+
}
|
|
2547
|
+
});
|
|
2548
|
+
} finally {
|
|
2549
|
+
streamWritable = false;
|
|
2550
|
+
manager.dispose();
|
|
2551
|
+
writer.close().catch(() => {
|
|
2552
|
+
});
|
|
2553
|
+
}
|
|
2554
|
+
})();
|
|
2555
|
+
return new Response(stream.readable, {
|
|
2556
|
+
status: 200,
|
|
2557
|
+
headers: {
|
|
2558
|
+
"Content-Type": "text/event-stream",
|
|
2559
|
+
"Cache-Control": "no-cache, no-transform",
|
|
2560
|
+
"Connection": "keep-alive",
|
|
2561
|
+
"X-Accel-Buffering": "no"
|
|
2562
|
+
}
|
|
2563
|
+
});
|
|
2524
2564
|
} catch (error) {
|
|
2565
|
+
const err = error instanceof Error ? error : new Error("Unknown error");
|
|
2566
|
+
console.error("[MCP Next Handler] Failed to handle RPC", {
|
|
2567
|
+
identity,
|
|
2568
|
+
message: err.message,
|
|
2569
|
+
stack: err.stack,
|
|
2570
|
+
rawBody: rawBody.slice(0, 500)
|
|
2571
|
+
});
|
|
2525
2572
|
return Response.json(
|
|
2526
2573
|
{
|
|
2527
2574
|
error: {
|
|
2528
2575
|
code: "EXECUTION_ERROR",
|
|
2529
|
-
message:
|
|
2576
|
+
message: err.message
|
|
2530
2577
|
}
|
|
2531
2578
|
},
|
|
2532
2579
|
{ status: 500 }
|