Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ export {
type McpClientHandle,
type McpToolMeta,
type McpResourceMeta,
type McpResourceTemplateMeta,
type McpPromptMeta,
type ConnectAllResult,
type BuildMcpServerOpts,
Expand Down
9 changes: 8 additions & 1 deletion packages/core/src/mcp/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ async function writeFakeServer(
: '';
const resourceBlock = resources
? `
import { ListResourcesRequestSchema, ReadResourceRequestSchema } from '${TYPES_INDEX}';
import { ListResourcesRequestSchema, ReadResourceRequestSchema, ListResourceTemplatesRequestSchema } from '${TYPES_INDEX}';
const RESOURCES = ${JSON.stringify(resources)};
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
resources: RESOURCES.map((r) => ({ uri: r.uri, name: r.name, mimeType: r.mimeType })),
Expand All @@ -80,6 +80,9 @@ server.setRequestHandler(ReadResourceRequestSchema, async (req) => {
if (!found) throw new Error('no such resource: ' + req.params.uri);
return { contents: [{ uri: found.uri, mimeType: found.mimeType ?? 'text/plain', text: found.text }] };
});
server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
resourceTemplates: [{ uriTemplate: 'file:///{path}', name: 'file', description: 'a file by path' }],
}));
`
: '';
const promptBlock = prompts
Expand Down Expand Up @@ -279,6 +282,10 @@ describe('MCP client', () => {
'mem://note',
]);

// resources/templates/list populated the handle's templates
expect(handle.resourceTemplates.map((t) => t.uriTemplate)).toEqual(['file:///{path}']);
expect(handle.resourceTemplates[0]!.name).toBe('file');

// readMcpResource flattens contents to text
expect(await readMcpResource(handle, 'file:///readme.md')).toContain('# Hello');

Expand Down
24 changes: 24 additions & 0 deletions packages/core/src/mcp/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ export interface McpResourceMeta {
mimeType?: string;
}

/** A parameterized resource a server exposes (from resources/templates/list). */
export interface McpResourceTemplateMeta {
/** RFC 6570 URI template, e.g. `file:///{path}`. */
uriTemplate: string;
name: string;
description?: string;
mimeType?: string;
}

/** A prompt a server exposes (from prompts/list). */
export interface McpPromptMeta {
name: string;
Expand Down Expand Up @@ -88,6 +97,8 @@ export interface McpClientHandle {
tools: ToolHandler[];
/** Resources the server advertised (empty if it has no `resources` capability). */
resources: McpResourceMeta[];
/** Parameterized resource templates the server advertised. */
resourceTemplates: McpResourceTemplateMeta[];
/** Prompts the server advertised (empty if it has no `prompts` capability). */
prompts: McpPromptMeta[];
close(): Promise<void>;
Expand Down Expand Up @@ -295,6 +306,7 @@ export async function connectMcpServer(
// Resources (best-effort, capability-gated). A server without the `resources`
// capability — or one that errors on resources/list — just yields [].
let resources: McpResourceMeta[] = [];
let resourceTemplates: McpResourceTemplateMeta[] = [];
if (client.getServerCapabilities()?.resources) {
try {
const r = await client.listResources();
Expand All @@ -307,6 +319,17 @@ export async function connectMcpServer(
} catch {
/* server advertised resources but list failed — degrade to none */
}
try {
const rt = await client.listResourceTemplates();
resourceTemplates = (rt.resourceTemplates ?? []).map((t) => ({
uriTemplate: t.uriTemplate,
name: t.name,
description: t.description,
mimeType: t.mimeType,
}));
} catch {
/* templates are optional even within the resources capability */
}
}

// Prompts (best-effort, capability-gated — same degradation as resources).
Expand All @@ -331,6 +354,7 @@ export async function connectMcpServer(
transportKind: kind,
tools,
resources,
resourceTemplates,
prompts,
async close() {
await client.close();
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/mcp/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export {
type McpClientHandle,
type McpToolMeta,
type McpResourceMeta,
type McpResourceTemplateMeta,
type McpPromptMeta,
type McpTransportKind,
type ConnectAllResult,
Expand Down
Loading