Files
oh-my-pi/packages/coding-agent/src/exa/factory.ts
T
can1357 ad2e5c0578 refactor(coding-agent/exa): renamed tool detail field from response to raw
- Updated the Exa tool factory to emit details using a raw field rather than response.
- Updated the MCP wrapped Exa tool to emit details using a raw field rather than response.
2026-05-14 06:16:31 +02:00

60 lines
2.1 KiB
TypeScript

/**
* Shared factory for creating Exa tools with consistent error handling and response formatting.
*/
import type { TObject, TProperties } from "@sinclair/typebox";
import type { CustomTool } from "../extensibility/custom-tools/types";
import { callExaTool, findApiKey, formatGenericResponse, formatSearchResults, isSearchResponse } from "./mcp-client";
import type { ExaRenderDetails } from "./types";
/** Creates an Exa tool with standardized API key handling, error wrapping, and optional search response formatting. */
export function createExaTool(
name: string,
label: string,
description: string,
parameters: TObject<TProperties>,
mcpToolName: string,
options?: {
/** When true, checks isSearchResponse and formats with formatSearchResults. Default: true */
formatResponse?: boolean;
/** Transform params before passing to callExaTool */
transformParams?: (params: Record<string, unknown>) => Record<string, unknown>;
},
): CustomTool<any, ExaRenderDetails> {
const formatResponse = options?.formatResponse ?? true;
const transformParams = options?.transformParams;
return {
name,
label,
description,
parameters,
async execute(_toolCallId, params, _onUpdate, _ctx, _signal) {
try {
const apiKey = findApiKey();
// Exa MCP endpoint is publicly accessible; API key is optional
const args = transformParams ? transformParams(params as Record<string, unknown>) : params;
const response = await callExaTool(mcpToolName, args, apiKey);
if (formatResponse && isSearchResponse(response)) {
const formatted = formatSearchResults(response);
return {
content: [{ type: "text" as const, text: formatted }],
details: { response, toolName: name },
};
}
return {
content: [{ type: "text" as const, text: formatGenericResponse(response) }],
details: { raw: response, toolName: name },
};
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
return {
content: [{ type: "text" as const, text: `Error: ${message}` }],
details: { error: message, toolName: name },
};
}
},
};
}