mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-03-31 06:12:26 +00:00
133 lines
3.4 KiB
TypeScript
133 lines
3.4 KiB
TypeScript
import z from "zod"
|
|
import { Tool } from "./tool"
|
|
import DESCRIPTION from "./codesearch.txt"
|
|
import { abortAfterAny } from "../util/abort"
|
|
|
|
const API_CONFIG = {
|
|
BASE_URL: "https://mcp.exa.ai",
|
|
ENDPOINTS: {
|
|
CONTEXT: "/mcp",
|
|
},
|
|
} as const
|
|
|
|
interface McpCodeRequest {
|
|
jsonrpc: string
|
|
id: number
|
|
method: string
|
|
params: {
|
|
name: string
|
|
arguments: {
|
|
query: string
|
|
tokensNum: number
|
|
}
|
|
}
|
|
}
|
|
|
|
interface McpCodeResponse {
|
|
jsonrpc: string
|
|
result: {
|
|
content: Array<{
|
|
type: string
|
|
text: string
|
|
}>
|
|
}
|
|
}
|
|
|
|
export const CodeSearchTool = Tool.define("codesearch", {
|
|
description: DESCRIPTION,
|
|
parameters: z.object({
|
|
query: z
|
|
.string()
|
|
.describe(
|
|
"Search query to find relevant context for APIs, Libraries, and SDKs. For example, 'React useState hook examples', 'Python pandas dataframe filtering', 'Express.js middleware', 'Next js partial prerendering configuration'",
|
|
),
|
|
tokensNum: z
|
|
.number()
|
|
.min(1000)
|
|
.max(50000)
|
|
.default(5000)
|
|
.describe(
|
|
"Number of tokens to return (1000-50000). Default is 5000 tokens. Adjust this value based on how much context you need - use lower values for focused queries and higher values for comprehensive documentation.",
|
|
),
|
|
}),
|
|
async execute(params, ctx) {
|
|
await ctx.ask({
|
|
permission: "codesearch",
|
|
patterns: [params.query],
|
|
always: ["*"],
|
|
metadata: {
|
|
query: params.query,
|
|
tokensNum: params.tokensNum,
|
|
},
|
|
})
|
|
|
|
const codeRequest: McpCodeRequest = {
|
|
jsonrpc: "2.0",
|
|
id: 1,
|
|
method: "tools/call",
|
|
params: {
|
|
name: "get_code_context_exa",
|
|
arguments: {
|
|
query: params.query,
|
|
tokensNum: params.tokensNum || 5000,
|
|
},
|
|
},
|
|
}
|
|
|
|
const { signal, clearTimeout } = abortAfterAny(30000, ctx.abort)
|
|
|
|
try {
|
|
const headers: Record<string, string> = {
|
|
accept: "application/json, text/event-stream",
|
|
"content-type": "application/json",
|
|
}
|
|
|
|
const response = await fetch(`${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.CONTEXT}`, {
|
|
method: "POST",
|
|
headers,
|
|
body: JSON.stringify(codeRequest),
|
|
signal,
|
|
})
|
|
|
|
clearTimeout()
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text()
|
|
throw new Error(`Code search error (${response.status}): ${errorText}`)
|
|
}
|
|
|
|
const responseText = await response.text()
|
|
|
|
// Parse SSE response
|
|
const lines = responseText.split("\n")
|
|
for (const line of lines) {
|
|
if (line.startsWith("data: ")) {
|
|
const data: McpCodeResponse = JSON.parse(line.substring(6))
|
|
if (data.result && data.result.content && data.result.content.length > 0) {
|
|
return {
|
|
output: data.result.content[0].text,
|
|
title: `Code search: ${params.query}`,
|
|
metadata: {},
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
output:
|
|
"No code snippets or documentation found. Please try a different query, be more specific about the library or programming concept, or check the spelling of framework names.",
|
|
title: `Code search: ${params.query}`,
|
|
metadata: {},
|
|
}
|
|
} catch (error) {
|
|
clearTimeout()
|
|
|
|
if (error instanceof Error && error.name === "AbortError") {
|
|
throw new Error("Code search request timed out")
|
|
}
|
|
|
|
throw error
|
|
}
|
|
},
|
|
})
|