ListMcpResourcesTool.ts
tools/ListMcpResourcesTool/ListMcpResourcesTool.ts
124
Lines
3907
Bytes
2
Exports
10
Imports
10
Keywords
What this is
This page documents one file from the repository and includes its full source so you can read it without leaving the docs site.
Beginner explanation
This file is part of the tool layer, which means it describes actions the system can perform for the user or model.
How it is used
Start from the exports list and related files. Those are the easiest clues for where this file fits into the system.
Expert explanation
Architecturally, this file intersects with tool-system, mcp. It contains 124 lines, 10 detected imports, and 2 detected exports.
Important relationships
Detected exports
OutputListMcpResourcesTool
Keywords
servernameclientinputschemaoutputschemadescribecontentutilsdescriptionresources
Detected imports
zod/v4../../services/mcp/client.js../../Tool.js../../utils/errors.js../../utils/lazySchema.js../../utils/log.js../../utils/slowOperations.js../../utils/terminal.js./prompt.js./UI.js
Source notes
This page embeds the full file contents. Small or leaf files are still indexed honestly instead of being over-explained.
Full source
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>)