deepsearch
Execute web searches using the DeepSearch model to retrieve structured results, balancing search quality against processing time for comprehensive information retrieval.
Instructions
使用 DeepSearch 模型执行广域检索并返回结构化结果,拥有比AI Agent内置搜索更好的搜索效果但更耗时,需要平衡需求
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | ||
| top_k | No | ||
| locale | No | ||
| filters | No |
Implementation Reference
- main.ts:78-99 (handler)The handler function for the 'deepsearch' MCP tool. It receives args, logs, invokes DeepSearchAgent.search(), validates the result with searchResultSchema.parse(), logs the item count, and returns formatted content and structured result.async (args) => { const toolLogger = logger.child({ tool: "deepsearch" }); toolLogger.info("收到工具调用", args); const result = await deepsearchAgent.search(args.query, { top_k: args.top_k, locale: args.locale, filters: args.filters, }); const structured = searchResultSchema.parse(result); toolLogger.info("完成工具调用", { itemCount: structured.items.length }); return { content: [ { type: "text" as const, text: JSON.stringify(structured, null, 2), }, ], structuredContent: structured, }; },
- main.ts:10-44 (schema)Zod schema definitions for 'deepsearch' tool input (deepSearchInputShape), output (searchResultShape via searchItemShape), and compiled schemas. Note: deepSearchWebInputShape is for the related web tool.const searchItemShape = { title: z.string(), snippet: z.string(), url: z.string(), score: z.number().nullable(), }; const searchResultShape = { items: z.array(z.object(searchItemShape)), metadata: z.record(z.any()), usage: z.object({ input_tokens: z.number(), output_tokens: z.number(), }), }; const deepSearchInputShape = { query: z.string().min(1, "query 不能为空"), top_k: z.number().int().min(1).max(10).optional(), locale: z.string().optional(), filters: z.record(z.any()).default({}), }; const deepSearchWebInputShape = { ...deepSearchInputShape, filters: z .record(z.any()) .refine((filters) => typeof filters.site === "string" || typeof filters.time_range === "string", { message: "filters 需要包含 site 或 time_range 字段", }), }; const deepSearchInputSchema = z.object(deepSearchInputShape); const deepSearchWebInputSchema = z.object(deepSearchWebInputShape); const searchResultSchema = z.object(searchResultShape);
- main.ts:70-100 (registration)Registration of the 'deepsearch' tool on the McpServer instance, specifying title, description, input/output schemas, and the handler function.server.registerTool( "deepsearch", { title: "DeepSearch 通用检索", description: "使用 DeepSearch 模型执行广域检索并返回结构化结果,拥有比AI Agent内置搜索更好的搜索效果但更耗时,需要平衡需求", inputSchema: deepSearchInputShape, outputSchema: searchResultShape, }, async (args) => { const toolLogger = logger.child({ tool: "deepsearch" }); toolLogger.info("收到工具调用", args); const result = await deepsearchAgent.search(args.query, { top_k: args.top_k, locale: args.locale, filters: args.filters, }); const structured = searchResultSchema.parse(result); toolLogger.info("完成工具调用", { itemCount: structured.items.length }); return { content: [ { type: "text" as const, text: JSON.stringify(structured, null, 2), }, ], structuredContent: structured, }; }, );
- Helper class DeepSearchAgent that initializes a DeepSearchMCPClient with toolName 'deepsearch' and provides search method delegated to the client. Used by the MCP handler.export class DeepSearchAgent { private readonly client: DeepSearchMCPClient; private readonly transport?: DeepSearchTransport; private readonly logger: Logger; constructor(options: AgentOptions = {}) { const agentLogger = logger.child({ agent: "deepsearch" }); agentLogger.debug("初始化 DeepSearchAgent", { providedClient: Boolean(options.client) }); if (options.client) { this.client = options.client; this.transport = options.transport; } else { const transport = options.transport ?? DeepSearchTransport.fromEnv(); this.client = new DeepSearchMCPClient(transport, { toolName: "deepsearch" }); this.transport = transport; } this.logger = agentLogger; } search(query: string, options: SearchOptions = {}): Promise<SearchResult> { this.logger.info("执行检索", { query, options }); return this.client.search(query, options); } close(): void { this.logger.debug("关闭代理"); this.transport?.close(); } }
- source/api.ts:101-254 (helper)Core helper DeepSearchTransport.fromEnv() that implements invokeTool by making HTTP requests to external DeepSearch API (Gemini with googleSearch tool), building specific prompts for 'deepsearch', parsing JSON responses into SearchResult format.export class DeepSearchTransport { private readonly config: DeepSearchConfig; private readonly endpoint: URL; private readonly logger = logger.child({ module: "DeepSearchTransport" }); constructor(options: DeepSearchTransportOptions | DeepSearchConfig) { this.config = options instanceof DeepSearchConfig ? options : new DeepSearchConfig(options); this.endpoint = new URL("/v1beta/models/gemini-2.5-flash:generateContent", this.config.baseUrl); this.endpoint.searchParams.set("key", this.config.apiKey); } static fromEnv(): DeepSearchTransport { return new DeepSearchTransport(DeepSearchConfig.fromEnv()); } async invokeTool(toolName: string, payload: InvokePayload): Promise<DeepSearchResponsePayload> { const body = this.buildRequest(toolName, payload); this.logger.info("调用 DeepSearch API", { toolName, endpoint: this.endpoint.toString(), timeoutMs: this.config.timeoutMs, }); const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), this.config.timeoutMs); try { const response = await fetch(this.endpoint, { method: "POST", headers: { "Content-Type": "application/json", "Accept": "application/json", }, body: JSON.stringify(body), signal: controller.signal, }); if (!response.ok) { const message = `DeepSearch API 返回错误状态: ${response.status}`; this.logger.warn(message, { responseHeaders: Object.fromEntries(response.headers.entries()) }); throw new DeepSearchAPIError(message); } const data = (await response.json()) as Record<string, unknown>; this.logger.debug("收到 DeepSearch API 响应", data); return this.parseResponse(data); } catch (error) { if (error instanceof DeepSearchAPIError) { this.logger.error("DeepSearch API 调用失败", error); throw error; } if ((error as Error).name === "AbortError") { const timeoutError = new DeepSearchAPIError("DeepSearch API 请求超时", { cause: error as Error }); this.logger.error("DeepSearch API 请求超时", timeoutError); throw timeoutError; } const wrapped = new DeepSearchAPIError(`DeepSearch API 请求失败: ${(error as Error).message}`, { cause: error as Error, }); this.logger.error("DeepSearch API 调用过程中发生异常", wrapped); throw wrapped; } finally { clearTimeout(timeout); } } private buildRequest(toolName: string, payload: InvokePayload): Record<string, unknown> { const prompt = this.buildUserPrompt(toolName, payload); return { contents: [ { role: "user", parts: [{ text: prompt }], }, ], tools: [{ googleSearch: {} }], }; } private parseResponse(data: Record<string, unknown>): DeepSearchResponsePayload { const candidates = data?.candidates; if (!Array.isArray(candidates) || candidates.length === 0) { this.logger.error("DeepSearch API 响应缺少候选项", data); throw new DeepSearchAPIError("DeepSearch API 响应缺少有效的消息内容"); } const content = extractTextFromCandidate(candidates[0] as Record<string, unknown>); if (!content) { this.logger.error("DeepSearch API 响应无法提取文本", candidates[0]); throw new DeepSearchAPIError("DeepSearch API 响应内容不是合法的 JSON"); } let parsed: Record<string, unknown>; try { parsed = JSON.parse(sanitizeJsonContent(content)); } catch (error) { this.logger.error("DeepSearch API 响应 JSON 解析失败", { content, error }); throw new DeepSearchAPIError("DeepSearch API 响应内容不是合法的 JSON", { cause: error as Error }); } const itemsRaw = Array.isArray(parsed.items) ? (parsed.items as DeepSearchItem[]) : []; const metadata = (parsed.metadata as Record<string, unknown>) ?? {}; let usage = parsed.usage as DeepSearchUsage | undefined; if (!usage) { const usageMetadata = (data.usageMetadata ?? {}) as Record<string, unknown>; usage = { input_tokens: Number(usageMetadata.promptTokenCount ?? 0), output_tokens: Number(usageMetadata.candidatesTokenCount ?? usageMetadata.cachedContentTokenCount ?? 0), }; } return { items: itemsRaw.map((item) => ({ title: item.title, snippet: item.snippet ?? "", url: item.url, score: item.score ?? null, })), metadata, usage, }; } private buildUserPrompt(toolName: string, payload: InvokePayload): string { const topK = payload.top_k ?? 5; const locale = payload.locale ?? "zh-CN"; const filters = payload.filters ?? {}; const filterInstruction = toolName === "deepsearch-web" ? "必须使用 filters 中的 site/time_range 限制,确保返回结果满足条件。" : "可结合 filters 中提供的约束优化检索。"; return [ "任务: 调用 googleSearch 工具检索并汇总最新的权威信息。", `查询: ${payload.query}`, `语言: ${locale}`, `返回条数: ${topK}`, `附加筛选: ${JSON.stringify(filters)}`, filterInstruction, "输出格式要求: 必须返回合法 JSON,不能包含 Markdown、注释、额外文本或代码块标记。", "最终只输出以下结构: {\"items\":[{\"title\":string,\"snippet\":string,\"url\":string,\"score\":number|null}],\"metadata\":{\"source\":string,\"locale\":string,\"top_k\":number,\"filters\":object},\"usage\":{\"input_tokens\":number,\"output_tokens\":number}}。", "items 按相关度降序,snippet 使用中文简洁总结,score 为可信度(0-1),无法给出则为 null。", "metadata.source 固定为 'google-search',并补充 locale/top_k/filters 信息。", "⚠️ 严禁输出任何额外字符(包括 ```、解释文字、列表、粗体等)。", ].join("\n"); } close(): void { // 当前实现使用无状态 HTTP 请求,无需保留连接 } }