Files
orcs-code/src/tools/MCPTool/MCPTool.ts
FluxLuFFy 91e4cfb15b fix: WebSearch providers + MCPTool bugs (#593)
* fix: WebSearch providers + MCPTool bugs

WebSearchTool:
- custom.ts: fix buildAuthHeadersForPreset WEB_AUTH_HEADER opt-out
- custom.ts: fix WEB_AUTH_SCHEME empty string handling
- custom.ts: fix walkJsonPath null safety for jsonPath parsing
- duckduckgo.ts: use SafeSearchType enum instead of raw 0
- mojeek.ts: always send Accept: application/json header
- README: fix timeout documentation (15s -> 120s to match code)
- custom.test.ts: add tests for auth header behavior

MCPTool:
- MCPTool.ts: fix outputSchema to accept ContentBlockParam[] (not just string)
- MCPTool.ts: fix isResultTruncated for array output (iterates text blocks)

* fix: address PR #593 review feedback

1. Export buildAuthHeadersForPreset and add direct tests for:
   - WEB_AUTH_HEADER="" explicit opt-out behavior
   - WEB_AUTH_SCHEME="" stripping scheme prefix
   - Preset defaults (authHeader + authScheme)
   - No WEB_KEY returns empty headers

2. Add duckduckgo.test.ts verifying SafeSearchType.STRICT === 0,
   confirming the enum change is semantically identical to the
   previous raw value.

Addresses review by @Vasanthdev2004 at
pullrequestreview-4093533095

---------

Co-authored-by: FluxLuFFy <flux@openclaude.dev>
Co-authored-by: Fix Bot <fix@openclaude.local>
2026-04-11 21:07:20 +08:00

103 lines
2.9 KiB
TypeScript

import { z } from 'zod/v4'
import { buildTool, type ToolDef } from '../../Tool.js'
import { lazySchema } from '../../utils/lazySchema.js'
import type { PermissionResult } from '../../utils/permissions/PermissionResult.js'
import { isOutputLineTruncated } from '../../utils/terminal.js'
import { DESCRIPTION, PROMPT } from './prompt.js'
import {
renderToolResultMessage,
renderToolUseMessage,
renderToolUseProgressMessage,
} from './UI.js'
// Allow any input object since MCP tools define their own schemas
export const inputSchema = lazySchema(() => z.object({}).passthrough())
type InputSchema = ReturnType<typeof inputSchema>
// MCP tools can return either a plain string or an array of content blocks
// (text, images, etc.). The outputSchema must reflect both shapes so the model
// knows rich content is possible.
export const outputSchema = lazySchema(() =>
z.union([
z.string().describe('MCP tool execution result as text'),
z
.array(
z.object({
type: z.string(),
text: z.string().optional(),
}),
)
.describe('MCP tool execution result as content blocks'),
]),
)
type OutputSchema = ReturnType<typeof outputSchema>
export type Output = z.infer<OutputSchema>
// Re-export MCPProgress from centralized types to break import cycles
export type { MCPProgress } from '../../types/tools.js'
export const MCPTool = buildTool({
isMcp: true,
// Overridden in mcpClient.ts with the real MCP tool name + args
isOpenWorld() {
return false
},
// Overridden in mcpClient.ts
name: 'mcp',
maxResultSizeChars: 100_000,
// Overridden in mcpClient.ts
async description() {
return DESCRIPTION
},
// Overridden in mcpClient.ts
async prompt() {
return PROMPT
},
get inputSchema(): InputSchema {
return inputSchema()
},
get outputSchema(): OutputSchema {
return outputSchema()
},
// Overridden in mcpClient.ts
async call() {
return {
data: '',
}
},
async checkPermissions(): Promise<PermissionResult> {
return {
behavior: 'passthrough',
message: 'MCPTool requires permission.',
}
},
renderToolUseMessage,
// Overridden in mcpClient.ts
userFacingName: () => 'mcp',
renderToolUseProgressMessage,
renderToolResultMessage,
isResultTruncated(output: Output): boolean {
if (typeof output === 'string') {
return isOutputLineTruncated(output)
}
// Array of content blocks — check if any text block exceeds the display limit
if (Array.isArray(output)) {
return output.some(
block =>
block?.type === 'text' &&
typeof block.text === 'string' &&
isOutputLineTruncated(block.text),
)
}
return false
},
mapToolResultToToolResultBlockParam(content, toolUseID) {
return {
tool_use_id: toolUseID,
type: 'tool_result',
content,
}
},
} satisfies ToolDef<InputSchema, Output>)