@mcpmesh/sdk 1.4.1 → 2.0.0-beta.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.
Files changed (242) hide show
  1. package/dist/__tests__/a2a/a2a-bearer.spec.d.ts +2 -0
  2. package/dist/__tests__/a2a/a2a-bearer.spec.d.ts.map +1 -0
  3. package/dist/__tests__/a2a/a2a-bearer.spec.js +58 -0
  4. package/dist/__tests__/a2a/a2a-bearer.spec.js.map +1 -0
  5. package/dist/__tests__/a2a/a2a-client.spec.d.ts +2 -0
  6. package/dist/__tests__/a2a/a2a-client.spec.d.ts.map +1 -0
  7. package/dist/__tests__/a2a/a2a-client.spec.js +334 -0
  8. package/dist/__tests__/a2a/a2a-client.spec.js.map +1 -0
  9. package/dist/__tests__/a2a/a2a-job.spec.d.ts +2 -0
  10. package/dist/__tests__/a2a/a2a-job.spec.d.ts.map +1 -0
  11. package/dist/__tests__/a2a/a2a-job.spec.js +255 -0
  12. package/dist/__tests__/a2a/a2a-job.spec.js.map +1 -0
  13. package/dist/__tests__/a2a/a2a-stream.spec.d.ts +2 -0
  14. package/dist/__tests__/a2a/a2a-stream.spec.d.ts.map +1 -0
  15. package/dist/__tests__/a2a/a2a-stream.spec.js +278 -0
  16. package/dist/__tests__/a2a/a2a-stream.spec.js.map +1 -0
  17. package/dist/__tests__/a2a/agent-a2a-config.spec.d.ts +2 -0
  18. package/dist/__tests__/a2a/agent-a2a-config.spec.d.ts.map +1 -0
  19. package/dist/__tests__/a2a/agent-a2a-config.spec.js +262 -0
  20. package/dist/__tests__/a2a/agent-a2a-config.spec.js.map +1 -0
  21. package/dist/__tests__/a2a/producer/auth-filter.spec.d.ts +2 -0
  22. package/dist/__tests__/a2a/producer/auth-filter.spec.d.ts.map +1 -0
  23. package/dist/__tests__/a2a/producer/auth-filter.spec.js +127 -0
  24. package/dist/__tests__/a2a/producer/auth-filter.spec.js.map +1 -0
  25. package/dist/__tests__/a2a/producer/card-builder.spec.d.ts +2 -0
  26. package/dist/__tests__/a2a/producer/card-builder.spec.d.ts.map +1 -0
  27. package/dist/__tests__/a2a/producer/card-builder.spec.js +113 -0
  28. package/dist/__tests__/a2a/producer/card-builder.spec.js.map +1 -0
  29. package/dist/__tests__/a2a/producer/dispatcher.spec.d.ts +2 -0
  30. package/dist/__tests__/a2a/producer/dispatcher.spec.d.ts.map +1 -0
  31. package/dist/__tests__/a2a/producer/dispatcher.spec.js +850 -0
  32. package/dist/__tests__/a2a/producer/dispatcher.spec.js.map +1 -0
  33. package/dist/__tests__/a2a/producer/mount-surface-push.spec.d.ts +2 -0
  34. package/dist/__tests__/a2a/producer/mount-surface-push.spec.d.ts.map +1 -0
  35. package/dist/__tests__/a2a/producer/mount-surface-push.spec.js +164 -0
  36. package/dist/__tests__/a2a/producer/mount-surface-push.spec.js.map +1 -0
  37. package/dist/__tests__/a2a/producer/mount.spec.d.ts +2 -0
  38. package/dist/__tests__/a2a/producer/mount.spec.d.ts.map +1 -0
  39. package/dist/__tests__/a2a/producer/mount.spec.js +433 -0
  40. package/dist/__tests__/a2a/producer/mount.spec.js.map +1 -0
  41. package/dist/__tests__/a2a/producer/public-url-cache.spec.d.ts +2 -0
  42. package/dist/__tests__/a2a/producer/public-url-cache.spec.d.ts.map +1 -0
  43. package/dist/__tests__/a2a/producer/public-url-cache.spec.js +116 -0
  44. package/dist/__tests__/a2a/producer/public-url-cache.spec.js.map +1 -0
  45. package/dist/__tests__/a2a/producer/sse-emitter.spec.d.ts +2 -0
  46. package/dist/__tests__/a2a/producer/sse-emitter.spec.d.ts.map +1 -0
  47. package/dist/__tests__/a2a/producer/sse-emitter.spec.js +754 -0
  48. package/dist/__tests__/a2a/producer/sse-emitter.spec.js.map +1 -0
  49. package/dist/__tests__/a2a/producer/state-translator.spec.d.ts +2 -0
  50. package/dist/__tests__/a2a/producer/state-translator.spec.d.ts.map +1 -0
  51. package/dist/__tests__/a2a/producer/state-translator.spec.js +124 -0
  52. package/dist/__tests__/a2a/producer/state-translator.spec.js.map +1 -0
  53. package/dist/__tests__/a2a/producer/task-store.spec.d.ts +2 -0
  54. package/dist/__tests__/a2a/producer/task-store.spec.d.ts.map +1 -0
  55. package/dist/__tests__/a2a/producer/task-store.spec.js +180 -0
  56. package/dist/__tests__/a2a/producer/task-store.spec.js.map +1 -0
  57. package/dist/__tests__/agent-add-tool.spec.d.ts +2 -0
  58. package/dist/__tests__/agent-add-tool.spec.d.ts.map +1 -0
  59. package/dist/__tests__/agent-add-tool.spec.js +483 -0
  60. package/dist/__tests__/agent-add-tool.spec.js.map +1 -0
  61. package/dist/__tests__/api-runtime-race.spec.d.ts +2 -0
  62. package/dist/__tests__/api-runtime-race.spec.d.ts.map +1 -0
  63. package/dist/__tests__/api-runtime-race.spec.js +193 -0
  64. package/dist/__tests__/api-runtime-race.spec.js.map +1 -0
  65. package/dist/__tests__/claim-dispatcher.spec.d.ts +2 -0
  66. package/dist/__tests__/claim-dispatcher.spec.d.ts.map +1 -0
  67. package/dist/__tests__/claim-dispatcher.spec.js +408 -0
  68. package/dist/__tests__/claim-dispatcher.spec.js.map +1 -0
  69. package/dist/__tests__/inbound-job-dispatch.spec.d.ts +2 -0
  70. package/dist/__tests__/inbound-job-dispatch.spec.d.ts.map +1 -0
  71. package/dist/__tests__/inbound-job-dispatch.spec.js +185 -0
  72. package/dist/__tests__/inbound-job-dispatch.spec.js.map +1 -0
  73. package/dist/__tests__/job-controller-progress.spec.d.ts +2 -0
  74. package/dist/__tests__/job-controller-progress.spec.d.ts.map +1 -0
  75. package/dist/__tests__/job-controller-progress.spec.js +85 -0
  76. package/dist/__tests__/job-controller-progress.spec.js.map +1 -0
  77. package/dist/__tests__/jobs-cancel-route.spec.d.ts +2 -0
  78. package/dist/__tests__/jobs-cancel-route.spec.d.ts.map +1 -0
  79. package/dist/__tests__/jobs-cancel-route.spec.js +88 -0
  80. package/dist/__tests__/jobs-cancel-route.spec.js.map +1 -0
  81. package/dist/__tests__/llm-agent-stream.test.d.ts +14 -0
  82. package/dist/__tests__/llm-agent-stream.test.d.ts.map +1 -0
  83. package/dist/__tests__/llm-agent-stream.test.js +341 -0
  84. package/dist/__tests__/llm-agent-stream.test.js.map +1 -0
  85. package/dist/__tests__/mesh-job-submitter.spec.d.ts +2 -0
  86. package/dist/__tests__/mesh-job-submitter.spec.d.ts.map +1 -0
  87. package/dist/__tests__/mesh-job-submitter.spec.js +110 -0
  88. package/dist/__tests__/mesh-job-submitter.spec.js.map +1 -0
  89. package/dist/__tests__/proxy-stream.test.d.ts +9 -0
  90. package/dist/__tests__/proxy-stream.test.d.ts.map +1 -0
  91. package/dist/__tests__/proxy-stream.test.js +347 -0
  92. package/dist/__tests__/proxy-stream.test.js.map +1 -0
  93. package/dist/__tests__/resolver-meshjob.spec.d.ts +26 -0
  94. package/dist/__tests__/resolver-meshjob.spec.d.ts.map +1 -0
  95. package/dist/__tests__/resolver-meshjob.spec.js +201 -0
  96. package/dist/__tests__/resolver-meshjob.spec.js.map +1 -0
  97. package/dist/__tests__/schema-verdict-policy.test.d.ts +6 -0
  98. package/dist/__tests__/schema-verdict-policy.test.d.ts.map +1 -0
  99. package/dist/__tests__/schema-verdict-policy.test.js +126 -0
  100. package/dist/__tests__/schema-verdict-policy.test.js.map +1 -0
  101. package/dist/__tests__/sse-stream.test.d.ts +12 -0
  102. package/dist/__tests__/sse-stream.test.d.ts.map +1 -0
  103. package/dist/__tests__/sse-stream.test.js +170 -0
  104. package/dist/__tests__/sse-stream.test.js.map +1 -0
  105. package/dist/a2a/a2a-bearer.d.ts +27 -0
  106. package/dist/a2a/a2a-bearer.d.ts.map +1 -0
  107. package/dist/a2a/a2a-bearer.js +63 -0
  108. package/dist/a2a/a2a-bearer.js.map +1 -0
  109. package/dist/a2a/a2a-client.d.ts +114 -0
  110. package/dist/a2a/a2a-client.d.ts.map +1 -0
  111. package/dist/a2a/a2a-client.js +405 -0
  112. package/dist/a2a/a2a-client.js.map +1 -0
  113. package/dist/a2a/a2a-event.d.ts +25 -0
  114. package/dist/a2a/a2a-event.d.ts.map +1 -0
  115. package/dist/a2a/a2a-event.js +9 -0
  116. package/dist/a2a/a2a-event.js.map +1 -0
  117. package/dist/a2a/a2a-job.d.ts +58 -0
  118. package/dist/a2a/a2a-job.d.ts.map +1 -0
  119. package/dist/a2a/a2a-job.js +264 -0
  120. package/dist/a2a/a2a-job.js.map +1 -0
  121. package/dist/a2a/a2a-stream.d.ts +39 -0
  122. package/dist/a2a/a2a-stream.d.ts.map +1 -0
  123. package/dist/a2a/a2a-stream.js +290 -0
  124. package/dist/a2a/a2a-stream.js.map +1 -0
  125. package/dist/a2a/errors.d.ts +29 -0
  126. package/dist/a2a/errors.d.ts.map +1 -0
  127. package/dist/a2a/errors.js +48 -0
  128. package/dist/a2a/errors.js.map +1 -0
  129. package/dist/a2a/index.d.ts +12 -0
  130. package/dist/a2a/index.d.ts.map +1 -0
  131. package/dist/a2a/index.js +11 -0
  132. package/dist/a2a/index.js.map +1 -0
  133. package/dist/a2a/producer/auth-filter.d.ts +34 -0
  134. package/dist/a2a/producer/auth-filter.d.ts.map +1 -0
  135. package/dist/a2a/producer/auth-filter.js +39 -0
  136. package/dist/a2a/producer/auth-filter.js.map +1 -0
  137. package/dist/a2a/producer/card-builder.d.ts +59 -0
  138. package/dist/a2a/producer/card-builder.d.ts.map +1 -0
  139. package/dist/a2a/producer/card-builder.js +59 -0
  140. package/dist/a2a/producer/card-builder.js.map +1 -0
  141. package/dist/a2a/producer/dispatcher.d.ts +276 -0
  142. package/dist/a2a/producer/dispatcher.d.ts.map +1 -0
  143. package/dist/a2a/producer/dispatcher.js +896 -0
  144. package/dist/a2a/producer/dispatcher.js.map +1 -0
  145. package/dist/a2a/producer/index.d.ts +26 -0
  146. package/dist/a2a/producer/index.d.ts.map +1 -0
  147. package/dist/a2a/producer/index.js +23 -0
  148. package/dist/a2a/producer/index.js.map +1 -0
  149. package/dist/a2a/producer/mount.d.ts +75 -0
  150. package/dist/a2a/producer/mount.d.ts.map +1 -0
  151. package/dist/a2a/producer/mount.js +422 -0
  152. package/dist/a2a/producer/mount.js.map +1 -0
  153. package/dist/a2a/producer/public-url-cache.d.ts +73 -0
  154. package/dist/a2a/producer/public-url-cache.d.ts.map +1 -0
  155. package/dist/a2a/producer/public-url-cache.js +0 -0
  156. package/dist/a2a/producer/public-url-cache.js.map +1 -0
  157. package/dist/a2a/producer/registry.d.ts +138 -0
  158. package/dist/a2a/producer/registry.d.ts.map +1 -0
  159. package/dist/a2a/producer/registry.js +117 -0
  160. package/dist/a2a/producer/registry.js.map +1 -0
  161. package/dist/a2a/producer/sse-emitter.d.ts +85 -0
  162. package/dist/a2a/producer/sse-emitter.d.ts.map +1 -0
  163. package/dist/a2a/producer/sse-emitter.js +405 -0
  164. package/dist/a2a/producer/sse-emitter.js.map +1 -0
  165. package/dist/a2a/producer/state-translator.d.ts +63 -0
  166. package/dist/a2a/producer/state-translator.d.ts.map +1 -0
  167. package/dist/a2a/producer/state-translator.js +108 -0
  168. package/dist/a2a/producer/state-translator.js.map +1 -0
  169. package/dist/a2a/producer/task-store.d.ts +128 -0
  170. package/dist/a2a/producer/task-store.d.ts.map +1 -0
  171. package/dist/a2a/producer/task-store.js +128 -0
  172. package/dist/a2a/producer/task-store.js.map +1 -0
  173. package/dist/agent.d.ts +72 -0
  174. package/dist/agent.d.ts.map +1 -1
  175. package/dist/agent.js +618 -13
  176. package/dist/agent.js.map +1 -1
  177. package/dist/api-runtime.d.ts +25 -0
  178. package/dist/api-runtime.d.ts.map +1 -1
  179. package/dist/api-runtime.js +75 -2
  180. package/dist/api-runtime.js.map +1 -1
  181. package/dist/claim-dispatcher.d.ts +126 -0
  182. package/dist/claim-dispatcher.d.ts.map +1 -0
  183. package/dist/claim-dispatcher.js +478 -0
  184. package/dist/claim-dispatcher.js.map +1 -0
  185. package/dist/express.d.ts.map +1 -1
  186. package/dist/express.js +33 -6
  187. package/dist/express.js.map +1 -1
  188. package/dist/inbound-job-dispatch.d.ts +105 -0
  189. package/dist/inbound-job-dispatch.d.ts.map +1 -0
  190. package/dist/inbound-job-dispatch.js +335 -0
  191. package/dist/inbound-job-dispatch.js.map +1 -0
  192. package/dist/index.d.ts +37 -4
  193. package/dist/index.d.ts.map +1 -1
  194. package/dist/index.js +29 -3
  195. package/dist/index.js.map +1 -1
  196. package/dist/job-context.d.ts +107 -0
  197. package/dist/job-context.d.ts.map +1 -0
  198. package/dist/job-context.js +95 -0
  199. package/dist/job-context.js.map +1 -0
  200. package/dist/jobs-cancel-route.d.ts +36 -0
  201. package/dist/jobs-cancel-route.d.ts.map +1 -0
  202. package/dist/jobs-cancel-route.js +60 -0
  203. package/dist/jobs-cancel-route.js.map +1 -0
  204. package/dist/jobs-helper-tools.d.ts +48 -0
  205. package/dist/jobs-helper-tools.d.ts.map +1 -0
  206. package/dist/jobs-helper-tools.js +133 -0
  207. package/dist/jobs-helper-tools.js.map +1 -0
  208. package/dist/llm-agent.d.ts +62 -53
  209. package/dist/llm-agent.d.ts.map +1 -1
  210. package/dist/llm-agent.js +211 -292
  211. package/dist/llm-agent.js.map +1 -1
  212. package/dist/llm-provider.d.ts +4 -4
  213. package/dist/llm.d.ts +4 -1
  214. package/dist/llm.d.ts.map +1 -1
  215. package/dist/llm.js +7 -17
  216. package/dist/llm.js.map +1 -1
  217. package/dist/mesh-job-submitter.d.ts +83 -0
  218. package/dist/mesh-job-submitter.d.ts.map +1 -0
  219. package/dist/mesh-job-submitter.js +143 -0
  220. package/dist/mesh-job-submitter.js.map +1 -0
  221. package/dist/proxy.d.ts +30 -0
  222. package/dist/proxy.d.ts.map +1 -1
  223. package/dist/proxy.js +351 -1
  224. package/dist/proxy.js.map +1 -1
  225. package/dist/resolver-meshjob.d.ts +170 -0
  226. package/dist/resolver-meshjob.d.ts.map +1 -0
  227. package/dist/resolver-meshjob.js +159 -0
  228. package/dist/resolver-meshjob.js.map +1 -0
  229. package/dist/route.d.ts +4 -0
  230. package/dist/route.d.ts.map +1 -1
  231. package/dist/route.js.map +1 -1
  232. package/dist/schema-normalize.d.ts +62 -0
  233. package/dist/schema-normalize.d.ts.map +1 -0
  234. package/dist/schema-normalize.js +128 -0
  235. package/dist/schema-normalize.js.map +1 -0
  236. package/dist/sse-stream.d.ts +44 -0
  237. package/dist/sse-stream.d.ts.map +1 -0
  238. package/dist/sse-stream.js +173 -0
  239. package/dist/sse-stream.js.map +1 -0
  240. package/dist/types.d.ts +351 -9
  241. package/dist/types.d.ts.map +1 -1
  242. package/package.json +4 -3
