@mcp-ts/sdk 1.0.0 → 1.0.1
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 +3 -3
- package/dist/adapters/agui-adapter.d.mts +19 -42
- package/dist/adapters/agui-adapter.d.ts +19 -42
- package/dist/adapters/agui-adapter.js +69 -69
- package/dist/adapters/agui-adapter.js.map +1 -1
- package/dist/adapters/agui-adapter.mjs +69 -70
- package/dist/adapters/agui-adapter.mjs.map +1 -1
- package/dist/adapters/agui-middleware.d.mts +24 -136
- package/dist/adapters/agui-middleware.d.ts +24 -136
- package/dist/adapters/agui-middleware.js +275 -350
- package/dist/adapters/agui-middleware.js.map +1 -1
- package/dist/adapters/agui-middleware.mjs +275 -350
- package/dist/adapters/agui-middleware.mjs.map +1 -1
- package/dist/client/index.d.mts +2 -2
- package/dist/client/index.d.ts +2 -2
- package/dist/client/react.d.mts +2 -2
- package/dist/client/react.d.ts +2 -2
- package/dist/client/vue.d.mts +2 -2
- package/dist/client/vue.d.ts +2 -2
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -1
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.d.mts +3 -3
- package/dist/server/index.d.ts +3 -3
- package/dist/server/index.js +2 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +2 -1
- package/dist/server/index.mjs.map +1 -1
- package/dist/shared/index.d.mts +1 -1
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/index.mjs.map +1 -1
- package/dist/{types-SbDlA2VX.d.mts → types-CLccx9wW.d.mts} +1 -1
- package/dist/{types-SbDlA2VX.d.ts → types-CLccx9wW.d.ts} +1 -1
- package/package.json +2 -2
- package/src/adapters/agui-adapter.ts +98 -109
- package/src/adapters/agui-middleware.ts +424 -512
- package/src/server/handlers/sse-handler.ts +4 -1
- package/src/server/storage/types.ts +1 -1
- package/src/shared/types.ts +1 -1
|
@@ -4,410 +4,335 @@ var rxjs = require('rxjs');
|
|
|
4
4
|
var client = require('@ag-ui/client');
|
|
5
5
|
|
|
6
6
|
var __defProp = Object.defineProperty;
|
|
7
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
7
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
|
-
var __esm = (fn, res) => function __init() {
|
|
10
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
-
};
|
|
12
|
-
var __export = (target, all) => {
|
|
13
|
-
for (var name in all)
|
|
14
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
-
};
|
|
16
8
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
17
9
|
|
|
18
10
|
// src/adapters/agui-adapter.ts
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
* 2. Extracts text content from the result
|
|
37
|
-
* 3. Returns the result as a string or JSON
|
|
38
|
-
*
|
|
39
|
-
* @returns Array of AguiTool objects
|
|
40
|
-
*/
|
|
41
|
-
async getTools() {
|
|
42
|
-
const isMultiSession = typeof this.client.getClients === "function";
|
|
43
|
-
if (isMultiSession) {
|
|
44
|
-
const clients = this.client.getClients();
|
|
45
|
-
const allTools = [];
|
|
46
|
-
for (const client of clients) {
|
|
47
|
-
const tools = await this.transformTools(client);
|
|
48
|
-
allTools.push(...tools);
|
|
49
|
-
}
|
|
50
|
-
return allTools;
|
|
51
|
-
}
|
|
52
|
-
return this.transformTools(this.client);
|
|
53
|
-
}
|
|
54
|
-
async transformTools(client) {
|
|
55
|
-
if (!client.isConnected()) {
|
|
56
|
-
return [];
|
|
57
|
-
}
|
|
58
|
-
const result = await client.listTools();
|
|
59
|
-
const prefix = this.options.prefix ?? client.getServerId() ?? "mcp";
|
|
60
|
-
const tools = [];
|
|
61
|
-
for (const tool of result.tools) {
|
|
62
|
-
const toolName = `${prefix}_${tool.name}`;
|
|
63
|
-
tools.push({
|
|
64
|
-
name: toolName,
|
|
65
|
-
description: tool.description || `Execute ${tool.name}`,
|
|
66
|
-
parameters: tool.inputSchema || { type: "object", properties: {} },
|
|
67
|
-
handler: async (args) => {
|
|
68
|
-
console.log(`[AguiAdapter] Executing MCP tool: ${tool.name}`, args);
|
|
69
|
-
const result2 = await client.callTool(tool.name, args);
|
|
70
|
-
if (result2.content && Array.isArray(result2.content)) {
|
|
71
|
-
const textContent = result2.content.filter((c) => c.type === "text").map((c) => c.text).join("\n");
|
|
72
|
-
return textContent || result2;
|
|
73
|
-
}
|
|
74
|
-
return result2;
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
return tools;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Get tools as a function (for dynamic loading).
|
|
82
|
-
*
|
|
83
|
-
* @returns Function that returns a Promise of tools
|
|
84
|
-
*/
|
|
85
|
-
getToolsFunction() {
|
|
86
|
-
return async () => this.getTools();
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Get tool definitions in JSON Schema format for passing to remote agents.
|
|
90
|
-
*
|
|
91
|
-
* This format is compatible with:
|
|
92
|
-
* - OpenAI's function calling API
|
|
93
|
-
* - AG-UI input.tools format
|
|
94
|
-
* - Most LLM tool/function calling implementations
|
|
95
|
-
*
|
|
96
|
-
* @returns Array of AguiToolDefinition objects
|
|
97
|
-
*/
|
|
98
|
-
async getToolDefinitions() {
|
|
99
|
-
const isMultiSession = typeof this.client.getClients === "function";
|
|
100
|
-
if (isMultiSession) {
|
|
101
|
-
const clients = this.client.getClients();
|
|
102
|
-
const allTools = [];
|
|
103
|
-
for (const client of clients) {
|
|
104
|
-
const tools = await this.transformToolDefinitions(client);
|
|
105
|
-
allTools.push(...tools);
|
|
106
|
-
}
|
|
107
|
-
return allTools;
|
|
108
|
-
}
|
|
109
|
-
return this.transformToolDefinitions(this.client);
|
|
110
|
-
}
|
|
111
|
-
async transformToolDefinitions(client) {
|
|
112
|
-
if (!client.isConnected()) {
|
|
113
|
-
return [];
|
|
114
|
-
}
|
|
115
|
-
const result = await client.listTools();
|
|
116
|
-
const prefix = this.options.prefix ?? client.getServerId() ?? "mcp";
|
|
117
|
-
const tools = [];
|
|
118
|
-
for (const tool of result.tools) {
|
|
119
|
-
tools.push({
|
|
120
|
-
name: `${prefix}_${tool.name}`,
|
|
121
|
-
description: tool.description || `Execute ${tool.name}`,
|
|
122
|
-
parameters: tool.inputSchema || { type: "object", properties: {} }
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
return tools;
|
|
11
|
+
function cleanSchema(schema) {
|
|
12
|
+
if (!schema) {
|
|
13
|
+
return { type: "object", properties: {} };
|
|
14
|
+
}
|
|
15
|
+
const cleaned = { ...schema };
|
|
16
|
+
delete cleaned.$schema;
|
|
17
|
+
delete cleaned.$id;
|
|
18
|
+
delete cleaned.$comment;
|
|
19
|
+
delete cleaned.$defs;
|
|
20
|
+
delete cleaned.definitions;
|
|
21
|
+
if (cleaned.properties && typeof cleaned.properties === "object") {
|
|
22
|
+
const cleanedProps = {};
|
|
23
|
+
for (const [key, value] of Object.entries(cleaned.properties)) {
|
|
24
|
+
if (typeof value === "object" && value !== null) {
|
|
25
|
+
cleanedProps[key] = cleanSchema(value);
|
|
26
|
+
} else {
|
|
27
|
+
cleanedProps[key] = value;
|
|
126
28
|
}
|
|
127
|
-
}
|
|
29
|
+
}
|
|
30
|
+
cleaned.properties = cleanedProps;
|
|
128
31
|
}
|
|
129
|
-
|
|
32
|
+
if (cleaned.items && typeof cleaned.items === "object") {
|
|
33
|
+
cleaned.items = cleanSchema(cleaned.items);
|
|
34
|
+
}
|
|
35
|
+
if (cleaned.additionalProperties && typeof cleaned.additionalProperties === "object") {
|
|
36
|
+
cleaned.additionalProperties = cleanSchema(cleaned.additionalProperties);
|
|
37
|
+
}
|
|
38
|
+
return cleaned;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/adapters/agui-middleware.ts
|
|
130
42
|
var McpMiddleware = class extends client.Middleware {
|
|
131
43
|
constructor(config) {
|
|
132
44
|
super();
|
|
133
|
-
__publicField(this, "client");
|
|
134
|
-
__publicField(this, "toolPrefix");
|
|
135
|
-
__publicField(this, "actions");
|
|
136
45
|
__publicField(this, "tools");
|
|
137
|
-
__publicField(this, "
|
|
138
|
-
this
|
|
139
|
-
this.
|
|
140
|
-
this.
|
|
141
|
-
this.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Convert actions to AG-UI tool format
|
|
149
|
-
*/
|
|
150
|
-
actionsToTools(actions) {
|
|
151
|
-
return actions.map((action) => ({
|
|
152
|
-
name: action.name,
|
|
153
|
-
description: action.description,
|
|
154
|
-
parameters: action.parameters || { type: "object", properties: {} }
|
|
46
|
+
__publicField(this, "toolSchemas");
|
|
47
|
+
__publicField(this, "maxResultLength");
|
|
48
|
+
this.tools = config.tools;
|
|
49
|
+
this.maxResultLength = config.maxResultLength ?? 5e4;
|
|
50
|
+
this.toolSchemas = this.tools.map((t) => ({
|
|
51
|
+
name: t.name,
|
|
52
|
+
description: t.description,
|
|
53
|
+
parameters: cleanSchema(t.parameters)
|
|
155
54
|
}));
|
|
156
55
|
}
|
|
157
|
-
/**
|
|
158
|
-
* Check if a tool name is an MCP tool (matches the configured prefix)
|
|
159
|
-
*/
|
|
160
56
|
isMcpTool(toolName) {
|
|
161
|
-
return
|
|
57
|
+
return this.tools.some((t) => t.name === toolName);
|
|
162
58
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
59
|
+
parseArgs(argsString) {
|
|
60
|
+
if (!argsString?.trim()) return {};
|
|
61
|
+
try {
|
|
62
|
+
return JSON.parse(argsString);
|
|
63
|
+
} catch {
|
|
64
|
+
const trimmed = argsString.trim();
|
|
65
|
+
if (trimmed.includes("}{")) {
|
|
66
|
+
const firstObject = trimmed.slice(0, trimmed.indexOf("}{") + 1);
|
|
67
|
+
try {
|
|
68
|
+
return JSON.parse(firstObject);
|
|
69
|
+
} catch {
|
|
70
|
+
console.error(`[McpMiddleware] Failed to parse JSON:`, firstObject);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
console.error(`[McpMiddleware] Failed to parse args:`, argsString);
|
|
74
|
+
return {};
|
|
75
|
+
}
|
|
172
76
|
}
|
|
173
|
-
/**
|
|
174
|
-
* Execute an MCP tool and return the result as a string
|
|
175
|
-
*/
|
|
176
77
|
async executeTool(toolName, args) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
return `Error: Tool not found: ${toolName}`;
|
|
181
|
-
}
|
|
182
|
-
if (!action.handler) {
|
|
183
|
-
return `Error: Tool has no handler: ${toolName}`;
|
|
78
|
+
const tool = this.tools.find((t) => t.name === toolName);
|
|
79
|
+
if (!tool?.handler) {
|
|
80
|
+
return `Error: Tool ${tool ? "has no handler" : "not found"}: ${toolName}`;
|
|
184
81
|
}
|
|
185
82
|
try {
|
|
186
83
|
console.log(`[McpMiddleware] Executing tool: ${toolName}`, args);
|
|
187
|
-
const result = await
|
|
188
|
-
|
|
189
|
-
|
|
84
|
+
const result = await tool.handler(args);
|
|
85
|
+
let resultStr = typeof result === "string" ? result : JSON.stringify(result);
|
|
86
|
+
if (resultStr.length > this.maxResultLength) {
|
|
87
|
+
const original = resultStr.length;
|
|
88
|
+
resultStr = resultStr.slice(0, this.maxResultLength) + `
|
|
89
|
+
|
|
90
|
+
[... Truncated from ${original} to ${this.maxResultLength} chars]`;
|
|
91
|
+
console.log(`[McpMiddleware] Tool result truncated from ${original} to ${this.maxResultLength} chars`);
|
|
92
|
+
}
|
|
93
|
+
console.log(`[McpMiddleware] Tool result:`, resultStr.slice(0, 200));
|
|
94
|
+
return resultStr;
|
|
190
95
|
} catch (error) {
|
|
191
96
|
console.error(`[McpMiddleware] Error executing tool:`, error);
|
|
192
|
-
return `Error
|
|
97
|
+
return `Error: ${error.message || String(error)}`;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
generateId(prefix) {
|
|
101
|
+
return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
102
|
+
}
|
|
103
|
+
ensureIds(input) {
|
|
104
|
+
const anyInput = input;
|
|
105
|
+
if (!anyInput.threadId) anyInput.threadId = this.generateId("mcp_thread");
|
|
106
|
+
if (!anyInput.runId) anyInput.runId = this.generateId("mcp_run");
|
|
107
|
+
}
|
|
108
|
+
/** Process tool call events and update state */
|
|
109
|
+
handleToolCallEvent(event, state) {
|
|
110
|
+
const { toolCallArgsBuffer, toolCallNames, pendingMcpCalls } = state;
|
|
111
|
+
if (event.type === client.EventType.TEXT_MESSAGE_CHUNK) {
|
|
112
|
+
const e = event;
|
|
113
|
+
if (e.delta) {
|
|
114
|
+
state.textContent = (state.textContent || "") + e.delta;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (event.type === client.EventType.TOOL_CALL_START) {
|
|
118
|
+
const e = event;
|
|
119
|
+
if (e.toolCallId && e.toolCallName) {
|
|
120
|
+
toolCallNames.set(e.toolCallId, e.toolCallName);
|
|
121
|
+
if (this.isMcpTool(e.toolCallName)) {
|
|
122
|
+
pendingMcpCalls.add(e.toolCallId);
|
|
123
|
+
}
|
|
124
|
+
console.log(`[McpMiddleware] TOOL_CALL_START: ${e.toolCallName} (id: ${e.toolCallId}, isMCP: ${this.isMcpTool(e.toolCallName)})`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (event.type === client.EventType.TOOL_CALL_ARGS) {
|
|
128
|
+
const e = event;
|
|
129
|
+
if (e.toolCallId && e.delta) {
|
|
130
|
+
const existing = toolCallArgsBuffer.get(e.toolCallId) || "";
|
|
131
|
+
toolCallArgsBuffer.set(e.toolCallId, existing + e.delta);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (event.type === client.EventType.TOOL_CALL_END) {
|
|
135
|
+
const e = event;
|
|
136
|
+
console.log(`[McpMiddleware] TOOL_CALL_END: ${toolCallNames.get(e.toolCallId) ?? "unknown"} (id: ${e.toolCallId})`);
|
|
137
|
+
}
|
|
138
|
+
if (event.type === client.EventType.MESSAGES_SNAPSHOT) {
|
|
139
|
+
const messages = event.messages || [];
|
|
140
|
+
if (messages.length > 0) {
|
|
141
|
+
const lastMsg = messages[messages.length - 1];
|
|
142
|
+
if (lastMsg.role === "assistant" && lastMsg.content) {
|
|
143
|
+
state.textContent = lastMsg.content;
|
|
144
|
+
}
|
|
145
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
146
|
+
const msg = messages[i];
|
|
147
|
+
const tools = Array.isArray(msg.toolCalls) ? msg.toolCalls : Array.isArray(msg.tool_calls) ? msg.tool_calls : [];
|
|
148
|
+
if (msg.role === "assistant" && tools.length > 0) {
|
|
149
|
+
for (const tc of tools) {
|
|
150
|
+
if (tc.id && tc.function?.name && !toolCallNames.has(tc.id)) {
|
|
151
|
+
toolCallNames.set(tc.id, tc.function.name);
|
|
152
|
+
toolCallArgsBuffer.set(tc.id, tc.function.arguments || "{}");
|
|
153
|
+
if (this.isMcpTool(tc.function.name)) {
|
|
154
|
+
pendingMcpCalls.add(tc.id);
|
|
155
|
+
console.log(`[McpMiddleware] MESSAGES_SNAPSHOT: Discovered ${tc.function.name} (id: ${tc.id})`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
193
163
|
}
|
|
194
164
|
}
|
|
195
|
-
/**
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
165
|
+
/** Execute pending MCP tools and return results */
|
|
166
|
+
async executeTools(state) {
|
|
167
|
+
const { toolCallArgsBuffer, toolCallNames, pendingMcpCalls } = state;
|
|
168
|
+
const results = [];
|
|
169
|
+
const promises = [...pendingMcpCalls].map(async (toolCallId) => {
|
|
170
|
+
const toolName = toolCallNames.get(toolCallId);
|
|
171
|
+
if (!toolName) return;
|
|
172
|
+
const args = this.parseArgs(toolCallArgsBuffer.get(toolCallId) || "{}");
|
|
173
|
+
console.log(`[McpMiddleware] Executing pending tool: ${toolName}`);
|
|
174
|
+
const result = await this.executeTool(toolName, args);
|
|
175
|
+
results.push({
|
|
176
|
+
toolCallId,
|
|
177
|
+
toolName,
|
|
178
|
+
result,
|
|
179
|
+
messageId: this.generateId("mcp_result")
|
|
180
|
+
});
|
|
181
|
+
pendingMcpCalls.delete(toolCallId);
|
|
182
|
+
});
|
|
183
|
+
await Promise.all(promises);
|
|
184
|
+
return results;
|
|
185
|
+
}
|
|
186
|
+
/** Emit tool results (without RUN_FINISHED - that's emitted when truly done) */
|
|
187
|
+
emitToolResults(observer, results) {
|
|
188
|
+
for (const { toolCallId, toolName, result, messageId } of results) {
|
|
189
|
+
observer.next({
|
|
190
|
+
type: client.EventType.TOOL_CALL_RESULT,
|
|
191
|
+
toolCallId,
|
|
192
|
+
messageId,
|
|
193
|
+
content: result,
|
|
194
|
+
role: "tool",
|
|
195
|
+
timestamp: Date.now()
|
|
196
|
+
});
|
|
197
|
+
console.log(`[McpMiddleware] Emitting TOOL_CALL_RESULT for: ${toolName}`);
|
|
198
|
+
}
|
|
200
199
|
}
|
|
201
|
-
/**
|
|
202
|
-
* Run the middleware, intercepting and executing MCP tool calls
|
|
203
|
-
*/
|
|
204
200
|
run(input, next) {
|
|
205
201
|
return new rxjs.Observable((observer) => {
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
202
|
+
const state = {
|
|
203
|
+
toolCallArgsBuffer: /* @__PURE__ */ new Map(),
|
|
204
|
+
toolCallNames: /* @__PURE__ */ new Map(),
|
|
205
|
+
pendingMcpCalls: /* @__PURE__ */ new Set(),
|
|
206
|
+
textContent: "",
|
|
207
|
+
error: false
|
|
208
|
+
};
|
|
209
|
+
this.ensureIds(input);
|
|
210
|
+
const anyInput = input;
|
|
211
|
+
console.log(`[McpMiddleware] === NEW RUN ===`);
|
|
212
|
+
console.log(`[McpMiddleware] threadId: ${anyInput.threadId}, runId: ${anyInput.runId}`);
|
|
213
|
+
console.log(`[McpMiddleware] messages: ${input.messages?.length ?? 0}, tools: ${this.tools?.length ?? 0}`);
|
|
214
|
+
if (this.toolSchemas?.length) {
|
|
215
|
+
input.tools = [...input.tools || [], ...this.toolSchemas];
|
|
216
|
+
console.log(`[McpMiddleware] Injected ${this.toolSchemas.length} tools:`, this.toolSchemas.map((t) => t.name));
|
|
217
217
|
}
|
|
218
|
-
const handleRunFinished = async (
|
|
219
|
-
if (
|
|
220
|
-
|
|
218
|
+
const handleRunFinished = async () => {
|
|
219
|
+
if (state.error) return;
|
|
220
|
+
if (state.pendingMcpCalls.size === 0) {
|
|
221
|
+
observer.next({
|
|
222
|
+
type: client.EventType.RUN_FINISHED,
|
|
223
|
+
threadId: anyInput.threadId,
|
|
224
|
+
runId: anyInput.runId,
|
|
225
|
+
timestamp: Date.now()
|
|
226
|
+
});
|
|
221
227
|
observer.complete();
|
|
222
228
|
return;
|
|
223
229
|
}
|
|
224
|
-
console.log(`[McpMiddleware] RUN_FINISHED
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
230
|
+
console.log(`[McpMiddleware] RUN_FINISHED with ${state.pendingMcpCalls.size} pending calls`);
|
|
231
|
+
const toolCalls = [];
|
|
232
|
+
for (const toolCallId of state.pendingMcpCalls) {
|
|
233
|
+
const name = state.toolCallNames.get(toolCallId);
|
|
234
|
+
const args = state.toolCallArgsBuffer.get(toolCallId) || "{}";
|
|
235
|
+
if (name) {
|
|
236
|
+
toolCalls.push({
|
|
237
|
+
id: toolCallId,
|
|
238
|
+
type: "function",
|
|
239
|
+
function: { name, arguments: args }
|
|
240
|
+
});
|
|
234
241
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
role: "tool",
|
|
244
|
-
timestamp: Date.now()
|
|
242
|
+
}
|
|
243
|
+
if (toolCalls.length > 0 || state.textContent) {
|
|
244
|
+
const assistantMsg = {
|
|
245
|
+
id: this.generateId("msg_ast"),
|
|
246
|
+
role: "assistant",
|
|
247
|
+
content: state.textContent || null,
|
|
248
|
+
// Ensure null if empty string for strict LLMs
|
|
249
|
+
tool_calls: toolCalls.length > 0 ? toolCalls : void 0
|
|
245
250
|
};
|
|
246
|
-
|
|
247
|
-
|
|
251
|
+
input.messages.push(assistantMsg);
|
|
252
|
+
console.log(`[McpMiddleware] Added assistant message to history before tools: ${state.textContent?.slice(0, 50)}... [${toolCalls.length} tools]`);
|
|
253
|
+
}
|
|
254
|
+
const results = await this.executeTools(state);
|
|
255
|
+
this.emitToolResults(observer, results);
|
|
256
|
+
console.log(`[McpMiddleware] Triggering continuation with ${results.length} results`);
|
|
257
|
+
for (const { toolCallId, result, messageId } of results) {
|
|
248
258
|
input.messages.push({
|
|
249
259
|
id: messageId,
|
|
250
260
|
role: "tool",
|
|
251
|
-
toolCallId,
|
|
261
|
+
tool_call_id: toolCallId,
|
|
252
262
|
content: result
|
|
253
263
|
});
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
264
|
+
}
|
|
265
|
+
state.toolCallArgsBuffer.clear();
|
|
266
|
+
state.toolCallNames.clear();
|
|
267
|
+
state.textContent = "";
|
|
268
|
+
anyInput.runId = this.generateId("mcp_run");
|
|
269
|
+
console.log(`[McpMiddleware] === CONTINUATION RUN === messages: ${input.messages.length}`);
|
|
270
|
+
next.run(input).subscribe({
|
|
271
|
+
next: (event) => {
|
|
272
|
+
if (state.error) return;
|
|
273
|
+
this.handleToolCallEvent(event, state);
|
|
274
|
+
if (event.type === client.EventType.RUN_ERROR) {
|
|
275
|
+
console.log(`[McpMiddleware] RUN_ERROR received in continuation`);
|
|
276
|
+
state.error = true;
|
|
277
|
+
observer.next(event);
|
|
278
|
+
observer.complete();
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (event.type === client.EventType.RUN_STARTED) {
|
|
282
|
+
console.log(`[McpMiddleware] Filtering RUN_STARTED from continuation`);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (event.type === client.EventType.RUN_FINISHED) {
|
|
286
|
+
if (state.pendingMcpCalls.size > 0) {
|
|
287
|
+
handleRunFinished();
|
|
288
|
+
} else {
|
|
289
|
+
observer.next(event);
|
|
290
|
+
observer.complete();
|
|
291
|
+
}
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
observer.next(event);
|
|
295
|
+
},
|
|
296
|
+
error: (err) => {
|
|
297
|
+
state.error = true;
|
|
298
|
+
observer.error(err);
|
|
299
|
+
},
|
|
300
|
+
complete: () => {
|
|
301
|
+
if (!state.error && state.pendingMcpCalls.size === 0) observer.complete();
|
|
302
|
+
}
|
|
263
303
|
});
|
|
264
|
-
console.log(`[McpMiddleware] Triggering new run`);
|
|
265
|
-
this.triggerNewRun(observer, input, next, toolCallArgsBuffer, toolCallNames, pendingMcpCalls);
|
|
266
304
|
};
|
|
267
305
|
const subscription = next.run(input).subscribe({
|
|
268
306
|
next: (event) => {
|
|
269
|
-
if (
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
if (event.type === client.EventType.TOOL_CALL_ARGS) {
|
|
281
|
-
const argsEvent = event;
|
|
282
|
-
if (argsEvent.toolCallId && argsEvent.delta) {
|
|
283
|
-
const existing = toolCallArgsBuffer.get(argsEvent.toolCallId) || "";
|
|
284
|
-
toolCallArgsBuffer.set(argsEvent.toolCallId, existing + argsEvent.delta);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
if (event.type === client.EventType.TOOL_CALL_END) {
|
|
288
|
-
const endEvent = event;
|
|
289
|
-
const toolName = toolCallNames.get(endEvent.toolCallId);
|
|
290
|
-
console.log(`[McpMiddleware] TOOL_CALL_END: ${toolName ?? "unknown"} (id: ${endEvent.toolCallId})`);
|
|
307
|
+
if (state.error) return;
|
|
308
|
+
this.handleToolCallEvent(event, state);
|
|
309
|
+
if (event.type === client.EventType.RUN_ERROR) {
|
|
310
|
+
console.log(`[McpMiddleware] RUN_ERROR received`);
|
|
311
|
+
state.error = true;
|
|
312
|
+
observer.next(event);
|
|
313
|
+
observer.complete();
|
|
314
|
+
return;
|
|
291
315
|
}
|
|
292
316
|
if (event.type === client.EventType.RUN_FINISHED) {
|
|
293
|
-
handleRunFinished(
|
|
317
|
+
handleRunFinished();
|
|
294
318
|
return;
|
|
295
319
|
}
|
|
296
320
|
observer.next(event);
|
|
297
321
|
},
|
|
298
|
-
error: (
|
|
299
|
-
|
|
322
|
+
error: (err) => {
|
|
323
|
+
state.error = true;
|
|
324
|
+
observer.error(err);
|
|
300
325
|
},
|
|
301
326
|
complete: () => {
|
|
302
|
-
if (pendingMcpCalls.size === 0)
|
|
303
|
-
observer.complete();
|
|
304
|
-
}
|
|
327
|
+
if (!state.error && state.pendingMcpCalls.size === 0) observer.complete();
|
|
305
328
|
}
|
|
306
329
|
});
|
|
307
|
-
return () =>
|
|
308
|
-
subscription.unsubscribe();
|
|
309
|
-
};
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
triggerNewRun(observer, input, next, toolCallArgsBuffer, toolCallNames, pendingMcpCalls) {
|
|
313
|
-
toolCallArgsBuffer.clear();
|
|
314
|
-
toolCallNames.clear();
|
|
315
|
-
pendingMcpCalls.clear();
|
|
316
|
-
console.log(`[McpMiddleware] Starting new run with updated messages`);
|
|
317
|
-
next.run(input).subscribe({
|
|
318
|
-
next: (event) => {
|
|
319
|
-
if (event.type === client.EventType.TOOL_CALL_START) {
|
|
320
|
-
const startEvent = event;
|
|
321
|
-
if (startEvent.toolCallId && startEvent.toolCallName) {
|
|
322
|
-
toolCallNames.set(startEvent.toolCallId, startEvent.toolCallName);
|
|
323
|
-
const isMcp = this.isMcpTool(startEvent.toolCallName);
|
|
324
|
-
console.log(`[McpMiddleware] TOOL_CALL_START: ${startEvent.toolCallName} (id: ${startEvent.toolCallId}, isMCP: ${isMcp})`);
|
|
325
|
-
if (isMcp) {
|
|
326
|
-
pendingMcpCalls.add(startEvent.toolCallId);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
if (event.type === client.EventType.TOOL_CALL_ARGS) {
|
|
331
|
-
const argsEvent = event;
|
|
332
|
-
if (argsEvent.toolCallId && argsEvent.delta) {
|
|
333
|
-
const existing = toolCallArgsBuffer.get(argsEvent.toolCallId) || "";
|
|
334
|
-
toolCallArgsBuffer.set(argsEvent.toolCallId, existing + argsEvent.delta);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
if (event.type === client.EventType.TOOL_CALL_END) {
|
|
338
|
-
const endEvent = event;
|
|
339
|
-
const toolName = toolCallNames.get(endEvent.toolCallId);
|
|
340
|
-
console.log(`[McpMiddleware] TOOL_CALL_END: ${toolName ?? "unknown"} (id: ${endEvent.toolCallId})`);
|
|
341
|
-
}
|
|
342
|
-
if (event.type === client.EventType.RUN_FINISHED) {
|
|
343
|
-
if (pendingMcpCalls.size > 0) {
|
|
344
|
-
console.log(`[McpMiddleware] RUN_FINISHED with ${pendingMcpCalls.size} pending calls, executing...`);
|
|
345
|
-
this.handlePendingCalls(observer, input, next, toolCallArgsBuffer, toolCallNames, pendingMcpCalls);
|
|
346
|
-
} else {
|
|
347
|
-
observer.next(event);
|
|
348
|
-
observer.complete();
|
|
349
|
-
}
|
|
350
|
-
return;
|
|
351
|
-
}
|
|
352
|
-
observer.next(event);
|
|
353
|
-
},
|
|
354
|
-
error: (error) => observer.error(error),
|
|
355
|
-
complete: () => {
|
|
356
|
-
if (pendingMcpCalls.size === 0) {
|
|
357
|
-
observer.complete();
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
async handlePendingCalls(observer, input, next, toolCallArgsBuffer, toolCallNames, pendingMcpCalls) {
|
|
363
|
-
const callPromises = [...pendingMcpCalls].map(async (toolCallId) => {
|
|
364
|
-
const toolName = toolCallNames.get(toolCallId);
|
|
365
|
-
if (!toolName) return;
|
|
366
|
-
const argsString = toolCallArgsBuffer.get(toolCallId) || "{}";
|
|
367
|
-
let args = {};
|
|
368
|
-
try {
|
|
369
|
-
args = JSON.parse(argsString);
|
|
370
|
-
} catch (e) {
|
|
371
|
-
console.error(`[McpMiddleware] Failed to parse args:`, argsString);
|
|
372
|
-
}
|
|
373
|
-
console.log(`[McpMiddleware] Executing pending tool: ${toolName}`);
|
|
374
|
-
const result = await this.executeTool(toolName, args);
|
|
375
|
-
const messageId = this.generateMessageId();
|
|
376
|
-
const resultEvent = {
|
|
377
|
-
type: client.EventType.TOOL_CALL_RESULT,
|
|
378
|
-
toolCallId,
|
|
379
|
-
messageId,
|
|
380
|
-
content: result,
|
|
381
|
-
role: "tool",
|
|
382
|
-
timestamp: Date.now()
|
|
383
|
-
};
|
|
384
|
-
console.log(`[McpMiddleware] Emitting TOOL_CALL_RESULT for: ${toolName}`);
|
|
385
|
-
observer.next(resultEvent);
|
|
386
|
-
input.messages.push({
|
|
387
|
-
id: messageId,
|
|
388
|
-
role: "tool",
|
|
389
|
-
toolCallId,
|
|
390
|
-
content: result
|
|
391
|
-
});
|
|
392
|
-
pendingMcpCalls.delete(toolCallId);
|
|
393
|
-
});
|
|
394
|
-
await Promise.all(callPromises);
|
|
395
|
-
console.log(`[McpMiddleware] Pending tools executed, emitting RUN_FINISHED`);
|
|
396
|
-
observer.next({
|
|
397
|
-
type: client.EventType.RUN_FINISHED,
|
|
398
|
-
threadId: input.threadId,
|
|
399
|
-
runId: input.runId,
|
|
400
|
-
timestamp: Date.now()
|
|
330
|
+
return () => subscription.unsubscribe();
|
|
401
331
|
});
|
|
402
|
-
console.log(`[McpMiddleware] Triggering new run`);
|
|
403
|
-
this.triggerNewRun(observer, input, next, toolCallArgsBuffer, toolCallNames, pendingMcpCalls);
|
|
404
332
|
}
|
|
405
333
|
};
|
|
406
|
-
function createMcpMiddleware(
|
|
407
|
-
const middleware = new McpMiddleware(
|
|
408
|
-
client,
|
|
409
|
-
...options
|
|
410
|
-
});
|
|
334
|
+
function createMcpMiddleware(options) {
|
|
335
|
+
const middleware = new McpMiddleware(options);
|
|
411
336
|
return (input, next) => {
|
|
412
337
|
return middleware.run(input, next);
|
|
413
338
|
};
|