@mcp-ts/sdk 1.3.7 → 1.3.10
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 -21
- package/README.md +397 -404
- package/dist/adapters/agui-middleware.js.map +1 -1
- package/dist/adapters/agui-middleware.mjs.map +1 -1
- package/dist/bin/mcp-ts.js +0 -0
- package/dist/bin/mcp-ts.js.map +1 -1
- package/dist/bin/mcp-ts.mjs +0 -0
- package/dist/bin/mcp-ts.mjs.map +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/index.mjs.map +1 -1
- package/dist/client/react.d.mts +10 -28
- package/dist/client/react.d.ts +10 -28
- package/dist/client/react.js +101 -52
- package/dist/client/react.js.map +1 -1
- package/dist/client/react.mjs +102 -53
- package/dist/client/react.mjs.map +1 -1
- package/dist/client/vue.js.map +1 -1
- package/dist/client/vue.mjs.map +1 -1
- package/dist/index.js +78 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +73 -6
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.js +78 -6
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +73 -6
- package/dist/server/index.mjs.map +1 -1
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/index.mjs.map +1 -1
- package/package.json +185 -185
- package/src/adapters/agui-middleware.ts +382 -382
- package/src/bin/mcp-ts.ts +102 -102
- package/src/client/core/app-host.ts +417 -417
- package/src/client/core/sse-client.ts +371 -371
- package/src/client/core/types.ts +31 -31
- package/src/client/index.ts +27 -27
- package/src/client/react/index.ts +20 -16
- package/src/client/react/use-app-host.ts +74 -73
- package/src/client/react/use-mcp-apps.tsx +224 -214
- package/src/client/react/use-mcp.ts +669 -641
- package/src/client/vue/index.ts +10 -10
- package/src/client/vue/use-mcp.ts +617 -617
- package/src/index.ts +11 -11
- package/src/server/handlers/nextjs-handler.ts +204 -204
- package/src/server/handlers/sse-handler.ts +631 -631
- package/src/server/index.ts +57 -57
- package/src/server/mcp/multi-session-client.ts +228 -228
- package/src/server/mcp/oauth-client.ts +1188 -1188
- package/src/server/mcp/storage-oauth-provider.ts +272 -272
- package/src/server/storage/crypto.ts +92 -0
- package/src/server/storage/file-backend.ts +157 -157
- package/src/server/storage/index.ts +176 -176
- package/src/server/storage/memory-backend.ts +123 -123
- package/src/server/storage/redis-backend.ts +276 -276
- package/src/server/storage/redis.ts +160 -160
- package/src/server/storage/sqlite-backend.ts +182 -182
- package/src/server/storage/supabase-backend.ts +229 -228
- package/src/server/storage/types.ts +116 -116
- package/src/shared/constants.ts +29 -29
- package/src/shared/errors.ts +133 -133
- package/src/shared/event-routing.ts +28 -28
- package/src/shared/events.ts +180 -180
- package/src/shared/index.ts +75 -75
- package/src/shared/tool-utils.ts +61 -61
- package/src/shared/types.ts +282 -282
- package/src/shared/utils.ts +38 -38
- package/supabase/migrations/20260330195700_install_mcp_sessions.sql +84 -84
|
@@ -1,214 +1,224 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP Apps Hook
|
|
3
|
-
*
|
|
4
|
-
* Provides utilities for rendering interactive UI components from MCP servers.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import React, {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
*
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
1
|
+
/**
|
|
2
|
+
* MCP Apps Hook
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for rendering interactive UI components from MCP servers.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, {
|
|
8
|
+
useState,
|
|
9
|
+
useEffect,
|
|
10
|
+
useCallback,
|
|
11
|
+
useRef,
|
|
12
|
+
memo,
|
|
13
|
+
useMemo,
|
|
14
|
+
type MutableRefObject,
|
|
15
|
+
} from 'react';
|
|
16
|
+
import { useAppHost } from './use-app-host.js';
|
|
17
|
+
import type { SSEClient } from '../core/sse-client.js';
|
|
18
|
+
|
|
19
|
+
export interface McpClient {
|
|
20
|
+
connections: Array<{
|
|
21
|
+
sessionId: string;
|
|
22
|
+
tools: Array<{
|
|
23
|
+
name: string;
|
|
24
|
+
mcpApp?: {
|
|
25
|
+
resourceUri: string;
|
|
26
|
+
};
|
|
27
|
+
_meta?: {
|
|
28
|
+
ui?: {
|
|
29
|
+
resourceUri?: string;
|
|
30
|
+
};
|
|
31
|
+
'ui/resourceUri'?: string;
|
|
32
|
+
};
|
|
33
|
+
}>;
|
|
34
|
+
}>;
|
|
35
|
+
sseClient?: SSEClient | null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface McpAppMetadata {
|
|
39
|
+
toolName: string;
|
|
40
|
+
resourceUri: string;
|
|
41
|
+
sessionId: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Props for {@link useMcpApps}'s `McpAppRenderer` (client is supplied via the hook). */
|
|
45
|
+
export interface McpAppRendererProps {
|
|
46
|
+
name: string;
|
|
47
|
+
input?: Record<string, unknown>;
|
|
48
|
+
result?: unknown;
|
|
49
|
+
status: 'executing' | 'inProgress' | 'complete' | 'idle';
|
|
50
|
+
/** Custom CSS class for the container */
|
|
51
|
+
className?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
type McpAppViewProps = McpAppRendererProps & {
|
|
55
|
+
/**
|
|
56
|
+
* Ref avoids tying `McpAppRenderer` identity to `mcpClient`: when `connections` updates, `useMcp()` still
|
|
57
|
+
* returns a new object (correct for `useEffect` deps), but the iframe must not remount.
|
|
58
|
+
*/
|
|
59
|
+
clientRef: MutableRefObject<McpClient | null>;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/** Renders one MCP App in a sandboxed iframe; reads the latest client from `clientRef` each render. */
|
|
63
|
+
const McpAppView = memo(function McpAppView({
|
|
64
|
+
clientRef,
|
|
65
|
+
name,
|
|
66
|
+
input,
|
|
67
|
+
result,
|
|
68
|
+
status,
|
|
69
|
+
className,
|
|
70
|
+
}: McpAppViewProps) {
|
|
71
|
+
const mcpClient = clientRef.current;
|
|
72
|
+
const metadata = getMcpAppMetadata(mcpClient, name);
|
|
73
|
+
const sseClient = mcpClient?.sseClient ?? null;
|
|
74
|
+
const resourceUri = metadata?.resourceUri;
|
|
75
|
+
const appSessionId = metadata?.sessionId;
|
|
76
|
+
|
|
77
|
+
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
78
|
+
const { host, error: hostError } = useAppHost(sseClient as SSEClient, iframeRef);
|
|
79
|
+
const [isLaunched, setIsLaunched] = useState(false);
|
|
80
|
+
const [error, setError] = useState<Error | null>(null);
|
|
81
|
+
|
|
82
|
+
const sentInputRef = useRef(false);
|
|
83
|
+
const sentResultRef = useRef(false);
|
|
84
|
+
const lastInputRef = useRef(input);
|
|
85
|
+
const lastResultRef = useRef(result);
|
|
86
|
+
const lastStatusRef = useRef(status);
|
|
87
|
+
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
setIsLaunched(false);
|
|
90
|
+
setError(null);
|
|
91
|
+
}, [resourceUri, appSessionId]);
|
|
92
|
+
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
if (!host || !resourceUri || !appSessionId) return;
|
|
95
|
+
|
|
96
|
+
host
|
|
97
|
+
.launch(resourceUri, appSessionId)
|
|
98
|
+
.then(() => setIsLaunched(true))
|
|
99
|
+
.catch((err) => setError(err instanceof Error ? err : new Error(String(err))));
|
|
100
|
+
}, [host, resourceUri, appSessionId]);
|
|
101
|
+
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
if (!host || !isLaunched || !resourceUri || !appSessionId || !input) return;
|
|
104
|
+
|
|
105
|
+
if (!sentInputRef.current || JSON.stringify(input) !== JSON.stringify(lastInputRef.current)) {
|
|
106
|
+
sentInputRef.current = true;
|
|
107
|
+
lastInputRef.current = input;
|
|
108
|
+
host.sendToolInput(input);
|
|
109
|
+
}
|
|
110
|
+
}, [host, isLaunched, input, resourceUri, appSessionId, name]);
|
|
111
|
+
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
if (!host || !isLaunched || !resourceUri || !appSessionId || result === undefined) return;
|
|
114
|
+
if (status !== 'complete') return;
|
|
115
|
+
|
|
116
|
+
if (!sentResultRef.current || JSON.stringify(result) !== JSON.stringify(lastResultRef.current)) {
|
|
117
|
+
sentResultRef.current = true;
|
|
118
|
+
lastResultRef.current = result;
|
|
119
|
+
const formattedResult =
|
|
120
|
+
typeof result === 'string'
|
|
121
|
+
? { content: [{ type: 'text', text: result }] }
|
|
122
|
+
: result;
|
|
123
|
+
host.sendToolResult(formattedResult);
|
|
124
|
+
}
|
|
125
|
+
}, [host, isLaunched, result, status, resourceUri, appSessionId, name]);
|
|
126
|
+
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
if (status === 'executing' && lastStatusRef.current !== 'executing') {
|
|
129
|
+
sentInputRef.current = false;
|
|
130
|
+
sentResultRef.current = false;
|
|
131
|
+
}
|
|
132
|
+
lastStatusRef.current = status;
|
|
133
|
+
}, [status]);
|
|
134
|
+
|
|
135
|
+
if (!metadata || !sseClient) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const displayError = error || hostError;
|
|
140
|
+
if (displayError) {
|
|
141
|
+
return (
|
|
142
|
+
<div className={`p-4 bg-red-900/20 border border-red-700 rounded text-red-200 ${className || ''}`}>
|
|
143
|
+
Error: {displayError.message || String(displayError)}
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<div className={`w-full border border-gray-700 rounded overflow-hidden bg-white min-h-96 my-2 relative ${className || ''}`}>
|
|
150
|
+
<iframe
|
|
151
|
+
ref={iframeRef}
|
|
152
|
+
sandbox="allow-scripts allow-same-origin allow-forms allow-modals allow-popups allow-downloads"
|
|
153
|
+
className="w-full h-full min-h-96"
|
|
154
|
+
style={{ height: 'auto' }}
|
|
155
|
+
title="MCP App"
|
|
156
|
+
/>
|
|
157
|
+
{!isLaunched && (
|
|
158
|
+
<div className="absolute inset-0 bg-gray-900/50 flex items-center justify-center pointer-events-none">
|
|
159
|
+
<div className="w-6 h-6 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" />
|
|
160
|
+
</div>
|
|
161
|
+
)}
|
|
162
|
+
</div>
|
|
163
|
+
);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Helpers scoped to one `mcpClient`. Pass the client here once; `McpAppRenderer` only needs per-tool props (`name`, `input`, `result`, `status`).
|
|
168
|
+
*
|
|
169
|
+
* @param mcpClient - From `useMcp()` or context (for example `useMcpContext()`).
|
|
170
|
+
*/
|
|
171
|
+
export function useMcpApps(mcpClient: McpClient | null) {
|
|
172
|
+
// Stable `McpAppRenderer` type: parent re-renders and `connections` updates must not remount the iframe.
|
|
173
|
+
const clientRef = useRef(mcpClient);
|
|
174
|
+
clientRef.current = mcpClient;
|
|
175
|
+
|
|
176
|
+
const getAppMetadata = useCallback(
|
|
177
|
+
(toolName: string) => getMcpAppMetadata(clientRef.current, toolName),
|
|
178
|
+
[]
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
const McpAppRenderer = useMemo(() => {
|
|
182
|
+
const Renderer = memo(function McpAppRenderer(props: McpAppRendererProps) {
|
|
183
|
+
return <McpAppView clientRef={clientRef} {...props} />;
|
|
184
|
+
});
|
|
185
|
+
Renderer.displayName = 'McpAppRenderer';
|
|
186
|
+
return Renderer;
|
|
187
|
+
}, []);
|
|
188
|
+
|
|
189
|
+
return { getAppMetadata, McpAppRenderer };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function extractToolName(fullName: string): string {
|
|
193
|
+
const match = fullName.match(/(?:tool_[^_]+_)?(.+)$/);
|
|
194
|
+
return match?.[1] || fullName;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function getMcpAppMetadata(
|
|
198
|
+
mcpClient: McpClient | null,
|
|
199
|
+
toolName: string
|
|
200
|
+
): McpAppMetadata | undefined {
|
|
201
|
+
if (!mcpClient) return undefined;
|
|
202
|
+
|
|
203
|
+
const extractedName = extractToolName(toolName);
|
|
204
|
+
|
|
205
|
+
for (const conn of mcpClient.connections) {
|
|
206
|
+
for (const tool of conn.tools) {
|
|
207
|
+
const candidateName = extractToolName(tool.name);
|
|
208
|
+
const resourceUri =
|
|
209
|
+
tool.mcpApp?.resourceUri ??
|
|
210
|
+
tool._meta?.ui?.resourceUri ??
|
|
211
|
+
tool._meta?.['ui/resourceUri'];
|
|
212
|
+
|
|
213
|
+
if (resourceUri && candidateName === extractedName) {
|
|
214
|
+
return {
|
|
215
|
+
toolName: candidateName,
|
|
216
|
+
resourceUri,
|
|
217
|
+
sessionId: conn.sessionId,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|