π File detail
tools/ReadMcpResourceTool/ReadMcpResourceTool.ts
π§© .tsπ 159 linesπΎ 4,654 bytesπ text
β Back to All Filesπ― Use case
This module implements the βReadMcpResourceToolβ tool (Read Mcp Resource) β something the model can call at runtime alongside other agent tools. On the API surface it exposes inputSchema, outputSchema, Output, and ReadMcpResourceTool β mainly functions, hooks, or classes. Dependencies touch @modelcontextprotocol and schema validation. It composes internal code from services, Tool, utils, prompt, and UI (relative imports).
Generated from folder role, exports, dependency roots, and inline comments β not hand-reviewed for every path.
π§ Inline summary
import { type ReadResourceResult, ReadResourceResultSchema, } from '@modelcontextprotocol/sdk/types.js' import { z } from 'zod/v4'
π€ Exports (heuristic)
inputSchemaoutputSchemaOutputReadMcpResourceTool
π External import roots
Package roots from from "β¦" (relative paths omitted).
@modelcontextprotocolzod
π₯οΈ Source preview
import {
type ReadResourceResult,
ReadResourceResultSchema,
} from '@modelcontextprotocol/sdk/types.js'
import { z } from 'zod/v4'
import { ensureConnectedClient } from '../../services/mcp/client.js'
import { buildTool, type ToolDef } from '../../Tool.js'
import { lazySchema } from '../../utils/lazySchema.js'
import {
getBinaryBlobSavedMessage,
persistBinaryContent,
} from '../../utils/mcpOutputStorage.js'
import { jsonStringify } from '../../utils/slowOperations.js'
import { isOutputLineTruncated } from '../../utils/terminal.js'
import { DESCRIPTION, PROMPT } from './prompt.js'
import {
renderToolResultMessage,
renderToolUseMessage,
userFacingName,
} from './UI.js'
export const inputSchema = lazySchema(() =>
z.object({
server: z.string().describe('The MCP server name'),
uri: z.string().describe('The resource URI to read'),
}),
)
type InputSchema = ReturnType<typeof inputSchema>
export const outputSchema = lazySchema(() =>
z.object({
contents: z.array(
z.object({
uri: z.string().describe('Resource URI'),
mimeType: z.string().optional().describe('MIME type of the content'),
text: z.string().optional().describe('Text content of the resource'),
blobSavedTo: z
.string()
.optional()
.describe('Path where binary blob content was saved'),
}),
),
}),
)
type OutputSchema = ReturnType<typeof outputSchema>
export type Output = z.infer<OutputSchema>
export const ReadMcpResourceTool = buildTool({
isConcurrencySafe() {
return true
},
isReadOnly() {
return true
},
toAutoClassifierInput(input) {
return `${input.server} ${input.uri}`
},
shouldDefer: true,
name: 'ReadMcpResourceTool',
searchHint: 'read a specific MCP resource by URI',
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: serverName, uri } = input
const client = mcpClients.find(client => client.name === serverName)
if (!client) {
throw new Error(
`Server "${serverName}" not found. Available servers: ${mcpClients.map(c => c.name).join(', ')}`,
)
}
if (client.type !== 'connected') {
throw new Error(`Server "${serverName}" is not connected`)
}
if (!client.capabilities?.resources) {
throw new Error(`Server "${serverName}" does not support resources`)
}
const connectedClient = await ensureConnectedClient(client)
const result = (await connectedClient.client.request(
{
method: 'resources/read',
params: { uri },
},
ReadResourceResultSchema,
)) as ReadResourceResult
// Intercept any blob fields: decode, write raw bytes to disk with a
// mime-derived extension, and replace with a path. Otherwise the base64
// would be stringified straight into the context.
const contents = await Promise.all(
result.contents.map(async (c, i) => {
if ('text' in c) {
return { uri: c.uri, mimeType: c.mimeType, text: c.text }
}
if (!('blob' in c) || typeof c.blob !== 'string') {
return { uri: c.uri, mimeType: c.mimeType }
}
const persistId = `mcp-resource-${Date.now()}-${i}-${Math.random().toString(36).slice(2, 8)}`
const persisted = await persistBinaryContent(
Buffer.from(c.blob, 'base64'),
c.mimeType,
persistId,
)
if ('error' in persisted) {
return {
uri: c.uri,
mimeType: c.mimeType,
text: `Binary content could not be saved to disk: ${persisted.error}`,
}
}
return {
uri: c.uri,
mimeType: c.mimeType,
blobSavedTo: persisted.filepath,
text: getBinaryBlobSavedMessage(
persisted.filepath,
c.mimeType,
persisted.size,
`[Resource from ${serverName} at ${c.uri}] `,
),
}
}),
)
return {
data: { contents },
}
},
renderToolUseMessage,
userFacingName,
renderToolResultMessage,
isResultTruncated(output: Output): boolean {
return isOutputLineTruncated(jsonStringify(output))
},
mapToolResultToToolResultBlockParam(content, toolUseID) {
return {
tool_use_id: toolUseID,
type: 'tool_result',
content: jsonStringify(content),
}
},
} satisfies ToolDef<InputSchema, Output>)