@mastra/mcp 1.0.0-beta.8 → 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.
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './client/index.js';
2
2
  export * from './server/index.js';
3
+ export * from './shared/index.js';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -22,6 +22,7 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
22
22
  import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
23
23
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
24
24
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
25
+ export { UnauthorizedError, auth, buildDiscoveryUrls, discoverAuthorizationServerMetadata, discoverOAuthMetadata, discoverOAuthProtectedResourceMetadata, exchangeAuthorization, extractResourceMetadataUrl, parseErrorResponse, refreshAuthorization, registerClient, selectResourceURL, startAuthorization } from '@modelcontextprotocol/sdk/client/auth.js';
25
26
 
26
27
  // src/client/client.ts
27
28
 
@@ -577,7 +578,7 @@ var InternalMastraMCPClient = class extends MastraBase {
577
578
  }
578
579
  }
579
580
  async connectHttp(url) {
580
- const { requestInit, eventSourceInit, authProvider, connectTimeout, fetch } = this.serverConfig;
581
+ const { requestInit, eventSourceInit, authProvider, connectTimeout, fetch: fetch2 } = this.serverConfig;
581
582
  this.log("debug", `Attempting to connect to URL: ${url}`);
582
583
  let shouldTrySSE = url.pathname.endsWith(`/sse`);
583
584
  if (!shouldTrySSE) {
@@ -587,7 +588,7 @@ var InternalMastraMCPClient = class extends MastraBase {
587
588
  requestInit,
588
589
  reconnectionOptions: this.serverConfig.reconnectionOptions,
589
590
  authProvider,
590
- fetch
591
+ fetch: fetch2
591
592
  });
592
593
  await this.client.connect(streamableTransport, {
593
594
  timeout: connectTimeout ?? DEFAULT_SERVER_CONNECT_TIMEOUT_MSEC
@@ -606,12 +607,12 @@ var InternalMastraMCPClient = class extends MastraBase {
606
607
  if (shouldTrySSE) {
607
608
  this.log("debug", "Falling back to deprecated HTTP+SSE transport...");
608
609
  try {
609
- const sseEventSourceInit = fetch ? { ...eventSourceInit, fetch } : eventSourceInit;
610
+ const sseEventSourceInit = fetch2 ? { ...eventSourceInit, fetch: fetch2 } : eventSourceInit;
610
611
  const sseTransport = new SSEClientTransport(url, {
611
612
  requestInit,
612
613
  eventSourceInit: sseEventSourceInit,
613
614
  authProvider,
614
- fetch
615
+ fetch: fetch2
615
616
  });
616
617
  await this.client.connect(sseTransport, { timeout: this.serverConfig.timeout ?? this.timeout });
617
618
  this.transport = sseTransport;
@@ -1814,6 +1815,190 @@ To fix this you have three different options:
1814
1815
  }
1815
1816
  };
1816
1817
 
1818
+ // src/client/oauth-provider.ts
1819
+ var InMemoryOAuthStorage = class {
1820
+ data = /* @__PURE__ */ new Map();
1821
+ set(key, value) {
1822
+ this.data.set(key, value);
1823
+ }
1824
+ get(key) {
1825
+ return this.data.get(key);
1826
+ }
1827
+ delete(key) {
1828
+ this.data.delete(key);
1829
+ }
1830
+ clear() {
1831
+ this.data.clear();
1832
+ }
1833
+ };
1834
+ var MCPOAuthClientProvider = class {
1835
+ _redirectUrl;
1836
+ _clientMetadata;
1837
+ storage;
1838
+ onRedirect;
1839
+ generateState;
1840
+ _clientInfo;
1841
+ constructor(options) {
1842
+ this._redirectUrl = options.redirectUrl;
1843
+ this._clientMetadata = options.clientMetadata;
1844
+ this._clientInfo = options.clientInformation;
1845
+ this.storage = options.storage ?? new InMemoryOAuthStorage();
1846
+ this.onRedirect = options.onRedirectToAuthorization;
1847
+ this.generateState = options.stateGenerator ?? (() => crypto.randomUUID());
1848
+ }
1849
+ /**
1850
+ * The URL to redirect the user agent to after authorization.
1851
+ */
1852
+ get redirectUrl() {
1853
+ return this._redirectUrl;
1854
+ }
1855
+ /**
1856
+ * Metadata about this OAuth client.
1857
+ */
1858
+ get clientMetadata() {
1859
+ return this._clientMetadata;
1860
+ }
1861
+ /**
1862
+ * Returns a OAuth2 state parameter.
1863
+ */
1864
+ async state() {
1865
+ return this.generateState();
1866
+ }
1867
+ /**
1868
+ * Loads information about this OAuth client.
1869
+ */
1870
+ async clientInformation() {
1871
+ if (this._clientInfo) {
1872
+ return this._clientInfo;
1873
+ }
1874
+ const stored = await this.storage.get("client_info");
1875
+ if (stored) {
1876
+ try {
1877
+ return JSON.parse(stored);
1878
+ } catch {
1879
+ }
1880
+ }
1881
+ return void 0;
1882
+ }
1883
+ /**
1884
+ * Saves dynamically registered client information.
1885
+ */
1886
+ async saveClientInformation(clientInformation) {
1887
+ this._clientInfo = clientInformation;
1888
+ await this.storage.set("client_info", JSON.stringify(clientInformation));
1889
+ }
1890
+ /**
1891
+ * Loads existing OAuth tokens.
1892
+ */
1893
+ async tokens() {
1894
+ const stored = await this.storage.get("tokens");
1895
+ if (stored) {
1896
+ try {
1897
+ return JSON.parse(stored);
1898
+ } catch {
1899
+ }
1900
+ }
1901
+ return void 0;
1902
+ }
1903
+ /**
1904
+ * Stores new OAuth tokens after successful authorization.
1905
+ */
1906
+ async saveTokens(tokens) {
1907
+ await this.storage.set("tokens", JSON.stringify(tokens));
1908
+ }
1909
+ /**
1910
+ * Invoked to redirect the user agent to the authorization URL.
1911
+ */
1912
+ async redirectToAuthorization(authorizationUrl) {
1913
+ if (this.onRedirect) {
1914
+ await this.onRedirect(authorizationUrl);
1915
+ } else {
1916
+ console.info(`Authorization required. Please visit: ${authorizationUrl.toString()}`);
1917
+ }
1918
+ }
1919
+ /**
1920
+ * Saves a PKCE code verifier before redirecting to authorization.
1921
+ */
1922
+ async saveCodeVerifier(codeVerifier) {
1923
+ await this.storage.set("code_verifier", codeVerifier);
1924
+ }
1925
+ /**
1926
+ * Loads the PKCE code verifier for validating authorization result.
1927
+ */
1928
+ async codeVerifier() {
1929
+ const verifier = await this.storage.get("code_verifier");
1930
+ if (!verifier) {
1931
+ throw new Error("No code verifier found. Authorization flow may not have started properly.");
1932
+ }
1933
+ return verifier;
1934
+ }
1935
+ /**
1936
+ * Optional: Custom client authentication for token requests.
1937
+ * Uses default behavior if not implemented.
1938
+ */
1939
+ async addClientAuthentication(_headers, _params, _url, _metadata) {
1940
+ }
1941
+ /**
1942
+ * Invalidate credentials when server indicates they're no longer valid.
1943
+ */
1944
+ async invalidateCredentials(scope) {
1945
+ switch (scope) {
1946
+ case "all":
1947
+ await this.storage.delete("tokens");
1948
+ await this.storage.delete("client_info");
1949
+ await this.storage.delete("code_verifier");
1950
+ this._clientInfo = void 0;
1951
+ break;
1952
+ case "client":
1953
+ await this.storage.delete("client_info");
1954
+ this._clientInfo = void 0;
1955
+ break;
1956
+ case "tokens":
1957
+ await this.storage.delete("tokens");
1958
+ break;
1959
+ case "verifier":
1960
+ await this.storage.delete("code_verifier");
1961
+ break;
1962
+ }
1963
+ }
1964
+ /**
1965
+ * Clear all stored OAuth data.
1966
+ * Useful for logging out or resetting state.
1967
+ */
1968
+ async clear() {
1969
+ await this.invalidateCredentials("all");
1970
+ }
1971
+ /**
1972
+ * Check if the provider has valid (non-expired) tokens.
1973
+ */
1974
+ async hasValidTokens() {
1975
+ const currentTokens = await this.tokens();
1976
+ if (!currentTokens) return false;
1977
+ if (!currentTokens.access_token) return false;
1978
+ return true;
1979
+ }
1980
+ };
1981
+ function createSimpleTokenProvider(accessToken, options) {
1982
+ const tokens = {
1983
+ access_token: accessToken,
1984
+ token_type: options.tokenType ?? "Bearer",
1985
+ refresh_token: options.refreshToken,
1986
+ expires_in: options.expiresIn,
1987
+ scope: options.scope
1988
+ };
1989
+ const storage = new InMemoryOAuthStorage();
1990
+ storage.set("tokens", JSON.stringify(tokens));
1991
+ if (options.clientInformation) {
1992
+ storage.set("client_info", JSON.stringify(options.clientInformation));
1993
+ }
1994
+ return new MCPOAuthClientProvider({
1995
+ redirectUrl: options.redirectUrl,
1996
+ clientMetadata: options.clientMetadata,
1997
+ clientInformation: options.clientInformation,
1998
+ storage
1999
+ });
2000
+ }
2001
+
1817
2002
  // ../../node_modules/.pnpm/hono@4.11.3/node_modules/hono/dist/utils/stream.js
1818
2003
  var StreamingApi = class {
1819
2004
  writer;
@@ -2517,6 +2702,12 @@ var MCPServer = class extends MCPServerBase {
2517
2702
  if (tool.outputSchema) {
2518
2703
  toolSpec.outputSchema = tool.outputSchema.jsonSchema;
2519
2704
  }
2705
+ if (tool.mcp?.annotations) {
2706
+ toolSpec.annotations = tool.mcp.annotations;
2707
+ }
2708
+ if (tool.mcp?._meta) {
2709
+ toolSpec._meta = tool.mcp._meta;
2710
+ }
2520
2711
  return toolSpec;
2521
2712
  })
2522
2713
  };
@@ -3888,7 +4079,210 @@ Provided arguments: ${JSON.stringify(args, null, 2)}`,
3888
4079
  }
3889
4080
  }
3890
4081
  };
4082
+ function escapeHeaderValue(value) {
4083
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
4084
+ }
4085
+ function generateWWWAuthenticateHeader(options = {}) {
4086
+ const params = [];
4087
+ if (options.resourceMetadataUrl) {
4088
+ params.push(`resource_metadata="${escapeHeaderValue(options.resourceMetadataUrl)}"`);
4089
+ }
4090
+ if (options.additionalParams) {
4091
+ for (const [key, value] of Object.entries(options.additionalParams)) {
4092
+ params.push(`${key}="${escapeHeaderValue(value)}"`);
4093
+ }
4094
+ }
4095
+ if (params.length === 0) {
4096
+ return "Bearer";
4097
+ }
4098
+ return `Bearer ${params.join(", ")}`;
4099
+ }
4100
+ function generateProtectedResourceMetadata(config) {
4101
+ return {
4102
+ resource: config.resource,
4103
+ authorization_servers: config.authorizationServers,
4104
+ scopes_supported: config.scopesSupported ?? ["mcp:read", "mcp:write"],
4105
+ bearer_methods_supported: ["header"],
4106
+ ...config.resourceName && { resource_name: config.resourceName },
4107
+ ...config.resourceDocumentation && {
4108
+ resource_documentation: config.resourceDocumentation
4109
+ }
4110
+ };
4111
+ }
4112
+ function extractBearerToken(authHeader) {
4113
+ if (!authHeader) return void 0;
4114
+ const prefix = "bearer ";
4115
+ if (authHeader.length <= prefix.length) return void 0;
4116
+ if (authHeader.slice(0, prefix.length).toLowerCase() !== prefix) return void 0;
4117
+ const token = authHeader.slice(prefix.length).trim();
4118
+ return token || void 0;
4119
+ }
4120
+
4121
+ // src/server/oauth-middleware.ts
4122
+ function createOAuthMiddleware(options) {
4123
+ const { oauth, mcpPath = "/mcp", logger } = options;
4124
+ const protectedResourceMetadata = generateProtectedResourceMetadata(oauth);
4125
+ const wellKnownPath = "/.well-known/oauth-protected-resource";
4126
+ const resourceMetadataUrl = new URL(wellKnownPath, oauth.resource).toString();
4127
+ return async function oauthMiddleware(req, res, url) {
4128
+ logger?.debug?.(`OAuth middleware: ${req.method} ${url.pathname}`);
4129
+ if (url.pathname === wellKnownPath && req.method === "GET") {
4130
+ logger?.debug?.("OAuth middleware: Serving Protected Resource Metadata");
4131
+ res.writeHead(200, {
4132
+ "Content-Type": "application/json",
4133
+ "Cache-Control": "max-age=3600",
4134
+ "Access-Control-Allow-Origin": "*"
4135
+ });
4136
+ res.end(JSON.stringify(protectedResourceMetadata));
4137
+ return { proceed: false, handled: true };
4138
+ }
4139
+ if (url.pathname === wellKnownPath && req.method === "OPTIONS") {
4140
+ res.writeHead(204, {
4141
+ "Access-Control-Allow-Origin": "*",
4142
+ "Access-Control-Allow-Methods": "GET, OPTIONS",
4143
+ "Access-Control-Allow-Headers": "Content-Type",
4144
+ "Access-Control-Max-Age": "86400"
4145
+ });
4146
+ res.end();
4147
+ return { proceed: false, handled: true };
4148
+ }
4149
+ if (!url.pathname.startsWith(mcpPath)) {
4150
+ return { proceed: true, handled: false };
4151
+ }
4152
+ const authHeader = req.headers["authorization"];
4153
+ const token = extractBearerToken(authHeader);
4154
+ if (!token) {
4155
+ logger?.debug?.("OAuth middleware: No bearer token provided");
4156
+ res.writeHead(401, {
4157
+ "Content-Type": "application/json",
4158
+ "WWW-Authenticate": generateWWWAuthenticateHeader({ resourceMetadataUrl })
4159
+ });
4160
+ res.end(
4161
+ JSON.stringify({
4162
+ error: "unauthorized",
4163
+ error_description: "Bearer token required"
4164
+ })
4165
+ );
4166
+ return { proceed: false, handled: true };
4167
+ }
4168
+ if (oauth.validateToken) {
4169
+ logger?.debug?.("OAuth middleware: Validating token");
4170
+ const validationResult = await oauth.validateToken(token, oauth.resource);
4171
+ if (!validationResult.valid) {
4172
+ logger?.debug?.(`OAuth middleware: Token validation failed: ${validationResult.error}`);
4173
+ res.writeHead(401, {
4174
+ "Content-Type": "application/json",
4175
+ "WWW-Authenticate": generateWWWAuthenticateHeader({
4176
+ resourceMetadataUrl,
4177
+ additionalParams: {
4178
+ error: validationResult.error || "invalid_token",
4179
+ ...validationResult.errorDescription && {
4180
+ error_description: validationResult.errorDescription
4181
+ }
4182
+ }
4183
+ })
4184
+ });
4185
+ res.end(
4186
+ JSON.stringify({
4187
+ error: validationResult.error || "invalid_token",
4188
+ error_description: validationResult.errorDescription || "Token validation failed"
4189
+ })
4190
+ );
4191
+ return { proceed: false, handled: true, tokenValidation: validationResult };
4192
+ }
4193
+ logger?.debug?.("OAuth middleware: Token validated successfully");
4194
+ return { proceed: true, handled: false, tokenValidation: validationResult };
4195
+ }
4196
+ logger?.debug?.("OAuth middleware: No token validation configured, accepting token");
4197
+ return {
4198
+ proceed: true,
4199
+ handled: false,
4200
+ tokenValidation: { valid: true }
4201
+ };
4202
+ };
4203
+ }
4204
+ function createStaticTokenValidator(validTokens) {
4205
+ const tokenSet = new Set(validTokens);
4206
+ return async (token) => {
4207
+ if (tokenSet.has(token)) {
4208
+ return { valid: true, scopes: ["mcp:read", "mcp:write"] };
4209
+ }
4210
+ return {
4211
+ valid: false,
4212
+ error: "invalid_token",
4213
+ errorDescription: "Token not recognized"
4214
+ };
4215
+ };
4216
+ }
4217
+ function createIntrospectionValidator(introspectionEndpoint, clientCredentials) {
4218
+ return async (token, resource) => {
4219
+ try {
4220
+ const headers = {
4221
+ "Content-Type": "application/x-www-form-urlencoded"
4222
+ };
4223
+ if (clientCredentials) {
4224
+ if (clientCredentials.clientId.includes(":")) {
4225
+ return {
4226
+ valid: false,
4227
+ error: "invalid_request",
4228
+ errorDescription: "clientId cannot contain a colon character per RFC 7617"
4229
+ };
4230
+ }
4231
+ const credentials = Buffer.from(`${clientCredentials.clientId}:${clientCredentials.clientSecret}`).toString(
4232
+ "base64"
4233
+ );
4234
+ headers["Authorization"] = `Basic ${credentials}`;
4235
+ }
4236
+ const response = await fetch(introspectionEndpoint, {
4237
+ method: "POST",
4238
+ headers,
4239
+ body: new URLSearchParams({
4240
+ token,
4241
+ token_type_hint: "access_token"
4242
+ })
4243
+ });
4244
+ if (!response.ok) {
4245
+ return {
4246
+ valid: false,
4247
+ error: "server_error",
4248
+ errorDescription: `Introspection failed: ${response.status}`
4249
+ };
4250
+ }
4251
+ const data = await response.json();
4252
+ if (!data.active) {
4253
+ return {
4254
+ valid: false,
4255
+ error: "invalid_token",
4256
+ errorDescription: "Token is not active"
4257
+ };
4258
+ }
4259
+ if (data.aud) {
4260
+ const audiences = Array.isArray(data.aud) ? data.aud : [data.aud];
4261
+ if (!audiences.includes(resource)) {
4262
+ return {
4263
+ valid: false,
4264
+ error: "invalid_token",
4265
+ errorDescription: "Token audience does not match this resource"
4266
+ };
4267
+ }
4268
+ }
4269
+ return {
4270
+ valid: true,
4271
+ scopes: data.scope?.trim().split(" ").filter((s) => s !== "") || [],
4272
+ subject: data.sub,
4273
+ expiresAt: data.exp,
4274
+ claims: data
4275
+ };
4276
+ } catch (error) {
4277
+ return {
4278
+ valid: false,
4279
+ error: "server_error",
4280
+ errorDescription: error instanceof Error ? error.message : "Introspection failed"
4281
+ };
4282
+ }
4283
+ };
4284
+ }
3891
4285
 
3892
- export { InternalMastraMCPClient, MCPClient, MCPServer };
4286
+ export { InMemoryOAuthStorage, InternalMastraMCPClient, MCPClient, MCPOAuthClientProvider, MCPServer, createIntrospectionValidator, createOAuthMiddleware, createSimpleTokenProvider, createStaticTokenValidator, extractBearerToken, generateProtectedResourceMetadata, generateWWWAuthenticateHeader };
3893
4287
  //# sourceMappingURL=index.js.map
3894
4288
  //# sourceMappingURL=index.js.map