( toolName: string, fileContent: string )
| 1758 | } |
| 1759 | |
| 1760 | function extractToolInfo( |
| 1761 | toolName: string, |
| 1762 | fileContent: string |
| 1763 | ): { |
| 1764 | description: string |
| 1765 | params: Array<{ name: string; type: string; required: boolean; description: string }> |
| 1766 | outputs: Record<string, any> |
| 1767 | } | null { |
| 1768 | try { |
| 1769 | // First, try to find the specific tool definition by its ID |
| 1770 | // Look for: id: 'toolName' or id: "toolName" |
| 1771 | const toolIdRegex = new RegExp(`id:\\s*['"]${toolName}['"]`) |
| 1772 | const toolIdMatch = fileContent.match(toolIdRegex) |
| 1773 | |
| 1774 | let toolContent = fileContent |
| 1775 | if (toolIdMatch && toolIdMatch.index !== undefined) { |
| 1776 | // Find the tool definition block that contains this ID |
| 1777 | // Search backwards for 'export const' or start of object |
| 1778 | const beforeId = fileContent.substring(0, toolIdMatch.index) |
| 1779 | const exportMatch = beforeId.match(/export\s+const\s+\w+[^=]*=\s*\{[\s\S]*$/) |
| 1780 | |
| 1781 | if (exportMatch && exportMatch.index !== undefined) { |
| 1782 | const startIndex = exportMatch.index + exportMatch[0].length - 1 |
| 1783 | const endIndex = findMatchingClose(fileContent, startIndex) |
| 1784 | |
| 1785 | if (endIndex !== -1) { |
| 1786 | toolContent = fileContent.substring(startIndex, endIndex) |
| 1787 | } |
| 1788 | } |
| 1789 | } |
| 1790 | |
| 1791 | // Prefer the params block scoped to this specific tool so that files |
| 1792 | // defining multiple tools (e.g. file_compress + file_decompress in |
| 1793 | // compress.ts) don't all inherit the first tool's params. Fall back to the |
| 1794 | // full file for tools that inherit params via spread from a base object. |
| 1795 | const toolConfigRegex = |
| 1796 | /params\s*:\s*{([\s\S]*?)},?\s*(?:outputs|oauth|request|directExecution|postProcess|transformResponse)\s*:/ |
| 1797 | const toolConfigMatch = toolContent.match(toolConfigRegex) ?? fileContent.match(toolConfigRegex) |
| 1798 | |
| 1799 | // Description should come from the specific tool block if found |
| 1800 | // Only search before nested objects (params, outputs, request, etc.) to avoid matching |
| 1801 | // descriptions inside outputs or params |
| 1802 | let descriptionSearchContent = toolContent |
| 1803 | const nestedObjectPatterns = [ |
| 1804 | /\bparams\s*:\s*[{]/, |
| 1805 | /\boutputs\s*:\s*\{/, |
| 1806 | /\brequest\s*:\s*\{/, |
| 1807 | /\boauth\s*:\s*\{/, |
| 1808 | /\btransformResponse\s*:/, |
| 1809 | ] |
| 1810 | let cutoffIndex = toolContent.length |
| 1811 | for (const pattern of nestedObjectPatterns) { |
| 1812 | const match = toolContent.match(pattern) |
| 1813 | if (match && match.index !== undefined && match.index < cutoffIndex) { |
| 1814 | cutoffIndex = match.index |
| 1815 | } |
| 1816 | } |
| 1817 | descriptionSearchContent = toolContent.substring(0, cutoffIndex) |
no test coverage detected