@mcp-ts/sdk 1.0.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/LICENSE +21 -0
- package/README.md +297 -0
- package/dist/adapters/agui-adapter.d.mts +119 -0
- package/dist/adapters/agui-adapter.d.ts +119 -0
- package/dist/adapters/agui-adapter.js +109 -0
- package/dist/adapters/agui-adapter.js.map +1 -0
- package/dist/adapters/agui-adapter.mjs +107 -0
- package/dist/adapters/agui-adapter.mjs.map +1 -0
- package/dist/adapters/agui-middleware.d.mts +171 -0
- package/dist/adapters/agui-middleware.d.ts +171 -0
- package/dist/adapters/agui-middleware.js +429 -0
- package/dist/adapters/agui-middleware.js.map +1 -0
- package/dist/adapters/agui-middleware.mjs +417 -0
- package/dist/adapters/agui-middleware.mjs.map +1 -0
- package/dist/adapters/ai-adapter.d.mts +38 -0
- package/dist/adapters/ai-adapter.d.ts +38 -0
- package/dist/adapters/ai-adapter.js +82 -0
- package/dist/adapters/ai-adapter.js.map +1 -0
- package/dist/adapters/ai-adapter.mjs +80 -0
- package/dist/adapters/ai-adapter.mjs.map +1 -0
- package/dist/adapters/langchain-adapter.d.mts +46 -0
- package/dist/adapters/langchain-adapter.d.ts +46 -0
- package/dist/adapters/langchain-adapter.js +102 -0
- package/dist/adapters/langchain-adapter.js.map +1 -0
- package/dist/adapters/langchain-adapter.mjs +100 -0
- package/dist/adapters/langchain-adapter.mjs.map +1 -0
- package/dist/adapters/mastra-adapter.d.mts +49 -0
- package/dist/adapters/mastra-adapter.d.ts +49 -0
- package/dist/adapters/mastra-adapter.js +95 -0
- package/dist/adapters/mastra-adapter.js.map +1 -0
- package/dist/adapters/mastra-adapter.mjs +93 -0
- package/dist/adapters/mastra-adapter.mjs.map +1 -0
- package/dist/client/index.d.mts +119 -0
- package/dist/client/index.d.ts +119 -0
- package/dist/client/index.js +225 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/index.mjs +223 -0
- package/dist/client/index.mjs.map +1 -0
- package/dist/client/react.d.mts +151 -0
- package/dist/client/react.d.ts +151 -0
- package/dist/client/react.js +492 -0
- package/dist/client/react.js.map +1 -0
- package/dist/client/react.mjs +489 -0
- package/dist/client/react.mjs.map +1 -0
- package/dist/client/vue.d.mts +157 -0
- package/dist/client/vue.d.ts +157 -0
- package/dist/client/vue.js +474 -0
- package/dist/client/vue.js.map +1 -0
- package/dist/client/vue.mjs +471 -0
- package/dist/client/vue.mjs.map +1 -0
- package/dist/events-BP6WyRNh.d.mts +110 -0
- package/dist/events-BP6WyRNh.d.ts +110 -0
- package/dist/index.d.mts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +2784 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2723 -0
- package/dist/index.mjs.map +1 -0
- package/dist/multi-session-client-BOFgPypS.d.ts +389 -0
- package/dist/multi-session-client-DMF3ED2O.d.mts +389 -0
- package/dist/server/index.d.mts +269 -0
- package/dist/server/index.d.ts +269 -0
- package/dist/server/index.js +2444 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/index.mjs +2414 -0
- package/dist/server/index.mjs.map +1 -0
- package/dist/shared/index.d.mts +24 -0
- package/dist/shared/index.d.ts +24 -0
- package/dist/shared/index.js +223 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/shared/index.mjs +190 -0
- package/dist/shared/index.mjs.map +1 -0
- package/dist/types-SbDlA2VX.d.mts +153 -0
- package/dist/types-SbDlA2VX.d.ts +153 -0
- package/dist/utils-0qmYrqoa.d.mts +92 -0
- package/dist/utils-0qmYrqoa.d.ts +92 -0
- package/package.json +165 -0
- package/src/adapters/agui-adapter.ts +210 -0
- package/src/adapters/agui-middleware.ts +512 -0
- package/src/adapters/ai-adapter.ts +115 -0
- package/src/adapters/langchain-adapter.ts +127 -0
- package/src/adapters/mastra-adapter.ts +126 -0
- package/src/client/core/sse-client.ts +340 -0
- package/src/client/index.ts +26 -0
- package/src/client/react/index.ts +10 -0
- package/src/client/react/useMcp.ts +558 -0
- package/src/client/vue/index.ts +10 -0
- package/src/client/vue/useMcp.ts +542 -0
- package/src/index.ts +11 -0
- package/src/server/handlers/nextjs-handler.ts +216 -0
- package/src/server/handlers/sse-handler.ts +699 -0
- package/src/server/index.ts +57 -0
- package/src/server/mcp/multi-session-client.ts +132 -0
- package/src/server/mcp/oauth-client.ts +1168 -0
- package/src/server/mcp/storage-oauth-provider.ts +239 -0
- package/src/server/storage/file-backend.ts +169 -0
- package/src/server/storage/index.ts +115 -0
- package/src/server/storage/memory-backend.ts +132 -0
- package/src/server/storage/redis-backend.ts +210 -0
- package/src/server/storage/redis.ts +160 -0
- package/src/server/storage/types.ts +109 -0
- package/src/shared/constants.ts +29 -0
- package/src/shared/errors.ts +133 -0
- package/src/shared/events.ts +166 -0
- package/src/shared/index.ts +70 -0
- package/src/shared/types.ts +274 -0
- package/src/shared/utils.ts +16 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js App Router Handler for MCP SSE
|
|
3
|
+
* Provides a clean, zero-boilerplate API for Next.js applications
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { SSEConnectionManager, type ClientMetadata } from './sse-handler.js';
|
|
7
|
+
import type { McpConnectionEvent, McpObservabilityEvent } from '../../shared/events.js';
|
|
8
|
+
import type { McpRpcResponse } from '../../shared/types.js';
|
|
9
|
+
|
|
10
|
+
export interface NextMcpHandlerOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Extract identity from request (default: from 'identity' query param)
|
|
13
|
+
*/
|
|
14
|
+
getIdentity?: (request: Request) => string | null;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Extract auth token from request (default: from 'token' query param or Authorization header)
|
|
18
|
+
*/
|
|
19
|
+
getAuthToken?: (request: Request) => string | null;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Authenticate user and verify access (optional)
|
|
23
|
+
* Return true if user is authenticated, false otherwise
|
|
24
|
+
*/
|
|
25
|
+
authenticate?: (identity: string, token: string | null) => Promise<boolean> | boolean;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Heartbeat interval in milliseconds (default: 30000)
|
|
29
|
+
*/
|
|
30
|
+
heartbeatInterval?: number;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Static OAuth client metadata defaults (for all connections)
|
|
34
|
+
* Use this for single-tenant applications with fixed branding
|
|
35
|
+
*/
|
|
36
|
+
clientDefaults?: ClientMetadata;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Dynamic OAuth client metadata getter (per-request, useful for multi-tenant)
|
|
40
|
+
* Use this when you need different branding based on request (tenant, domain, etc.)
|
|
41
|
+
* Takes precedence over clientDefaults
|
|
42
|
+
*/
|
|
43
|
+
getClientMetadata?: (request: Request) => ClientMetadata | Promise<ClientMetadata>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Global manager store - shared across requests for the same user
|
|
47
|
+
const managers = new Map<string, SSEConnectionManager>();
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Creates Next.js App Router handlers (GET and POST) for MCP SSE endpoint
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* // app/api/mcp/route.ts
|
|
55
|
+
* import { createNextMcpHandler } from '@mcp-ts/core/server';
|
|
56
|
+
*
|
|
57
|
+
* export const { GET, POST } = createNextMcpHandler();
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export function createNextMcpHandler(options: NextMcpHandlerOptions = {}) {
|
|
61
|
+
const {
|
|
62
|
+
getIdentity = (request: Request) => new URL(request.url).searchParams.get('identity'),
|
|
63
|
+
getAuthToken = (request: Request) => {
|
|
64
|
+
const url = new URL(request.url);
|
|
65
|
+
return url.searchParams.get('token') || request.headers.get('authorization');
|
|
66
|
+
},
|
|
67
|
+
authenticate = () => true,
|
|
68
|
+
heartbeatInterval = 30000,
|
|
69
|
+
clientDefaults,
|
|
70
|
+
getClientMetadata,
|
|
71
|
+
} = options;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* GET handler - Establishes SSE connection
|
|
75
|
+
*/
|
|
76
|
+
async function GET(request: Request): Promise<Response> {
|
|
77
|
+
const identity = getIdentity(request);
|
|
78
|
+
const authToken = getAuthToken(request);
|
|
79
|
+
|
|
80
|
+
if (!identity) {
|
|
81
|
+
return new Response('Missing identity', { status: 400 });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Validate auth
|
|
85
|
+
const isAuthorized = await authenticate(identity, authToken);
|
|
86
|
+
if (!isAuthorized) {
|
|
87
|
+
return new Response('Unauthorized', { status: 401 });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Create TransformStream for SSE
|
|
91
|
+
const stream = new TransformStream();
|
|
92
|
+
const writer = stream.writable.getWriter();
|
|
93
|
+
const encoder = new TextEncoder();
|
|
94
|
+
|
|
95
|
+
// Helper to send SSE events
|
|
96
|
+
const sendSSE = (event: string, data: any) => {
|
|
97
|
+
const message = `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
|
|
98
|
+
writer.write(encoder.encode(message)).catch(() => {
|
|
99
|
+
// Client disconnected, ignore write errors
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Send initial connection event
|
|
104
|
+
sendSSE('connected', { timestamp: Date.now() });
|
|
105
|
+
|
|
106
|
+
// Clean up previous manager if exists (prevents memory leaks on reconnect)
|
|
107
|
+
const previousManager = managers.get(identity);
|
|
108
|
+
if (previousManager) {
|
|
109
|
+
previousManager.dispose();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Resolve client metadata (dynamic takes precedence over static)
|
|
113
|
+
const resolvedClientMetadata = getClientMetadata
|
|
114
|
+
? await getClientMetadata(request)
|
|
115
|
+
: clientDefaults;
|
|
116
|
+
|
|
117
|
+
// Create new manager
|
|
118
|
+
const manager = new SSEConnectionManager(
|
|
119
|
+
{
|
|
120
|
+
identity,
|
|
121
|
+
heartbeatInterval,
|
|
122
|
+
clientDefaults: resolvedClientMetadata, // Pass resolved metadata
|
|
123
|
+
},
|
|
124
|
+
(event: McpConnectionEvent | McpObservabilityEvent | McpRpcResponse) => {
|
|
125
|
+
// Determine event type and send via SSE
|
|
126
|
+
if ('id' in event) {
|
|
127
|
+
// RPC response
|
|
128
|
+
sendSSE('rpc-response', event);
|
|
129
|
+
} else if ('type' in event && 'sessionId' in event) {
|
|
130
|
+
// Connection event
|
|
131
|
+
sendSSE('connection', event);
|
|
132
|
+
} else {
|
|
133
|
+
// Observability event
|
|
134
|
+
sendSSE('observability', event);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
managers.set(identity, manager);
|
|
140
|
+
|
|
141
|
+
// Handle client disconnect
|
|
142
|
+
const abortController = new AbortController();
|
|
143
|
+
request.signal?.addEventListener('abort', () => {
|
|
144
|
+
manager.dispose();
|
|
145
|
+
managers.delete(identity);
|
|
146
|
+
writer.close().catch(() => { });
|
|
147
|
+
abortController.abort();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Return SSE response
|
|
151
|
+
return new Response(stream.readable, {
|
|
152
|
+
status: 200,
|
|
153
|
+
headers: {
|
|
154
|
+
'Content-Type': 'text/event-stream',
|
|
155
|
+
'Cache-Control': 'no-cache, no-transform',
|
|
156
|
+
'Connection': 'keep-alive',
|
|
157
|
+
'X-Accel-Buffering': 'no',
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* POST handler - Handles RPC requests
|
|
164
|
+
*/
|
|
165
|
+
async function POST(request: Request): Promise<Response> {
|
|
166
|
+
const identity = getIdentity(request);
|
|
167
|
+
const authToken = getAuthToken(request);
|
|
168
|
+
|
|
169
|
+
if (!identity) {
|
|
170
|
+
return Response.json({ error: { code: 'MISSING_IDENTITY', message: 'Missing identity' } }, { status: 400 });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Validate auth
|
|
174
|
+
const isAuthorized = await authenticate(identity, authToken);
|
|
175
|
+
if (!isAuthorized) {
|
|
176
|
+
return Response.json({ error: { code: 'UNAUTHORIZED', message: 'Unauthorized' } }, { status: 401 });
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
const body = await request.json();
|
|
181
|
+
|
|
182
|
+
// Get existing manager (created by GET endpoint)
|
|
183
|
+
const manager = managers.get(identity);
|
|
184
|
+
|
|
185
|
+
if (!manager) {
|
|
186
|
+
return Response.json(
|
|
187
|
+
{
|
|
188
|
+
error: {
|
|
189
|
+
code: 'NO_CONNECTION',
|
|
190
|
+
message: 'No SSE connection found. Please establish SSE connection first.',
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
{ status: 400 }
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Handle the request - response will be sent via SSE
|
|
198
|
+
await manager.handleRequest(body);
|
|
199
|
+
|
|
200
|
+
// Return acknowledgment (actual response goes through SSE)
|
|
201
|
+
return Response.json({ acknowledged: true });
|
|
202
|
+
} catch (error) {
|
|
203
|
+
return Response.json(
|
|
204
|
+
{
|
|
205
|
+
error: {
|
|
206
|
+
code: 'EXECUTION_ERROR',
|
|
207
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
{ status: 500 }
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return { GET, POST };
|
|
216
|
+
}
|