|
1 | 1 | #!/usr/bin/env node |
2 | 2 | /** |
3 | | - * Demonstrates calling a vendor method via the three-arg `client.request(method, params, resultSchema)` |
4 | | - * overload and registering a vendor notification handler with the three-arg `setNotificationHandler` |
5 | | - * overload. |
| 3 | + * Calling vendor-specific (non-spec) JSON-RPC methods from a `Client`. |
6 | 4 | * |
7 | | - * Pair with: examples/server/src/customMethodExample.ts (which contains the in-memory pair). |
| 5 | + * - Send a custom request: 3-arg `client.request(method, params, resultSchema)` |
| 6 | + * - Send a custom notification: `client.notification({ method, params })` (unchanged from v1) |
| 7 | + * - Receive a custom notification: 3-arg `client.setNotificationHandler(method, paramsSchema, handler)` |
| 8 | + * |
| 9 | + * These overloads are on `Client` and `Server` directly — you do NOT need a raw |
| 10 | + * `Protocol` instance for custom methods. |
| 11 | + * |
| 12 | + * Pair with the server in examples/server/src/customMethodExample.ts. |
8 | 13 | */ |
9 | 14 |
|
10 | | -import { Client, InMemoryTransport, Protocol } from '@modelcontextprotocol/client'; |
| 15 | +import { Client, StdioClientTransport } from '@modelcontextprotocol/client'; |
11 | 16 | import { z } from 'zod'; |
12 | 17 |
|
13 | | -const SearchParams = z.object({ query: z.string() }); |
14 | 18 | const SearchResult = z.object({ hits: z.array(z.string()) }); |
15 | 19 | const ProgressParams = z.object({ stage: z.string(), pct: z.number() }); |
16 | 20 |
|
17 | | -async function main() { |
18 | | - const peer = new Protocol(); |
19 | | - peer.setRequestHandler('acme/search', SearchParams, async p => { |
20 | | - await peer.notification('acme/searchProgress', { stage: 'started', pct: 0 }); |
21 | | - await peer.notification('acme/searchProgress', { stage: 'done', pct: 100 }); |
22 | | - return { hits: [p.query, p.query + '-2'] }; |
23 | | - }); |
24 | | - // The bare Protocol must respond to MCP initialize so Client.connect() completes. |
25 | | - peer.setRequestHandler('initialize', req => ({ |
26 | | - protocolVersion: req.params.protocolVersion, |
27 | | - capabilities: {}, |
28 | | - serverInfo: { name: 'peer', version: '1.0.0' } |
29 | | - })); |
30 | | - |
31 | | - const client = new Client({ name: 'c', version: '1.0.0' }, { capabilities: {} }); |
32 | | - client.setNotificationHandler('acme/searchProgress', ProgressParams, p => { |
33 | | - console.log(`[client] progress: ${p.stage} ${p.pct}%`); |
34 | | - }); |
35 | | - |
36 | | - const [t1, t2] = InMemoryTransport.createLinkedPair(); |
37 | | - await peer.connect(t2); |
38 | | - await client.connect(t1); |
39 | | - |
40 | | - const r = await client.request('acme/search', { query: 'widgets' }, SearchResult); |
41 | | - console.log('[client] hits=' + JSON.stringify(r.hits)); |
42 | | - |
43 | | - await client.close(); |
44 | | - await peer.close(); |
45 | | -} |
46 | | - |
47 | | -await main(); |
| 21 | +const client = new Client({ name: 'custom-method-client', version: '1.0.0' }, { capabilities: {} }); |
| 22 | + |
| 23 | +client.setNotificationHandler('acme/searchProgress', ProgressParams, p => { |
| 24 | + console.log(`[client] progress: ${p.stage} ${p.pct}%`); |
| 25 | +}); |
| 26 | + |
| 27 | +await client.connect(new StdioClientTransport({ command: 'node', args: ['../server/dist/customMethodExample.js'] })); |
| 28 | + |
| 29 | +const r = await client.request('acme/search', { query: 'widgets' }, SearchResult); |
| 30 | +console.log('[client] hits=' + JSON.stringify(r.hits)); |
| 31 | + |
| 32 | +await client.notification({ method: 'acme/tick', params: { n: 1 } }); |
| 33 | +await client.notification({ method: 'acme/tick', params: { n: 2 } }); |
| 34 | + |
| 35 | +await client.close(); |
0 commit comments