Skip to content

Commit 747c838

Browse files
Add server instructions, structured output, and more to docs/client.md
Expand the client guide with four new topics and their companion `.examples.ts` snippets: - Server instructions: retrieving `getInstructions()` and folding it into a system prompt - Pagination: `listTools()`, `listResources()`, and `listPrompts()` examples now loop on `nextCursor` to collect all pages - Structured tool output: `structuredContent` for machine-readable results alongside LLM-facing `content` - Progress tracking: `onprogress`, `resetTimeoutOnProgress`, and `maxTotalTimeout` options for long-running tool calls Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a62d231 commit 747c838

2 files changed

Lines changed: 141 additions & 15 deletions

File tree

docs/client.md

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ A client connects to a server, discovers what it offers — tools, resources, pr
1313
The examples below use these imports. Adjust based on which features and transport you need:
1414

1515
```ts source="../examples/client/src/clientGuide.examples.ts#imports"
16+
import type { Prompt, Resource, Tool } from '@modelcontextprotocol/client';
1617
import {
1718
applyMiddlewares,
1819
CallToolResultSchema,
@@ -97,6 +98,18 @@ await client.close();
9798

9899
For stdio, `client.close()` handles graceful process shutdown (closes stdin, then SIGTERM, then SIGKILL if needed).
99100

101+
### Server instructions
102+
103+
Servers can provide an `instructions` string during initialization that describes how to use them — cross-tool relationships, workflow patterns, and constraints (see [Instructions](https://modelcontextprotocol.io/specification/latest/basic/lifecycle#instructions) in the MCP specification). Retrieve it after connecting and include it in the model's system prompt:
104+
105+
```ts source="../examples/client/src/clientGuide.examples.ts#serverInstructions_basic"
106+
const instructions = client.getInstructions();
107+
108+
const systemPrompt = ['You are a helpful assistant.', instructions].filter(Boolean).join('\n\n');
109+
110+
console.log(systemPrompt);
111+
```
112+
100113
## Authentication
101114

102115
MCP servers can require OAuth 2.0 authentication before accepting client connections (see [Authorization](https://modelcontextprotocol.io/specification/latest/basic/authorization) in the MCP specification). Pass an `authProvider` to {@linkcode @modelcontextprotocol/client!client/streamableHttp.StreamableHTTPClientTransport | StreamableHTTPClientTransport} to enable this — the SDK provides built-in providers for common machine-to-machine flows, or you can implement the full {@linkcode @modelcontextprotocol/client!client/auth.OAuthClientProvider | OAuthClientProvider} interface for user-facing OAuth.
@@ -144,13 +157,19 @@ For a complete working OAuth flow, see [`simpleOAuthClient.ts`](https://github.c
144157

145158
Tools are callable actions offered by servers — discovering and invoking them is usually how your client enables an LLM to take action (see [Tools](https://modelcontextprotocol.io/docs/learn/server-concepts#tools) in the MCP overview).
146159

147-
Use {@linkcode @modelcontextprotocol/client!client/client.Client#listTools | listTools()} to discover available tools, and {@linkcode @modelcontextprotocol/client!client/client.Client#callTool | callTool()} to invoke one:
160+
Use {@linkcode @modelcontextprotocol/client!client/client.Client#listTools | listTools()} to discover available tools, and {@linkcode @modelcontextprotocol/client!client/client.Client#callTool | callTool()} to invoke one. Results may be paginated — loop on `nextCursor` to collect all pages:
148161

149162
```ts source="../examples/client/src/clientGuide.examples.ts#callTool_basic"
150-
const { tools } = await client.listTools();
163+
const allTools: Tool[] = [];
164+
let toolCursor: string | undefined;
165+
do {
166+
const { tools, nextCursor } = await client.listTools({ cursor: toolCursor });
167+
allTools.push(...tools);
168+
toolCursor = nextCursor;
169+
} while (toolCursor);
151170
console.log(
152171
'Available tools:',
153-
tools.map(t => t.name)
172+
allTools.map(t => t.name)
154173
);
155174