package/dist/llm-agent.js CHANGED
@@ -4,20 +4,18 @@
4
4
  * This class handles:
5
5
  * - System prompt rendering (with Handlebars templates)
6
6
  * - Agentic loop with tool execution
7
- * - LLM provider calls (direct Vercel AI SDK or mesh delegation)
7
+ * - LLM provider calls via mesh delegation
8
8
  * - Response parsing with Zod validation
9
9
  * - Metadata tracking (tokens, latency, tool calls)
10
10
  *
11
11
  * Configuration Hierarchy (ENV > Config):
12
- * - MESH_LLM_PROVIDER: Override provider for direct mode (e.g., "claude", "openai", "gemini")
13
12
  * - MESH_LLM_MODEL: Override model (e.g., "gpt-4o", "gemini-2.0-flash")
14
13
  * - MESH_LLM_MAX_ITERATIONS: Override max agentic loop iterations
15
- * - MESH_LLM_FILTER_MODE: Override tool filter mode ("all", "include", "exclude")
16
14
  *
17
15
  * @example
18
16
  * ```typescript
19
17
  * const agent = new MeshLlmAgent({
20
- * provider: "claude",
18
+ * provider: { capability: "llm", tags: ["+claude"] },
21
19
  * model: "anthropic/claude-sonnet-4-5",
22
20
  * systemPrompt: "file://prompts/assistant.hbs",
23
21
  * maxIterations: 10,
@@ -27,6 +25,7 @@
27
25
  * const result = await agent.run("Help me calculate 2+2", {
28
26
  * templateContext: { user: "John" },
29
27
  * tools: resolvedToolProxies,
28
+ * meshProvider: { endpoint: "http://provider:9000", functionName: "process_chat" },
30
29
  * });
31
30
  * ```
