@mcp-ts/sdk 1.0.0 → 1.1.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.
Files changed (77) hide show
  1. package/README.md +25 -13
  2. package/dist/adapters/agui-adapter.d.mts +21 -44
  3. package/dist/adapters/agui-adapter.d.ts +21 -44
  4. package/dist/adapters/agui-adapter.js +93 -67
  5. package/dist/adapters/agui-adapter.js.map +1 -1
  6. package/dist/adapters/agui-adapter.mjs +93 -68
  7. package/dist/adapters/agui-adapter.mjs.map +1 -1
  8. package/dist/adapters/agui-middleware.d.mts +32 -134
  9. package/dist/adapters/agui-middleware.d.ts +32 -134
  10. package/dist/adapters/agui-middleware.js +314 -350
  11. package/dist/adapters/agui-middleware.js.map +1 -1
  12. package/dist/adapters/agui-middleware.mjs +314 -351
  13. package/dist/adapters/agui-middleware.mjs.map +1 -1
  14. package/dist/adapters/ai-adapter.d.mts +2 -2
  15. package/dist/adapters/ai-adapter.d.ts +2 -2
  16. package/dist/adapters/langchain-adapter.d.mts +2 -2
  17. package/dist/adapters/langchain-adapter.d.ts +2 -2
  18. package/dist/adapters/mastra-adapter.d.mts +2 -2
  19. package/dist/adapters/mastra-adapter.d.ts +2 -2
  20. package/dist/client/index.d.mts +184 -57
  21. package/dist/client/index.d.ts +184 -57
  22. package/dist/client/index.js +535 -130
  23. package/dist/client/index.js.map +1 -1
  24. package/dist/client/index.mjs +535 -131
  25. package/dist/client/index.mjs.map +1 -1
  26. package/dist/client/react.d.mts +40 -6
  27. package/dist/client/react.d.ts +40 -6
  28. package/dist/client/react.js +587 -142
  29. package/dist/client/react.js.map +1 -1
  30. package/dist/client/react.mjs +586 -143
  31. package/dist/client/react.mjs.map +1 -1
  32. package/dist/client/vue.d.mts +5 -5
  33. package/dist/client/vue.d.ts +5 -5
  34. package/dist/client/vue.js +545 -140
  35. package/dist/client/vue.js.map +1 -1
  36. package/dist/client/vue.mjs +545 -141
  37. package/dist/client/vue.mjs.map +1 -1
  38. package/dist/{events-BP6WyRNh.d.mts → events-BgeztGYZ.d.mts} +12 -1
  39. package/dist/{events-BP6WyRNh.d.ts → events-BgeztGYZ.d.ts} +12 -1
  40. package/dist/index.d.mts +4 -4
  41. package/dist/index.d.ts +4 -4
  42. package/dist/index.js +779 -248
  43. package/dist/index.js.map +1 -1
  44. package/dist/index.mjs +775 -245
  45. package/dist/index.mjs.map +1 -1
  46. package/dist/{multi-session-client-DMF3ED2O.d.mts → multi-session-client-CxogNckF.d.mts} +1 -1
  47. package/dist/{multi-session-client-BOFgPypS.d.ts → multi-session-client-cox_WXUj.d.ts} +1 -1
  48. package/dist/server/index.d.mts +44 -40
  49. package/dist/server/index.d.ts +44 -40
  50. package/dist/server/index.js +242 -116
  51. package/dist/server/index.js.map +1 -1
  52. package/dist/server/index.mjs +238 -112
  53. package/dist/server/index.mjs.map +1 -1
  54. package/dist/shared/index.d.mts +2 -2
  55. package/dist/shared/index.d.ts +2 -2
  56. package/dist/shared/index.js.map +1 -1
  57. package/dist/shared/index.mjs.map +1 -1
  58. package/dist/{types-SbDlA2VX.d.mts → types-CLccx9wW.d.mts} +1 -1
  59. package/dist/{types-SbDlA2VX.d.ts → types-CLccx9wW.d.ts} +1 -1
  60. package/package.json +8 -1
  61. package/src/adapters/agui-adapter.ts +121 -107
  62. package/src/adapters/agui-middleware.ts +474 -512
  63. package/src/client/core/app-host.ts +417 -0
  64. package/src/client/core/sse-client.ts +365 -212
  65. package/src/client/core/types.ts +31 -0
  66. package/src/client/index.ts +1 -0
  67. package/src/client/react/index.ts +1 -0
  68. package/src/client/react/use-mcp-app.ts +73 -0
  69. package/src/client/react/useMcp.ts +18 -0
  70. package/src/server/handlers/nextjs-handler.ts +8 -7
  71. package/src/server/handlers/sse-handler.ts +131 -164
  72. package/src/server/mcp/oauth-client.ts +32 -2
  73. package/src/server/storage/index.ts +17 -1
  74. package/src/server/storage/sqlite-backend.ts +185 -0
  75. package/src/server/storage/types.ts +1 -1
  76. package/src/shared/events.ts +12 -0
  77. package/src/shared/types.ts +4 -2