156175
const result = await client.callTool({
@@ -160,17 +179,52 @@ const result = await client.callTool({
160179
console.log(result.content);
161180
```
162181

182+
Tool results may include a `structuredContent` field — a machine-readable JSON object for programmatic use by the client application, complementing `content` which is for the LLM:
183+
184+
```ts source="../examples/client/src/clientGuide.examples.ts#callTool_structuredOutput"
185+
const result = await client.callTool({
186+
name: 'calculate-bmi',
187+
arguments: { weightKg: 70, heightM: 1.75 }
188+
});
189+
190+
// Machine-readable output for the client application
191+
if (result.structuredContent) {
192+
console.log(result.structuredContent); // e.g. { bmi: 22.86 }
193+
}
194+
```
195+
196+
### Tracking progress
197+
198+
Pass `onprogress` to receive incremental progress notifications from long-running tools. Use `resetTimeoutOnProgress` to keep the request alive while the server is actively reporting, and `maxTotalTimeout` as an absolute cap:
199+
200+
```ts source="../examples/client/src/clientGuide.examples.ts#callTool_progress"
201+
const result = await client.callTool({ name: 'long-operation', arguments: {} }, undefined, {
202+
onprogress: ({ progress, total }) => {
203+
console.log(`Progress: ${progress}/${total ?? '?'}`);
204+
},
205+
resetTimeoutOnProgress: true,
206+
maxTotalTimeout: 600_000
207+
});
208+
console.log(result.content);
209+
```
210+
163211
## Resources
164212

165213
Resources are read-only data — files, database schemas, configuration — that your application can retrieve from a server and attach as context for the model (see [Resources](https://modelcontextprotocol.io/docs/learn/server-concepts#resources) in the MCP overview).
166214

167-
Use {@linkcode @modelcontextprotocol/client!client/client.Client#listResources | listResources()} and {@linkcode @modelcontextprotocol/client!client/client.Client#readResource | readResource()} to discover and read server-provided data:
215+
Use {@linkcode @modelcontextprotocol/client!client/client.Client#listResources | listResources()} and {@linkcode @modelcontextprotocol/client!client/client.Client#readResource | readResource()} to discover and read server-provided data. Results may be paginated — loop on `nextCursor` to collect all pages:
168216

169217
```ts source="../examples/client/src/clientGuide.examples.ts#readResource_basic"
170-
const { resources } = await client.listResources();
218+
const allResources: Resource[] = [];
219+
let resourceCursor: string | undefined;
220+
do {
221+
const { resources, nextCursor } = await client.listResources({ cursor: resourceCursor });
222+
allResources.push(...resources);
223+
resourceCursor = nextCursor;
224+
} while (resourceCursor);
171225
console.log(
172226
'Available resources:',
173-
resources.map(r => r.name)
227+
allResources.map(r => r.name)
174228
);
175229

176230
const { contents } = await client.readResource({ uri: 'config://app' });
@@ -203,13 +257,19 @@ await client.unsubscribeResource({ uri: 'config://app' });
203257

204258
Prompts are reusable message templates that servers offer to help structure interactions with models (see [Prompts](https://modelcontextprotocol.io/docs/learn/server-concepts#prompts) in the MCP overview).
205259

206-
Use {@linkcode @modelcontextprotocol/client!client/client.Client#listPrompts | listPrompts()} and {@linkcode @modelcontextprotocol/client!client/client.Client#getPrompt | getPrompt()} to list available prompts and retrieve them with arguments:
260+
Use {@linkcode @modelcontextprotocol/client!client/client.Client#listPrompts | listPrompts()} and {@linkcode @modelcontextprotocol/client!client/client.Client#getPrompt | getPrompt()} to list available prompts and retrieve them with arguments. Results may be paginated — loop on `nextCursor` to collect all pages:
207261

208262
```ts source="../examples/client/src/clientGuide.examples.ts#getPrompt_basic"
209-
const { prompts } = await client.listPrompts();
263+
const allPrompts: Prompt[] = [];
264+
let promptCursor: string | undefined;
265+
do {
266+
const { prompts, nextCursor } = await client.listPrompts({ cursor: promptCursor });
267+
allPrompts.push(...prompts);
268+
promptCursor = nextCursor;
269+
} while (promptCursor);
210270
console.log(
211271
'Available prompts:',
212-
prompts.map(p => p.name)
272+
allPrompts.map(p => p.name)
213273
);
214274

215275
const { messages } = await client.getPrompt({

examples/client/src/clientGuide.examples.ts

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
//#region imports
11+
import type { Prompt, Resource, Tool } from '@modelcontextprotocol/client';
1112
import {
1213
applyMiddlewares,
1314
CallToolResultSchema,
@@ -86,6 +87,21 @@ async function disconnect_streamableHttp(client: Client, transport: StreamableHT
8687
//#endregion disconnect_streamableHttp
8788
}
8889

90+
// ---------------------------------------------------------------------------
91+
// Server instructions
92+
// ---------------------------------------------------------------------------
93+
94+
/** Example: Access server instructions after connecting. */
95+
async function serverInstructions_basic(client: Client) {
96+
//#region serverInstructions_basic
97+
const instructions = client.getInstructions();
98+
99+
const systemPrompt = ['You are a helpful assistant.', instructions].filter(Boolean).join('\n\n');
100+
101+
console.log(systemPrompt);
102+
//#endregion serverInstructions_basic
103+
}
104+
89105
// ---------------------------------------------------------------------------
90106
// Authentication
91107
// ---------------------------------------------------------------------------
@@ -127,10 +143,16 @@ async function auth_privateKeyJwt(pemEncodedKey: string) {
127143
/** Example: List and call tools. */
128144
async function callTool_basic(client: Client) {
129145
//#region callTool_basic
130-
const { tools } = await client.listTools();
146+
const allTools: Tool[] = [];
147+
let toolCursor: string | undefined;
148+
do {
149+
const { tools, nextCursor } = await client.listTools({ cursor: toolCursor });
150+
allTools.push(...tools);
151+
toolCursor = nextCursor;
152+
} while (toolCursor);
131153
console.log(
132154
'Available tools:',
133-
tools.map(t => t.name)
155+
allTools.map(t => t.name)
134156
);
135157

136158
const result = await client.callTool({
@@ -141,13 +163,48 @@ async function callTool_basic(client: Client) {
141163
//#endregion callTool_basic
142164
}
143165

166+
/** Example: Structured tool output. */
167+
async function callTool_structuredOutput(client: Client) {
168+
//#region callTool_structuredOutput
169+
const result = await client.callTool({
170+
name: 'calculate-bmi',
171+
arguments: { weightKg: 70, heightM: 1.75 }
172+
});
173+
174+
// Machine-readable output for the client application
175+
if (result.structuredContent) {
176+
console.log(result.structuredContent); // e.g. { bmi: 22.86 }
177+
}
178+
//#endregion callTool_structuredOutput
179+
}
180+
181+
/** Example: Track progress of a long-running tool call. */
182+
async function callTool_progress(client: Client) {
183+
//#region callTool_progress
184+
const result = await client.callTool({ name: 'long-operation', arguments: {} }, undefined, {
185+
onprogress: ({ progress, total }) => {
186+
console.log(`Progress: ${progress}/${total ?? '?'}`);
187+
},
188+
resetTimeoutOnProgress: true,
189+
maxTotalTimeout: 600_000
190+
});
191+
console.log(result.content);
192+
//#endregion callTool_progress
193+
}
194+
144195
/** Example: List and read resources. */
145196
async function readResource_basic(client: Client) {
146197
//#region readResource_basic
147-
const { resources } = await client.listResources();
198+
const allResources: Resource[] = [];
199+
let resourceCursor: string | undefined;
200+
do {
201+
const { resources, nextCursor } = await client.listResources({ cursor: resourceCursor });
202+
allResources.push(...resources);
203+
resourceCursor = nextCursor;
204+
} while (resourceCursor);
148205
console.log(
149206
'Available resources:',
150-
resources.map(r => r.name)
207+
allResources.map(r => r.name)
151208
);
152209

153210
const { contents } = await client.readResource({ uri: 'config://app' });
@@ -177,10 +234,16 @@ async function subscribeResource_basic(client: Client) {
177234
/** Example: List and get prompts. */
178235
async function getPrompt_basic(client: Client) {
179236
//#region getPrompt_basic
180-
const { prompts } = await client.listPrompts();
237+
const allPrompts: Prompt[] = [];
238+
let promptCursor: string | undefined;
239+
do {
240+
const { prompts, nextCursor } = await client.listPrompts({ cursor: promptCursor });
241+
allPrompts.push(...prompts);
242+
promptCursor = nextCursor;
243+
} while (promptCursor);
181244
console.log(
182245
'Available prompts:',
183-
prompts.map(p => p.name)
246+
allPrompts.map(p => p.name)
184247
);
185248

186249
const { messages } = await client.getPrompt({
@@ -447,9 +510,12 @@ void connect_streamableHttp;
447510
void connect_stdio;
448511
void connect_sseFallback;
449512
void disconnect_streamableHttp;
513+
void serverInstructions_basic;
450514
void auth_clientCredentials;
451515
void auth_privateKeyJwt;
452516
void callTool_basic;
517+
void callTool_structuredOutput;
518+
void callTool_progress;
453519
void readResource_basic;
454520
void subscribeResource_basic;
455521
void getPrompt_basic;

0 commit comments

Comments
 (0)