@mastra/mcp-docs-server 1.2.27 → 1.2.28-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.docs/docs/connections/mcp.md +15 -9
- package/.docs/docs/server/server-adapters.md +16 -18
- package/.docs/models/gateways/openrouter.md +1 -2
- package/.docs/models/index.md +1 -1
- package/.docs/models/providers/crossmodel.md +4 -1
- package/.docs/models/providers/kilo.md +4 -5
- package/.docs/models/providers/llmgateway-providers.md +3 -1
- package/.docs/models/providers/llmgateway.md +3 -1
- package/.docs/models/providers/nano-gpt.md +7 -4
- package/.docs/models/providers/opencode.md +0 -1
- package/.docs/reference/configuration.md +10 -6
- package/.docs/reference/core/getMCPServer.md +6 -10
- package/.docs/reference/server/elysia-adapter.md +2 -2
- package/.docs/reference/server/express-adapter.md +1 -1
- package/.docs/reference/server/fastify-adapter.md +1 -1
- package/.docs/reference/server/hono-adapter.md +1 -1
- package/.docs/reference/server/koa-adapter.md +1 -1
- package/.docs/reference/server/nestjs-adapter.md +0 -2
- package/.docs/reference/tools/mcp-client.md +77 -276
- package/.docs/reference/tools/mcp-server.md +205 -537
- package/package.json +3 -3
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
The `MCPClient` class provides a way to manage multiple MCP server connections and their tools in a Mastra application. It handles connection lifecycle and tool namespacing while providing access to tools across all configured servers.
|
|
8
8
|
|
|
9
|
+
> **Note:** Upgrading from `@mastra/mcp` 1.x? See the [migration guide](https://mastra.ai/reference/migrations/mcp-v2).
|
|
10
|
+
|
|
9
11
|
## Constructor
|
|
10
12
|
|
|
11
13
|
Creates a new instance of the MCPClient class.
|
|
@@ -31,7 +33,9 @@ constructor({
|
|
|
31
33
|
Each server in the `servers` map is configured using the `MastraMCPServerDefinition` type. The transport type is detected based on the provided parameters:
|
|
32
34
|
|
|
33
35
|
- If `command` is provided, it uses the Stdio transport.
|
|
34
|
-
- If `url` is provided, it
|
|
36
|
+
- If `url` is provided, it uses the Streamable HTTP transport.
|
|
37
|
+
|
|
38
|
+
The client speaks the MCP `2026-07-28` revision. On connect it asks the server which revisions it offers (`server/discover`) and falls back to the pre-2026 `initialize` handshake for servers that haven't upgraded, so tools, resources and prompts keep working against either. Features the older revisions lack (`resources.subscribe()`, `inputRequests`) fail with an error naming the negotiated revision. Set `protocolVersion` to skip the probe.
|
|
35
39
|
|
|
36
40
|
**command** (`string`): For Stdio servers: The command to execute.
|
|
37
41
|
|
|
@@ -41,25 +45,31 @@ Each server in the `servers` map is configured using the `MastraMCPServerDefinit
|
|
|
41
45
|
|
|
42
46
|
**inheritDefaultEnv** (`boolean`): For Stdio servers: Whether the subprocess environment starts from the MCP SDK's default inherited environment. The default is a curated whitelist, not the full process environment: on POSIX it inherits HOME, LOGNAME, PATH, SHELL, TERM, and USER; on Windows it inherits APPDATA, HOMEDRIVE, HOMEPATH, LOCALAPPDATA, PATH, PROCESSOR\_ARCHITECTURE, SYSTEMDRIVE, SYSTEMROOT, TEMP, USERNAME, and USERPROFILE. When set to false, only the variables explicitly listed in env are passed to the subprocess. Note that a subprocess without PATH may fail to spawn commands that are not absolute paths. (Default: `true`)
|
|
43
47
|
|
|
44
|
-
**url** (`URL`): For HTTP servers
|
|
48
|
+
**url** (`URL`): For HTTP servers: The URL of the server.
|
|
45
49
|
|
|
46
50
|
**requestInit** (`RequestInit`): For HTTP servers: Request configuration for the fetch API.
|
|
47
51
|
|
|
48
|
-
**
|
|
49
|
-
|
|
50
|
-
**fetch** (`MastraFetchLike`): For HTTP servers: Custom fetch implementation used for all network requests. Receives an optional third requestContext parameter containing request-scoped data (e.g., authentication cookies, bearer tokens) from the incoming request. When provided, this function will be used for all HTTP requests, allowing you to add dynamic authentication headers, forward request-scoped credentials to the MCP server, customize request behavior per-request, or intercept and modify requests/responses. When fetch is provided, requestInit, eventSourceInit, and authProvider become optional, as you can handle these concerns within your custom fetch function.
|
|
52
|
+
**fetch** (`MastraFetchLike`): For HTTP servers: Custom fetch implementation used for all network requests. Receives an optional third requestContext parameter containing request-scoped data (e.g., authentication cookies, bearer tokens) from the incoming request. When provided, this function will be used for all HTTP requests, allowing you to add dynamic authentication headers, forward request-scoped credentials to the MCP server, customize request behavior per-request, or intercept and modify requests/responses. When fetch is provided, requestInit and authProvider become optional, as you can handle these concerns within your custom fetch function.
|
|
51
53
|
|
|
52
54
|
**allowedHosts** (`string[]`): For HTTP servers: Opt-in allowlist of hosts the client may contact on behalf of this server. Each entry is matched against the URL host (hostname plus port when the URL carries a non-default port), for example "api.example.com" or "localhost:8080". Matching is exact and case-insensitive on the hostname; wildcards are not supported and the URL scheme is not checked. An empty array denies all requests. When unset, no restriction is applied. See the Security section below for enforcement details.
|
|
53
55
|
|
|
54
56
|
**logger** (`LogHandler`): Optional additional handler for logging.
|
|
55
57
|
|
|
56
|
-
**
|
|
58
|
+
**protocolVersion** (`'2026-07-28' | 'legacy'`): Pins the protocol revision instead of probing for it. '2026-07-28' connects only to servers that offer the current revision. 'legacy' uses the pre-2026 initialize handshake without probing, for servers known not to have upgraded. When omitted the client probes with server/discover and speaks whichever revision the server offers.
|
|
59
|
+
|
|
60
|
+
**timeout** (`number`): Server-specific timeout in milliseconds. Falls back to the client-level timeout when omitted.
|
|
57
61
|
|
|
58
|
-
**capabilities** (`
|
|
62
|
+
**capabilities** (`MCPClientCapabilities`): Client capabilities to advertise: elicitation and extensions. elicitation is only accepted together with an inputRequests handler and defaults to form support when a handler is configured. Roots and sampling are not supported.
|
|
63
|
+
|
|
64
|
+
**inputRequests** (`MCPInputRequestHandler`): Answers input requests from this server. When a tool, resource or prompt call ends as input\_required, the handler is called with each embedded request and the client retries the call with the answers. Without a handler an input\_required result surfaces as an error. See Answering input requests below.
|
|
59
65
|
|
|
60
66
|
**authProvider** (`OAuthClientProvider`): For HTTP servers: OAuth authentication provider for automatic token refresh and OAuth flow management. Use MCPOAuthClientProvider for a ready-to-use implementation.
|
|
61
67
|
|
|
62
|
-
**enableServerLogs** (`boolean`): Whether to
|
|
68
|
+
**enableServerLogs** (`boolean`): Whether to request per-request server logs. Attaches the io.modelcontextprotocol/logLevel metadata key to every request so the server delivers notifications/message for it, and forwards delivered messages to logger. (Default: `true`)
|
|
69
|
+
|
|
70
|
+
**serverLogLevel** (`LoggingLevel`): Minimum severity requested from the server for per-request logs. (Default: `'info'`)
|
|
71
|
+
|
|
72
|
+
**enableProgressTracking** (`boolean`): Whether to send a progressToken with tool calls so the server can report progress. See the progress property. (Default: `false`)
|
|
63
73
|
|
|
64
74
|
**traceContext** (`() => MCPTraceContext | undefined`): Returns the W3C traceparent, optional tracestate and baggage to send as request \_meta on every call to this server. Called when each request is sent so it can read a request-local carrier. Explicit \_meta keys on a tool call take precedence. Servers built with MCPServer expose the received values to tools as requestContext.get("traceContext"); they are observability data and are never used for authorization.
|
|
65
75
|
|
|
@@ -69,6 +79,8 @@ Each server in the `servers` map is configured using the `MastraMCPServerDefinit
|
|
|
69
79
|
|
|
70
80
|
**requireToolApproval** (`boolean | (params: RequireToolApprovalContext) => boolean | Promise<boolean>`): Require human approval before executing tools from this server. When set to true, all tools require approval. When set to a function, the function is called with the tool name, arguments, request context, and any tool annotations advertised by the server to dynamically decide whether approval is needed.
|
|
71
81
|
|
|
82
|
+
**onToolError** (`'throw' | 'return'`): How to handle tool execution failures the server reports in-band with isError: true. 'throw' raises a MastraError carrying the server's error text, so the failure reaches tool spans, stream chunks, scorers, and the model. 'return' resolves with the raw result and ignores isError. (Default: `'throw'`)
|
|
83
|
+
|
|
72
84
|
**jsonSchemaValidator** (`JsonSchemaValidator`): Validator used for MCP tool schemas and structured results. The default supports JSON Schema 2020-12. Provide a compatible validator such as CfWorkerJsonSchemaValidator in runtimes that disallow dynamic code generation.
|
|
73
85
|
|
|
74
86
|
## Tool approval
|
|
@@ -209,7 +221,7 @@ const mcp = new MCPClient({
|
|
|
209
221
|
Enforcement details:
|
|
210
222
|
|
|
211
223
|
- On the default fetch path, requests to disallowed hosts, including every redirect hop, are blocked **before** they're sent. Redirects are followed manually (up to 5 hops) so each hop is validated, and the `Authorization` header isn't carried across hops to a different origin (any scheme, host, or port change drops it, matching standard fetch behavior).
|
|
212
|
-
- When you supply a custom `fetch
|
|
224
|
+
- When you supply a custom `fetch`, the initial URL is still checked before the request, but redirect hops are validated **after the fact** using `response.url`: the outbound hop may occur, and the response is discarded when its final URL points at a disallowed host. A hand-built `Response` with an empty `response.url` skips this post-hoc check.
|
|
213
225
|
- OAuth requests made through `authProvider` (authorization server metadata discovery, token exchange, refresh) are also validated. If your authorization server runs on a different host than the MCP server, add that host to `allowedHosts` too.
|
|
214
226
|
- A blocked host fails the connection with a clear error and is never retried by the reconnect logic.
|
|
215
227
|
|
|
@@ -573,145 +585,6 @@ mcpClient.resources.onListChanged('myWeatherServer', () => {
|
|
|
573
585
|
})
|
|
574
586
|
```
|
|
575
587
|
|
|
576
|
-
### `elicitation` Property
|
|
577
|
-
|
|
578
|
-
The `MCPClient` instance has an `elicitation` property that provides access to elicitation-related operations. Elicitation allows MCP servers to request structured information from users.
|
|
579
|
-
|
|
580
|
-
```typescript
|
|
581
|
-
const mcpClient = new MCPClient({/* ...servers configuration... */})
|
|
582
|
-
|
|
583
|
-
// Set up elicitation handler
|
|
584
|
-
mcpClient.elicitation.onRequest('serverName', async request => {
|
|
585
|
-
// Handle elicitation request from server
|
|
586
|
-
console.log('Server requests:', request.message)
|
|
587
|
-
console.log('Schema:', request.requestedSchema)
|
|
588
|
-
|
|
589
|
-
// Return user response
|
|
590
|
-
return {
|
|
591
|
-
action: 'accept',
|
|
592
|
-
content: { name: 'John Doe', email: 'john@example.com' },
|
|
593
|
-
}
|
|
594
|
-
})
|
|
595
|
-
```
|
|
596
|
-
|
|
597
|
-
#### `elicitation.onRequest(serverName: string, handler: ElicitationHandler)`
|
|
598
|
-
|
|
599
|
-
Sets up a handler function that will be called when any connected MCP server sends an elicitation request. The handler receives the request and must return a response.
|
|
600
|
-
|
|
601
|
-
##### `ElicitationHandler` Function
|
|
602
|
-
|
|
603
|
-
The handler function receives a request object with:
|
|
604
|
-
|
|
605
|
-
- `message`: A human-readable message describing what information is needed
|
|
606
|
-
- `requestedSchema`: A JSON schema defining the structure of the expected response
|
|
607
|
-
|
|
608
|
-
The handler must return an `ElicitResult` with:
|
|
609
|
-
|
|
610
|
-
- `action`: One of `'accept'`, `'decline'`, or `'cancel'`
|
|
611
|
-
- `content`: The user's data (only when action is `'accept'`)
|
|
612
|
-
|
|
613
|
-
**Example:**
|
|
614
|
-
|
|
615
|
-
```typescript
|
|
616
|
-
mcpClient.elicitation.onRequest('serverName', async request => {
|
|
617
|
-
console.log(`Server requests: ${request.message}`)
|
|
618
|
-
|
|
619
|
-
// Example: Simple user input collection
|
|
620
|
-
if (request.requestedSchema.properties.name) {
|
|
621
|
-
// Simulate user accepting and providing data
|
|
622
|
-
return {
|
|
623
|
-
action: 'accept',
|
|
624
|
-
content: {
|
|
625
|
-
name: 'Alice Smith',
|
|
626
|
-
email: 'alice@example.com',
|
|
627
|
-
},
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
// Simulate user declining the request
|
|
632
|
-
return { action: 'decline' }
|
|
633
|
-
})
|
|
634
|
-
```
|
|
635
|
-
|
|
636
|
-
**Complete Interactive Example:**
|
|
637
|
-
|
|
638
|
-
```typescript
|
|
639
|
-
import { MCPClient } from '@mastra/mcp'
|
|
640
|
-
import { createInterface } from 'readline'
|
|
641
|
-
|
|
642
|
-
const readline = createInterface({
|
|
643
|
-
input: process.stdin,
|
|
644
|
-
output: process.stdout,
|
|
645
|
-
})
|
|
646
|
-
|
|
647
|
-
function askQuestion(question: string): Promise<string> {
|
|
648
|
-
return new Promise(resolve => {
|
|
649
|
-
readline.question(question, answer => resolve(answer.trim()))
|
|
650
|
-
})
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
const mcpClient = new MCPClient({
|
|
654
|
-
servers: {
|
|
655
|
-
interactiveServer: {
|
|
656
|
-
url: new URL('http://localhost:3000/mcp'),
|
|
657
|
-
},
|
|
658
|
-
},
|
|
659
|
-
})
|
|
660
|
-
|
|
661
|
-
// Set up interactive elicitation handler
|
|
662
|
-
await mcpClient.elicitation.onRequest('interactiveServer', async request => {
|
|
663
|
-
console.log(`\n📋 Server Request: ${request.message}`)
|
|
664
|
-
console.log('Required information:')
|
|
665
|
-
|
|
666
|
-
const schema = request.requestedSchema
|
|
667
|
-
const properties = schema.properties || {}
|
|
668
|
-
const required = schema.required || []
|
|
669
|
-
const content: Record<string, any> = {}
|
|
670
|
-
|
|
671
|
-
// Collect input for each field
|
|
672
|
-
for (const [fieldName, fieldSchema] of Object.entries(properties)) {
|
|
673
|
-
const field = fieldSchema as any
|
|
674
|
-
const isRequired = required.includes(fieldName)
|
|
675
|
-
|
|
676
|
-
let prompt = `${field.title || fieldName}`
|
|
677
|
-
if (field.description) prompt += ` (${field.description})`
|
|
678
|
-
if (isRequired) prompt += ' *required*'
|
|
679
|
-
prompt += ': '
|
|
680
|
-
|
|
681
|
-
const answer = await askQuestion(prompt)
|
|
682
|
-
|
|
683
|
-
// Handle cancellation
|
|
684
|
-
if (answer.toLowerCase() === 'cancel') {
|
|
685
|
-
return { action: 'cancel' }
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
// Validate required fields
|
|
689
|
-
if (answer === '' && isRequired) {
|
|
690
|
-
console.log(`❌ ${fieldName} is required`)
|
|
691
|
-
return { action: 'decline' }
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
if (answer !== '') {
|
|
695
|
-
content[fieldName] = answer
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
// Confirm submission
|
|
700
|
-
console.log('\n📝 You provided:')
|
|
701
|
-
console.log(JSON.stringify(content, null, 2))
|
|
702
|
-
|
|
703
|
-
const confirm = await askQuestion('\nSubmit this information? (yes/no/cancel): ')
|
|
704
|
-
|
|
705
|
-
if (confirm.toLowerCase() === 'yes' || confirm.toLowerCase() === 'y') {
|
|
706
|
-
return { action: 'accept', content }
|
|
707
|
-
} else if (confirm.toLowerCase() === 'cancel') {
|
|
708
|
-
return { action: 'cancel' }
|
|
709
|
-
} else {
|
|
710
|
-
return { action: 'decline' }
|
|
711
|
-
}
|
|
712
|
-
})
|
|
713
|
-
```
|
|
714
|
-
|
|
715
588
|
### `prompts` Property
|
|
716
589
|
|
|
717
590
|
The `MCPClient` instance has a `prompts` property that provides access to prompt-related operations.
|
|
@@ -753,7 +626,7 @@ const { prompts, errors, errorDetails } = await mcpClient.prompts.listWithErrors
|
|
|
753
626
|
console.log(prompts, errors, errorDetails)
|
|
754
627
|
```
|
|
755
628
|
|
|
756
|
-
#### `prompts.get({ serverName, name, args
|
|
629
|
+
#### `prompts.get({ serverName, name, args? })`
|
|
757
630
|
|
|
758
631
|
Retrieves a specific prompt and its messages from a server.
|
|
759
632
|
|
|
@@ -762,24 +635,21 @@ async get({
|
|
|
762
635
|
serverName,
|
|
763
636
|
name,
|
|
764
637
|
args?,
|
|
765
|
-
version?,
|
|
766
638
|
}: {
|
|
767
639
|
serverName: string;
|
|
768
640
|
name: string;
|
|
769
641
|
args?: Record<string, any>;
|
|
770
|
-
|
|
771
|
-
}): Promise<{ prompt: Prompt; messages: PromptMessage[] }>
|
|
642
|
+
}): Promise<GetPromptResult>
|
|
772
643
|
```
|
|
773
644
|
|
|
774
645
|
Example:
|
|
775
646
|
|
|
776
647
|
```typescript
|
|
777
|
-
const {
|
|
648
|
+
const { messages } = await mcpClient.prompts.get({
|
|
778
649
|
serverName: 'myWeatherServer',
|
|
779
650
|
name: 'current',
|
|
780
651
|
args: { location: 'London' },
|
|
781
652
|
})
|
|
782
|
-
console.log(prompt)
|
|
783
653
|
console.log(messages)
|
|
784
654
|
```
|
|
785
655
|
|
|
@@ -832,7 +702,7 @@ const mcpClient = new MCPClient({
|
|
|
832
702
|
servers: {
|
|
833
703
|
myServer: {
|
|
834
704
|
url: new URL('http://localhost:4111/api/mcp/myServer/mcp'),
|
|
835
|
-
//
|
|
705
|
+
// Off by default: opt in so tool calls carry a progressToken
|
|
836
706
|
enableProgressTracking: true,
|
|
837
707
|
},
|
|
838
708
|
},
|
|
@@ -864,37 +734,12 @@ async onUpdate(
|
|
|
864
734
|
|
|
865
735
|
Notes:
|
|
866
736
|
|
|
867
|
-
- When `enableProgressTracking` is true
|
|
737
|
+
- When `enableProgressTracking` is true, tool calls include a `progressToken` so you can correlate updates to a specific run. Servers only send progress for requests that carry a token.
|
|
868
738
|
- If you pass a `runId` when executing a tool, it will be used as the `progressToken`.
|
|
869
739
|
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
```typescript
|
|
873
|
-
const mcpClient = new MCPClient({
|
|
874
|
-
servers: {
|
|
875
|
-
myServer: {
|
|
876
|
-
url: new URL('http://localhost:4111/api/mcp/myServer/mcp'),
|
|
877
|
-
enableProgressTracking: false,
|
|
878
|
-
},
|
|
879
|
-
},
|
|
880
|
-
})
|
|
881
|
-
```
|
|
882
|
-
|
|
883
|
-
## Elicitation
|
|
884
|
-
|
|
885
|
-
Elicitation is a feature that allows MCP servers to request structured information from users. When a server needs additional data, it can send an elicitation request that the client handles by prompting the user. A common example is during a tool call.
|
|
886
|
-
|
|
887
|
-
### How Elicitation Works
|
|
740
|
+
## Answering input requests
|
|
888
741
|
|
|
889
|
-
|
|
890
|
-
2. **Client Handler**: Your elicitation handler function is called with the request
|
|
891
|
-
3. **User Interaction**: Your handler collects user input (via UI, CLI, etc.)
|
|
892
|
-
4. **Response**: Your handler returns the user's response (accept/decline/cancel)
|
|
893
|
-
5. **Tool Continuation**: The server tool receives the response and continues execution
|
|
894
|
-
|
|
895
|
-
### Setting Up Elicitation
|
|
896
|
-
|
|
897
|
-
You must set up an elicitation handler before tools that use elicitation are called:
|
|
742
|
+
A server tool, resource or prompt that needs something from the user ends its call with an `input_required` result instead of a value. The result carries one or more embedded requests, each with a server-chosen `key` and either a form (`requestedSchema`) or a URL the user must visit. Configure `inputRequests` on the server definition to answer them: the client calls the handler once per embedded request, retries the original call with the answers, and resolves the tool call with the final result. Without a handler an `input_required` result surfaces as an error.
|
|
898
743
|
|
|
899
744
|
```typescript
|
|
900
745
|
import { MCPClient } from '@mastra/mcp'
|
|
@@ -903,30 +748,33 @@ const mcpClient = new MCPClient({
|
|
|
903
748
|
servers: {
|
|
904
749
|
interactiveServer: {
|
|
905
750
|
url: new URL('http://localhost:3000/mcp'),
|
|
751
|
+
inputRequests: async ({ key, params, signal }) => {
|
|
752
|
+
if (params.mode === 'url') {
|
|
753
|
+
return { action: 'decline' }
|
|
754
|
+
}
|
|
755
|
+
const content = await collectUserInput(params.message, params.requestedSchema, { signal })
|
|
756
|
+
return { action: 'accept', content }
|
|
757
|
+
},
|
|
906
758
|
},
|
|
907
759
|
},
|
|
908
760
|
})
|
|
761
|
+
```
|
|
909
762
|
|
|
910
|
-
|
|
911
|
-
mcpClient.elicitation.onRequest('interactiveServer', async request => {
|
|
912
|
-
// Handle the server's request for user input
|
|
913
|
-
console.log(`Server needs: ${request.message}`)
|
|
763
|
+
### `MCPInputRequest`
|
|
914
764
|
|
|
915
|
-
|
|
916
|
-
const userData = await collectUserInput(request.requestedSchema)
|
|
765
|
+
The handler receives:
|
|
917
766
|
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
```
|
|
767
|
+
- `key`: The server-chosen key the answer is filed under. Pass it through to your UI when several requests arrive in one round.
|
|
768
|
+
- `params`: The request. In form mode it has `message` and `requestedSchema`, a flat JSON Schema object of primitive fields. In url mode it has `message` and `url`.
|
|
769
|
+
- `signal`: Aborts when the originating call is cancelled or times out.
|
|
770
|
+
|
|
771
|
+
Configuring a handler advertises form support to the server. To accept url-mode requests as well, widen `capabilities.elicitation` on the same server definition.
|
|
924
772
|
|
|
925
773
|
### Response types
|
|
926
774
|
|
|
927
|
-
|
|
775
|
+
Return an `ElicitResult`:
|
|
928
776
|
|
|
929
|
-
- **Accept**:
|
|
777
|
+
- **Accept**: The user provided data and confirmed submission. `content` must match `requestedSchema`.
|
|
930
778
|
|
|
931
779
|
```typescript
|
|
932
780
|
return {
|
|
@@ -935,40 +783,37 @@ Your elicitation handler must return one of three response types:
|
|
|
935
783
|
}
|
|
936
784
|
```
|
|
937
785
|
|
|
938
|
-
- **Decline**:
|
|
786
|
+
- **Decline**: The user explicitly declined to provide the information.
|
|
939
787
|
|
|
940
788
|
```typescript
|
|
941
789
|
return { action: 'decline' }
|
|
942
790
|
```
|
|
943
791
|
|
|
944
|
-
- **Cancel**:
|
|
792
|
+
- **Cancel**: The user dismissed the request.
|
|
945
793
|
|
|
946
794
|
```typescript
|
|
947
795
|
return { action: 'cancel' }
|
|
948
796
|
```
|
|
949
797
|
|
|
950
|
-
|
|
798
|
+
A declined or cancelled request ends the tool call with an error. A server can ask again after an accepted answer, so the handler may run several times for one tool call.
|
|
951
799
|
|
|
952
|
-
|
|
800
|
+
### Schema-based input collection
|
|
801
|
+
|
|
802
|
+
Walk `requestedSchema` to prompt for each field:
|
|
953
803
|
|
|
954
804
|
```typescript
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
const content: Record<string, any> = {}
|
|
805
|
+
inputRequests: async ({ params }) => {
|
|
806
|
+
if (params.mode === 'url') return { action: 'decline' }
|
|
958
807
|
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
const isRequired = required.includes(fieldName)
|
|
808
|
+
const { properties, required = [] } = params.requestedSchema
|
|
809
|
+
const content: Record<string, string | number | boolean> = {}
|
|
962
810
|
|
|
963
|
-
|
|
811
|
+
for (const [fieldName, fieldSchema] of Object.entries(properties)) {
|
|
964
812
|
const value = await promptUser({
|
|
965
813
|
name: fieldName,
|
|
966
|
-
title:
|
|
967
|
-
description:
|
|
968
|
-
|
|
969
|
-
required: isRequired,
|
|
970
|
-
format: field.format,
|
|
971
|
-
enum: field.enum,
|
|
814
|
+
title: fieldSchema.title,
|
|
815
|
+
description: fieldSchema.description,
|
|
816
|
+
required: required.includes(fieldName),
|
|
972
817
|
})
|
|
973
818
|
|
|
974
819
|
if (value !== null) {
|
|
@@ -977,16 +822,16 @@ await mcpClient.elicitation.onRequest('interactiveServer', async request => {
|
|
|
977
822
|
}
|
|
978
823
|
|
|
979
824
|
return { action: 'accept', content }
|
|
980
|
-
}
|
|
825
|
+
}
|
|
981
826
|
```
|
|
982
827
|
|
|
983
828
|
### Best practices
|
|
984
829
|
|
|
985
|
-
- **
|
|
986
|
-
- **Validate input**: Check that required fields are provided
|
|
987
|
-
- **Respect user choice**: Handle decline and cancel responses gracefully
|
|
988
|
-
- **Clear UI**: Make it obvious what information is being requested and why
|
|
989
|
-
- **Security**: Never auto-accept requests for sensitive information
|
|
830
|
+
- **Configure the handler up front**: it's part of the server definition, so a tool that asks for input never runs without one.
|
|
831
|
+
- **Validate input**: Check that required fields are provided.
|
|
832
|
+
- **Respect user choice**: Handle decline and cancel responses gracefully.
|
|
833
|
+
- **Clear UI**: Make it obvious what information is being requested and why.
|
|
834
|
+
- **Security**: Never auto-accept requests for sensitive information, and treat url-mode requests as links to untrusted sites.
|
|
990
835
|
|
|
991
836
|
## OAuth authentication
|
|
992
837
|
|
|
@@ -1189,12 +1034,12 @@ const mcp = new MCPClient({
|
|
|
1189
1034
|
env: {
|
|
1190
1035
|
API_KEY: 'your-api-key',
|
|
1191
1036
|
},
|
|
1192
|
-
|
|
1037
|
+
logger: logMessage => {
|
|
1193
1038
|
console.log(`[${logMessage.level}] ${logMessage.message}`)
|
|
1194
1039
|
},
|
|
1195
1040
|
},
|
|
1196
1041
|
weather: {
|
|
1197
|
-
url: new URL('http://localhost:8080/
|
|
1042
|
+
url: new URL('http://localhost:8080/mcp'),
|
|
1198
1043
|
},
|
|
1199
1044
|
},
|
|
1200
1045
|
timeout: 30000, // Global 30s timeout
|
|
@@ -1271,7 +1116,7 @@ const mcp = new MCPClient({
|
|
|
1271
1116
|
timeout: 20000, // Server-specific timeout
|
|
1272
1117
|
},
|
|
1273
1118
|
weather: {
|
|
1274
|
-
url: new URL('http://localhost:8080/
|
|
1119
|
+
url: new URL('http://localhost:8080/mcp'),
|
|
1275
1120
|
requestInit: {
|
|
1276
1121
|
headers: {
|
|
1277
1122
|
Authorization: `Bearer user-123-token`,
|
|
@@ -1337,7 +1182,7 @@ For HTTP servers, you can provide a custom `fetch` function to handle runtime-de
|
|
|
1337
1182
|
|
|
1338
1183
|
The custom `fetch` function receives an optional third `requestContext` parameter, which provides access to request-scoped data (e.g., authentication cookies, bearer tokens) set by middleware or passed during agent/tool execution. The `requestContext` is `null` during the initial connection handshake.
|
|
1339
1184
|
|
|
1340
|
-
When `fetch` is provided, `requestInit
|
|
1185
|
+
When `fetch` is provided, `requestInit` and `authProvider` become optional, as you can handle these concerns within your custom fetch function.
|
|
1341
1186
|
|
|
1342
1187
|
```typescript
|
|
1343
1188
|
const mcpClient = new MCPClient({
|
|
@@ -1373,11 +1218,7 @@ await agent.generate('Hello!', {
|
|
|
1373
1218
|
|
|
1374
1219
|
## Handling auth failures inside custom fetch
|
|
1375
1220
|
|
|
1376
|
-
A custom `fetch` shouldn't `throw` when authentication is unavailable.
|
|
1377
|
-
|
|
1378
|
-
Return a synthetic `Response` instead. The [MCP Streamable HTTP specification](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports) defines `405 Method Not Allowed` as the signal a server returns when it doesn't offer the GET SSE stream, and the SDK honors it as a terminal status that stops the listener cleanly. Use this to disable the listener when your server doesn't push notifications.
|
|
1379
|
-
|
|
1380
|
-
The following pattern waits for an auth token on POST requests, attaches it to outgoing headers, and short-circuits the GET listener with a synthetic 405:
|
|
1221
|
+
A custom `fetch` shouldn't `throw` when authentication is unavailable. Every MCP request is a `POST`, and the SDK surfaces a non-2xx response as an error to the caller of `listTools()`, `tools/call` and so on, which is the behavior you want. A thrown `fetch` is reported the same way but loses the server's status and body. The only long-lived request is the `subscriptions/listen` `POST` the client opens for `resources.subscribe()`, and it's retried after a failure, so an unauthenticated stream can loop. Wait for the token, then forward the request:
|
|
1381
1222
|
|
|
1382
1223
|
```typescript
|
|
1383
1224
|
async function waitForToken(timeoutMs = 5000): Promise<string | null> {
|
|
@@ -1390,20 +1231,9 @@ const mcpClient = new MCPClient({
|
|
|
1390
1231
|
apiServer: {
|
|
1391
1232
|
url: new URL('https://api.example.com/mcp'),
|
|
1392
1233
|
fetch: async (url, init) => {
|
|
1393
|
-
const method = (init?.method || 'GET').toUpperCase()
|
|
1394
|
-
|
|
1395
|
-
// The SDK opens a background GET stream for server-pushed notifications.
|
|
1396
|
-
// If your server does not use it, short-circuit with 405 to stop reconnect attempts.
|
|
1397
|
-
if (method === 'GET') {
|
|
1398
|
-
return new Response(null, { status: 405, statusText: 'Method Not Allowed' })
|
|
1399
|
-
}
|
|
1400
|
-
|
|
1401
|
-
// POST: wait for the token, then forward the request with an Authorization header.
|
|
1402
1234
|
const token = await waitForToken()
|
|
1403
1235
|
if (!token) {
|
|
1404
1236
|
// Forward the request without a token and let the server reject it.
|
|
1405
|
-
// The SDK surfaces non-2xx POST responses as errors to the caller of
|
|
1406
|
-
// tools/list, tools/call, etc., which is the desired behavior here.
|
|
1407
1237
|
return fetch(url, init)
|
|
1408
1238
|
}
|
|
1409
1239
|
|
|
@@ -1416,58 +1246,29 @@ const mcpClient = new MCPClient({
|
|
|
1416
1246
|
})
|
|
1417
1247
|
```
|
|
1418
1248
|
|
|
1419
|
-
|
|
1249
|
+
## Sending request headers
|
|
1420
1250
|
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
When using the legacy SSE MCP transport, you must configure both `requestInit` and `eventSourceInit` due to a bug in the MCP SDK. Alternatively, you can use a custom `fetch` function which will be automatically used for both POST requests and SSE connections:
|
|
1251
|
+
Static headers go in `requestInit`. They're sent on every request, including the `subscriptions/listen` stream:
|
|
1424
1252
|
|
|
1425
1253
|
```ts
|
|
1426
|
-
|
|
1427
|
-
const sseClient = new MCPClient({
|
|
1254
|
+
const client = new MCPClient({
|
|
1428
1255
|
servers: {
|
|
1429
1256
|
exampleServer: {
|
|
1430
|
-
url: new URL('https://your-mcp-server.com/
|
|
1431
|
-
// Note: requestInit alone isn't enough for SSE
|
|
1257
|
+
url: new URL('https://your-mcp-server.com/mcp'),
|
|
1432
1258
|
requestInit: {
|
|
1433
1259
|
headers: {
|
|
1434
1260
|
Authorization: 'Bearer your-token',
|
|
1435
1261
|
},
|
|
1436
1262
|
},
|
|
1437
|
-
// This is also required for SSE connections with custom headers
|
|
1438
|
-
eventSourceInit: {
|
|
1439
|
-
fetch(input: Request | URL | string, init?: RequestInit) {
|
|
1440
|
-
const headers = new Headers(init?.headers || {})
|
|
1441
|
-
headers.set('Authorization', 'Bearer your-token')
|
|
1442
|
-
return fetch(input, {
|
|
1443
|
-
...init,
|
|
1444
|
-
headers,
|
|
1445
|
-
})
|
|
1446
|
-
},
|
|
1447
|
-
},
|
|
1448
|
-
},
|
|
1449
|
-
},
|
|
1450
|
-
})
|
|
1451
|
-
|
|
1452
|
-
// Option 2: Using custom fetch (simpler, works for both Streamable HTTP and SSE)
|
|
1453
|
-
const sseClientWithFetch = new MCPClient({
|
|
1454
|
-
servers: {
|
|
1455
|
-
exampleServer: {
|
|
1456
|
-
url: new URL('https://your-mcp-server.com/sse'),
|
|
1457
|
-
fetch: async (url, init) => {
|
|
1458
|
-
const headers = new Headers(init?.headers || {})
|
|
1459
|
-
headers.set('Authorization', 'Bearer your-token')
|
|
1460
|
-
return fetch(url, {
|
|
1461
|
-
...init,
|
|
1462
|
-
headers,
|
|
1463
|
-
})
|
|
1464
|
-
},
|
|
1465
1263
|
},
|
|
1466
1264
|
},
|
|
1467
1265
|
})
|
|
1468
1266
|
```
|
|
1469
1267
|
|
|
1268
|
+
Use a custom `fetch` when the value changes per request.
|
|
1269
|
+
|
|
1470
1270
|
## Related information
|
|
1471
1271
|
|
|
1472
1272
|
- For creating MCP servers, see the [MCPServer documentation](https://mastra.ai/reference/tools/mcp-server).
|
|
1273
|
+
- Migrating from `@mastra/mcp` 1.x: [Migrate @mastra/mcp from v1 to v2](https://mastra.ai/reference/migrations/mcp-v2).
|
|
1473
1274
|
- For more about the Model Context Protocol, see the [@modelcontextprotocol/sdk documentation](https://github.com/modelcontextprotocol/typescript-sdk).
|