|
1 | 1 | import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; |
2 | 2 | import type { ComposableMCPServer } from "../../compose.ts"; |
3 | 3 | import type { SamplingConfig } from "../../types.ts"; |
4 | | -import { CompiledPrompts } from "../../prompts/index.ts"; |
5 | 4 | import { Ajv } from "ajv"; |
6 | 5 | import { AggregateAjvError } from "@segment/ajv-human-errors"; |
7 | 6 | import addFormats from "ajv-formats"; |
@@ -103,7 +102,7 @@ export abstract class BaseSamplingExecutor { |
103 | 102 | content: { |
104 | 103 | type: "text", |
105 | 104 | text: |
106 | | - 'Return ONLY raw JSON (no code fences or explanations). The JSON MUST include action and decision. Example: {"action":"<tool>","decision":"proceed|complete","<tool>":{}}', |
| 105 | + 'Return ONE AND ONLY ONE raw JSON object (no code fences, explanations, or multiple objects). The JSON MUST include action and decision. Example: {"action":"<tool>","decision":"proceed|complete","<tool>":{}}', |
107 | 106 | }, |
108 | 107 | }]; |
109 | 108 |
|
@@ -160,15 +159,14 @@ export abstract class BaseSamplingExecutor { |
160 | 159 | continue; |
161 | 160 | } |
162 | 161 |
|
163 | | - if (parsedData) { |
164 | | - this.conversationHistory.push({ |
165 | | - role: "assistant", |
166 | | - content: { |
167 | | - type: "text", |
168 | | - text: JSON.stringify(parsedData, null, 2), |
169 | | - }, |
170 | | - }); |
171 | | - } |
| 162 | + // Always show LLM what we parsed - this allows self-correction |
| 163 | + this.conversationHistory.push({ |
| 164 | + role: "assistant", |
| 165 | + content: { |
| 166 | + type: "text", |
| 167 | + text: JSON.stringify(parsedData, null, 2), |
| 168 | + }, |
| 169 | + }); |
172 | 170 |
|
173 | 171 | const action = parsedData["action"]; |
174 | 172 |
|
@@ -285,28 +283,18 @@ export abstract class BaseSamplingExecutor { |
285 | 283 | } |
286 | 284 |
|
287 | 285 | protected addParsingErrorToHistory( |
288 | | - responseText: string, |
| 286 | + _responseText: string, |
289 | 287 | parseError: unknown, |
290 | 288 | ): void { |
291 | | - this.conversationHistory.push({ |
292 | | - role: "assistant", |
293 | | - content: { |
294 | | - type: "text", |
295 | | - text: `JSON parsing failed. Response was: ${responseText}`, |
296 | | - }, |
297 | | - }); |
| 289 | + const errorMsg = parseError instanceof Error |
| 290 | + ? parseError.message |
| 291 | + : String(parseError); |
298 | 292 |
|
299 | 293 | this.conversationHistory.push({ |
300 | 294 | role: "user", |
301 | 295 | content: { |
302 | 296 | type: "text", |
303 | | - text: CompiledPrompts.errorResponse({ |
304 | | - errorMessage: `JSON parsing failed: ${ |
305 | | - parseError instanceof Error |
306 | | - ? parseError.message |
307 | | - : String(parseError) |
308 | | - }\n\nPlease respond with valid JSON.`, |
309 | | - }), |
| 297 | + text: `Invalid JSON: ${errorMsg}\n\nRespond with valid JSON.`, |
310 | 298 | }, |
311 | 299 | }); |
312 | 300 | } |
@@ -513,11 +501,11 @@ ${history}`, |
513 | 501 | schema, |
514 | 502 | schemaPrefix = "JSON schema:", |
515 | 503 | schemaSuffix = `STRICT REQUIREMENTS: |
516 | | -1. Return ONLY raw JSON that passes JSON.parse() - no markdown, code blocks, explanatory text, or extra characters |
| 504 | +1. Return ONE AND ONLY ONE raw JSON object that passes JSON.parse() - no markdown, code blocks, explanatory text, or multiple JSON objects |
517 | 505 | 2. Include ALL required fields with correct data types and satisfy ALL schema constraints (anyOf, oneOf, allOf, not, enum, pattern, min/max, conditionals) |
518 | | -3. Your response must be the JSON object itself, nothing else |
| 506 | +3. Your response must be a single JSON object, nothing else |
519 | 507 |
|
520 | | -INVALID: \`\`\`json{"key":"value"}\`\`\` or "Here is: {"key":"value"}" |
| 508 | +INVALID: \`\`\`json{"key":"value"}\`\`\` or "Here is: {"key":"value"}" or {"key":"value"}{"key":"value"} |
521 | 509 | VALID: {"key":"value"}`, |
522 | 510 | }: { |
523 | 511 | prompt?: string; |
|
0 commit comments