@mastra/mcp 1.14.0-alpha.0 → 1.15.0-alpha.0

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.
@@ -207,6 +207,34 @@ const instructionsByServer = mcp.getServerInstructions()
207
207
  console.log(instructionsByServer.db)
208
208
  ```
209
209
 
210
+ ### `authenticate()`
211
+
212
+ Runs the interactive OAuth authorization-code flow for a server configured with an `MCPOAuthClientProvider` whose redirect URL points at a loopback address. Starts a local callback server, delivers the authorization URL through the provider's `onRedirectToAuthorization` callback, waits for the browser to return the authorization code, exchanges it for tokens, and reconnects. See [Interactive browser authentication](#interactive-browser-authentication).
213
+
214
+ The optional `timeoutMs` bounds how long the flow waits for the browser to return the authorization code before rejecting, and defaults to 5 minutes.
215
+
216
+ ```typescript
217
+ async authenticate(serverName: string, options?: { timeoutMs?: number }): Promise<void>
218
+ ```
219
+
220
+ ### `getServerAuthState()`
221
+
222
+ Returns the OAuth authorization state of a configured server: `'needs-auth'` after a connection attempt was rejected with an authorization error, `'authorized'` once the server accepted the provider's credentials, or `undefined` for servers without an `authProvider` or that haven't attempted a connection yet.
223
+
224
+ ```typescript
225
+ getServerAuthState(serverName: string): 'needs-auth' | 'authorized' | undefined
226
+ ```
227
+
228
+ ### `cancelAuthentication()`
229
+
230
+ Cancels an in-progress `authenticate()` flow for a server, so an abandoned browser authorization does not leave the client waiting indefinitely. It aborts the flow (including its setup phase, before the callback server binds), closes the local callback server if one is listening, and the pending `authenticate()` call rejects. Returns `true` if a flow was cancelled, or `false` when no flow was in progress.
231
+
232
+ The resulting `getServerAuthState()` depends on how far the flow had progressed: a flow cancelled after the server rejected the connection with `401` stays at `'needs-auth'` and can be retried immediately, while a flow cancelled during the setup phase — before any connection was attempted — leaves the state unchanged (typically `undefined`).
233
+
234
+ ```typescript
235
+ async cancelAuthentication(serverName: string): Promise<boolean>
236
+ ```
237
+
210
238
  ### `disconnect()`
211
239
 
212
240
  Disconnects from all MCP servers and cleans up resources.
@@ -821,6 +849,74 @@ const client = new MCPClient({
821
849
  })
822
850
  ```
823
851
 
852
+ Give each server its own `MCPOAuthClientProvider` instance. A provider holds per-server session and credential state during authorization, so sharing one instance across multiple servers lets their flows overwrite each other. When configuring several protected servers, construct a separate provider for each.
853
+
854
+ ### Interactive browser authentication
855
+
856
+ When a server rejects a connection because authorization is required, the client records a `'needs-auth'` state instead of failing outright. Calling `authenticate()` then completes the flow end to end: it starts a one-shot callback server on the provider's loopback redirect URL (falling back to the next sequential ports when it is in use), lets the SDK run discovery and dynamic client registration, delivers the authorization URL through `onRedirectToAuthorization` — open it in the user's browser — and finishes the token exchange once the browser returns the authorization code:
857
+
858
+ ```typescript
859
+ import { MCPClient, MCPOAuthClientProvider } from '@mastra/mcp'
860
+
861
+ const oauthProvider = new MCPOAuthClientProvider({
862
+ redirectUrl: 'http://127.0.0.1:5533/oauth/callback',
863
+ clientMetadata: {
864
+ redirect_uris: ['http://127.0.0.1:5533/oauth/callback'],
865
+ client_name: 'My MCP Client',
866
+ grant_types: ['authorization_code', 'refresh_token'],
867
+ response_types: ['code'],
868
+ },
869
+ onRedirectToAuthorization: url => {
870
+ // Open the user's browser at the consent page
871
+ console.log(`Please visit: ${url}`)
872
+ },
873
+ })
874
+
875
+ const mcp = new MCPClient({
876
+ servers: {
877
+ protectedServer: {
878
+ url: new URL('https://mcp.example.com/mcp'),
879
+ authProvider: oauthProvider,
880
+ },
881
+ },
882
+ })
883
+
884
+ try {
885
+ await mcp.listTools()
886
+ } catch {
887
+ if (mcp.getServerAuthState('protectedServer') === 'needs-auth') {
888
+ await mcp.authenticate('protectedServer')
889
+ }
890
+ }
891
+ ```
892
+
893
+ Concurrent `authenticate()` calls for the same server join the pending flow; different servers authenticate independently. With valid stored tokens the call simply reconnects without opening a browser.
894
+
895
+ Hosts that drive the flow themselves can capture the authorization code with the exported `createOAuthCallbackServer` helper, which binds a one-shot loopback server, validates the OAuth `state` parameter, and resolves with the code. It creates a plain HTTP server, so it is only for local loopback redirects. Web applications that use an HTTPS redirect URL must host their own callback endpoint and drive the provider directly rather than using this helper:
896
+
897
+ ```typescript
898
+ import { createOAuthCallbackServer, getCallbackUrlCandidates } from '@mastra/mcp'
899
+
900
+ // getCallbackUrlCandidates() lists every URL the helper may bind, so register
901
+ // all of them as redirect_uris during client registration to cover port fallback.
902
+ const redirectUris = getCallbackUrlCandidates('http://127.0.0.1:5533/oauth/callback').map(url =>
903
+ url.toString(),
904
+ )
905
+
906
+ const server = await createOAuthCallbackServer({
907
+ redirectUrl: 'http://127.0.0.1:5533/oauth/callback',
908
+ state: expectedState,
909
+ })
910
+
911
+ // server.url reflects the port actually bound — use it as the redirect_uri.
912
+ try {
913
+ const { code } = await server.waitForCode()
914
+ // Exchange the code here.
915
+ } finally {
916
+ await server.close()
917
+ }
918
+ ```
919
+
824
920
  ### Quick Token Provider
825
921
 
826
922
  For testing or when you already have a valid access token: