@mcpmesh/sdk 0.8.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 (145) hide show
  1. package/dist/__tests__/claude-handler.test.d.ts +7 -0
  2. package/dist/__tests__/claude-handler.test.d.ts.map +1 -0
  3. package/dist/__tests__/claude-handler.test.js +455 -0
  4. package/dist/__tests__/claude-handler.test.js.map +1 -0
  5. package/dist/__tests__/config.test.d.ts +7 -0
  6. package/dist/__tests__/config.test.d.ts.map +1 -0
  7. package/dist/__tests__/config.test.js +156 -0
  8. package/dist/__tests__/config.test.js.map +1 -0
  9. package/dist/__tests__/errors.test.d.ts +7 -0
  10. package/dist/__tests__/errors.test.d.ts.map +1 -0
  11. package/dist/__tests__/errors.test.js +170 -0
  12. package/dist/__tests__/errors.test.js.map +1 -0
  13. package/dist/__tests__/generic-handler.test.d.ts +7 -0
  14. package/dist/__tests__/generic-handler.test.d.ts.map +1 -0
  15. package/dist/__tests__/generic-handler.test.js +243 -0
  16. package/dist/__tests__/generic-handler.test.js.map +1 -0
  17. package/dist/__tests__/llm-provider.test.d.ts +7 -0
  18. package/dist/__tests__/llm-provider.test.d.ts.map +1 -0
  19. package/dist/__tests__/llm-provider.test.js +217 -0
  20. package/dist/__tests__/llm-provider.test.js.map +1 -0
  21. package/dist/__tests__/openai-handler.test.d.ts +7 -0
  22. package/dist/__tests__/openai-handler.test.d.ts.map +1 -0
  23. package/dist/__tests__/openai-handler.test.js +359 -0
  24. package/dist/__tests__/openai-handler.test.js.map +1 -0
  25. package/dist/__tests__/provider-handler-registry.test.d.ts +9 -0
  26. package/dist/__tests__/provider-handler-registry.test.d.ts.map +1 -0
  27. package/dist/__tests__/provider-handler-registry.test.js +187 -0
  28. package/dist/__tests__/provider-handler-registry.test.js.map +1 -0
  29. package/dist/__tests__/response-parser.test.d.ts +7 -0
  30. package/dist/__tests__/response-parser.test.d.ts.map +1 -0
  31. package/dist/__tests__/response-parser.test.js +360 -0
  32. package/dist/__tests__/response-parser.test.js.map +1 -0
  33. package/dist/__tests__/route.test.d.ts +7 -0
  34. package/dist/__tests__/route.test.d.ts.map +1 -0
  35. package/dist/__tests__/route.test.js +281 -0
  36. package/dist/__tests__/route.test.js.map +1 -0
  37. package/dist/__tests__/sse.test.d.ts +7 -0
  38. package/dist/__tests__/sse.test.d.ts.map +1 -0
  39. package/dist/__tests__/sse.test.js +172 -0
  40. package/dist/__tests__/sse.test.js.map +1 -0
  41. package/dist/__tests__/template.test.d.ts +7 -0
  42. package/dist/__tests__/template.test.d.ts.map +1 -0
  43. package/dist/__tests__/template.test.js +176 -0
  44. package/dist/__tests__/template.test.js.map +1 -0
  45. package/dist/__tests__/tracing.test.d.ts +7 -0
  46. package/dist/__tests__/tracing.test.d.ts.map +1 -0
  47. package/dist/__tests__/tracing.test.js +264 -0
  48. package/dist/__tests__/tracing.test.js.map +1 -0
  49. package/dist/agent.d.ts +165 -0
  50. package/dist/agent.d.ts.map +1 -0
  51. package/dist/agent.js +626 -0
  52. package/dist/agent.js.map +1 -0
  53. package/dist/api-runtime.d.ts +166 -0
  54. package/dist/api-runtime.d.ts.map +1 -0
  55. package/dist/api-runtime.js +459 -0
  56. package/dist/api-runtime.js.map +1 -0
  57. package/dist/config.d.ts +31 -0
  58. package/dist/config.d.ts.map +1 -0
  59. package/dist/config.js +60 -0
  60. package/dist/config.js.map +1 -0
  61. package/dist/debug.d.ts +47 -0
  62. package/dist/debug.d.ts.map +1 -0
  63. package/dist/debug.js +86 -0
  64. package/dist/debug.js.map +1 -0
  65. package/dist/errors.d.ts +99 -0
  66. package/dist/errors.d.ts.map +1 -0
  67. package/dist/errors.js +110 -0
  68. package/dist/errors.js.map +1 -0
  69. package/dist/express.d.ts +146 -0
  70. package/dist/express.d.ts.map +1 -0
  71. package/dist/express.js +371 -0
  72. package/dist/express.js.map +1 -0
  73. package/dist/index.d.ts +96 -0
  74. package/dist/index.d.ts.map +1 -0
  75. package/dist/index.js +107 -0
  76. package/dist/index.js.map +1 -0
  77. package/dist/llm-agent.d.ts +193 -0
  78. package/dist/llm-agent.d.ts.map +1 -0
  79. package/dist/llm-agent.js +634 -0
  80. package/dist/llm-agent.js.map +1 -0
  81. package/dist/llm-provider.d.ts +323 -0
  82. package/dist/llm-provider.d.ts.map +1 -0
  83. package/dist/llm-provider.js +446 -0
  84. package/dist/llm-provider.js.map +1 -0
  85. package/dist/llm.d.ts +194 -0
  86. package/dist/llm.d.ts.map +1 -0
  87. package/dist/llm.js +304 -0
  88. package/dist/llm.js.map +1 -0
  89. package/dist/provider-handlers/claude-handler.d.ts +98 -0
  90. package/dist/provider-handlers/claude-handler.d.ts.map +1 -0
  91. package/dist/provider-handlers/claude-handler.js +268 -0
  92. package/dist/provider-handlers/claude-handler.js.map +1 -0
  93. package/dist/provider-handlers/gemini-handler.d.ts +82 -0
  94. package/dist/provider-handlers/gemini-handler.d.ts.map +1 -0
  95. package/dist/provider-handlers/gemini-handler.js +152 -0
  96. package/dist/provider-handlers/gemini-handler.js.map +1 -0
  97. package/dist/provider-handlers/generic-handler.d.ts +78 -0
  98. package/dist/provider-handlers/generic-handler.d.ts.map +1 -0
  99. package/dist/provider-handlers/generic-handler.js +152 -0
  100. package/dist/provider-handlers/generic-handler.js.map +1 -0
  101. package/dist/provider-handlers/index.d.ts +29 -0
  102. package/dist/provider-handlers/index.d.ts.map +1 -0
  103. package/dist/provider-handlers/index.js +32 -0
  104. package/dist/provider-handlers/index.js.map +1 -0
  105. package/dist/provider-handlers/openai-handler.d.ts +86 -0
  106. package/dist/provider-handlers/openai-handler.d.ts.map +1 -0
  107. package/dist/provider-handlers/openai-handler.js +160 -0
  108. package/dist/provider-handlers/openai-handler.js.map +1 -0
  109. package/dist/provider-handlers/provider-handler-registry.d.ts +124 -0
  110. package/dist/provider-handlers/provider-handler-registry.d.ts.map +1 -0
  111. package/dist/provider-handlers/provider-handler-registry.js +180 -0
  112. package/dist/provider-handlers/provider-handler-registry.js.map +1 -0
  113. package/dist/provider-handlers/provider-handler.d.ts +245 -0
  114. package/dist/provider-handlers/provider-handler.d.ts.map +1 -0
  115. package/dist/provider-handlers/provider-handler.js +238 -0
  116. package/dist/provider-handlers/provider-handler.js.map +1 -0
  117. package/dist/proxy.d.ts +44 -0
  118. package/dist/proxy.d.ts.map +1 -0
  119. package/dist/proxy.js +324 -0
  120. package/dist/proxy.js.map +1 -0
  121. package/dist/response-parser.d.ts +103 -0
  122. package/dist/response-parser.d.ts.map +1 -0
  123. package/dist/response-parser.js +232 -0
  124. package/dist/response-parser.js.map +1 -0
  125. package/dist/route.d.ts +185 -0
  126. package/dist/route.d.ts.map +1 -0
  127. package/dist/route.js +310 -0
  128. package/dist/route.js.map +1 -0
  129. package/dist/sse.d.ts +45 -0
  130. package/dist/sse.d.ts.map +1 -0
  131. package/dist/sse.js +77 -0
  132. package/dist/sse.js.map +1 -0
  133. package/dist/template.d.ts +86 -0
  134. package/dist/template.d.ts.map +1 -0
  135. package/dist/template.js +206 -0
  136. package/dist/template.js.map +1 -0
  137. package/dist/tracing.d.ts +88 -0
  138. package/dist/tracing.d.ts.map +1 -0
  139. package/dist/tracing.js +193 -0
  140. package/dist/tracing.js.map +1 -0
  141. package/dist/types.d.ts +618 -0
  142. package/dist/types.d.ts.map +1 -0
  143. package/dist/types.js +5 -0
  144. package/dist/types.js.map +1 -0
  145. package/package.json +68 -0