32
31
  */
@@ -35,259 +34,11 @@ import { renderTemplate } from "./template.js";
35
34
  import { ResponseParser } from "./response-parser.js";
36
35
  import { MaxIterationsError, LLMAPIError, ToolExecutionError, } from "./errors.js";
37
36
  import { parseSSEResponse } from "./sse.js";
38
- import { loadProvider, extractVendorFromModel, extractModelName, } from "./llm-provider.js";
39
- import { ProviderHandlerRegistry } from "./provider-handlers/index.js";
40
37
  import { resolveMediaInputs } from "./media/index.js";
41
- import { getCurrentTraceContext, getCurrentPropagatedHeaders } from "./proxy.js";
38
+ import { getCurrentTraceContext, getCurrentPropagatedHeaders, streamMcpTool, DEFAULT_CALL_OPTIONS, } from "./proxy.js";
42
39
  import { generateSpanId, publishTraceSpan, createTraceHeaders, injectTraceContext, } from "./tracing.js";
43
40
  import { fetchWithTimeout, isTimeoutError } from "./timeout-utils.js";
44
41
  import { getDispatcher } from "./http-pool.js";
45
- /**
46
- * Default LiteLLM provider using HTTP proxy.
47
- * Assumes LiteLLM proxy is running at LITELLM_URL or localhost:4000.
48
- */
49
- export class LiteLLMProvider {
50
- baseUrl;
51
- constructor(baseUrl) {
52
- this.baseUrl = baseUrl || process.env.LITELLM_URL || "http://localhost:4000";
53
- }
54
- async complete(model, messages, tools, options) {
55
- const body = {
56
- model,
57
- messages,
58
- };
59
- if (tools && tools.length > 0) {
60
- body.tools = tools;
61
- body.tool_choice = "auto";
62
- }
63
- if (options?.maxOutputTokens)
64
- body.max_tokens = options.maxOutputTokens;
65
- if (options?.temperature !== undefined)
66
- body.temperature = options.temperature;
67
- if (options?.topP !== undefined)
68
- body.top_p = options.topP;
69
- if (options?.stop)
70
- body.stop = options.stop;
71
- // Set up timeout (default 300s to match Python SDK's stream_timeout)
72
- const timeoutMs = parseInt(process.env.LITELLM_TIMEOUT_MS || "300000", 10);
73
- let response;
74
- try {
75
- response = await fetchWithTimeout(`${this.baseUrl}/v1/chat/completions`, {
76
- method: "POST",
77
- headers: {
78
- "Content-Type": "application/json",
79
- },
80
- body: JSON.stringify(body),
81
- timeout: timeoutMs,
82
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
83
- dispatcher: getDispatcher(`${this.baseUrl}/v1/chat/completions`),
84
- });
85
- }
86
- catch (error) {
87
- if (isTimeoutError(error)) {
88
- throw new LLMAPIError(408, `Request timed out after ${timeoutMs}ms`, "litellm");
89
- }
90
- throw new LLMAPIError(0, `Fetch failed: ${error instanceof Error ? error.message : String(error)}`, "litellm");
91
- }
92
- if (!response.ok) {
93
- const error = await response.text();
94
- throw new LLMAPIError(response.status, error, "litellm");
95
- }
96
- return (await response.json());
97
- }
98
- }
99
- /**
100
- * Default model mappings for provider shorthand names.
101
- */
102
- const DEFAULT_MODELS = {
103
- claude: "anthropic/claude-sonnet-4-5",
104
- openai: "openai/gpt-4o",
105
- anthropic: "anthropic/claude-sonnet-4-5",
106
- gemini: "google/gemini-3-flash-preview",
107
- google: "google/gemini-3-flash-preview",
108
- gpt4: "openai/gpt-4o",
109
- gpt35: "openai/gpt-3.5-turbo",
110
- };
111
- /**
112
- * Direct Vercel AI SDK provider.
113
- * Uses Vercel AI SDK (@ai-sdk/anthropic, @ai-sdk/openai, etc.) directly
114
- * without needing a proxy server.
115
- */
116
- export class VercelDirectProvider {
117
- providerSpec;
118
- cachedProvider = null;
119
- providerLoadAttempted = false;
120
- toolProxies = new Map();
121
- maxSteps = 10;
122
- constructor(providerSpec) {
123
- this.providerSpec = providerSpec;
124
- }
125
- /**
126
- * Set tool proxies for execute callbacks in the Vercel AI SDK agentic loop.
127
- * When set, tools are created with execute callbacks and maxSteps is enabled,
128
- * letting the SDK handle the tool execution loop internally.
129
- */
130
- setToolProxies(tools, maxIterations) {
131
- this.toolProxies.clear();
132
- for (const tool of tools) {
133
- this.toolProxies.set(tool.name, tool);
134
- }
135
- this.maxSteps = maxIterations ?? 10;
136
- }
137
- /**
138
- * Resolve the full model string from provider spec.
139
- * E.g., "claude" -> "anthropic/claude-sonnet-4-5"
140
- */
141
- resolveModel(model) {
142
- // If explicit model provided, use it
143
- if (model && model !== "default") {
144
- // If model already has vendor prefix, use as-is
145
- if (model.includes("/")) {
146
- return model;
147
- }
148
- // Otherwise, try to add vendor prefix from provider spec
149
- const vendor = extractVendorFromModel(DEFAULT_MODELS[this.providerSpec.toLowerCase()] ?? this.providerSpec);
150
- if (vendor) {
151
- return `${vendor}/${model}`;
152
- }
153
- return model;
154
- }
155
- // Map shorthand provider to full model
156
- const defaultModel = DEFAULT_MODELS[this.providerSpec.toLowerCase()];
157
- if (defaultModel) {
158
- return defaultModel;
159
- }
160
- // Assume provider spec is already a model identifier
161
- return this.providerSpec;
162
- }
163
- async complete(model, messages, tools, options) {
164
- const fullModel = this.resolveModel(model);
165
- const vendor = extractVendorFromModel(fullModel);
166
- const modelName = extractModelName(fullModel);
167
- if (!vendor) {
168
- throw new LLMAPIError(400, `Cannot determine vendor from model: ${fullModel}. Use format "vendor/model" (e.g., "anthropic/claude-sonnet-4-5")`, "vercel");
169
- }
170
- // Load provider if not cached
171
- if (!this.providerLoadAttempted) {
172
- this.providerLoadAttempted = true;
173
- this.cachedProvider = await loadProvider(vendor);
174
- }
175
- if (!this.cachedProvider) {
176
- throw new LLMAPIError(500, `Vercel AI SDK provider for '${vendor}' not available. Install: npm install @ai-sdk/${vendor}`, "vercel");
177
- }
178
- // Create the model instance
179
- const aiModel = this.cachedProvider(modelName);
180
- // Get vendor-specific handler for optimizations
181
- const handler = ProviderHandlerRegistry.getHandler(vendor);
182
- // Import generateText from ai package
183
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
184
- const aiModule = (await import("ai"));
185
- const generateText = aiModule.generateText;
186
- const jsonSchema = aiModule.jsonSchema;
187
- const aiTool = aiModule.tool;
188
- // Convert tools to Vercel AI SDK format
189
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
190
- let vercelTools;
191
- if (tools && tools.length > 0) {
192
- vercelTools = {};
193
- for (const tool of tools) {
194
- const rawSchema = tool.function.parameters ?? {
195
- type: "object",
196
- properties: {},
197
- };
198
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
199
- const { $schema, ...schemaWithoutMeta } = rawSchema;
200
- const cleanSchema = {
201
- type: "object",
202
- ...schemaWithoutMeta,
203
- };
204
- const proxy = this.toolProxies.get(tool.function.name);
205
- vercelTools[tool.function.name] = aiTool({
206
- description: tool.function.description ?? "",
207
- inputSchema: jsonSchema(cleanSchema),
208
- ...(proxy ? {
209
- execute: async (args) => {
210
- const result = await proxy(args);
211
- return typeof result === "string" ? result : JSON.stringify(result);
212
- },
213
- } : {}),
214
- });
215
- }
216
- }
217
- // Apply vendor-specific request preparation
218
- const preparedRequest = handler.prepareRequest(messages, null, // tools handled separately
219
- options?.outputSchema ?? null, {
220
- temperature: options?.temperature,
221
- maxOutputTokens: options?.maxOutputTokens,
222
- topP: options?.topP,
223
- });
224
- // Build request options
225
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
226
- const requestOptions = {
227
- model: aiModel,
228
- messages: preparedRequest.messages,
229
- };
230
- if (vercelTools && Object.keys(vercelTools).length > 0) {
231
- requestOptions.tools = vercelTools;
232
- // When tool proxies are set, the SDK handles the agentic loop via execute callbacks.
233
- // maxSteps allows the SDK to call tools and feed results back to the LLM automatically.
234
- if (this.toolProxies.size > 0) {
235
- requestOptions.maxSteps = this.maxSteps;
236
- }
237
- }
238
- if (options?.maxOutputTokens) {
239
- requestOptions.maxTokens = options.maxOutputTokens;
240
- }
241
- if (options?.temperature !== undefined) {
242
- requestOptions.temperature = options.temperature;
243
- }
244
- if (options?.topP !== undefined) {
245
- requestOptions.topP = options.topP;
246
- }
247
- try {
248
- const result = await generateText(requestOptions);
249
- // Convert Vercel AI SDK response to LlmCompletionResponse format.
250
- // When maxSteps is active, the SDK executed tools internally — don't
251
- // expose intermediate tool_calls to the consumer's outer loop.
252
- const sdkHandledLoop = requestOptions.maxSteps != null;
253
- const response = {
254
- id: `vercel-${Date.now()}`,
255
- object: "chat.completion",
256
- created: Math.floor(Date.now() / 1000),
257
- model: fullModel,
258
- choices: [
259
- {
260
- index: 0,
261
- message: {
262
- role: "assistant",
263
- content: result.text || null,
264
- tool_calls: sdkHandledLoop ? undefined : result.toolCalls?.map((tc) => ({
265
- id: tc.toolCallId,
266
- type: "function",
267
- function: {
268
- name: tc.toolName,
269
- arguments: JSON.stringify(tc.args ?? {}),
270
- },
271
- })),
272
- },
273
- finish_reason: result.finishReason ?? "stop",
274
- },
275
- ],
276
- usage: {
277
- prompt_tokens: result.usage?.promptTokens ?? 0,
278
- completion_tokens: result.usage?.completionTokens ?? 0,
279
- total_tokens: (result.usage?.promptTokens ?? 0) +
280
- (result.usage?.completionTokens ?? 0),
281
- },
282
- };
283
- return response;
284
- }
285
- catch (error) {
286
- const message = error instanceof Error ? error.message : String(error);
287
- throw new LLMAPIError(500, `Vercel AI SDK error: ${message}`, "vercel");
288
- }
289
- }
290
- }
291
42
  /**
292
43
  * Mesh provider that delegates to an LLM provider discovered via mesh.
293
44
  */
