Something went wrong. Try again.
source dump of claude code forked from oppi.li/claude-code
Something went wrong. Try again.
6.3 kB · 196 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197import { Server } from '@modelcontextprotocol/sdk/server/index.js'import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'import { CallToolRequestSchema, type CallToolResult, ListToolsRequestSchema, type ListToolsResult, type Tool,} from '@modelcontextprotocol/sdk/types.js'import { getDefaultAppState } from 'src/state/AppStateStore.js'import review from '../commands/review.js'import type { Command } from '../commands.js'import { findToolByName, getEmptyToolPermissionContext, type ToolUseContext,} from '../Tool.js'import { getTools } from '../tools.js'import { createAbortController } from '../utils/abortController.js'import { createFileStateCacheWithSizeLimit } from '../utils/fileStateCache.js'import { logError } from '../utils/log.js'import { createAssistantMessage } from '../utils/messages.js'import { getMainLoopModel } from '../utils/model/model.js'import { hasPermissionsToUseTool } from '../utils/permissions/permissions.js'import { setCwd } from '../utils/Shell.js'import { jsonStringify } from '../utils/slowOperations.js'import { getErrorParts } from '../utils/toolErrors.js'import { zodToJsonSchema } from '../utils/zodToJsonSchema.js'
type ToolInput = Tool['inputSchema']type ToolOutput = Tool['outputSchema']
const MCP_COMMANDS: Command[] = [review]
export async function startMCPServer( cwd: string, debug: boolean, verbose: boolean,): Promise<void> { // Use size-limited LRU cache for readFileState to prevent unbounded memory growth // 100 files and 25MB limit should be sufficient for MCP server operations const READ_FILE_STATE_CACHE_SIZE = 100 const readFileStateCache = createFileStateCacheWithSizeLimit( READ_FILE_STATE_CACHE_SIZE, ) setCwd(cwd) const server = new Server( { name: 'claude/tengu', version: MACRO.VERSION, }, { capabilities: { tools: {}, }, }, )
server.setRequestHandler( ListToolsRequestSchema, async (): Promise<ListToolsResult> => { // TODO: Also re-expose any MCP tools const toolPermissionContext = getEmptyToolPermissionContext() const tools = getTools(toolPermissionContext) return { tools: await Promise.all( tools.map(async tool => { let outputSchema: ToolOutput | undefined if (tool.outputSchema) { const convertedSchema = zodToJsonSchema(tool.outputSchema) // MCP SDK requires outputSchema to have type: "object" at root level // Skip schemas with anyOf/oneOf at root (from z.union, z.discriminatedUnion, etc.) // See: https://github.com/anthropics/claude-code/issues/8014 if ( typeof convertedSchema === 'object' && convertedSchema !== null && 'type' in convertedSchema && convertedSchema.type === 'object' ) { outputSchema = convertedSchema as ToolOutput } } return { ...tool, description: await tool.prompt({ getToolPermissionContext: async () => toolPermissionContext, tools, agents: [], }), inputSchema: zodToJsonSchema(tool.inputSchema) as ToolInput, outputSchema, } }), ), } }, )
server.setRequestHandler( CallToolRequestSchema, async ({ params: { name, arguments: args } }): Promise<CallToolResult> => { const toolPermissionContext = getEmptyToolPermissionContext() // TODO: Also re-expose any MCP tools const tools = getTools(toolPermissionContext) const tool = findToolByName(tools, name) if (!tool) { throw new Error(`Tool ${name} not found`) }
// Assume MCP servers do not read messages separately from the tool // call arguments. const toolUseContext: ToolUseContext = { abortController: createAbortController(), options: { commands: MCP_COMMANDS, tools, mainLoopModel: getMainLoopModel(), thinkingConfig: { type: 'disabled' }, mcpClients: [], mcpResources: {}, isNonInteractiveSession: true, debug, verbose, agentDefinitions: { activeAgents: [], allAgents: [] }, }, getAppState: () => getDefaultAppState(), setAppState: () => {}, messages: [], readFileState: readFileStateCache, setInProgressToolUseIDs: () => {}, setResponseLength: () => {}, updateFileHistoryState: () => {}, updateAttributionState: () => {}, }
// TODO: validate input types with zod try { if (!tool.isEnabled()) { throw new Error(`Tool ${name} is not enabled`) } const validationResult = await tool.validateInput?.( (args as never) ?? {}, toolUseContext, ) if (validationResult && !validationResult.result) { throw new Error( `Tool ${name} input is invalid: ${validationResult.message}`, ) } const finalResult = await tool.call( (args ?? {}) as never, toolUseContext, hasPermissionsToUseTool, createAssistantMessage({ content: [], }), )
return { content: [ { type: 'text' as const, text: typeof finalResult === 'string' ? finalResult : jsonStringify(finalResult.data), }, ], } } catch (error) { logError(error)
const parts = error instanceof Error ? getErrorParts(error) : [String(error)] const errorText = parts.filter(Boolean).join('\n').trim() || 'Error'
return { isError: true, content: [ { type: 'text', text: errorText, }, ], } } }, )
async function runServer() { const transport = new StdioServerTransport() await server.connect(transport) }
return await runServer()}