package/dist/proxy.js ADDED
@@ -0,0 +1,324 @@
1
+ /**
2
+ * MCP Mesh proxy implementation for dependency injection.
3
+ *
4
+ * Provides HTTP client proxies for calling remote MCP agents.
5
+ */
6
+ import { AsyncLocalStorage } from "node:async_hooks";
7
+ import { generateSpanId, publishTraceSpan, createTraceHeaders, } from "./tracing.js";
8
+ /**
9
+ * AsyncLocalStorage for trace context - provides async-safe context propagation.
10
+ * Unlike module-level variables, this correctly handles concurrent requests
11
+ * without trace context bleeding between them.
12
+ */
13
+ const traceContextStorage = new AsyncLocalStorage();
14
+ /**
15
+ * Run a function with trace context.
16
+ * The context is automatically propagated to all async operations within the callback.
17
+ * This is the preferred way to set trace context for tool execution.
18
+ */
19
+ export function runWithTraceContext(ctx, fn) {
20
+ return traceContextStorage.run(ctx, fn);
21
+ }
22
+ /**
23
+ * Get the current trace context.
24
+ * Returns the trace context for the current async execution context,
25
+ * or null if not within a traced context.
26
+ */
27
+ export function getCurrentTraceContext() {
28
+ return traceContextStorage.getStore() ?? null;
29
+ }
30
+ /**
31
+ * @deprecated Use runWithTraceContext() instead for async-safe context propagation.
32
+ * This function is kept for backward compatibility but does nothing.
33
+ */
34
+ export function setCurrentTraceContext(_ctx) {
35
+ // No-op - use runWithTraceContext() instead
36
+ // This is kept for backward compatibility with any external code
37
+ }
38
+ /**
39
+ * Create an McpMeshAgent proxy for a resolved dependency.
40
+ *
41
+ * The returned object is callable (invokes the bound function)
42
+ * and also has methods for calling other tools on the agent.
43
+ */
44
+ export function createProxy(endpoint, capability, functionName, kwargs) {
45
+ const timeout = (kwargs?.timeout ?? 30) * 1000; // Convert to ms
46
+ const maxAttempts = kwargs?.maxAttempts ?? 1;
47
+ // The proxy function that calls the bound tool
48
+ // Returns parsed object (like Python) or raw string if not JSON
49
+ const proxyFn = async (args) => {
50
+ const result = await callMcpTool(endpoint, functionName, args, timeout, maxAttempts, capability);
51
+ // Parse JSON if possible, otherwise return raw string (matches Python behavior)
52
+ try {
53
+ return JSON.parse(result);
54
+ }
55
+ catch {
56
+ return result;
57
+ }
58
+ };
59
+ // Attach properties and methods
60
+ Object.defineProperties(proxyFn, {
61
+ endpoint: { value: endpoint, writable: false },
62
+ capability: { value: capability, writable: false },
63
+ functionName: { value: functionName, writable: false },
64
+ isAvailable: { value: true, writable: false },
65
+ callTool: {
66
+ value: async (toolName, args) => {
67
+ const result = await callMcpTool(endpoint, toolName, args, timeout, maxAttempts, capability);
68
+ try {
69
+ return JSON.parse(result);
70
+ }
71
+ catch {
72
+ return result;
73
+ }
74
+ },
75
+ writable: false,
76
+ },
77
+ });
78
+ return proxyFn;
79
+ }
80
+ /**
81
+ * Call an MCP tool via HTTP POST.
82
+ *
83
+ * Uses the MCP HTTP Streamable protocol:
84
+ * POST /mcp with JSON-RPC 2.0 payload.
85
+ * Includes distributed tracing: propagates trace context and publishes spans.
86
+ */
87
+ async function callMcpTool(endpoint, toolName, args, timeout, maxAttempts, capability) {
88
+ // Ensure endpoint ends with /mcp
89
+ const mcpEndpoint = endpoint.endsWith("/mcp")
90
+ ? endpoint
91
+ : `${endpoint.replace(/\/$/, "")}/mcp`;
92
+ // Tracing: create span for this outgoing proxy call
93
+ // Use AsyncLocalStorage to get trace context for the current async execution
94
+ const traceCtx = getCurrentTraceContext();
95
+ const spanId = traceCtx ? generateSpanId() : null;
96
+ const startTime = Date.now() / 1000;
97
+ // Build arguments with trace context injection (for downstream agents)
98
+ // This is the fallback mechanism since fastmcp doesn't expose HTTP headers
99
+ const argsWithTrace = { ...(args ?? {}) };
100
+ if (traceCtx && spanId) {
101
+ // Inject trace context into arguments - downstream agent will extract these
102
+ // spanId is the proxy span we're about to publish, which becomes child's parent
103
+ argsWithTrace._trace_id = traceCtx.traceId;
104
+ argsWithTrace._parent_span = spanId;
105
+ }
106
+ const payload = {
107
+ jsonrpc: "2.0",
108
+ id: generateRequestId(),
109
+ method: "tools/call",
110
+ params: {
111
+ name: toolName,
112
+ arguments: argsWithTrace,
113
+ },
114
+ };
115
+ let lastError = null;
116
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
117
+ try {
118
+ const controller = new AbortController();
119
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
120
+ // Build headers with trace context propagation
121
+ const headers = {
122
+ "Content-Type": "application/json",
123
+ Accept: "application/json, text/event-stream",
124
+ };
125
+ // Propagate trace context to downstream agent
126
+ if (traceCtx && spanId) {
127
+ Object.assign(headers, createTraceHeaders(traceCtx.traceId, spanId));
128
+ }
129
+ const response = await fetch(mcpEndpoint, {
130
+ method: "POST",
131
+ headers,
132
+ body: JSON.stringify(payload),
133
+ signal: controller.signal,
134
+ });
135
+ clearTimeout(timeoutId);
136
+ if (!response.ok) {
137
+ throw new Error(`MCP call failed: ${response.status} ${response.statusText}`);
138
+ }
139
+ const contentType = response.headers.get("content-type") ?? "";
140
+ // Handle SSE streaming response
141
+ if (contentType.includes("text/event-stream")) {
142
+ const sseResult = await parseSSEResponse(response);
143
+ // Publish success span
144
+ publishProxySpan(traceCtx, spanId, startTime, toolName, capability, endpoint, true, null, typeof sseResult);
145
+ return sseResult;
146
+ }
147
+ // Handle JSON response
148
+ const result = (await response.json());
149
+ if (result.error) {
150
+ const errorMsg = result.error.message ?? JSON.stringify(result.error);
151
+ // Publish error span
152
+ publishProxySpan(traceCtx, spanId, startTime, toolName, capability, endpoint, false, errorMsg, "error");
153
+ throw new Error(`MCP error: ${errorMsg}`);
154
+ }
155
+ // Extract content from result
156
+ const content = extractContent(result.result);
157
+ // Publish success span
158
+ publishProxySpan(traceCtx, spanId, startTime, toolName, capability, endpoint, true, null, typeof content);
159
+ return content;
160
+ }
161
+ catch (err) {
162
+ lastError = err instanceof Error ? err : new Error(String(err));
163
+ // Don't retry on abort (timeout)
164
+ if (lastError.name === "AbortError") {
165
+ publishProxySpan(traceCtx, spanId, startTime, toolName, capability, endpoint, false, "timeout", "error");
166
+ throw new Error(`MCP call timed out after ${timeout}ms`);
167
+ }
168
+ // Retry on network errors
169
+ if (attempt < maxAttempts - 1) {
170
+ await sleep(100 * (attempt + 1)); // Exponential backoff
171
+ continue;
172
+ }
173
+ }
174
+ }
175
+ // All retries failed
176
+ publishProxySpan(traceCtx, spanId, startTime, toolName, capability, endpoint, false, lastError?.message ?? "unknown", "error");
177
+ throw lastError ?? new Error("MCP call failed");
178
+ }
179
+ /**
180
+ * Helper to publish a proxy call span (fire and forget).
181
+ */
182
+ function publishProxySpan(traceCtx, spanId, startTime, _toolName, _capability, endpoint, success, error, resultType) {
183
+ if (!traceCtx || !spanId)
184
+ return;
185
+ const endTime = Date.now() / 1000;
186
+ const durationMs = (endTime - startTime) * 1000;
187
+ // Fire and forget - don't await
188
+ publishTraceSpan({
189
+ traceId: traceCtx.traceId,
190
+ spanId,
191
+ parentSpan: traceCtx.parentSpanId,
192
+ functionName: "proxy_call_wrapper",
193
+ startTime,
194
+ endTime,
195
+ durationMs,
196
+ success,
197
+ error,
198
+ resultType,
199
+ argsCount: 0,
200
+ kwargsCount: 0,
201
+ dependencies: [endpoint],
202
+ injectedDependencies: 0,
203
+ meshPositions: [],
204
+ }).catch(() => {
205
+ // Silently ignore publish errors
206
+ });
207
+ }
208
+ /**
209
+ * Parse SSE response from MCP HTTP Streamable transport.
210
+ */
211
+ async function parseSSEResponse(response) {
212
+ const reader = response.body?.getReader();
213
+ if (!reader) {
214
+ throw new Error("No response body");
215
+ }
216
+ const decoder = new TextDecoder();
217
+ let buffer = "";
218
+ let result = "";
219
+ while (true) {
220
+ const { done, value } = await reader.read();
221
+ if (done)
222
+ break;
223
+ buffer += decoder.decode(value, { stream: true });
224
+ // Parse SSE events
225
+ const lines = buffer.split("\n");
226
+ buffer = lines.pop() ?? ""; // Keep incomplete line
227
+ for (const line of lines) {
228
+ if (line.startsWith("data: ")) {
229
+ const data = line.slice(6);
230
+ if (data === "[DONE]")
231
+ continue;
232
+ try {
233
+ const event = JSON.parse(data);
234
+ // Handle JSON-RPC response
235
+ const jsonRpcEvent = event;
236
+ if (jsonRpcEvent.result) {
237
+ result = extractContent(jsonRpcEvent.result);
238
+ }
239
+ else if (jsonRpcEvent.error) {
240
+ throw new Error(`MCP error: ${jsonRpcEvent.error.message ?? JSON.stringify(jsonRpcEvent.error)}`);
241
+ }
242
+ }
243
+ catch (e) {
244
+ // Ignore parse errors for non-JSON data events
245
+ if (!(e instanceof SyntaxError))
246
+ throw e;
247
+ }
248
+ }
249
+ }
250
+ }
251
+ return result;
252
+ }
253
+ /**
254
+ * Extract content from MCP CallToolResult.
255
+ *
256
+ * Handles various content formats:
257
+ * - { content: [{ type: "text", text: "..." }] }
258
+ * - { content: "..." }
259
+ * - Direct string
260
+ */
261
+ function extractContent(result) {
262
+ if (typeof result === "string") {
263
+ return result;
264
+ }
265
+ if (result && typeof result === "object") {
266
+ const obj = result;
267
+ // Handle { content: [...] } format
268
+ if (Array.isArray(obj.content)) {
269
+ const textParts = [];
270
+ for (const item of obj.content) {
271
+ if (item && typeof item === "object" && "text" in item) {
272
+ textParts.push(String(item.text));
273
+ }
274
+ else if (typeof item === "string") {
275
+ textParts.push(item);
276
+ }
277
+ }
278
+ const text = textParts.join("");
279
+ // Try to parse as JSON if it looks like JSON
280
+ if (text.startsWith("{") || text.startsWith("[")) {
281
+ try {
282
+ return JSON.stringify(JSON.parse(text));
283
+ }
284
+ catch {
285
+ return text;
286
+ }
287
+ }
288
+ return text;
289
+ }
290
+ // Handle { content: "..." } format
291
+ if (typeof obj.content === "string") {
292
+ return obj.content;
293
+ }
294
+ // Return JSON stringified
295
+ return JSON.stringify(result);
296
+ }
297
+ return String(result);
298
+ }
299
+ /**
300
+ * Generate a unique request ID.
301
+ */
302
+ function generateRequestId() {
303
+ return `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
304
+ }
305
+ /**
306
+ * Sleep for a given duration.
307
+ */
308
+ function sleep(ms) {
309
+ return new Promise((resolve) => setTimeout(resolve, ms));
310
+ }
311
+ /**
312
+ * Normalize a DependencySpec to canonical form.
313
+ */
314
+ export function normalizeDependency(dep) {
315
+ if (typeof dep === "string") {
316
+ return { capability: dep, tags: [] };
317
+ }
318
+ return {
319
+ capability: dep.capability,
320
+ tags: dep.tags ?? [],
321
+ version: dep.version,
322
+ };
323
+ }
324
+ //# sourceMappingURL=proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Response parser for LLM outputs with Zod schema validation.
3
+ *
4
+ * Handles extraction of structured data from LLM responses, including:
5
+ * - JSON extraction from code blocks or raw text
6
+ * - Schema validation using Zod
7
+ * - Error handling with informative messages
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const schema = z.object({
12
+ * answer: z.string(),
13
+ * confidence: z.number(),
14
+ * });
15
+ *
16
+ * const parser = new ResponseParser(schema);
17
+ * const result = parser.parse('{"answer": "hello", "confidence": 0.95}');
18
+ * // => { answer: "hello", confidence: 0.95 }
19
+ * ```
20
+ */
21
+ import type { ZodType, ZodError as ZodErrorType } from "zod";
22
+ import { ResponseParseError } from "./errors.js";
23
+ export { ResponseParseError };
24
+ /**
25
+ * Extract JSON from a string that may contain markdown code blocks.
26
+ *
27
+ * Handles:
28
+ * - ```json ... ``` code blocks
29
+ * - ``` ... ``` code blocks (no language)
30
+ * - Raw JSON (object or array) using progressive JSON.parse
31
+ */
32
+ export declare function extractJson(content: string): string | null;
33
+ /**
34
+ * Parser for LLM responses with optional Zod schema validation.
35
+ *
36
+ * @template T - The output type (inferred from Zod schema)
37
+ */
38
+ export declare class ResponseParser<T = string> {
39
+ private schema;
40
+ /**
41
+ * Create a new ResponseParser.
42
+ *
43
+ * @param schema - Optional Zod schema for validation. If not provided, returns raw string.
44
+ */
45
+ constructor(schema?: ZodType<T>);
46
+ /**
47
+ * Parse an LLM response string.
48
+ *
49
+ * If a schema is provided, attempts to extract and validate JSON.
50
+ * If no schema, returns the raw string content.
51
+ *
52
+ * @param content - Raw LLM response content
53
+ * @returns Parsed and validated result
54
+ * @throws ResponseParseError if parsing or validation fails
55
+ */
56
+ parse(content: string): T;
57
+ /**
58
+ * Try to parse, returning null on failure instead of throwing.
59
+ *
60
+ * @param content - Raw LLM response content
61
+ * @returns Parsed result or null if parsing fails
62
+ */
63
+ tryParse(content: string): T | null;
64
+ /**
65
+ * Get the Zod schema (if any).
66
+ */
67
+ getSchema(): ZodType<T> | null;
68
+ /**
69
+ * Check if this parser has a schema.
70
+ */
71
+ hasSchema(): boolean;
72
+ }
73
+ /**
74
+ * Create a response parser with an optional Zod schema.
75
+ *
76
+ * @param schema - Optional Zod schema for validation
77
+ * @returns ResponseParser instance
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * // No schema - returns raw string
82
+ * const stringParser = createResponseParser();
83
+ * const str = stringParser.parse("Hello world");
84
+ *
85
+ * // With schema - validates and returns typed object
86
+ * const objectParser = createResponseParser(z.object({
87
+ * name: z.string(),
88
+ * age: z.number(),
89
+ * }));
90
+ * const obj = objectParser.parse('{"name": "John", "age": 30}');
91
+ * ```
92
+ */
93
+ export declare function createResponseParser<T = string>(schema?: ZodType<T>): ResponseParser<T>;
94
+ /**
95
+ * Convert a Zod schema to a human-readable description for LLM prompting.
96
+ * Uses zod-to-json-schema for forward compatibility with Zod v4.
97
+ */
98
+ export declare function zodSchemaToPromptDescription(schema: ZodType): string;
99
+ /**
100
+ * Format a Zod error into a human-readable string.
101
+ */
102
+ export declare function formatZodError(error: ZodErrorType): string;
103
+ //# sourceMappingURL=response-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-parser.d.ts","sourceRoot":"","sources":["../src/response-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,KAAK,CAAC;AAE7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGjD,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAE9B;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAuB1D;AA2CD;;;;GAIG;AACH,qBAAa,cAAc,CAAC,CAAC,GAAG,MAAM;IACpC,OAAO,CAAC,MAAM,CAAoB;IAElC;;;;OAIG;gBACS,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAI/B;;;;;;;;;OASG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,CAAC;IA0CzB;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI;IAQnC;;OAEG;IACH,SAAS,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI;IAI9B;;OAEG;IACH,SAAS,IAAI,OAAO;CAGrB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,GAAG,MAAM,EAC7C,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,GAClB,cAAc,CAAC,CAAC,CAAC,CAEnB;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CA2BpE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAO1D"}
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Response parser for LLM outputs with Zod schema validation.
3
+ *
4
+ * Handles extraction of structured data from LLM responses, including:
5
+ * - JSON extraction from code blocks or raw text
6
+ * - Schema validation using Zod
7
+ * - Error handling with informative messages
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const schema = z.object({
12
+ * answer: z.string(),
13
+ * confidence: z.number(),
14
+ * });
15
+ *
16
+ * const parser = new ResponseParser(schema);
17
+ * const result = parser.parse('{"answer": "hello", "confidence": 0.95}');
18
+ * // => { answer: "hello", confidence: 0.95 }
19
+ * ```
20
+ */
21
+ import { zodToJsonSchema } from "zod-to-json-schema";
22
+ import { ResponseParseError } from "./errors.js";
23
+ // Re-export for backwards compatibility
24
+ export { ResponseParseError };
25
+ /**
26
+ * Extract JSON from a string that may contain markdown code blocks.
27
+ *
28
+ * Handles:
29
+ * - ```json ... ``` code blocks
30
+ * - ``` ... ``` code blocks (no language)
31
+ * - Raw JSON (object or array) using progressive JSON.parse
32
+ */
33
+ export function extractJson(content) {
34
+ // Strategy 1: Try to extract from markdown code blocks first
35
+ const codeBlockMatch = content.match(/```(?:json)?\s*([\s\S]*?)```/);
36
+ if (codeBlockMatch) {
37
+ return codeBlockMatch[1].trim();
38
+ }
39
+ // Strategy 2: Try to find JSON object using progressive JSON.parse
40
+ // This handles braces inside strings correctly
41
+ const braceStart = content.indexOf("{");
42
+ if (braceStart !== -1) {
43
+ const result = tryProgressiveParse(content, braceStart, "{", "}");
44
+ if (result)
45
+ return result;
46
+ }
47
+ // Strategy 3: Try to find JSON array using progressive JSON.parse
48
+ const bracketStart = content.indexOf("[");
49
+ if (bracketStart !== -1) {
50
+ const result = tryProgressiveParse(content, bracketStart, "[", "]");
51
+ if (result)
52
+ return result;
53
+ }
54
+ return null;
55
+ }
56
+ /**
57
+ * Try to extract valid JSON by progressively extending the end position.
58
+ * This correctly handles braces/brackets inside string values.
59
+ */
60
+ function tryProgressiveParse(content, start, openChar, closeChar) {
61
+ // Quick check: count characters to find potential end positions
62
+ let depth = 0;
63
+ const potentialEnds = [];
64
+ for (let i = start; i < content.length; i++) {
65
+ const char = content[i];
66
+ if (char === openChar) {
67
+ depth++;
68
+ }
69
+ else if (char === closeChar) {
70
+ depth--;
71
+ if (depth === 0) {
72
+ potentialEnds.push(i);
73
+ }
74
+ }
75
+ }
76
+ // Try each potential end position (shortest first for efficiency)
77
+ for (const end of potentialEnds) {
78
+ const candidate = content.slice(start, end + 1);
79
+ try {
80
+ JSON.parse(candidate);
81
+ return candidate;
82
+ }
83
+ catch {
84
+ // Not valid JSON, try next potential end
85
+ continue;
86
+ }
87
+ }
88
+ return null;
89
+ }
90
+ /**
91
+ * Parser for LLM responses with optional Zod schema validation.
92
+ *
93
+ * @template T - The output type (inferred from Zod schema)
94
+ */
95
+ export class ResponseParser {
96
+ schema;
97
+ /**
98
+ * Create a new ResponseParser.
99
+ *
100
+ * @param schema - Optional Zod schema for validation. If not provided, returns raw string.
101
+ */
102
+ constructor(schema) {
103
+ this.schema = schema ?? null;
104
+ }
105
+ /**
106
+ * Parse an LLM response string.
107
+ *
108
+ * If a schema is provided, attempts to extract and validate JSON.
109
+ * If no schema, returns the raw string content.
110
+ *
111
+ * @param content - Raw LLM response content
112
+ * @returns Parsed and validated result
113
+ * @throws ResponseParseError if parsing or validation fails
114
+ */
115
+ parse(content) {
116
+ // No schema - return raw string
117
+ if (!this.schema) {
118
+ return content;
119
+ }
120
+ // Extract JSON from content
121
+ const jsonStr = extractJson(content);
122
+ if (!jsonStr) {
123
+ throw new ResponseParseError("Could not extract JSON from response. Expected JSON object or array.", content);
124
+ }
125
+ // Parse JSON
126
+ let parsed;
127
+ try {
128
+ parsed = JSON.parse(jsonStr);
129
+ }
130
+ catch (err) {
131
+ throw new ResponseParseError(`Invalid JSON: ${err instanceof Error ? err.message : String(err)}`, content);
132
+ }
133
+ // Validate with Zod schema
134
+ const result = this.schema.safeParse(parsed);
135
+ if (!result.success) {
136
+ const issues = result.error.issues
137
+ .map((i) => `${i.path.join(".")}: ${i.message}`)
138
+ .join("; ");
139
+ throw new ResponseParseError(`Schema validation failed: ${issues}`, content, result.error);
140
+ }
141
+ return result.data;
142
+ }
143
+ /**
144
+ * Try to parse, returning null on failure instead of throwing.
145
+ *
146
+ * @param content - Raw LLM response content
147
+ * @returns Parsed result or null if parsing fails
148
+ */
149
+ tryParse(content) {
150
+ try {
151
+ return this.parse(content);
152
+ }
153
+ catch {
154
+ return null;
155
+ }
156
+ }
157
+ /**
158
+ * Get the Zod schema (if any).
159
+ */
160
+ getSchema() {
161
+ return this.schema;
162
+ }
163
+ /**
164
+ * Check if this parser has a schema.
165
+ */
166
+ hasSchema() {
167
+ return this.schema !== null;
168
+ }
169
+ }
170
+ /**
171
+ * Create a response parser with an optional Zod schema.
172
+ *
173
+ * @param schema - Optional Zod schema for validation
174
+ * @returns ResponseParser instance
175
+ *
176
+ * @example
177
+ * ```typescript
178
+ * // No schema - returns raw string
179
+ * const stringParser = createResponseParser();
180
+ * const str = stringParser.parse("Hello world");
181
+ *
182
+ * // With schema - validates and returns typed object
183
+ * const objectParser = createResponseParser(z.object({
184
+ * name: z.string(),
185
+ * age: z.number(),
186
+ * }));
187
+ * const obj = objectParser.parse('{"name": "John", "age": 30}');
188
+ * ```
189
+ */
190
+ export function createResponseParser(schema) {
191
+ return new ResponseParser(schema);
192
+ }
193
+ /**
194
+ * Convert a Zod schema to a human-readable description for LLM prompting.
195
+ * Uses zod-to-json-schema for forward compatibility with Zod v4.
196
+ */
197
+ export function zodSchemaToPromptDescription(schema) {
198
+ // Convert to JSON Schema using the public API (Zod v4 compatible)
199
+ const jsonSchema = zodToJsonSchema(schema, { $refStrategy: "none" });
200
+ // Check for top-level description
201
+ if (jsonSchema.description && typeof jsonSchema.description === "string") {
202
+ return jsonSchema.description;
203
+ }
204
+ // If it's an object type, describe the fields
205
+ if (jsonSchema.type === "object" && jsonSchema.properties) {
206
+ const properties = jsonSchema.properties;
207
+ const fields = Object.keys(properties).map((key) => {
208
+ const prop = properties[key];
209
+ const fieldType = prop.type || "unknown";
210
+ const fieldDesc = prop.description ? ` (${prop.description})` : "";
211
+ return ` - ${key}: ${fieldType}${fieldDesc}`;
212
+ });
213
+ return `JSON object with fields:\n${fields.join("\n")}`;
214
+ }
215
+ // Fallback for other types
216
+ if (jsonSchema.type) {
217
+ return `JSON ${jsonSchema.type}`;
218
+ }
219
+ return "JSON";
220
+ }
221
+ /**
222
+ * Format a Zod error into a human-readable string.
223
+ */
224
+ export function formatZodError(error) {
225
+ return error.issues
226
+ .map((issue) => {
227
+ const path = issue.path.length > 0 ? issue.path.join(".") + ": " : "";
228
+ return `${path}${issue.message}`;
229
+ })
230
+ .join("\n");
231
+ }
232
+ //# sourceMappingURL=response-parser.js.map