@@ -486,6 +237,61 @@ export class MeshDelegatedProvider {
486
237
  }
487
238
  }
488
239
  }
240
+ /**
241
+ * Stream chunks from the mesh-delegated provider's streaming variant.
242
+ *
243
+ * Builds the same ``{request: <MeshLlmRequest>}`` body that ``complete()``
244
+ * produces, then calls the streaming MCP tool via ``streamMcpTool()`` from
245
+ * ``./proxy``. Each ``notifications/progress`` chunk is yielded as a string;
246
+ * the final ``result`` event ends the stream and is NOT yielded (matches the
247
+ * Python ``MeshLlmAgent.stream()`` contract).
248
+ *
249
+ * The provider's ``functionName`` is expected to already be the streaming
250
+ * variant — the registry resolver picks it based on the consumer's
251
+ * ``ai.mcpmesh.stream`` tag opt-in (see ``MeshLlmAgent.stream()``).
252
+ */
253
+ async *streamComplete(model, messages, tools, options) {
254
+ // Build MeshLlmRequest body — same shape as complete()
255
+ const modelParams = {};
256
+ if (model && model !== "default") {
257
+ modelParams.model = model;
258
+ }
259
+ if (options?.maxOutputTokens)
260
+ modelParams.max_tokens = options.maxOutputTokens;
261
+ if (options?.temperature !== undefined)
262
+ modelParams.temperature = options.temperature;
263
+ if (options?.topP !== undefined)
264
+ modelParams.top_p = options.topP;
265
+ if (options?.stop)
266
+ modelParams.stop = options.stop;
267
+ if (options?.outputSchema) {
268
+ modelParams.output_schema = options.outputSchema.schema;
269
+ modelParams.output_type_name = options.outputSchema.name;
270
+ }
271
+ if (this.parallelToolCalls) {
272
+ modelParams.parallel_tool_calls = true;
273
+ }
274
+ const request = { messages };
275
+ if (Object.keys(modelParams).length > 0) {
276
+ request.model_params = modelParams;
277
+ }
278
+ if (tools && tools.length > 0) {
279
+ request.tools = tools;
280
+ }
281
+ const args = { request };
282
+ // streamMcpTool() handles trace context injection / propagated headers /
283
+ // dispatcher pooling internally — same path as createProxy().stream().
284
+ // Match complete()'s env-backed timeout (MESH_PROVIDER_TIMEOUT_MS) so
285
+ // operators can tune both buffered and streaming provider calls with
286
+ // the same knob. Default 300s (matches Python SDK's stream_timeout).
287
+ const providerTimeoutMs = parseInt(process.env.MESH_PROVIDER_TIMEOUT_MS || "300000", 10);
288
+ const streamOptions = {
289
+ ...DEFAULT_CALL_OPTIONS,
290
+ timeout: providerTimeoutMs,
291
+ streamTimeout: providerTimeoutMs,
292
+ };
293
+ yield* streamMcpTool(this.endpoint, this.functionName, args, streamOptions, "mesh-llm-stream");
294
+ }
489
295
  }
