* Strips tool_reference blocks for tools that no longer exist from tool_result content. * This handles the case where a session was saved with MCP tools that are no longer * available (e.g., MCP server was disconnected, renamed, or removed). * Without this filtering, the API rejects with "Tool re
( message: UserMessage, availableToolNames: Set<string>, )
| 1539 | * Without this filtering, the API rejects with "Tool reference not found in available tools". |
| 1540 | */ |
| 1541 | function stripUnavailableToolReferencesFromUserMessage( |
| 1542 | message: UserMessage, |
| 1543 | availableToolNames: Set<string>, |
| 1544 | ): UserMessage { |
| 1545 | const content = message.message.content |
| 1546 | if (!Array.isArray(content)) { |
| 1547 | return message |
| 1548 | } |
| 1549 | |
| 1550 | // Check if any tool_reference blocks point to unavailable tools |
| 1551 | const hasUnavailableReference = content.some( |
| 1552 | block => |
| 1553 | block.type === 'tool_result' && |
| 1554 | Array.isArray(block.content) && |
| 1555 | block.content.some(c => { |
| 1556 | if (!isToolReferenceBlock(c)) return false |
| 1557 | const toolName = (c as { tool_name?: string }).tool_name |
| 1558 | return ( |
| 1559 | toolName && !availableToolNames.has(normalizeLegacyToolName(toolName)) |
| 1560 | ) |
| 1561 | }), |
| 1562 | ) |
| 1563 | |
| 1564 | if (!hasUnavailableReference) { |
| 1565 | return message |
| 1566 | } |
| 1567 | |
| 1568 | return { |
| 1569 | ...message, |
| 1570 | message: { |
| 1571 | ...message.message, |
| 1572 | content: content.map(block => { |
| 1573 | if (block.type !== 'tool_result' || !Array.isArray(block.content)) { |
| 1574 | return block |
| 1575 | } |
| 1576 | |
| 1577 | // Filter out tool_reference blocks for unavailable tools |
| 1578 | const filteredContent = block.content.filter(c => { |
| 1579 | if (!isToolReferenceBlock(c)) return true |
| 1580 | const rawToolName = (c as { tool_name?: string }).tool_name |
| 1581 | if (!rawToolName) return true |
| 1582 | const toolName = normalizeLegacyToolName(rawToolName) |
| 1583 | const isAvailable = availableToolNames.has(toolName) |
| 1584 | if (!isAvailable) { |
| 1585 | logForDebugging( |
| 1586 | `Filtering out tool_reference for unavailable tool: ${toolName}`, |
| 1587 | { level: 'warn' }, |
| 1588 | ) |
| 1589 | } |
| 1590 | return isAvailable |
| 1591 | }) |
| 1592 | |
| 1593 | // If all content was filtered out, replace with a placeholder |
| 1594 | if (filteredContent.length === 0) { |
| 1595 | return { |
| 1596 | ...block, |
| 1597 | content: [ |
| 1598 | { |
no test coverage detected