@mcp-ts/sdk 1.3.6 → 1.3.9
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 +398 -404
- package/dist/adapters/agui-adapter.d.mts +1 -1
- package/dist/adapters/agui-adapter.d.ts +1 -1
- package/dist/adapters/agui-adapter.js +2 -2
- package/dist/adapters/agui-adapter.js.map +1 -1
- package/dist/adapters/agui-adapter.mjs +2 -2
- package/dist/adapters/agui-adapter.mjs.map +1 -1
- package/dist/adapters/agui-middleware.d.mts +1 -1
- package/dist/adapters/agui-middleware.d.ts +1 -1
- package/dist/adapters/agui-middleware.js.map +1 -1
- package/dist/adapters/agui-middleware.mjs.map +1 -1
- package/dist/adapters/ai-adapter.d.mts +1 -1
- package/dist/adapters/ai-adapter.d.ts +1 -1
- package/dist/adapters/ai-adapter.js +1 -1
- package/dist/adapters/ai-adapter.js.map +1 -1
- package/dist/adapters/ai-adapter.mjs +1 -1
- package/dist/adapters/ai-adapter.mjs.map +1 -1
- package/dist/adapters/langchain-adapter.d.mts +1 -1
- package/dist/adapters/langchain-adapter.d.ts +1 -1
- package/dist/adapters/langchain-adapter.js +1 -1
- package/dist/adapters/langchain-adapter.js.map +1 -1
- package/dist/adapters/langchain-adapter.mjs +1 -1
- package/dist/adapters/langchain-adapter.mjs.map +1 -1
- package/dist/adapters/mastra-adapter.d.mts +1 -1
- package/dist/adapters/mastra-adapter.d.ts +1 -1
- package/dist/adapters/mastra-adapter.js +1 -1
- package/dist/adapters/mastra-adapter.js.map +1 -1
- package/dist/adapters/mastra-adapter.mjs +1 -1
- package/dist/adapters/mastra-adapter.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 +2 -2
- package/dist/client/react.d.ts +2 -2
- package/dist/client/react.js +25 -2
- package/dist/client/react.js.map +1 -1
- package/dist/client/react.mjs +26 -3
- 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.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +134 -71
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +134 -71
- package/dist/index.mjs.map +1 -1
- package/dist/{multi-session-client-BYLarghq.d.ts → multi-session-client-CHE8QpVE.d.ts} +75 -5
- package/dist/{multi-session-client-CzhMkE0k.d.mts → multi-session-client-CQsRbxYI.d.mts} +75 -5
- package/dist/server/index.d.mts +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +134 -71
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +134 -71
- package/dist/server/index.mjs.map +1 -1
- package/dist/shared/index.js +10 -2
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/index.mjs +10 -2
- package/dist/shared/index.mjs.map +1 -1
- package/package.json +185 -185
- package/src/adapters/agui-adapter.ts +222 -222
- package/src/adapters/agui-middleware.ts +382 -382
- package/src/adapters/ai-adapter.ts +115 -115
- package/src/adapters/langchain-adapter.ts +127 -127
- package/src/adapters/mastra-adapter.ts +126 -126
- 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 +16 -16
- package/src/client/react/use-app-host.ts +73 -73
- package/src/client/react/use-mcp-apps.tsx +247 -214
- package/src/client/react/use-mcp.ts +641 -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 -132
- package/src/server/mcp/oauth-client.ts +1188 -1188
- package/src/server/mcp/storage-oauth-provider.ts +272 -272
- package/src/server/storage/file-backend.ts +157 -170
- package/src/server/storage/index.ts +176 -175
- package/src/server/storage/memory-backend.ts +123 -136
- package/src/server/storage/redis-backend.ts +276 -289
- package/src/server/storage/redis.ts +160 -160
- package/src/server/storage/sqlite-backend.ts +182 -186
- package/src/server/storage/supabase-backend.ts +228 -227
- 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 -16
- package/supabase/migrations/20260330195700_install_mcp_sessions.sql +84 -84
|
@@ -1,222 +1,222 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP Adapter for AG-UI Integration
|
|
3
|
-
*
|
|
4
|
-
* This adapter transforms MCP tools into formats compatible with AG-UI agents.
|
|
5
|
-
* It provides tools with handlers for server-side execution and tool definitions
|
|
6
|
-
* in JSON Schema format for passing to remote agents.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```typescript
|
|
10
|
-
* import { MultiSessionClient } from '@mcp-ts/sdk/server';
|
|
11
|
-
* import { AguiAdapter } from '@mcp-ts/sdk/adapters/agui-adapter';
|
|
12
|
-
* import { createMcpMiddleware } from '@mcp-ts/sdk/adapters/agui-middleware';
|
|
13
|
-
* import { HttpAgent } from '@ag-ui/client';
|
|
14
|
-
*
|
|
15
|
-
* // Create MCP client
|
|
16
|
-
* const mcpClient = new MultiSessionClient('user_123');
|
|
17
|
-
* await mcpClient.connect();
|
|
18
|
-
*
|
|
19
|
-
* // Create adapter and get tools
|
|
20
|
-
* const adapter = new AguiAdapter(mcpClient);
|
|
21
|
-
* const tools = await adapter.getTools();
|
|
22
|
-
*
|
|
23
|
-
* // Use with AG-UI middleware
|
|
24
|
-
* const agent = new HttpAgent({ url: 'http://localhost:8000/agent' });
|
|
25
|
-
* agent.use(createMcpMiddleware({ tools }));
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
import { MCPClient } from '../server/mcp/oauth-client.js';
|
|
30
|
-
import { MultiSessionClient } from '../server/mcp/multi-session-client.js';
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Extended JSON Schema properties that Pydantic's strict validation rejects.
|
|
34
|
-
* These are valid JSON Schema extensions but not part of the core spec.
|
|
35
|
-
*/
|
|
36
|
-
const PYDANTIC_FORBIDDEN_PROPS = [
|
|
37
|
-
// JSON Schema meta-properties
|
|
38
|
-
'$schema', '$id', '$comment', '$defs', 'definitions',
|
|
39
|
-
// Extended properties used by some MCP servers (e.g., Apify)
|
|
40
|
-
'prefill', 'examples', 'enumTitles', 'enumDescriptions',
|
|
41
|
-
// Other common extensions
|
|
42
|
-
'deprecated', 'readOnly', 'writeOnly', 'contentMediaType', 'contentEncoding',
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Cleans a JSON Schema by removing meta-properties that cause issues with
|
|
47
|
-
* strict Pydantic validation (e.g., Google ADK, LangGraph).
|
|
48
|
-
*
|
|
49
|
-
* @param schema - The JSON Schema to clean
|
|
50
|
-
* @returns Cleaned schema without forbidden properties
|
|
51
|
-
*/
|
|
52
|
-
export function cleanSchema(schema: Record<string, any> | undefined): Record<string, any> {
|
|
53
|
-
if (!schema) {
|
|
54
|
-
return { type: 'object', properties: {} };
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const cleaned = { ...schema };
|
|
58
|
-
|
|
59
|
-
// Remove all forbidden properties
|
|
60
|
-
for (const prop of PYDANTIC_FORBIDDEN_PROPS) {
|
|
61
|
-
delete cleaned[prop];
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Recursively clean nested properties
|
|
65
|
-
if (cleaned.properties && typeof cleaned.properties === 'object') {
|
|
66
|
-
const cleanedProps: Record<string, any> = {};
|
|
67
|
-
for (const [key, value] of Object.entries(cleaned.properties)) {
|
|
68
|
-
if (typeof value === 'object' && value !== null) {
|
|
69
|
-
cleanedProps[key] = cleanSchema(value as Record<string, any>);
|
|
70
|
-
} else {
|
|
71
|
-
cleanedProps[key] = value;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
cleaned.properties = cleanedProps;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Clean items if it's an array schema
|
|
78
|
-
if (cleaned.items && typeof cleaned.items === 'object') {
|
|
79
|
-
cleaned.items = cleanSchema(cleaned.items);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Clean additionalProperties if it's an object schema
|
|
83
|
-
if (cleaned.additionalProperties && typeof cleaned.additionalProperties === 'object') {
|
|
84
|
-
cleaned.additionalProperties = cleanSchema(cleaned.additionalProperties);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return cleaned;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Configuration options for AguiAdapter
|
|
92
|
-
*/
|
|
93
|
-
export interface AguiAdapterOptions {
|
|
94
|
-
/**
|
|
95
|
-
* Prefix for tool names to avoid collision with other tools.
|
|
96
|
-
* @default serverId or 'mcp'
|
|
97
|
-
*/
|
|
98
|
-
prefix?: string;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* AG-UI Tool with handler for server-side execution.
|
|
103
|
-
*/
|
|
104
|
-
export interface AguiTool {
|
|
105
|
-
name: string;
|
|
106
|
-
description: string;
|
|
107
|
-
parameters?: Record<string, any>;
|
|
108
|
-
_meta?: Record<string, any>; // Add _meta to AguiTool
|
|
109
|
-
handler?: (args: any) => any | Promise<any>;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Tool definition format for passing to remote agents (without handler).
|
|
114
|
-
*/
|
|
115
|
-
export interface AguiToolDefinition {
|
|
116
|
-
name: string;
|
|
117
|
-
description: string;
|
|
118
|
-
parameters: Record<string, any>;
|
|
119
|
-
_meta?: Record<string, any>; // Add _meta to AguiToolDefinition
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Adapter that transforms MCP tools into AG-UI compatible formats.
|
|
124
|
-
*/
|
|
125
|
-
export class AguiAdapter {
|
|
126
|
-
constructor(
|
|
127
|
-
private client: MCPClient | MultiSessionClient,
|
|
128
|
-
private options: AguiAdapterOptions = {}
|
|
129
|
-
) { }
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Get tools with handlers for MCP tool execution.
|
|
133
|
-
*/
|
|
134
|
-
async getTools(): Promise<AguiTool[]> {
|
|
135
|
-
if (this.isMultiSession()) {
|
|
136
|
-
const clients = (this.client as MultiSessionClient).getClients();
|
|
137
|
-
const allTools: AguiTool[] = [];
|
|
138
|
-
for (const client of clients) {
|
|
139
|
-
allTools.push(...await this.transformTools(client));
|
|
140
|
-
}
|
|
141
|
-
return allTools;
|
|
142
|
-
}
|
|
143
|
-
return this.transformTools(this.client as MCPClient);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Get tool definitions in JSON Schema format for passing to remote agents.
|
|
148
|
-
*/
|
|
149
|
-
async getToolDefinitions(): Promise<AguiToolDefinition[]> {
|
|
150
|
-
if (this.isMultiSession()) {
|
|
151
|
-
const clients = (this.client as MultiSessionClient).getClients();
|
|
152
|
-
const allTools: AguiToolDefinition[] = [];
|
|
153
|
-
for (const client of clients) {
|
|
154
|
-
allTools.push(...await this.transformToolDefinitions(client));
|
|
155
|
-
}
|
|
156
|
-
return allTools;
|
|
157
|
-
}
|
|
158
|
-
return this.transformToolDefinitions(this.client as MCPClient);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Get tools as a function (for dynamic loading).
|
|
163
|
-
*/
|
|
164
|
-
getToolsFunction(): () => Promise<AguiTool[]> {
|
|
165
|
-
return () => this.getTools();
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
private isMultiSession(): boolean {
|
|
169
|
-
return typeof (this.client as any).getClients === 'function';
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
private async transformTools(client: MCPClient): Promise<AguiTool[]> {
|
|
173
|
-
if (!client.isConnected()) return [];
|
|
174
|
-
|
|
175
|
-
const result = await client.listTools();
|
|
176
|
-
const serverId = (typeof (client as any).getServerId === 'function'
|
|
177
|
-
? (client as any).getServerId()
|
|
178
|
-
: undefined) as string | undefined;
|
|
179
|
-
const normalizedPrefix =
|
|
180
|
-
const prefix = `tool_${normalizedPrefix}`;
|
|
181
|
-
|
|
182
|
-
return result.tools.map(tool => {
|
|
183
|
-
// Type assertion to access _meta if it exists on the tool object (it comes from MCP SDK)
|
|
184
|
-
const mcpTool = tool as any;
|
|
185
|
-
const mcpToolName = tool.name;
|
|
186
|
-
return {
|
|
187
|
-
name: `${prefix}_${tool.name}`,
|
|
188
|
-
description: tool.description || `Execute ${tool.name}`,
|
|
189
|
-
parameters: cleanSchema(tool.inputSchema),
|
|
190
|
-
_meta: { ...mcpTool._meta, sessionId: (client as any).getSessionId?.() },
|
|
191
|
-
handler: async (args: any) => {
|
|
192
|
-
// Call the actual MCP tool
|
|
193
|
-
const callResult = await (client as any).callTool(mcpToolName, args ?? {});
|
|
194
|
-
|
|
195
|
-
// Return the raw result object so middleware can inspect `_meta` (e.g. for UI triggers)
|
|
196
|
-
return callResult;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
private async transformToolDefinitions(client: MCPClient): Promise<AguiToolDefinition[]> {
|
|
203
|
-
if (!client.isConnected()) return [];
|
|
204
|
-
|
|
205
|
-
const result = await client.listTools();
|
|
206
|
-
const serverId = (typeof (client as any).getServerId === 'function'
|
|
207
|
-
? (client as any).getServerId()
|
|
208
|
-
: undefined) as string | undefined;
|
|
209
|
-
const normalizedPrefix =
|
|
210
|
-
const prefix = `tool_${normalizedPrefix}`;
|
|
211
|
-
|
|
212
|
-
return result.tools.map(tool => {
|
|
213
|
-
const mcpTool = tool as any;
|
|
214
|
-
return {
|
|
215
|
-
name: `${prefix}_${tool.name}`,
|
|
216
|
-
description: tool.description || `Execute ${tool.name}`,
|
|
217
|
-
parameters: cleanSchema(tool.inputSchema),
|
|
218
|
-
_meta: { ...mcpTool._meta, sessionId: (client as any).getSessionId?.() },
|
|
219
|
-
};
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* MCP Adapter for AG-UI Integration
|
|
3
|
+
*
|
|
4
|
+
* This adapter transforms MCP tools into formats compatible with AG-UI agents.
|
|
5
|
+
* It provides tools with handlers for server-side execution and tool definitions
|
|
6
|
+
* in JSON Schema format for passing to remote agents.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { MultiSessionClient } from '@mcp-ts/sdk/server';
|
|
11
|
+
* import { AguiAdapter } from '@mcp-ts/sdk/adapters/agui-adapter';
|
|
12
|
+
* import { createMcpMiddleware } from '@mcp-ts/sdk/adapters/agui-middleware';
|
|
13
|
+
* import { HttpAgent } from '@ag-ui/client';
|
|
14
|
+
*
|
|
15
|
+
* // Create MCP client
|
|
16
|
+
* const mcpClient = new MultiSessionClient('user_123');
|
|
17
|
+
* await mcpClient.connect();
|
|
18
|
+
*
|
|
19
|
+
* // Create adapter and get tools
|
|
20
|
+
* const adapter = new AguiAdapter(mcpClient);
|
|
21
|
+
* const tools = await adapter.getTools();
|
|
22
|
+
*
|
|
23
|
+
* // Use with AG-UI middleware
|
|
24
|
+
* const agent = new HttpAgent({ url: 'http://localhost:8000/agent' });
|
|
25
|
+
* agent.use(createMcpMiddleware({ tools }));
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { MCPClient } from '../server/mcp/oauth-client.js';
|
|
30
|
+
import { MultiSessionClient } from '../server/mcp/multi-session-client.js';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Extended JSON Schema properties that Pydantic's strict validation rejects.
|
|
34
|
+
* These are valid JSON Schema extensions but not part of the core spec.
|
|
35
|
+
*/
|
|
36
|
+
const PYDANTIC_FORBIDDEN_PROPS = [
|
|
37
|
+
// JSON Schema meta-properties
|
|
38
|
+
'$schema', '$id', '$comment', '$defs', 'definitions',
|
|
39
|
+
// Extended properties used by some MCP servers (e.g., Apify)
|
|
40
|
+
'prefill', 'examples', 'enumTitles', 'enumDescriptions',
|
|
41
|
+
// Other common extensions
|
|
42
|
+
'deprecated', 'readOnly', 'writeOnly', 'contentMediaType', 'contentEncoding',
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Cleans a JSON Schema by removing meta-properties that cause issues with
|
|
47
|
+
* strict Pydantic validation (e.g., Google ADK, LangGraph).
|
|
48
|
+
*
|
|
49
|
+
* @param schema - The JSON Schema to clean
|
|
50
|
+
* @returns Cleaned schema without forbidden properties
|
|
51
|
+
*/
|
|
52
|
+
export function cleanSchema(schema: Record<string, any> | undefined): Record<string, any> {
|
|
53
|
+
if (!schema) {
|
|
54
|
+
return { type: 'object', properties: {} };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const cleaned = { ...schema };
|
|
58
|
+
|
|
59
|
+
// Remove all forbidden properties
|
|
60
|
+
for (const prop of PYDANTIC_FORBIDDEN_PROPS) {
|
|
61
|
+
delete cleaned[prop];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Recursively clean nested properties
|
|
65
|
+
if (cleaned.properties && typeof cleaned.properties === 'object') {
|
|
66
|
+
const cleanedProps: Record<string, any> = {};
|
|
67
|
+
for (const [key, value] of Object.entries(cleaned.properties)) {
|
|
68
|
+
if (typeof value === 'object' && value !== null) {
|
|
69
|
+
cleanedProps[key] = cleanSchema(value as Record<string, any>);
|
|
70
|
+
} else {
|
|
71
|
+
cleanedProps[key] = value;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
cleaned.properties = cleanedProps;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Clean items if it's an array schema
|
|
78
|
+
if (cleaned.items && typeof cleaned.items === 'object') {
|
|
79
|
+
cleaned.items = cleanSchema(cleaned.items);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Clean additionalProperties if it's an object schema
|
|
83
|
+
if (cleaned.additionalProperties && typeof cleaned.additionalProperties === 'object') {
|
|
84
|
+
cleaned.additionalProperties = cleanSchema(cleaned.additionalProperties);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return cleaned;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Configuration options for AguiAdapter
|
|
92
|
+
*/
|
|
93
|
+
export interface AguiAdapterOptions {
|
|
94
|
+
/**
|
|
95
|
+
* Prefix for tool names to avoid collision with other tools.
|
|
96
|
+
* @default serverId or 'mcp'
|
|
97
|
+
*/
|
|
98
|
+
prefix?: string;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* AG-UI Tool with handler for server-side execution.
|
|
103
|
+
*/
|
|
104
|
+
export interface AguiTool {
|
|
105
|
+
name: string;
|
|
106
|
+
description: string;
|
|
107
|
+
parameters?: Record<string, any>;
|
|
108
|
+
_meta?: Record<string, any>; // Add _meta to AguiTool
|
|
109
|
+
handler?: (args: any) => any | Promise<any>;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Tool definition format for passing to remote agents (without handler).
|
|
114
|
+
*/
|
|
115
|
+
export interface AguiToolDefinition {
|
|
116
|
+
name: string;
|
|
117
|
+
description: string;
|
|
118
|
+
parameters: Record<string, any>;
|
|
119
|
+
_meta?: Record<string, any>; // Add _meta to AguiToolDefinition
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Adapter that transforms MCP tools into AG-UI compatible formats.
|
|
124
|
+
*/
|
|
125
|
+
export class AguiAdapter {
|
|
126
|
+
constructor(
|
|
127
|
+
private client: MCPClient | MultiSessionClient,
|
|
128
|
+
private options: AguiAdapterOptions = {}
|
|
129
|
+
) { }
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Get tools with handlers for MCP tool execution.
|
|
133
|
+
*/
|
|
134
|
+
async getTools(): Promise<AguiTool[]> {
|
|
135
|
+
if (this.isMultiSession()) {
|
|
136
|
+
const clients = (this.client as MultiSessionClient).getClients();
|
|
137
|
+
const allTools: AguiTool[] = [];
|
|
138
|
+
for (const client of clients) {
|
|
139
|
+
allTools.push(...await this.transformTools(client));
|
|
140
|
+
}
|
|
141
|
+
return allTools;
|
|
142
|
+
}
|
|
143
|
+
return this.transformTools(this.client as MCPClient);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get tool definitions in JSON Schema format for passing to remote agents.
|
|
148
|
+
*/
|
|
149
|
+
async getToolDefinitions(): Promise<AguiToolDefinition[]> {
|
|
150
|
+
if (this.isMultiSession()) {
|
|
151
|
+
const clients = (this.client as MultiSessionClient).getClients();
|
|
152
|
+
const allTools: AguiToolDefinition[] = [];
|
|
153
|
+
for (const client of clients) {
|
|
154
|
+
allTools.push(...await this.transformToolDefinitions(client));
|
|
155
|
+
}
|
|
156
|
+
return allTools;
|
|
157
|
+
}
|
|
158
|
+
return this.transformToolDefinitions(this.client as MCPClient);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Get tools as a function (for dynamic loading).
|
|
163
|
+
*/
|
|
164
|
+
getToolsFunction(): () => Promise<AguiTool[]> {
|
|
165
|
+
return () => this.getTools();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private isMultiSession(): boolean {
|
|
169
|
+
return typeof (this.client as any).getClients === 'function';
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private async transformTools(client: MCPClient): Promise<AguiTool[]> {
|
|
173
|
+
if (!client.isConnected()) return [];
|
|
174
|
+
|
|
175
|
+
const result = await client.listTools();
|
|
176
|
+
const serverId = (typeof (client as any).getServerId === 'function'
|
|
177
|
+
? (client as any).getServerId()
|
|
178
|
+
: undefined) as string | undefined;
|
|
179
|
+
const normalizedPrefix = this.options.prefix?.replace(/-/g, '') ?? serverId?.replace(/-/g, '').substring(0, 8) ?? 'mcp';
|
|
180
|
+
const prefix = `tool_${normalizedPrefix}`;
|
|
181
|
+
|
|
182
|
+
return result.tools.map(tool => {
|
|
183
|
+
// Type assertion to access _meta if it exists on the tool object (it comes from MCP SDK)
|
|
184
|
+
const mcpTool = tool as any;
|
|
185
|
+
const mcpToolName = tool.name;
|
|
186
|
+
return {
|
|
187
|
+
name: `${prefix}_${tool.name}`,
|
|
188
|
+
description: tool.description || `Execute ${tool.name}`,
|
|
189
|
+
parameters: cleanSchema(tool.inputSchema),
|
|
190
|
+
_meta: { ...mcpTool._meta, sessionId: (client as any).getSessionId?.() },
|
|
191
|
+
handler: async (args: any) => {
|
|
192
|
+
// Call the actual MCP tool
|
|
193
|
+
const callResult = await (client as any).callTool(mcpToolName, args ?? {});
|
|
194
|
+
|
|
195
|
+
// Return the raw result object so middleware can inspect `_meta` (e.g. for UI triggers)
|
|
196
|
+
return callResult;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private async transformToolDefinitions(client: MCPClient): Promise<AguiToolDefinition[]> {
|
|
203
|
+
if (!client.isConnected()) return [];
|
|
204
|
+
|
|
205
|
+
const result = await client.listTools();
|
|
206
|
+
const serverId = (typeof (client as any).getServerId === 'function'
|
|
207
|
+
? (client as any).getServerId()
|
|
208
|
+
: undefined) as string | undefined;
|
|
209
|
+
const normalizedPrefix = this.options.prefix?.replace(/-/g, '') ?? serverId?.replace(/-/g, '').substring(0, 8) ?? 'mcp';
|
|
210
|
+
const prefix = `tool_${normalizedPrefix}`;
|
|
211
|
+
|
|
212
|
+
return result.tools.map(tool => {
|
|
213
|
+
const mcpTool = tool as any;
|
|
214
|
+
return {
|
|
215
|
+
name: `${prefix}_${tool.name}`,
|
|
216
|
+
description: tool.description || `Execute ${tool.name}`,
|
|
217
|
+
parameters: cleanSchema(tool.inputSchema),
|
|
218
|
+
_meta: { ...mcpTool._meta, sessionId: (client as any).getSessionId?.() },
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|