490
296
  /**
491
297
  * MeshLlmAgent - The core agentic loop implementation.
@@ -633,11 +439,6 @@ export class MeshLlmAgent {
633
439
  // If schema conversion fails, skip
634
440
  }
635
441
  }
636
- // Set tool proxies on direct-mode providers so the Vercel AI SDK
637
- // can execute tools internally via maxSteps (agentic loop in the SDK).
638
- if (provider instanceof VercelDirectProvider && context.tools.length > 0) {
639
- provider.setToolProxies(context.tools, this.config.maxIterations);
640
- }
641
442
  // Agentic loop
642
443
  let iteration = 0;
643
444
  let finalContent = "";
@@ -731,6 +532,127 @@ export class MeshLlmAgent {
731
532
  // Parse and validate response
732
533
  return this.responseParser.parse(finalContent);
733
534
  }
535
+ /**
536
+ * Stream the final assistant text token-by-token from a mesh-delegated
537
+ * provider's streaming variant (Python's ``@mesh.llm_provider`` auto-
538
+ * generates a ``process_chat_stream`` MCP tool tagged
539
+ * ``ai.mcpmesh.stream``).
540
+ *
541
+ * **Tag opt-in (REQUIRED):** Unlike Python's ``@mesh.llm`` which auto-adds
542
+ * the ``ai.mcpmesh.stream`` tag based on the function's return-type
543
+ * (``Stream[str]`` vs ``str``), TypeScript users must EXPLICITLY include
544
+ * ``"ai.mcpmesh.stream"`` in their provider tag filter to get the
545
+ * streaming variant of the LLM provider:
546
+ *
547
+ * ```ts
548
+ * server.addTool(mesh.llm({
549
+ * name: "chat_stream",
550
+ * provider: { capability: "llm", tags: ["+claude", "ai.mcpmesh.stream"] },
551
+ * // ...
552
+ * execute: async ({ message }, { llm }) => {
553
+ * for await (const chunk of llm.stream(message)) {
554
+ * process.stdout.write(chunk);
555
+ * }
556
+ * return llm.meta?.outputTokens ? "ok" : "no-output";
557
+ * },
558
+ * }));
559
+ * ```
560
+ *
561
+ * Without the ``ai.mcpmesh.stream`` tag the resolver returns the
562
+ * buffered ``process_chat`` tool, and ``stream()`` will yield zero chunks
563
+ * (the buffered tool emits no progress notifications).
564
+ *
565
+ * @param messageInput - User message string or multi-turn message array
566
+ * @param context - Runtime context with tools, mesh provider, and options
567
+ * @returns AsyncIterable yielding text chunks as the provider emits them
568
+ */
569
+ async *stream(messageInput, context) {
570
+ if (!context.meshProvider) {
571
+ throw new Error("MeshLlmAgent.stream() requires a mesh-delegated provider. " +
572
+ "Configure your agent with provider: { capability: 'llm', tags: ['ai.mcpmesh.stream'] } " +
573
+ "to use a streaming @mesh.llm_provider.");
574
+ }
575
+ // Build the same message list complete()/run() builds (system prompt,
576
+ // multipart media, multi-turn array unwinding) — without the agentic
577
+ // loop. The mesh-delegated streaming provider runs its own loop on the
578
+ // server side and emits text chunks via notifications/progress; the
579
+ // consumer just yields each one.
580
+ const messages = [];
581
+ const isMeshDelegated = true; // by definition: we required meshProvider above
582
+ const toolDefs = this.buildToolDefinitions(context.tools, isMeshDelegated);
583
+ // System prompt with template rendering + tool schema injection.
584
+ // Mirrors run(): mesh-delegated path skips the output-schema hint
585
+ // because the provider applies vendor-specific output formatting.
586
+ const systemPromptTemplate = this.getSystemPrompt();
587
+ if (systemPromptTemplate) {
588
+ let systemContent = await renderTemplate(systemPromptTemplate, context.templateContext ?? {});
589
+ if (toolDefs.length > 0) {
590
+ systemContent += this.buildToolSchemaSection(toolDefs);
591
+ }
592
+ messages.push({ role: "system", content: systemContent });
593
+ }
594
+ // Resolve media items to OpenAI-compatible image_url parts
595
+ const mediaItems = context.options?.media;
596
+ let mediaParts = null;
597
+ if (mediaItems && mediaItems.length > 0) {
598
+ mediaParts = await resolveMediaInputs(mediaItems);
599
+ }
600
+ if (typeof messageInput === "string") {
601
+ if (mediaParts && mediaParts.length > 0) {
602
+ messages.push({
603
+ role: "user",
604
+ content: [
605
+ { type: "text", text: messageInput },
606
+ ...mediaParts,
607
+ ],
608
+ });
609
+ }
610
+ else {
611
+ messages.push({ role: "user", content: messageInput });
612
+ }
613
+ }
614
+ else {
615
+ for (let i = 0; i < messageInput.length; i++) {
616
+ const msg = messageInput[i];
617
+ const isLastUser = mediaParts &&
618
+ mediaParts.length > 0 &&
619
+ msg.role === "user" &&
620
+ i === messageInput.length - 1;
621
+ if (isLastUser) {
622
+ messages.push({
623
+ role: "user",
624
+ content: [
625
+ { type: "text", text: msg.content },
626
+ ...mediaParts,
627
+ ],
628
+ });
629
+ }
630
+ else {
631
+ messages.push({ role: msg.role, content: msg.content });
632
+ }
633
+ }
634
+ }
635
+ // Effective options (runtime > env > config)
636
+ const maxTokens = context.options?.maxOutputTokens ?? this.config.maxOutputTokens;
637
+ const temperature = context.options?.temperature ?? this.config.temperature;
638
+ const model = context.meshProvider?.model ??
639
+ process.env.MESH_LLM_MODEL ??
640
+ this.config.model ??
641
+ this.getDefaultModel();
642
+ let outputSchema;
643
+ if (this.config.returnSchema) {
644
+ try {
645
+ const jsonSchema = zodToJsonSchema(this.config.returnSchema);
646
+ const schemaName = jsonSchema.title ?? "Response";
647
+ outputSchema = { schema: jsonSchema, name: schemaName };
648
+ }
649
+ catch {
650
+ // skip
651
+ }
652
+ }
653
+ const provider = new MeshDelegatedProvider(context.meshProvider.endpoint, context.meshProvider.functionName, this.config.parallelToolCalls ?? false);
654
+ yield* provider.streamComplete(model, messages, toolDefs.length > 0 ? toolDefs : undefined, { maxOutputTokens: maxTokens, temperature, topP: this.config.topP, stop: this.config.stop, outputSchema });
655
+ }
734
656
  /**
735
657
  * Create a callable LlmAgent interface.
736
658
  */