@@ -4,410 +4,373 @@ 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
- var agui_adapter_exports = {};
20
- __export(agui_adapter_exports, {
21
- AguiAdapter: () => AguiAdapter
22
- });
23
- var AguiAdapter;
24
- var init_agui_adapter = __esm({
25
- "src/adapters/agui-adapter.ts"() {
26
- AguiAdapter = class {
27
- constructor(client, options = {}) {
28
- this.client = client;
29
- this.options = options;
30
- }
31
- /**
32
- * Get tools with handlers for MCP tool execution.
33
- *
34
- * Each tool includes a handler function that:
35
- * 1. Calls the MCP tool via the client
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
+ var PYDANTIC_FORBIDDEN_PROPS = [
12
+ // JSON Schema meta-properties
13
+ "$schema",
14
+ "$id",
15
+ "$comment",
16
+ "$defs",
17
+ "definitions",
18
+ // Extended properties used by some MCP servers (e.g., Apify)
19
+ "prefill",
20
+ "examples",
21
+ "enumTitles",
22
+ "enumDescriptions",
23
+ // Other common extensions
24
+ "deprecated",
25
+ "readOnly",
26
+ "writeOnly",
27
+ "contentMediaType",
28
+ "contentEncoding"
29
+ ];
30
+ function cleanSchema(schema) {
31
+ if (!schema) {
32
+ return { type: "object", properties: {} };
33
+ }
34
+ const cleaned = { ...schema };
35
+ for (const prop of PYDANTIC_FORBIDDEN_PROPS) {
36
+ delete cleaned[prop];
37
+ }
38
+ if (cleaned.properties && typeof cleaned.properties === "object") {
39
+ const cleanedProps = {};
40
+ for (const [key, value] of Object.entries(cleaned.properties)) {
41
+ if (typeof value === "object" && value !== null) {
42
+ cleanedProps[key] = cleanSchema(value);
43
+ } else {
44
+ cleanedProps[key] = value;
126
45
  }
127
- };
46
+ }
47
+ cleaned.properties = cleanedProps;
128
48
  }
129
- });
49
+ if (cleaned.items && typeof cleaned.items === "object") {
50
+ cleaned.items = cleanSchema(cleaned.items);
51
+ }
52
+ if (cleaned.additionalProperties && typeof cleaned.additionalProperties === "object") {
53
+ cleaned.additionalProperties = cleanSchema(cleaned.additionalProperties);
54
+ }
55
+ return cleaned;
56
+ }
57
+
58
+ // src/adapters/agui-middleware.ts
59
+ var MCP_APP_UI_EVENT = "mcp-apps-ui";
130
60
  var McpMiddleware = class extends client.Middleware {
131
61
  constructor(config) {
132
62
  super();
133
- __publicField(this, "client");
134
- __publicField(this, "toolPrefix");
135
- __publicField(this, "actions");
136
63
  __publicField(this, "tools");
137
- __publicField(this, "actionsLoaded", false);
138
- this.client = config.client;
139
- this.toolPrefix = config.toolPrefix ?? "server-";
140
- this.actions = config.tools ?? null;
141
- this.tools = null;
142
- if (this.actions) {
143
- this.actionsLoaded = true;
144
- this.tools = this.actionsToTools(this.actions);
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: {} }
64
+ __publicField(this, "toolSchemas");
65
+ this.tools = config.tools;
66
+ this.toolSchemas = this.tools.map((t) => ({
67
+ name: t.name,
68
+ description: t.description,
69
+ parameters: cleanSchema(t.parameters),
70
+ _meta: t._meta
71
+ // Include _meta in the tool definition passed to the agent
155
72
  }));
156
73
  }
157
- /**
158
- * Check if a tool name is an MCP tool (matches the configured prefix)
159
- */
160
74
  isMcpTool(toolName) {
161
- return toolName.startsWith(this.toolPrefix);
75
+ return this.tools.some((t) => t.name === toolName);
162
76
  }
163
- /**
164
- * Load actions from the MCP client if not already loaded
165
- */
166
- async ensureActionsLoaded() {
167
- if (this.actionsLoaded) return;
168
- const { AguiAdapter: AguiAdapter2 } = await Promise.resolve().then(() => (init_agui_adapter(), agui_adapter_exports));
169
- const adapter = new AguiAdapter2(this.client);
170
- this.actions = await adapter.getTools();
171
- this.actionsLoaded = true;
77
+ parseArgs(argsString) {
78
+ if (!argsString?.trim()) return {};
79
+ try {
80
+ return JSON.parse(argsString);
81
+ } catch {
82
+ const trimmed = argsString.trim();
83
+ if (trimmed.includes("}{")) {
84
+ const firstObject = trimmed.slice(0, trimmed.indexOf("}{") + 1);
85
+ try {
86
+ return JSON.parse(firstObject);
87
+ } catch {
88
+ console.error(`[McpMiddleware] Failed to parse JSON:`, firstObject);
89
+ }
90
+ }
91
+ console.error(`[McpMiddleware] Failed to parse args:`, argsString);
92
+ return {};
93
+ }
172
94
  }
173
- /**
174
- * Execute an MCP tool and return the result as a string
175
- */
176
95
  async executeTool(toolName, args) {
177
- await this.ensureActionsLoaded();
178
- const action = this.actions?.find((a) => a.name === toolName);
179
- if (!action) {
180
- return `Error: Tool not found: ${toolName}`;
181
- }
182
- if (!action.handler) {
183
- return `Error: Tool has no handler: ${toolName}`;
96
+ const tool = this.tools.find((t) => t.name === toolName);
97
+ if (!tool?.handler) {
98
+ return { resultStr: `Error: Tool ${tool ? "has no handler" : "not found"}: ${toolName}` };
184
99
  }
185
100
  try {
186
101
  console.log(`[McpMiddleware] Executing tool: ${toolName}`, args);
187
- const result = await action.handler(args);
188
- console.log(`[McpMiddleware] Tool result:`, typeof result === "string" ? result.slice(0, 200) : result);
189
- return typeof result === "string" ? result : JSON.stringify(result);
102
+ const result = await tool.handler(args);
103
+ let resultStr;
104
+ if (typeof result === "string") {
105
+ resultStr = result;
106
+ } else if (result && typeof result === "object") {
107
+ resultStr = JSON.stringify(result);
108
+ } else {
109
+ resultStr = String(result);
110
+ }
111
+ console.log(`[McpMiddleware] Tool result:`, resultStr.slice(0, 200));
112
+ return { resultStr, rawResult: result };
190
113
  } catch (error) {
191
114
  console.error(`[McpMiddleware] Error executing tool:`, error);
192
- return `Error executing tool: ${error.message || String(error)}`;
115
+ return { resultStr: `Error: ${error.message || String(error)}` };
116
+ }
117
+ }
118
+ generateId(prefix) {
119
+ return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
120
+ }
121
+ ensureIds(input) {
122
+ const anyInput = input;
123
+ if (!anyInput.threadId) anyInput.threadId = this.generateId("mcp_thread");
124
+ if (!anyInput.runId) anyInput.runId = this.generateId("mcp_run");
125
+ }
126
+ /** Process tool call events and update state */
127
+ handleToolCallEvent(event, state) {
128
+ const { toolCallArgsBuffer, toolCallNames, pendingMcpCalls } = state;
129
+ if (event.type === client.EventType.TEXT_MESSAGE_CHUNK) {
130
+ const e = event;
131
+ if (e.delta) {
132
+ state.textContent = (state.textContent || "") + e.delta;
133
+ }
134
+ }
135
+ if (event.type === client.EventType.TOOL_CALL_START) {
136
+ const e = event;
137
+ if (e.toolCallId && e.toolCallName) {
138
+ toolCallNames.set(e.toolCallId, e.toolCallName);
139
+ if (this.isMcpTool(e.toolCallName)) {
140
+ pendingMcpCalls.add(e.toolCallId);
141
+ }
142
+ console.log(`[McpMiddleware] TOOL_CALL_START: ${e.toolCallName} (id: ${e.toolCallId}, isMCP: ${this.isMcpTool(e.toolCallName)})`);
143
+ }
144
+ }
145
+ if (event.type === client.EventType.TOOL_CALL_ARGS) {
146
+ const e = event;
147
+ if (e.toolCallId && e.delta) {
148
+ const existing = toolCallArgsBuffer.get(e.toolCallId) || "";
149
+ toolCallArgsBuffer.set(e.toolCallId, existing + e.delta);
150
+ }
151
+ }
152
+ if (event.type === client.EventType.TOOL_CALL_END) {
153
+ const e = event;
154
+ console.log(`[McpMiddleware] TOOL_CALL_END: ${toolCallNames.get(e.toolCallId) ?? "unknown"} (id: ${e.toolCallId})`);
155
+ }
156
+ if (event.type === client.EventType.MESSAGES_SNAPSHOT) {
157
+ const messages = event.messages || [];
158
+ if (messages.length > 0) {
159
+ const lastMsg = messages[messages.length - 1];
160
+ if (lastMsg.role === "assistant" && lastMsg.content) {
161
+ state.textContent = lastMsg.content;
162
+ }
163
+ for (let i = messages.length - 1; i >= 0; i--) {
164
+ const msg = messages[i];
165
+ const tools = Array.isArray(msg.toolCalls) ? msg.toolCalls : Array.isArray(msg.tool_calls) ? msg.tool_calls : [];
166
+ if (msg.role === "assistant" && tools.length > 0) {
167
+ for (const tc of tools) {
168
+ if (tc.id && tc.function?.name && !toolCallNames.has(tc.id)) {
169
+ toolCallNames.set(tc.id, tc.function.name);
170
+ toolCallArgsBuffer.set(tc.id, tc.function.arguments || "{}");
171
+ if (this.isMcpTool(tc.function.name)) {
172
+ pendingMcpCalls.add(tc.id);
173
+ console.log(`[McpMiddleware] MESSAGES_SNAPSHOT: Discovered ${tc.function.name} (id: ${tc.id})`);
174
+ }
175
+ }
176
+ }
177
+ break;
178
+ }
179
+ }
180
+ }
193
181
  }
194
182
  }
195
- /**
196
- * Generate a unique message ID for tool results
197
- */
198
- generateMessageId() {
199
- return `mcp_result_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
183
+ /** Execute pending MCP tools and return results */
184
+ async executeTools(state) {
185
+ const { toolCallArgsBuffer, toolCallNames, pendingMcpCalls } = state;
186
+ const results = [];
187
+ const promises = [...pendingMcpCalls].map(async (toolCallId) => {
188
+ const toolName = toolCallNames.get(toolCallId);
189
+ if (!toolName) return;
190
+ const args = this.parseArgs(toolCallArgsBuffer.get(toolCallId) || "{}");
191
+ console.log(`[McpMiddleware] Executing pending tool: ${toolName}`);
192
+ const { resultStr, rawResult } = await this.executeTool(toolName, args);
193
+ results.push({
194
+ toolCallId,
195
+ toolName,
196
+ result: resultStr,
197
+ rawResult,
198
+ messageId: this.generateId("mcp_result")
199
+ });
200
+ pendingMcpCalls.delete(toolCallId);
201
+ });
202
+ await Promise.all(promises);
203
+ return results;
204
+ }
205
+ emitToolResults(observer, results) {
206
+ for (const { toolCallId, toolName, result, rawResult, messageId } of results) {
207
+ const toolDef = this.tools.find((t) => t.name === toolName);
208
+ const sessionId = toolDef?._meta?.sessionId;
209
+ const resourceUri = rawResult?._meta?.ui?.resourceUri ?? rawResult?._meta?.["ui/resourceUri"] ?? toolDef?._meta?.ui?.resourceUri ?? toolDef?._meta?.["ui/resourceUri"];
210
+ if (resourceUri) {
211
+ const payload = {
212
+ toolCallId,
213
+ resourceUri,
214
+ sessionId,
215
+ toolName,
216
+ result: rawResult ?? result
217
+ };
218
+ observer.next({
219
+ type: client.EventType.CUSTOM,
220
+ name: MCP_APP_UI_EVENT,
221
+ value: payload,
222
+ timestamp: Date.now(),
223
+ role: "tool"
224
+ });
225
+ console.log(`[McpMiddleware] Emitting CustomEvent(${MCP_APP_UI_EVENT}) for: ${toolName} (session: ${sessionId})`);
226
+ }
227
+ observer.next({
228
+ type: client.EventType.TOOL_CALL_RESULT,
229
+ toolCallId,
230
+ messageId,
231
+ content: result,
232
+ role: "tool",
233
+ timestamp: Date.now()
234
+ });
235
+ console.log(`[McpMiddleware] Emitting TOOL_CALL_RESULT for: ${toolName}`);
236
+ }
200
237
  }
201
- /**
202
- * Run the middleware, intercepting and executing MCP tool calls
203
- */
204
238
  run(input, next) {
205
239
  return new rxjs.Observable((observer) => {
206
- const toolCallArgsBuffer = /* @__PURE__ */ new Map();
207
- const toolCallNames = /* @__PURE__ */ new Map();
208
- const pendingMcpCalls = /* @__PURE__ */ new Set();
209
- console.log(`[McpMiddleware] Starting run with ${this.actions?.length ?? 0} registered actions`);
210
- console.log(`[McpMiddleware] Tool prefix: "${this.toolPrefix}"`);
211
- if (this.tools && this.tools.length > 0) {
212
- const existingTools = input.tools || [];
213
- input.tools = [...existingTools, ...this.tools];
214
- console.log(`[McpMiddleware] Injected ${this.tools.length} MCP tools into input.tools`);
215
- console.log(`[McpMiddleware] Total tools: ${input.tools.length}`);
216
- console.log(`[McpMiddleware] Tool names:`, this.tools.map((t) => t.name));
240
+ const state = {
241
+ toolCallArgsBuffer: /* @__PURE__ */ new Map(),
242
+ toolCallNames: /* @__PURE__ */ new Map(),
243
+ pendingMcpCalls: /* @__PURE__ */ new Set(),
244
+ textContent: "",
245
+ error: false
246
+ };
247
+ this.ensureIds(input);
248
+ const anyInput = input;
249
+ console.log(`[McpMiddleware] === NEW RUN ===`);
250
+ console.log(`[McpMiddleware] threadId: ${anyInput.threadId}, runId: ${anyInput.runId}`);
251
+ console.log(`[McpMiddleware] messages: ${input.messages?.length ?? 0}, tools: ${this.tools?.length ?? 0}`);
252
+ if (this.toolSchemas?.length) {
253
+ input.tools = [...input.tools || [], ...this.toolSchemas];
254
+ console.log(`[McpMiddleware] Injected ${this.toolSchemas.length} tools:`, this.toolSchemas.map((t) => t.name));
217
255
  }
218
- const handleRunFinished = async (event) => {
219
- if (pendingMcpCalls.size === 0) {
220
- observer.next(event);
256
+ const handleRunFinished = async () => {
257
+ if (state.error) return;
258
+ if (state.pendingMcpCalls.size === 0) {
259
+ observer.next({
260
+ type: client.EventType.RUN_FINISHED,
261
+ threadId: anyInput.threadId,
262
+ runId: anyInput.runId,
263
+ timestamp: Date.now()
264
+ });
221
265
  observer.complete();
222
266
  return;
223
267
  }
224
- console.log(`[McpMiddleware] RUN_FINISHED received with ${pendingMcpCalls.size} pending MCP calls`);
225
- const callPromises = [...pendingMcpCalls].map(async (toolCallId) => {
226
- const toolName = toolCallNames.get(toolCallId);
227
- if (!toolName) return;
228
- const argsString = toolCallArgsBuffer.get(toolCallId) || "{}";
229
- let args = {};
230
- try {
231
- args = JSON.parse(argsString);
232
- } catch (e) {
233
- console.error(`[McpMiddleware] Failed to parse args:`, argsString);
268
+ console.log(`[McpMiddleware] RUN_FINISHED with ${state.pendingMcpCalls.size} pending calls`);
269
+ const toolCalls = [];
270
+ for (const toolCallId of state.pendingMcpCalls) {
271
+ const name = state.toolCallNames.get(toolCallId);
272
+ const args = state.toolCallArgsBuffer.get(toolCallId) || "{}";
273
+ if (name) {
274
+ toolCalls.push({
275
+ id: toolCallId,
276
+ type: "function",
277
+ function: { name, arguments: args }
278
+ });
234
279
  }
235
- console.log(`[McpMiddleware] Executing pending tool: ${toolName}`);
236
- const result = await this.executeTool(toolName, args);
237
- const messageId = this.generateMessageId();
238
- const resultEvent = {
239
- type: client.EventType.TOOL_CALL_RESULT,
240
- toolCallId,
241
- messageId,
242
- content: result,
243
- role: "tool",
244
- timestamp: Date.now()
280
+ }
281
+ if (toolCalls.length > 0 || state.textContent) {
282
+ const assistantMsg = {
283
+ id: this.generateId("msg_ast"),
284
+ role: "assistant",
285
+ content: state.textContent || null,
286
+ // Ensure null if empty string for strict LLMs
287
+ tool_calls: toolCalls.length > 0 ? toolCalls : void 0
245
288
  };
246
- console.log(`[McpMiddleware] Emitting TOOL_CALL_RESULT for: ${toolName}`);
247
- observer.next(resultEvent);
289
+ input.messages.push(assistantMsg);
290
+ console.log(`[McpMiddleware] Added assistant message to history before tools: ${state.textContent?.slice(0, 50)}... [${toolCalls.length} tools]`);
291
+ }
292
+ const results = await this.executeTools(state);
293
+ this.emitToolResults(observer, results);
294
+ console.log(`[McpMiddleware] Triggering continuation with ${results.length} results`);
295
+ for (const { toolCallId, result, messageId } of results) {
248
296
  input.messages.push({
249
297
  id: messageId,
250
298
  role: "tool",
251
- toolCallId,
299
+ tool_call_id: toolCallId,
252
300
  content: result
253
301
  });
254
- pendingMcpCalls.delete(toolCallId);
255
- });
256
- await Promise.all(callPromises);
257
- console.log(`[McpMiddleware] All MCP tools executed, emitting RUN_FINISHED`);
258
- observer.next({
259
- type: client.EventType.RUN_FINISHED,
260
- threadId: input.threadId,
261
- runId: input.runId,
262
- timestamp: Date.now()
302
+ }
303
+ state.toolCallArgsBuffer.clear();
304
+ state.toolCallNames.clear();
305
+ state.textContent = "";
306
+ anyInput.runId = this.generateId("mcp_run");
307
+ console.log(`[McpMiddleware] === CONTINUATION RUN === messages: ${input.messages.length}`);
308
+ next.run(input).subscribe({
309
+ next: (event) => {
310
+ if (state.error) return;
311
+ this.handleToolCallEvent(event, state);
312
+ if (event.type === client.EventType.RUN_ERROR) {
313
+ console.log(`[McpMiddleware] RUN_ERROR received in continuation`);
314
+ state.error = true;
315
+ observer.next(event);
316
+ observer.complete();
317
+ return;
318
+ }
319
+ if (event.type === client.EventType.RUN_STARTED) {
320
+ console.log(`[McpMiddleware] Filtering RUN_STARTED from continuation`);
321
+ return;
322
+ }
323
+ if (event.type === client.EventType.RUN_FINISHED) {
324
+ if (state.pendingMcpCalls.size > 0) {
325
+ handleRunFinished();
326
+ } else {
327
+ observer.next(event);
328
+ observer.complete();
329
+ }
330
+ return;
331
+ }
332
+ observer.next(event);
333
+ },
334
+ error: (err) => {
335
+ state.error = true;
336
+ observer.error(err);
337
+ },
338
+ complete: () => {
339
+ if (!state.error && state.pendingMcpCalls.size === 0) observer.complete();
340
+ }
263
341
  });
264
- console.log(`[McpMiddleware] Triggering new run`);
265
- this.triggerNewRun(observer, input, next, toolCallArgsBuffer, toolCallNames, pendingMcpCalls);
266
342
  };
267
343
  const subscription = next.run(input).subscribe({
268
344
  next: (event) => {
269
- if (event.type === client.EventType.TOOL_CALL_START) {
270
- const startEvent = event;
271
- if (startEvent.toolCallId && startEvent.toolCallName) {
272
- toolCallNames.set(startEvent.toolCallId, startEvent.toolCallName);
273
- const isMcp = this.isMcpTool(startEvent.toolCallName);
274
- console.log(`[McpMiddleware] TOOL_CALL_START: ${startEvent.toolCallName} (id: ${startEvent.toolCallId}, isMCP: ${isMcp})`);
275
- if (isMcp) {
276
- pendingMcpCalls.add(startEvent.toolCallId);
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})`);
345
+ if (state.error) return;
346
+ this.handleToolCallEvent(event, state);
347
+ if (event.type === client.EventType.RUN_ERROR) {
348
+ console.log(`[McpMiddleware] RUN_ERROR received`);
349
+ state.error = true;
350
+ observer.next(event);
351
+ observer.complete();
352
+ return;
291
353
  }
292
354
  if (event.type === client.EventType.RUN_FINISHED) {
293
- handleRunFinished(event);
355
+ handleRunFinished();
294
356
  return;
295
357
  }
296
358
  observer.next(event);
297
359
  },
298
- error: (error) => {
299
- observer.error(error);
360
+ error: (err) => {
361
+ state.error = true;
362
+ observer.error(err);
300
363
  },
301
364
  complete: () => {
302
- if (pendingMcpCalls.size === 0) {
303
- observer.complete();
304
- }
305
- }
306
- });
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
- }
365
+ if (!state.error && state.pendingMcpCalls.size === 0) observer.complete();
329
366
  }
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
367
  });
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()
368
+ return () => subscription.unsubscribe();
401
369
  });
402
- console.log(`[McpMiddleware] Triggering new run`);
403
- this.triggerNewRun(observer, input, next, toolCallArgsBuffer, toolCallNames, pendingMcpCalls);
404
370
  }
405
371
  };
406
- function createMcpMiddleware(client, options = {}) {
407
- const middleware = new McpMiddleware({
408
- client,
409
- ...options
410
- });
372
+ function createMcpMiddleware(options) {
373
+ const middleware = new McpMiddleware(options);
411
374
  return (input, next) => {
412
375
  return middleware.run(input, next);
413
376
  };
@@ -421,6 +384,7 @@ Object.defineProperty(exports, "Middleware", {
421
384
  enumerable: true,
422
385
  get: function () { return client.Middleware; }
423
386
  });
387
+ exports.MCP_APP_UI_EVENT = MCP_APP_UI_EVENT;
424
388
  exports.McpMiddleware = McpMiddleware;
425
389
  exports.McpToolExecutorMiddleware = McpMiddleware;
426
390
  exports.createMcpMiddleware = createMcpMiddleware;