unwrapModelIntent strips the model_intent wrapper from tool call input so the remote MCP server receives only the original arguments. It handles three shapes the model may produce: 1. { model_intent, properties: {...} } — correct format 2. { model_intent, key: val, ... } — flat, no properties wrapp
(input string)
| 640 | // 2. { model_intent, key: val, ... } — flat, no properties wrapper |
| 641 | // 3. Anything else — returned as-is |
| 642 | func unwrapModelIntent(input string) string { |
| 643 | var parsed map[string]any |
| 644 | if err := json.Unmarshal([]byte(input), &parsed); err != nil { |
| 645 | return input |
| 646 | } |
| 647 | |
| 648 | delete(parsed, "model_intent") |
| 649 | |
| 650 | // Case 1: correct { model_intent, properties: {...} } format. |
| 651 | if props, ok := parsed["properties"]; ok { |
| 652 | if b, err := json.Marshal(props); err == nil { |
| 653 | return string(b) |
| 654 | } |
| 655 | } |
| 656 | |
| 657 | // Case 2: flat { model_intent, key: val, ... } without wrapper. |
| 658 | if b, err := json.Marshal(parsed); err == nil { |
| 659 | return string(b) |
| 660 | } |
| 661 | |
| 662 | return input |
| 663 | } |
| 664 | |
| 665 | // convertCallResult translates an MCP CallToolResult into a |
| 666 | // fantasy.ToolResponse. The fantasy response model supports a |