@@ -772,30 +694,44 @@ export class MeshLlmAgent {
772
694
  Object.defineProperty(callable, "setSystemPrompt", {
773
695
  value: (prompt) => agent.setSystemPrompt(prompt),
774
696
  });
697
+ // Attach stream method — async iterable for token-by-token output.
698
+ // Mirrors the callable's option-merging semantics so users get the same
699
+ // "context merge vs replace" behavior as the buffered call.
700
+ Object.defineProperty(callable, "stream", {
701
+ value: (message, options) => {
702
+ const contextMode = options?.contextMode ?? "merge";
703
+ let mergedTemplateContext;
704
+ if (contextMode === "replace" && options?.context) {
705
+ mergedTemplateContext = options.context;
706
+ }
707
+ else if (options?.context) {
708
+ mergedTemplateContext = { ...context.templateContext, ...options.context };
709
+ }
710
+ else {
711
+ mergedTemplateContext = context.templateContext ?? {};
712
+ }
713
+ const mergedContext = {
714
+ ...context,
715
+ options: options ? { ...context.options, ...options } : context.options,
716
+ templateContext: mergedTemplateContext,
717
+ };
718
+ return agent.stream(message, mergedContext);
719
+ },
720
+ });
775
721
  return callable;
776
722
  }
777
723
  /**
778
724
  * Resolve the LLM provider to use.
779
725
  *
780
- * Configuration Hierarchy (ENV > Config):
781
- * - MESH_LLM_PROVIDER: Override provider (only for direct mode, not mesh delegation)
726
+ * Mesh delegation only a resolved meshProvider is required.
782
727
  */
