init claude-code

This commit is contained in:
2026-04-01 17:32:37 +02:00
commit 73b208c009
1902 changed files with 513237 additions and 0 deletions
@@ -0,0 +1,123 @@
import { z } from 'zod/v4'
import {
ensureConnectedClient,
fetchResourcesForClient,
} from '../../services/mcp/client.js'
import { buildTool, type ToolDef } from '../../Tool.js'
import { errorMessage } from '../../utils/errors.js'
import { lazySchema } from '../../utils/lazySchema.js'
import { logMCPError } from '../../utils/log.js'
import { jsonStringify } from '../../utils/slowOperations.js'
import { isOutputLineTruncated } from '../../utils/terminal.js'
import { DESCRIPTION, LIST_MCP_RESOURCES_TOOL_NAME, PROMPT } from './prompt.js'
import { renderToolResultMessage, renderToolUseMessage } from './UI.js'
const inputSchema = lazySchema(() =>
z.object({
server: z
.string()
.optional()
.describe('Optional server name to filter resources by'),
}),
)
type InputSchema = ReturnType<typeof inputSchema>
const outputSchema = lazySchema(() =>
z.array(
z.object({
uri: z.string().describe('Resource URI'),
name: z.string().describe('Resource name'),
mimeType: z.string().optional().describe('MIME type of the resource'),
description: z.string().optional().describe('Resource description'),
server: z.string().describe('Server that provides this resource'),
}),
),
)
type OutputSchema = ReturnType<typeof outputSchema>
export type Output = z.infer<OutputSchema>
export const ListMcpResourcesTool = buildTool({
isConcurrencySafe() {
return true
},
isReadOnly() {
return true
},
toAutoClassifierInput(input) {
return input.server ?? ''
},
shouldDefer: true,
name: LIST_MCP_RESOURCES_TOOL_NAME,
searchHint: 'list resources from connected MCP servers',
maxResultSizeChars: 100_000,
async description() {
return DESCRIPTION
},
async prompt() {
return PROMPT
},
get inputSchema(): InputSchema {
return inputSchema()
},
get outputSchema(): OutputSchema {
return outputSchema()
},
async call(input, { options: { mcpClients } }) {
const { server: targetServer } = input
const clientsToProcess = targetServer
? mcpClients.filter(client => client.name === targetServer)
: mcpClients
if (targetServer && clientsToProcess.length === 0) {
throw new Error(
`Server "${targetServer}" not found. Available servers: ${mcpClients.map(c => c.name).join(', ')}`,
)
}
// fetchResourcesForClient is LRU-cached (by server name) and already
// warm from startup prefetch. Cache is invalidated on onclose and on
// resources/list_changed notifications, so results are never stale.
// ensureConnectedClient is a no-op when healthy (memoize hit), but after
// onclose it returns a fresh connection so the re-fetch succeeds.
const results = await Promise.all(
clientsToProcess.map(async client => {
if (client.type !== 'connected') return []
try {
const fresh = await ensureConnectedClient(client)
return await fetchResourcesForClient(fresh)
} catch (error) {
// One server's reconnect failure shouldn't sink the whole result.
logMCPError(client.name, errorMessage(error))
return []
}
}),
)
return {
data: results.flat(),
}
},
renderToolUseMessage,
userFacingName: () => 'listMcpResources',
renderToolResultMessage,
isResultTruncated(output: Output): boolean {
return isOutputLineTruncated(jsonStringify(output))
},
mapToolResultToToolResultBlockParam(content, toolUseID) {
if (!content || content.length === 0) {
return {
tool_use_id: toolUseID,
type: 'tool_result',
content:
'No resources found. MCP servers may still provide tools even if they have no resources.',
}
}
return {
tool_use_id: toolUseID,
type: 'tool_result',
content: jsonStringify(content),
}
},
} satisfies ToolDef<InputSchema, Output>)
+29
View File
@@ -0,0 +1,29 @@
import * as React from 'react';
import { MessageResponse } from '../../components/MessageResponse.js';
import { OutputLine } from '../../components/shell/OutputLine.js';
import { Text } from '../../ink.js';
import type { ToolProgressData } from '../../Tool.js';
import type { ProgressMessage } from '../../types/message.js';
import { jsonStringify } from '../../utils/slowOperations.js';
import type { Output } from './ListMcpResourcesTool.js';
export function renderToolUseMessage(input: Partial<{
server?: string;
}>): React.ReactNode {
return input.server ? `List MCP resources from server "${input.server}"` : `List all MCP resources`;
}
export function renderToolResultMessage(output: Output, _progressMessagesForMessage: ProgressMessage<ToolProgressData>[], {
verbose
}: {
verbose: boolean;
}): React.ReactNode {
if (!output || output.length === 0) {
return <MessageResponse height={1}>
<Text dimColor>(No resources found)</Text>
</MessageResponse>;
}
// eslint-disable-next-line no-restricted-syntax -- human-facing UI, not tool_result
const formattedOutput = jsonStringify(output, null, 2);
return <OutputLine content={formattedOutput} verbose={verbose} />;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIk1lc3NhZ2VSZXNwb25zZSIsIk91dHB1dExpbmUiLCJUZXh0IiwiVG9vbFByb2dyZXNzRGF0YSIsIlByb2dyZXNzTWVzc2FnZSIsImpzb25TdHJpbmdpZnkiLCJPdXRwdXQiLCJyZW5kZXJUb29sVXNlTWVzc2FnZSIsImlucHV0IiwiUGFydGlhbCIsInNlcnZlciIsIlJlYWN0Tm9kZSIsInJlbmRlclRvb2xSZXN1bHRNZXNzYWdlIiwib3V0cHV0IiwiX3Byb2dyZXNzTWVzc2FnZXNGb3JNZXNzYWdlIiwidmVyYm9zZSIsImxlbmd0aCIsImZvcm1hdHRlZE91dHB1dCJdLCJzb3VyY2VzIjpbIlVJLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IE1lc3NhZ2VSZXNwb25zZSB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvTWVzc2FnZVJlc3BvbnNlLmpzJ1xuaW1wb3J0IHsgT3V0cHV0TGluZSB9IGZyb20gJy4uLy4uL2NvbXBvbmVudHMvc2hlbGwvT3V0cHV0TGluZS5qcydcbmltcG9ydCB7IFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgdHlwZSB7IFRvb2xQcm9ncmVzc0RhdGEgfSBmcm9tICcuLi8uLi9Ub29sLmpzJ1xuaW1wb3J0IHR5cGUgeyBQcm9ncmVzc01lc3NhZ2UgfSBmcm9tICcuLi8uLi90eXBlcy9tZXNzYWdlLmpzJ1xuaW1wb3J0IHsganNvblN0cmluZ2lmeSB9IGZyb20gJy4uLy4uL3V0aWxzL3Nsb3dPcGVyYXRpb25zLmpzJ1xuaW1wb3J0IHR5cGUgeyBPdXRwdXQgfSBmcm9tICcuL0xpc3RNY3BSZXNvdXJjZXNUb29sLmpzJ1xuXG5leHBvcnQgZnVuY3Rpb24gcmVuZGVyVG9vbFVzZU1lc3NhZ2UoXG4gIGlucHV0OiBQYXJ0aWFsPHsgc2VydmVyPzogc3RyaW5nIH0+LFxuKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgcmV0dXJuIGlucHV0LnNlcnZlclxuICAgID8gYExpc3QgTUNQIHJlc291cmNlcyBmcm9tIHNlcnZlciBcIiR7aW5wdXQuc2VydmVyfVwiYFxuICAgIDogYExpc3QgYWxsIE1DUCByZXNvdXJjZXNgXG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZW5kZXJUb29sUmVzdWx0TWVzc2FnZShcbiAgb3V0cHV0OiBPdXRwdXQsXG4gIF9wcm9ncmVzc01lc3NhZ2VzRm9yTWVzc2FnZTogUHJvZ3Jlc3NNZXNzYWdlPFRvb2xQcm9ncmVzc0RhdGE+W10sXG4gIHsgdmVyYm9zZSB9OiB7IHZlcmJvc2U6IGJvb2xlYW4gfSxcbik6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGlmICghb3V0cHV0IHx8IG91dHB1dC5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gKFxuICAgICAgPE1lc3NhZ2VSZXNwb25zZSBoZWlnaHQ9ezF9PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj4oTm8gcmVzb3VyY2VzIGZvdW5kKTwvVGV4dD5cbiAgICAgIDwvTWVzc2FnZVJlc3BvbnNlPlxuICAgIClcbiAgfVxuXG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1yZXN0cmljdGVkLXN5bnRheCAtLSBodW1hbi1mYWNpbmcgVUksIG5vdCB0b29sX3Jlc3VsdFxuICBjb25zdCBmb3JtYXR0ZWRPdXRwdXQgPSBqc29uU3RyaW5naWZ5KG91dHB1dCwgbnVsbCwgMilcblxuICByZXR1cm4gPE91dHB1dExpbmUgY29udGVudD17Zm9ybWF0dGVkT3V0cHV0fSB2ZXJib3NlPXt2ZXJib3NlfSAvPlxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLGVBQWUsUUFBUSxxQ0FBcUM7QUFDckUsU0FBU0MsVUFBVSxRQUFRLHNDQUFzQztBQUNqRSxTQUFTQyxJQUFJLFFBQVEsY0FBYztBQUNuQyxjQUFjQyxnQkFBZ0IsUUFBUSxlQUFlO0FBQ3JELGNBQWNDLGVBQWUsUUFBUSx3QkFBd0I7QUFDN0QsU0FBU0MsYUFBYSxRQUFRLCtCQUErQjtBQUM3RCxjQUFjQyxNQUFNLFFBQVEsMkJBQTJCO0FBRXZELE9BQU8sU0FBU0Msb0JBQW9CQSxDQUNsQ0MsS0FBSyxFQUFFQyxPQUFPLENBQUM7RUFBRUMsTUFBTSxDQUFDLEVBQUUsTUFBTTtBQUFDLENBQUMsQ0FBQyxDQUNwQyxFQUFFWCxLQUFLLENBQUNZLFNBQVMsQ0FBQztFQUNqQixPQUFPSCxLQUFLLENBQUNFLE1BQU0sR0FDZixtQ0FBbUNGLEtBQUssQ0FBQ0UsTUFBTSxHQUFHLEdBQ2xELHdCQUF3QjtBQUM5QjtBQUVBLE9BQU8sU0FBU0UsdUJBQXVCQSxDQUNyQ0MsTUFBTSxFQUFFUCxNQUFNLEVBQ2RRLDJCQUEyQixFQUFFVixlQUFlLENBQUNELGdCQUFnQixDQUFDLEVBQUUsRUFDaEU7RUFBRVk7QUFBOEIsQ0FBckIsRUFBRTtFQUFFQSxPQUFPLEVBQUUsT0FBTztBQUFDLENBQUMsQ0FDbEMsRUFBRWhCLEtBQUssQ0FBQ1ksU0FBUyxDQUFDO0VBQ2pCLElBQUksQ0FBQ0UsTUFBTSxJQUFJQSxNQUFNLENBQUNHLE1BQU0sS0FBSyxDQUFDLEVBQUU7SUFDbEMsT0FDRSxDQUFDLGVBQWUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDakMsUUFBUSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsb0JBQW9CLEVBQUUsSUFBSTtBQUNqRCxNQUFNLEVBQUUsZUFBZSxDQUFDO0VBRXRCOztFQUVBO0VBQ0EsTUFBTUMsZUFBZSxHQUFHWixhQUFhLENBQUNRLE1BQU0sRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0VBRXRELE9BQU8sQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLENBQUNJLGVBQWUsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDRixPQUFPLENBQUMsR0FBRztBQUNuRSIsImlnbm9yZUxpc3QiOltdfQ==
+20
View File
@@ -0,0 +1,20 @@
export const LIST_MCP_RESOURCES_TOOL_NAME = 'ListMcpResourcesTool'
export const DESCRIPTION = `
Lists available resources from configured MCP servers.
Each resource object includes a 'server' field indicating which server it's from.
Usage examples:
- List all resources from all servers: \`listMcpResources\`
- List resources from a specific server: \`listMcpResources({ server: "myserver" })\`
`
export const PROMPT = `
List available resources from configured MCP servers.
Each returned resource will include all standard MCP resource fields plus a 'server' field
indicating which server the resource belongs to.
Parameters:
- server (optional): The name of a specific MCP server to get resources from. If not provided,
resources from all servers will be returned.
`