Call tools, read resources, get prompts
Every block on this page runs on a connected Client — Connect to a server shows the wiring — here paired in memory with an orders server that registers three tools, a resource, and a prompt.
List the tools and call one
listTools returns the tools the server advertises; callTool invokes one by name with a plain arguments object.
const { tools } = await client.listTools();
console.log(tools.map(tool => tool.name));
const result = await client.callTool({ name: 'lookup-order', arguments: { id: 'A-1041' } });
console.log(result.content);result.content is the content array the tool handler returned, unchanged:
[ 'lookup-order', 'order-total', 'export-orders' ]
[ { type: 'text', text: 'A-1041: 3 items, shipped' } ]TIP
A failed tool call is still a result: check isError on it before trusting content. Arguments the input schema rejects come back the same way. Only protocol-level failures — unknown tool, timeout — throw.
Let the SDK walk the pages
That listTools() already walked every page: when a server splits its list, the SDK follows nextCursor page by page and returns one aggregated list with nextCursor: undefined. listPrompts(), listResources(), and listResourceTemplates() aggregate the same way.
Pass a cursor — a page's nextCursor your application held on to — and listTools returns exactly that page, raw.
const page = await client.listTools({ cursor: heldCursor });
console.log(
page.tools.map(tool => tool.name),
page.nextCursor
);The orders server hands out its three tools two per page, and heldCursor names the second page — one tool, nothing left to follow:
[ 'export-orders' ] undefinedWARNING
ClientOptions.listMaxPages (default 64) caps the aggregate walk; a server whose pagination never terminates rejects the call with an SdkError whose code is LIST_PAGINATION_EXCEEDED. listMaxPages: 0 removes the cap. Explicit-cursor calls are never capped.
Read structured output
A tool that declares an outputSchema returns structuredContent next to content. It is typed unknown — check that it is present and narrow it before use.
const details = await client.callTool({ name: 'order-total', arguments: { id: 'A-1041' } });
const total: unknown = details.structuredContent;
if (typeof total === 'object' && total !== null && 'currency' in total) {
console.log(total);
}order-total declares { id, total, currency }, and that is what comes back:
{ id: 'A-1041', total: 61.5, currency: 'EUR' }When an earlier listTools() gave the client the tool's outputSchema, callTool validates structuredContent against it and rejects a result that does not match.
The wire encoding of structured results differs by protocol era — see Protocol versions.
Read a resource
listResources names what the server exposes; readResource fetches one URI.
const { resources } = await client.listResources();
console.log(resources.map(resource => resource.uri));
const { contents } = await client.readResource({ uri: 'orders://recent' });
console.log(contents[0]);Each item in contents carries the uri, a mimeType, and either text or a base64 blob:
[ 'orders://recent' ]
{
uri: 'orders://recent',
mimeType: 'application/json',
text: '["A-1041","A-1042"]'
}For parameterized URIs, listResourceTemplates() returns the server's URI templates — expand one and pass the resulting URI to readResource. To react when a resource changes instead of re-reading it on a timer, see Subscriptions.
Get a prompt
listPrompts advertises each prompt with its arguments; getPrompt fills them in and returns messages ready to send to a model.
const { prompts } = await client.listPrompts();
console.log(prompts.map(prompt => prompt.name));
const prompt = await client.getPrompt({ name: 'summarize-order', arguments: { id: 'A-1041', tone: 'terse' } });
console.log(prompt.messages);The server's template comes back with both arguments substituted:
[ 'summarize-order' ]
[
{
role: 'user',
content: {
type: 'text',
text: 'Write a terse status update for order A-1041.'
}
}
]Autocomplete an argument
complete asks the server for suggestions while the user types an argument: ref names the prompt (or resource template) and argument carries the partial value.
const { completion } = await client.complete({
ref: { type: 'ref/prompt', name: 'summarize-order' },
argument: { name: 'tone', value: 'f' }
});
console.log(completion.values);The server matches f against the values it accepts for tone:
[ 'formal', 'friendly' ]Track progress on a long call
Every verb takes request options as a second argument. onprogress receives each notifications/progress the server emits for this call; resetTimeoutOnProgress restarts the request timeout on every update and maxTotalTimeout is the absolute cap.
const exported = await client.callTool(
{ name: 'export-orders', arguments: { format: 'csv' } },
{
onprogress: update => console.log(update),
resetTimeoutOnProgress: true,
maxTotalTimeout: 600_000
}
);
console.log(exported.content);The updates stream in while the call is still pending; the return type does not change:
{ progress: 1, total: 2, message: 'exported A-1041' }
{ progress: 2, total: 2, message: 'exported A-1042' }
[ { type: 'text', text: '2 orders exported as csv' } ]Recap
listTools,listResources,listResourceTemplates, andlistPromptsaggregate every page;{ cursor }fetches a single raw page andlistMaxPagescaps the walk.callToolreturnscontentfor the model and, when the tool declares anoutputSchema,structuredContentfor your application.readResource({ uri })andgetPrompt({ name, arguments })follow the same list-then-fetch shape as tools.complete()returns the server's suggestions for a prompt or resource-template argument.onprogressin the request options streams progress updates without changing the call's return type.