783
728
  resolveProvider(context) {
784
- // If mesh provider is resolved, use it (mesh delegation)
785
- if (context.meshProvider) {
786
- return new MeshDelegatedProvider(context.meshProvider.endpoint, context.meshProvider.functionName, this.config.parallelToolCalls ?? false);
729
+ if (!context.meshProvider) {
730
+ throw new Error("MeshLlmAgent requires a mesh-delegated provider. " +
731
+ "Configure your agent with provider: { capability: 'llm', tags: ['+claude'] } " +
732
+ "and ensure a matching @mesh.llm_provider is registered in the mesh.");
787
733
  }
788
- // Use direct Vercel AI SDK provider
789
- // Check env var override first (only for string provider, not mesh delegation object)
790
- let providerSpec;
791
- if (typeof this.config.provider === "string") {
792
- providerSpec =
793
- process.env.MESH_LLM_PROVIDER || this.config.provider || "claude";
794
- }
795
- else {
796
- providerSpec = "claude"; // fallback default for non-mesh object config
797
- }
798
- return new VercelDirectProvider(providerSpec);
734
+ return new MeshDelegatedProvider(context.meshProvider.endpoint, context.meshProvider.functionName, this.config.parallelToolCalls ?? false);
799
735
  }
800
736
  /**
801
737
  * Get provider name for metadata.
@@ -804,29 +740,12 @@ export class MeshLlmAgent {
804
740
  if (context.meshProvider) {
805
741
  return `mesh:${context.meshProvider.endpoint}`;
806
742
  }
807
- if (typeof this.config.provider === "string") {
808
- // Return env var override if set, otherwise config value
809
- return process.env.MESH_LLM_PROVIDER || this.config.provider;
810
- }
811
743
  return `mesh:${this.config.provider.capability}`;
812
744
  }
813
745
  /**
814
- * Get default model based on provider.
746
+ * Get default model mesh delegation defers model selection to the provider.
815
747
  */
816
748
  getDefaultModel() {
817
- const provider = this.config.provider;
818
- if (typeof provider === "string") {
819
- // Map common provider names to models
820
- const defaultModels = {
821
- claude: "anthropic/claude-sonnet-4-5",
822
- openai: "gpt-4o",
823
- anthropic: "anthropic/claude-sonnet-4-5",
824
- gpt4: "gpt-4o",
825
- gpt35: "gpt-3.5-turbo",
826
- };
827
- return defaultModels[provider.toLowerCase()] ?? provider;
828
- }
829
- // Mesh delegation - model will be determined by the provider
830
749
  return "default";
831
750
  }
832
751
  /**