@mastra/mcp 1.0.0-beta.9 → 1.0.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.
@@ -46,9 +46,13 @@ export const testMcpClient = new MCPClient({
46
46
 
47
47
  Visit [MCPClient](https://mastra.ai/reference/v1/tools/mcp-client) for a full list of configuration options.
48
48
 
49
+ > **Note:** Authentication
50
+
51
+ For connecting to OAuth-protected MCP servers, see the [OAuth Authentication](https://mastra.ai/reference/v1/tools/mcp-client#oauth-authentication) section.
52
+
49
53
  ## Using `MCPClient` with an agent
50
54
 
51
- To use tools from an MCP server in an agent, import your `MCPClient` and call `.getTools()` in the `tools` parameter. This loads from the defined MCP servers, making them available to the agent.
55
+ To use tools from an MCP server in an agent, import your `MCPClient` and call `.listTools()` in the `tools` parameter. This loads from the defined MCP servers, making them available to the agent.
52
56
 
53
57
  ```typescript {3,15} title="src/mastra/agents/test-agent.ts"
54
58
  import { Agent } from "@mastra/core/agent";
@@ -56,6 +60,7 @@ import { Agent } from "@mastra/core/agent";
56
60
  import { testMcpClient } from "../mcp/test-mcp-client";
57
61
 
58
62
  export const testAgent = new Agent({
63
+ id: "test-agent",
59
64
  name: "Test Agent",
60
65
  description: "You are a helpful AI assistant",
61
66
  instructions: `
@@ -65,7 +70,7 @@ export const testAgent = new Agent({
65
70
 
66
71
  Answer questions using the information you find using the MCP Servers.`,
67
72
  model: "openai/gpt-5.1",
68
- tools: await testMcpClient.getTools(),
73
+ tools: await testMcpClient.listTools(),
69
74
  });
70
75
  ```
71
76
 
@@ -98,6 +103,10 @@ export const testMcpServer = new MCPServer({
98
103
 
99
104
  Visit [MCPServer](https://mastra.ai/reference/v1/tools/mcp-server) for a full list of configuration options.
100
105
 
106
+ > **Note:** Authentication
107
+
108
+ To protect your MCP server with OAuth, see the [OAuth Protection](https://mastra.ai/reference/v1/tools/mcp-server#oauth-protection) section.
109
+
101
110
  ## Registering an `MCPServer`
102
111
 
103
112
  To make an MCP server available to other systems or agents that support the protocol, register it in the main `Mastra` instance using `mcpServers`.
@@ -116,20 +125,20 @@ export const mastra = new Mastra({
116
125
 
117
126
  `MCPClient` offers two approaches to retrieving tools from connected servers, suitable for different application architectures:
118
127
 
119
- | Feature | Static Configuration (`await mcp.getTools()`) | Dynamic Configuration (`await mcp.getToolsets()`) |
120
- | :---------------- | :-------------------------------------------- | :--------------------------------------------------- |
121
- | **Use Case** | Single-user, static config (e.g., CLI tool) | Multi-user, dynamic config (e.g., SaaS app) |
122
- | **Configuration** | Fixed at agent initialization | Per-request, dynamic |
123
- | **Credentials** | Shared across all uses | Can vary per user/request |
124
- | **Agent Setup** | Tools added in `Agent` constructor | Tools passed in `.generate()` or `.stream()` options |
128
+ | Feature | Static Configuration (`await mcp.listTools()`) | Dynamic Configuration (`await mcp.listToolsets()`) |
129
+ | :---------------- | :--------------------------------------------- | :--------------------------------------------------- |
130
+ | **Use Case** | Single-user, static config (e.g., CLI tool) | Multi-user, dynamic config (e.g., SaaS app) |
131
+ | **Configuration** | Fixed at agent initialization | Per-request, dynamic |
132
+ | **Credentials** | Shared across all uses | Can vary per user/request |
133
+ | **Agent Setup** | Tools added in `Agent` constructor | Tools passed in `.generate()` or `.stream()` options |
125
134
 
126
135
  ### Static tools
127
136
 
128
- Use the `.getTools()` method to fetch tools from all configured MCP servers. This is suitable when configuration (such as API keys) is static and consistent across users or requests. Call it once and pass the result to the `tools` property when defining your agent.
137
+ Use the `.listTools()` method to fetch tools from all configured MCP servers. This is suitable when configuration (such as API keys) is static and consistent across users or requests. Call it once and pass the result to the `tools` property when defining your agent.
129
138
 
130
139
  > **Note:**
131
140
 
132
- Visit [getTools()](https://mastra.ai/reference/v1/tools/mcp-client#gettools) for more information.
141
+ Visit [listTools()](https://mastra.ai/reference/v1/tools/mcp-client#listtools) for more information.
133
142
 
134
143
  ```typescript {6} title="src/mastra/agents/test-agent.ts"
135
144
  import { Agent } from "@mastra/core/agent";
@@ -137,13 +146,14 @@ import { Agent } from "@mastra/core/agent";
137
146
  import { testMcpClient } from "../mcp/test-mcp-client";
138
147
 
139
148
  export const testAgent = new Agent({
140
- tools: await testMcpClient.getTools(),
149
+ id: "test-agent",
150
+ tools: await testMcpClient.listTools(),
141
151
  });
142
152
  ```
143
153
 
144
154
  ### Dynamic tools
145
155
 
146
- Use the `.getToolsets()` method when tool configuration may vary by request or user, such as in a multi-tenant system where each user provides their own API key. This method returns toolsets that can be passed to the `toolsets` option in the agent's `.generate()` or `.stream()` calls.
156
+ Use the `.listToolsets()` method when tool configuration may vary by request or user, such as in a multi-tenant system where each user provides their own API key. This method returns toolsets that can be passed to the `toolsets` option in the agent's `.generate()` or `.stream()` calls.
147
157
 
148
158
  ```typescript {5-16,21}
149
159
  import { MCPClient } from "@mastra/mcp";
@@ -166,7 +176,7 @@ async function handleRequest(userPrompt: string, userApiKey: string) {
166
176
  const agent = mastra.getAgent("testAgent");
167
177
 
168
178
  const response = await agent.generate(userPrompt, {
169
- toolsets: await userMcp.getToolsets(),
179
+ toolsets: await userMcp.listToolsets(),
170
180
  });
171
181
 
172
182
  await userMcp.disconnect();
@@ -179,7 +189,7 @@ async function handleRequest(userPrompt: string, userApiKey: string) {
179
189
 
180
190
  > **Note:**
181
191
 
182
- Visit [getToolsets()](https://mastra.ai/reference/v1/tools/mcp-client#gettoolsets) for more information.
192
+ Visit [listToolsets()](https://mastra.ai/reference/v1/tools/mcp-client#listtoolsets) for more information.
183
193
 
184
194
  ## Connecting to an MCP registry
185
195
 
@@ -104,8 +104,8 @@ const mcp = new MCPClient({
104
104
  });
105
105
 
106
106
  // You can then get tools or toolsets from this configuration to use in your agent
107
- const tools = await mcp.getTools();
108
- const toolsets = await mcp.getToolsets();
107
+ const tools = await mcp.listTools();
108
+ const toolsets = await mcp.listToolsets();
109
109
  ```
110
110
 
111
111
  Note: If you published without an organization scope, the `args` might just be `["-y", "your-package-name@latest"]`.
@@ -46,7 +46,7 @@ Retrieves all tools from all configured servers, with tool names namespaced by t
46
46
  Intended to be passed onto an Agent definition.
47
47
 
48
48
  ```ts
49
- new Agent({ tools: await mcp.listTools() });
49
+ new Agent({ id: "agent", tools: await mcp.listTools() });
50
50
  ```
51
51
 
52
52
  ### listToolsets()
@@ -591,6 +591,104 @@ await mcpClient.elicitation.onRequest("interactiveServer", async (request) => {
591
591
  - **Clear UI**: Make it obvious what information is being requested and why
592
592
  - **Security**: Never auto-accept requests for sensitive information
593
593
 
594
+ ## OAuth Authentication
595
+
596
+ For connecting to MCP servers that require OAuth authentication per the [MCP Auth Specification](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization), use the `MCPOAuthClientProvider`:
597
+
598
+ ```typescript
599
+ import { MCPClient, MCPOAuthClientProvider } from "@mastra/mcp";
600
+
601
+ // Create an OAuth provider
602
+ const oauthProvider = new MCPOAuthClientProvider({
603
+ redirectUrl: "http://localhost:3000/oauth/callback",
604
+ clientMetadata: {
605
+ redirect_uris: ["http://localhost:3000/oauth/callback"],
606
+ client_name: "My MCP Client",
607
+ grant_types: ["authorization_code", "refresh_token"],
608
+ response_types: ["code"],
609
+ },
610
+ onRedirectToAuthorization: (url) => {
611
+ // Handle authorization redirect (open browser, redirect response, etc.)
612
+ console.log(`Please visit: ${url}`);
613
+ },
614
+ });
615
+
616
+ // Use the provider with MCPClient
617
+ const client = new MCPClient({
618
+ servers: {
619
+ protectedServer: {
620
+ url: new URL("https://mcp.example.com/mcp"),
621
+ authProvider: oauthProvider,
622
+ },
623
+ },
624
+ });
625
+ ```
626
+
627
+ ### Quick Token Provider
628
+
629
+ For testing or when you already have a valid access token:
630
+
631
+ ```typescript
632
+ import { MCPClient, createSimpleTokenProvider } from "@mastra/mcp";
633
+
634
+ const provider = createSimpleTokenProvider("your-access-token", {
635
+ redirectUrl: "http://localhost:3000/callback",
636
+ clientMetadata: {
637
+ redirect_uris: ["http://localhost:3000/callback"],
638
+ client_name: "Test Client",
639
+ },
640
+ });
641
+
642
+ const client = new MCPClient({
643
+ servers: {
644
+ testServer: {
645
+ url: new URL("https://mcp.example.com/mcp"),
646
+ authProvider: provider,
647
+ },
648
+ },
649
+ });
650
+ ```
651
+
652
+ ### Custom Token Storage
653
+
654
+ For persistent token storage across sessions, implement the `OAuthStorage` interface:
655
+
656
+ ```typescript
657
+ import { MCPOAuthClientProvider, OAuthStorage } from "@mastra/mcp";
658
+
659
+ class DatabaseOAuthStorage implements OAuthStorage {
660
+ constructor(private db: Database, private userId: string) {}
661
+
662
+ async set(key: string, value: string): Promise<void> {
663
+ await this.db.query(
664
+ "INSERT INTO oauth_tokens (user_id, key, value) VALUES (?, ?, ?) ON CONFLICT DO UPDATE SET value = ?",
665
+ [this.userId, key, value, value]
666
+ );
667
+ }
668
+
669
+ async get(key: string): Promise<string | undefined> {
670
+ const result = await this.db.query(
671
+ "SELECT value FROM oauth_tokens WHERE user_id = ? AND key = ?",
672
+ [this.userId, key]
673
+ );
674
+ return result?.[0]?.value;
675
+ }
676
+
677
+ async delete(key: string): Promise<void> {
678
+ await this.db.query(
679
+ "DELETE FROM oauth_tokens WHERE user_id = ? AND key = ?",
680
+ [this.userId, key]
681
+ );
682
+ }
683
+ }
684
+
685
+ const provider = new MCPOAuthClientProvider({
686
+ redirectUrl: "http://localhost:3000/callback",
687
+ clientMetadata: { /* ... */ },
688
+ storage: new DatabaseOAuthStorage(db, "user-123"),
689
+ });
690
+ ```
691
+
594
692
  ## Examples
595
693
 
596
694
  ### Static Tool Configuration
@@ -622,6 +720,7 @@ const mcp = new MCPClient({
622
720
 
623
721
  // Create an agent with access to all tools
624
722
  const agent = new Agent({
723
+ id: "multi-tool-agent",
625
724
  name: "Multi-tool Agent",
626
725
  instructions: "You have access to multiple tool servers.",
627
726
  model: "openai/gpt-5.1",
@@ -677,6 +776,7 @@ import { MCPClient } from "@mastra/mcp";
677
776
 
678
777
  // Create the agent first, without any tools
679
778
  const agent = new Agent({
779
+ id: "multi-tool-agent",
680
780
  name: "Multi-tool Agent",
681
781
  instructions: "You help users check stocks and weather.",
682
782
  model: "openai/gpt-5.1",
@@ -1760,44 +1860,245 @@ type ElicitResult = {
1760
1860
  };
1761
1861
  ```
1762
1862
 
1863
+ ## OAuth Protection
1864
+
1865
+ To protect your MCP server with OAuth authentication per the [MCP Auth Specification](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization), use the `createOAuthMiddleware` function:
1866
+
1867
+ ```typescript
1868
+ import http from "node:http";
1869
+ import { MCPServer, createOAuthMiddleware, createStaticTokenValidator } from "@mastra/mcp";
1870
+
1871
+ const mcpServer = new MCPServer({
1872
+ id: "protected-server",
1873
+ name: "Protected MCP Server",
1874
+ version: "1.0.0",
1875
+ tools: { /* your tools */ },
1876
+ });
1877
+
1878
+ // Create OAuth middleware
1879
+ const oauthMiddleware = createOAuthMiddleware({
1880
+ oauth: {
1881
+ resource: "https://mcp.example.com/mcp",
1882
+ authorizationServers: ["https://auth.example.com"],
1883
+ scopesSupported: ["mcp:read", "mcp:write"],
1884
+ resourceName: "My Protected MCP Server",
1885
+ validateToken: createStaticTokenValidator(["allowed-token-1"]),
1886
+ },
1887
+ mcpPath: "/mcp",
1888
+ });
1889
+
1890
+ // Create HTTP server with OAuth protection
1891
+ const httpServer = http.createServer(async (req, res) => {
1892
+ const url = new URL(req.url || "", "https://mcp.example.com");
1893
+
1894
+ // Apply OAuth middleware first
1895
+ const result = await oauthMiddleware(req, res, url);
1896
+ if (!result.proceed) return; // Middleware handled response (401, metadata, etc.)
1897
+
1898
+ // Token is valid, proceed to MCP handler
1899
+ await mcpServer.startHTTP({ url, httpPath: "/mcp", req, res });
1900
+ });
1901
+
1902
+ httpServer.listen(3000);
1903
+ ```
1904
+
1905
+ The middleware automatically:
1906
+
1907
+ - Serves **Protected Resource Metadata** at `/.well-known/oauth-protected-resource` (RFC 9728)
1908
+ - Returns `401 Unauthorized` with proper `WWW-Authenticate` headers when authentication is required
1909
+ - Validates bearer tokens using your provided validator
1910
+
1911
+ ### Token Validation
1912
+
1913
+ For production, use proper token validation:
1914
+
1915
+ ```typescript
1916
+ import { createOAuthMiddleware, createIntrospectionValidator } from "@mastra/mcp";
1917
+
1918
+ // Option 1: Token introspection (RFC 7662)
1919
+ const middleware = createOAuthMiddleware({
1920
+ oauth: {
1921
+ resource: "https://mcp.example.com/mcp",
1922
+ authorizationServers: ["https://auth.example.com"],
1923
+ validateToken: createIntrospectionValidator(
1924
+ "https://auth.example.com/oauth/introspect",
1925
+ { clientId: "mcp-server", clientSecret: "secret" }
1926
+ ),
1927
+ },
1928
+ });
1929
+
1930
+ // Option 2: Custom validation (JWT, database lookup, etc.)
1931
+ const customMiddleware = createOAuthMiddleware({
1932
+ oauth: {
1933
+ resource: "https://mcp.example.com/mcp",
1934
+ authorizationServers: ["https://auth.example.com"],
1935
+ validateToken: async (token, resource) => {
1936
+ const decoded = await verifyJWT(token);
1937
+ if (!decoded) {
1938
+ return { valid: false, error: "invalid_token" };
1939
+ }
1940
+ return {
1941
+ valid: true,
1942
+ scopes: decoded.scope?.split(" ") || [],
1943
+ subject: decoded.sub,
1944
+ };
1945
+ },
1946
+ },
1947
+ });
1948
+ ```
1949
+
1950
+ ### OAuth Middleware Options
1951
+
1763
1952
  ## Authentication Context
1764
1953
 
1765
- Tools can access request metadata via `context.mcp.extra` when using HTTP-based transports:
1954
+ Tools can access request metadata via `context.mcp.extra` when using HTTP-based transports. This allows you to pass authentication info, user context, or any custom data from your HTTP middleware to your MCP tools.
1955
+
1956
+ ### How It Works
1957
+
1958
+ Whatever you set on `req.auth` in your HTTP middleware becomes available as `context.mcp.extra.authInfo` in your tools:
1959
+
1960
+ ```
1961
+ req.auth = { ... } → context?.mcp?.extra?.authInfo.extra = { ... }
1962
+ ```
1963
+
1964
+ ### Setting Up Authentication Middleware
1965
+
1966
+ To pass data to your tools, populate `req.auth` on the Node.js request object in your HTTP server middleware before calling `server.startHTTP()`.
1967
+
1968
+ ```typescript
1969
+ import express from "express";
1970
+
1971
+ const app = express();
1972
+
1973
+ // Auth middleware - set req.auth before the MCP handler
1974
+ app.use("/mcp", (req, res, next) => {
1975
+ const token = req.headers.authorization?.replace("Bearer ", "");
1976
+ const user = verifyToken(token);
1977
+
1978
+ // This entire object becomes context.mcp.extra.authInfo
1979
+ // @ts-ignore - req.auth is read by the MCP SDK
1980
+ req.auth = {
1981
+ token,
1982
+ userId: user.userId,
1983
+ email: user.email,
1984
+ };
1985
+ next();
1986
+ });
1987
+
1988
+ app.all("/mcp", async (req, res) => {
1989
+ const url = new URL(req.url, `http://${req.headers.host}`);
1990
+ await server.startHTTP({ url, httpPath: "/mcp", req, res });
1991
+ });
1992
+ ```
1993
+
1994
+ ### Accessing Auth Data in Tools
1995
+
1996
+ The `req.auth` object is available as `context.mcp.extra.authInfo` in your tool's execute function:
1766
1997
 
1767
1998
  ```typescript
1768
1999
  execute: async (inputData, context) => {
1769
- if (!context.mcp?.extra?.authInfo?.token) {
1770
- return "Authentication required";
2000
+ // Access the auth data you set in middleware
2001
+ const authInfo = context?.mcp?.extra?.authInfo;
2002
+
2003
+ if (!authInfo?.extra?.userId) {
2004
+ return { error: "Authentication required" };
1771
2005
  }
1772
2006
 
1773
- // Use the auth token
2007
+ // Use the auth data
2008
+ console.log("User ID:", authInfo.extra.userId);
2009
+ console.log("Email:", authInfo.extra.email);
2010
+
1774
2011
  const response = await fetch("/api/data", {
1775
- headers: { Authorization: `Bearer ${context.mcp.extra.authInfo.token}` },
1776
- signal: context.mcp.extra.signal,
2012
+ headers: { Authorization: `Bearer ${authInfo.token}` },
2013
+ signal: context?.mcp?.extra?.signal,
1777
2014
  });
1778
2015
 
1779
2016
  return response.json();
1780
2017
  };
1781
2018
  ```
1782
2019
 
1783
- The `extra` object contains:
1784
-
1785
- - `authInfo`: Authentication info (when provided by server middleware)
1786
- - `sessionId`: Session identifier
1787
- - `signal`: AbortSignal for cancellation
1788
- - `sendNotification`/`sendRequest`: MCP protocol functions
1789
-
1790
- > Note: To enable authentication, your HTTP server needs middleware that populates `req.auth` before calling `server.startHTTP()`. For example:
1791
- >
1792
- > ```typescript
1793
- > httpServer.createServer((req, res) => {
1794
- > // Add auth middleware
1795
- > req.auth = validateAuthToken(req.headers.authorization);
1796
- >
1797
- > // Then pass to MCP server
1798
- > await server.startHTTP({ url, httpPath, req, res });
1799
- > });
1800
- > ```
2020
+ ### The `extra` Object
2021
+
2022
+ The full `context.mcp.extra` object contains:
2023
+
2024
+ | Property | Description |
2025
+ |----------|-------------|
2026
+ | `authInfo` | Whatever you set on `req.auth` in your middleware |
2027
+ | `sessionId` | Session identifier for the MCP connection |
2028
+ | `signal` | AbortSignal for request cancellation |
2029
+ | `sendNotification` | MCP protocol function for sending notifications |
2030
+ | `sendRequest` | MCP protocol function for sending requests |
2031
+
2032
+ ### Complete Example
2033
+
2034
+ Here's a complete example showing the data flow from middleware to tool:
2035
+
2036
+ ```typescript
2037
+ import express from "express";
2038
+ import { MCPServer } from "@mastra/mcp";
2039
+ import { createTool } from "@mastra/core/tools";
2040
+ import { z } from "zod";
2041
+
2042
+ const verifyToken = (token: string) => {
2043
+ // TODO: Implement token verification
2044
+ return {
2045
+ userId: "123",
2046
+ email: "test@test.com",
2047
+ };
2048
+ };
2049
+
2050
+ // 1. Define your tool that uses auth context
2051
+ const getUserData = createTool({
2052
+ id: "get-user-data",
2053
+ description: "Fetches data for the authenticated user",
2054
+ inputSchema: z.object({}),
2055
+ execute: async (inputData, context) => {
2056
+ const authInfo = context?.mcp?.extra?.authInfo;
2057
+
2058
+ if (!authInfo?.extra?.userId) {
2059
+ return { error: "Authentication required" };
2060
+ }
2061
+
2062
+ // Access the data you set in middleware
2063
+ return {
2064
+ userId: authInfo.extra.userId,
2065
+ email: authInfo.extra.email,
2066
+ };
2067
+ },
2068
+ });
2069
+
2070
+ // 2. Create the MCP server with your tools
2071
+ const server = new MCPServer({
2072
+ id: "my-server",
2073
+ name: "My Server",
2074
+ version: "1.0.0",
2075
+ tools: { getUserData },
2076
+ });
2077
+
2078
+ // 3. Set up Express with auth middleware
2079
+ const app = express();
2080
+
2081
+ app.use("/mcp", (req, res, next) => {
2082
+ const token = req.headers.authorization?.replace("Bearer ", "");
2083
+ const user = verifyToken(token);
2084
+
2085
+ // This entire object becomes context.mcp.extra.authInfo
2086
+ // @ts-ignore - req.auth is read by the MCP SDK
2087
+ req.auth = {
2088
+ token,
2089
+ userId: user.userId,
2090
+ email: user.email,
2091
+ };
2092
+ next();
2093
+ });
2094
+
2095
+ app.all("/mcp", async (req, res) => {
2096
+ const url = new URL(req.url, `http://${req.headers.host}`);
2097
+ await server.startHTTP({ url, httpPath: "/mcp", req, res });
2098
+ });
2099
+
2100
+ app.listen(3000);
2101
+ ```
1801
2102
 
1802
2103
  ## Related Information
1803
2104
 
@@ -46,6 +46,10 @@ export const testMcpClient = new MCPClient({
46
46
 
47
47
  Visit [MCPClient](https://mastra.ai/reference/v1/tools/mcp-client) for a full list of configuration options.
48
48
 
49
+ > **Note:** Authentication
50
+
51
+ For connecting to OAuth-protected MCP servers, see the [OAuth Authentication](https://mastra.ai/reference/v1/tools/mcp-client#oauth-authentication) section.
52
+
49
53
  ## Using `MCPClient` with an agent
50
54
 
51
55
  To use tools from an MCP server in an agent, import your `MCPClient` and call `.listTools()` in the `tools` parameter. This loads from the defined MCP servers, making them available to the agent.
@@ -99,6 +103,10 @@ export const testMcpServer = new MCPServer({
99
103
 
100
104
  Visit [MCPServer](https://mastra.ai/reference/v1/tools/mcp-server) for a full list of configuration options.
101
105
 
106
+ > **Note:** Authentication
107
+
108
+ To protect your MCP server with OAuth, see the [OAuth Protection](https://mastra.ai/reference/v1/tools/mcp-server#oauth-protection) section.
109
+
102
110
  ### Serverless deployments
103
111
 
104
112
  `MCPServer` can be deployed in serverless environments (Cloudflare Workers, Vercel Edge Functions, AWS Lambda, etc.) by enabling the `serverless: true` option in `startHTTP()`. This runs the server in stateless mode, where each request is handled independently without session management.