@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.cjs CHANGED
@@ -24,6 +24,7 @@ var index_js$1 = require('@modelcontextprotocol/sdk/server/index.js');
24
24
  var sse_js$1 = require('@modelcontextprotocol/sdk/server/sse.js');
25
25
  var stdio_js$1 = require('@modelcontextprotocol/sdk/server/stdio.js');
26
26
  var streamableHttp_js$1 = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
27
+ var auth_js = require('@modelcontextprotocol/sdk/client/auth.js');
27
28
 
28
29
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
29
30
 
@@ -584,7 +585,7 @@ var InternalMastraMCPClient = class extends base.MastraBase {
584
585
  }
585
586
  }
586
587
  async connectHttp(url) {
587
- const { requestInit, eventSourceInit, authProvider, connectTimeout, fetch } = this.serverConfig;
588
+ const { requestInit, eventSourceInit, authProvider, connectTimeout, fetch: fetch2 } = this.serverConfig;
588
589
  this.log("debug", `Attempting to connect to URL: ${url}`);
589
590
  let shouldTrySSE = url.pathname.endsWith(`/sse`);
590
591
  if (!shouldTrySSE) {
@@ -594,7 +595,7 @@ var InternalMastraMCPClient = class extends base.MastraBase {
594
595
  requestInit,
595
596
  reconnectionOptions: this.serverConfig.reconnectionOptions,
596
597
  authProvider,
597
- fetch
598
+ fetch: fetch2
598
599
  });
599
600
  await this.client.connect(streamableTransport, {
600
601
  timeout: connectTimeout ?? DEFAULT_SERVER_CONNECT_TIMEOUT_MSEC
@@ -613,12 +614,12 @@ var InternalMastraMCPClient = class extends base.MastraBase {
613
614
  if (shouldTrySSE) {
614
615
  this.log("debug", "Falling back to deprecated HTTP+SSE transport...");
615
616
  try {
616
- const sseEventSourceInit = fetch ? { ...eventSourceInit, fetch } : eventSourceInit;
617
+ const sseEventSourceInit = fetch2 ? { ...eventSourceInit, fetch: fetch2 } : eventSourceInit;
617
618
  const sseTransport = new sse_js.SSEClientTransport(url, {
618
619
  requestInit,
619
620
  eventSourceInit: sseEventSourceInit,
620
621
  authProvider,
621
- fetch
622
+ fetch: fetch2
622
623
  });
623
624
  await this.client.connect(sseTransport, { timeout: this.serverConfig.timeout ?? this.timeout });
624
625
  this.transport = sseTransport;
@@ -1821,6 +1822,190 @@ To fix this you have three different options:
1821
1822
  }
1822
1823
  };
1823
1824
 
1825
+ // src/client/oauth-provider.ts
1826
+ var InMemoryOAuthStorage = class {
1827
+ data = /* @__PURE__ */ new Map();
1828
+ set(key, value) {
1829
+ this.data.set(key, value);
1830
+ }
1831
+ get(key) {
1832
+ return this.data.get(key);
1833
+ }
1834
+ delete(key) {
1835
+ this.data.delete(key);
1836
+ }
1837
+ clear() {
1838
+ this.data.clear();
1839
+ }
1840
+ };
1841
+ var MCPOAuthClientProvider = class {
1842
+ _redirectUrl;
1843
+ _clientMetadata;
1844
+ storage;
1845
+ onRedirect;
1846
+ generateState;
1847
+ _clientInfo;
1848
+ constructor(options) {
1849
+ this._redirectUrl = options.redirectUrl;
1850
+ this._clientMetadata = options.clientMetadata;
1851
+ this._clientInfo = options.clientInformation;
1852
+ this.storage = options.storage ?? new InMemoryOAuthStorage();
1853
+ this.onRedirect = options.onRedirectToAuthorization;
1854
+ this.generateState = options.stateGenerator ?? (() => crypto.randomUUID());
1855
+ }
1856
+ /**
1857
+ * The URL to redirect the user agent to after authorization.
1858
+ */
1859
+ get redirectUrl() {
1860
+ return this._redirectUrl;
1861
+ }
1862
+ /**
1863
+ * Metadata about this OAuth client.
1864
+ */
1865
+ get clientMetadata() {
1866
+ return this._clientMetadata;
1867
+ }
1868
+ /**
1869
+ * Returns a OAuth2 state parameter.
1870
+ */
1871
+ async state() {
1872
+ return this.generateState();
1873
+ }
1874
+ /**
1875
+ * Loads information about this OAuth client.
1876
+ */
1877
+ async clientInformation() {
1878
+ if (this._clientInfo) {
1879
+ return this._clientInfo;
1880
+ }
1881
+ const stored = await this.storage.get("client_info");
1882
+ if (stored) {
1883
+ try {
1884
+ return JSON.parse(stored);
1885
+ } catch {
1886
+ }
1887
+ }
1888
+ return void 0;
1889
+ }
1890
+ /**
1891
+ * Saves dynamically registered client information.
1892
+ */
1893
+ async saveClientInformation(clientInformation) {
1894
+ this._clientInfo = clientInformation;
1895
+ await this.storage.set("client_info", JSON.stringify(clientInformation));
1896
+ }
1897
+ /**
1898
+ * Loads existing OAuth tokens.
1899
+ */
1900
+ async tokens() {
1901
+ const stored = await this.storage.get("tokens");
1902
+ if (stored) {
1903
+ try {
1904
+ return JSON.parse(stored);
1905
+ } catch {
1906
+ }
1907
+ }
1908
+ return void 0;
1909
+ }
1910
+ /**
1911
+ * Stores new OAuth tokens after successful authorization.
1912
+ */
1913
+ async saveTokens(tokens) {
1914
+ await this.storage.set("tokens", JSON.stringify(tokens));
1915
+ }
1916
+ /**
1917
+ * Invoked to redirect the user agent to the authorization URL.
1918
+ */
1919
+ async redirectToAuthorization(authorizationUrl) {
1920
+ if (this.onRedirect) {
1921
+ await this.onRedirect(authorizationUrl);
1922
+ } else {
1923
+ console.info(`Authorization required. Please visit: ${authorizationUrl.toString()}`);
1924
+ }
1925
+ }
1926
+ /**
1927
+ * Saves a PKCE code verifier before redirecting to authorization.
1928
+ */
1929
+ async saveCodeVerifier(codeVerifier) {
1930
+ await this.storage.set("code_verifier", codeVerifier);
1931
+ }
1932
+ /**
1933
+ * Loads the PKCE code verifier for validating authorization result.
1934
+ */
1935
+ async codeVerifier() {
1936
+ const verifier = await this.storage.get("code_verifier");
1937
+ if (!verifier) {
1938
+ throw new Error("No code verifier found. Authorization flow may not have started properly.");
1939
+ }
1940
+ return verifier;
1941
+ }
1942
+ /**
1943
+ * Optional: Custom client authentication for token requests.
1944
+ * Uses default behavior if not implemented.
1945
+ */
1946
+ async addClientAuthentication(_headers, _params, _url, _metadata) {
1947
+ }
1948
+ /**
1949
+ * Invalidate credentials when server indicates they're no longer valid.
1950
+ */
1951
+ async invalidateCredentials(scope) {
1952
+ switch (scope) {
1953
+ case "all":
1954
+ await this.storage.delete("tokens");
1955
+ await this.storage.delete("client_info");
1956
+ await this.storage.delete("code_verifier");
1957
+ this._clientInfo = void 0;
1958
+ break;
1959
+ case "client":
1960
+ await this.storage.delete("client_info");
1961
+ this._clientInfo = void 0;
1962
+ break;
1963
+ case "tokens":
1964
+ await this.storage.delete("tokens");
1965
+ break;
1966
+ case "verifier":
1967
+ await this.storage.delete("code_verifier");
1968
+ break;
1969
+ }
1970
+ }
1971
+ /**
1972
+ * Clear all stored OAuth data.
1973
+ * Useful for logging out or resetting state.
1974
+ */
1975
+ async clear() {
1976
+ await this.invalidateCredentials("all");
1977
+ }
1978
+ /**
1979
+ * Check if the provider has valid (non-expired) tokens.
1980
+ */
1981
+ async hasValidTokens() {
1982
+ const currentTokens = await this.tokens();
1983
+ if (!currentTokens) return false;
1984
+ if (!currentTokens.access_token) return false;
1985
+ return true;
1986
+ }
1987
+ };
1988
+ function createSimpleTokenProvider(accessToken, options) {
1989
+ const tokens = {
1990
+ access_token: accessToken,
1991
+ token_type: options.tokenType ?? "Bearer",
1992
+ refresh_token: options.refreshToken,
1993
+ expires_in: options.expiresIn,
1994
+ scope: options.scope
1995
+ };
1996
+ const storage = new InMemoryOAuthStorage();
1997
+ storage.set("tokens", JSON.stringify(tokens));
1998
+ if (options.clientInformation) {
1999
+ storage.set("client_info", JSON.stringify(options.clientInformation));
2000
+ }
2001
+ return new MCPOAuthClientProvider({
2002
+ redirectUrl: options.redirectUrl,
2003
+ clientMetadata: options.clientMetadata,
2004
+ clientInformation: options.clientInformation,
2005
+ storage
2006
+ });
2007
+ }
2008
+
1824
2009
  // ../../node_modules/.pnpm/hono@4.11.3/node_modules/hono/dist/utils/stream.js
1825
2010
  var StreamingApi = class {
1826
2011
  writer;
@@ -2524,6 +2709,12 @@ var MCPServer = class extends mcp.MCPServerBase {
2524
2709
  if (tool.outputSchema) {
2525
2710
  toolSpec.outputSchema = tool.outputSchema.jsonSchema;
2526
2711
  }
2712
+ if (tool.mcp?.annotations) {
2713
+ toolSpec.annotations = tool.mcp.annotations;
2714
+ }
2715
+ if (tool.mcp?._meta) {
2716
+ toolSpec._meta = tool.mcp._meta;
2717
+ }
2527
2718
  return toolSpec;
2528
2719
  })
2529
2720
  };
@@ -3895,9 +4086,273 @@ Provided arguments: ${JSON.stringify(args, null, 2)}`,
3895
4086
  }
3896
4087
  }
3897
4088
  };
4089
+ function escapeHeaderValue(value) {
4090
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
4091
+ }
4092
+ function generateWWWAuthenticateHeader(options = {}) {
4093
+ const params = [];
4094
+ if (options.resourceMetadataUrl) {
4095
+ params.push(`resource_metadata="${escapeHeaderValue(options.resourceMetadataUrl)}"`);
4096
+ }
4097
+ if (options.additionalParams) {
4098
+ for (const [key, value] of Object.entries(options.additionalParams)) {
4099
+ params.push(`${key}="${escapeHeaderValue(value)}"`);
4100
+ }
4101
+ }
4102
+ if (params.length === 0) {
4103
+ return "Bearer";
4104
+ }
4105
+ return `Bearer ${params.join(", ")}`;
4106
+ }
4107
+ function generateProtectedResourceMetadata(config) {
4108
+ return {
4109
+ resource: config.resource,
4110
+ authorization_servers: config.authorizationServers,
4111
+ scopes_supported: config.scopesSupported ?? ["mcp:read", "mcp:write"],
4112
+ bearer_methods_supported: ["header"],
4113
+ ...config.resourceName && { resource_name: config.resourceName },
4114
+ ...config.resourceDocumentation && {
4115
+ resource_documentation: config.resourceDocumentation
4116
+ }
4117
+ };
4118
+ }
4119
+ function extractBearerToken(authHeader) {
4120
+ if (!authHeader) return void 0;
4121
+ const prefix = "bearer ";
4122
+ if (authHeader.length <= prefix.length) return void 0;
4123
+ if (authHeader.slice(0, prefix.length).toLowerCase() !== prefix) return void 0;
4124
+ const token = authHeader.slice(prefix.length).trim();
4125
+ return token || void 0;
4126
+ }
4127
+
4128
+ // src/server/oauth-middleware.ts
4129
+ function createOAuthMiddleware(options) {
4130
+ const { oauth, mcpPath = "/mcp", logger } = options;
4131
+ const protectedResourceMetadata = generateProtectedResourceMetadata(oauth);
4132
+ const wellKnownPath = "/.well-known/oauth-protected-resource";
4133
+ const resourceMetadataUrl = new URL(wellKnownPath, oauth.resource).toString();
4134
+ return async function oauthMiddleware(req, res, url) {
4135
+ logger?.debug?.(`OAuth middleware: ${req.method} ${url.pathname}`);
4136
+ if (url.pathname === wellKnownPath && req.method === "GET") {
4137
+ logger?.debug?.("OAuth middleware: Serving Protected Resource Metadata");
4138
+ res.writeHead(200, {
4139
+ "Content-Type": "application/json",
4140
+ "Cache-Control": "max-age=3600",
4141
+ "Access-Control-Allow-Origin": "*"
4142
+ });
4143
+ res.end(JSON.stringify(protectedResourceMetadata));
4144
+ return { proceed: false, handled: true };
4145
+ }
4146
+ if (url.pathname === wellKnownPath && req.method === "OPTIONS") {
4147
+ res.writeHead(204, {
4148
+ "Access-Control-Allow-Origin": "*",
4149
+ "Access-Control-Allow-Methods": "GET, OPTIONS",
4150
+ "Access-Control-Allow-Headers": "Content-Type",
4151
+ "Access-Control-Max-Age": "86400"
4152
+ });
4153
+ res.end();
4154
+ return { proceed: false, handled: true };
4155
+ }
4156
+ if (!url.pathname.startsWith(mcpPath)) {
4157
+ return { proceed: true, handled: false };
4158
+ }
4159
+ const authHeader = req.headers["authorization"];
4160
+ const token = extractBearerToken(authHeader);
4161
+ if (!token) {
4162
+ logger?.debug?.("OAuth middleware: No bearer token provided");
4163
+ res.writeHead(401, {
4164
+ "Content-Type": "application/json",
4165
+ "WWW-Authenticate": generateWWWAuthenticateHeader({ resourceMetadataUrl })
4166
+ });
4167
+ res.end(
4168
+ JSON.stringify({
4169
+ error: "unauthorized",
4170
+ error_description: "Bearer token required"
4171
+ })
4172
+ );
4173
+ return { proceed: false, handled: true };
4174
+ }
4175
+ if (oauth.validateToken) {
4176
+ logger?.debug?.("OAuth middleware: Validating token");
4177
+ const validationResult = await oauth.validateToken(token, oauth.resource);
4178
+ if (!validationResult.valid) {
4179
+ logger?.debug?.(`OAuth middleware: Token validation failed: ${validationResult.error}`);
4180
+ res.writeHead(401, {
4181
+ "Content-Type": "application/json",
4182
+ "WWW-Authenticate": generateWWWAuthenticateHeader({
4183
+ resourceMetadataUrl,
4184
+ additionalParams: {
4185
+ error: validationResult.error || "invalid_token",
4186
+ ...validationResult.errorDescription && {
4187
+ error_description: validationResult.errorDescription
4188
+ }
4189
+ }
4190
+ })
4191
+ });
4192
+ res.end(
4193
+ JSON.stringify({
4194
+ error: validationResult.error || "invalid_token",
4195
+ error_description: validationResult.errorDescription || "Token validation failed"
4196
+ })
4197
+ );
4198
+ return { proceed: false, handled: true, tokenValidation: validationResult };
4199
+ }
4200
+ logger?.debug?.("OAuth middleware: Token validated successfully");
4201
+ return { proceed: true, handled: false, tokenValidation: validationResult };
4202
+ }
4203
+ logger?.debug?.("OAuth middleware: No token validation configured, accepting token");
4204
+ return {
4205
+ proceed: true,
4206
+ handled: false,
4207
+ tokenValidation: { valid: true }
4208
+ };
4209
+ };
4210
+ }
4211
+ function createStaticTokenValidator(validTokens) {
4212
+ const tokenSet = new Set(validTokens);
4213
+ return async (token) => {
4214
+ if (tokenSet.has(token)) {
4215
+ return { valid: true, scopes: ["mcp:read", "mcp:write"] };
4216
+ }
4217
+ return {
4218
+ valid: false,
4219
+ error: "invalid_token",
4220
+ errorDescription: "Token not recognized"
4221
+ };
4222
+ };
4223
+ }
4224
+ function createIntrospectionValidator(introspectionEndpoint, clientCredentials) {
4225
+ return async (token, resource) => {
4226
+ try {
4227
+ const headers = {
4228
+ "Content-Type": "application/x-www-form-urlencoded"
4229
+ };
4230
+ if (clientCredentials) {
4231
+ if (clientCredentials.clientId.includes(":")) {
4232
+ return {
4233
+ valid: false,
4234
+ error: "invalid_request",
4235
+ errorDescription: "clientId cannot contain a colon character per RFC 7617"
4236
+ };
4237
+ }
4238
+ const credentials = Buffer.from(`${clientCredentials.clientId}:${clientCredentials.clientSecret}`).toString(
4239
+ "base64"
4240
+ );
4241
+ headers["Authorization"] = `Basic ${credentials}`;
4242
+ }
4243
+ const response = await fetch(introspectionEndpoint, {
4244
+ method: "POST",
4245
+ headers,
4246
+ body: new URLSearchParams({
4247
+ token,
4248
+ token_type_hint: "access_token"
4249
+ })
4250
+ });
4251
+ if (!response.ok) {
4252
+ return {
4253
+ valid: false,
4254
+ error: "server_error",
4255
+ errorDescription: `Introspection failed: ${response.status}`
4256
+ };
4257
+ }
4258
+ const data = await response.json();
4259
+ if (!data.active) {
4260
+ return {
4261
+ valid: false,
4262
+ error: "invalid_token",
4263
+ errorDescription: "Token is not active"
4264
+ };
4265
+ }
4266
+ if (data.aud) {
4267
+ const audiences = Array.isArray(data.aud) ? data.aud : [data.aud];
4268
+ if (!audiences.includes(resource)) {
4269
+ return {
4270
+ valid: false,
4271
+ error: "invalid_token",
4272
+ errorDescription: "Token audience does not match this resource"
4273
+ };
4274
+ }
4275
+ }
4276
+ return {
4277
+ valid: true,
4278
+ scopes: data.scope?.trim().split(" ").filter((s) => s !== "") || [],
4279
+ subject: data.sub,
4280
+ expiresAt: data.exp,
4281
+ claims: data
4282
+ };
4283
+ } catch (error) {
4284
+ return {
4285
+ valid: false,
4286
+ error: "server_error",
4287
+ errorDescription: error instanceof Error ? error.message : "Introspection failed"
4288
+ };
4289
+ }
4290
+ };
4291
+ }
3898
4292
 
4293
+ Object.defineProperty(exports, "UnauthorizedError", {
4294
+ enumerable: true,
4295
+ get: function () { return auth_js.UnauthorizedError; }
4296
+ });
4297
+ Object.defineProperty(exports, "auth", {
4298
+ enumerable: true,
4299
+ get: function () { return auth_js.auth; }
4300
+ });
4301
+ Object.defineProperty(exports, "buildDiscoveryUrls", {
4302
+ enumerable: true,
4303
+ get: function () { return auth_js.buildDiscoveryUrls; }
4304
+ });
4305
+ Object.defineProperty(exports, "discoverAuthorizationServerMetadata", {
4306
+ enumerable: true,
4307
+ get: function () { return auth_js.discoverAuthorizationServerMetadata; }
4308
+ });
4309
+ Object.defineProperty(exports, "discoverOAuthMetadata", {
4310
+ enumerable: true,
4311
+ get: function () { return auth_js.discoverOAuthMetadata; }
4312
+ });
4313
+ Object.defineProperty(exports, "discoverOAuthProtectedResourceMetadata", {
4314
+ enumerable: true,
4315
+ get: function () { return auth_js.discoverOAuthProtectedResourceMetadata; }
4316
+ });
4317
+ Object.defineProperty(exports, "exchangeAuthorization", {
4318
+ enumerable: true,
4319
+ get: function () { return auth_js.exchangeAuthorization; }
4320
+ });
4321
+ Object.defineProperty(exports, "extractResourceMetadataUrl", {
4322
+ enumerable: true,
4323
+ get: function () { return auth_js.extractResourceMetadataUrl; }
4324
+ });
4325
+ Object.defineProperty(exports, "parseErrorResponse", {
4326
+ enumerable: true,
4327
+ get: function () { return auth_js.parseErrorResponse; }
4328
+ });
4329
+ Object.defineProperty(exports, "refreshAuthorization", {
4330
+ enumerable: true,
4331
+ get: function () { return auth_js.refreshAuthorization; }
4332
+ });
4333
+ Object.defineProperty(exports, "registerClient", {
4334
+ enumerable: true,
4335
+ get: function () { return auth_js.registerClient; }
4336
+ });
4337
+ Object.defineProperty(exports, "selectResourceURL", {
4338
+ enumerable: true,
4339
+ get: function () { return auth_js.selectResourceURL; }
4340
+ });
4341
+ Object.defineProperty(exports, "startAuthorization", {
4342
+ enumerable: true,
4343
+ get: function () { return auth_js.startAuthorization; }
4344
+ });
4345
+ exports.InMemoryOAuthStorage = InMemoryOAuthStorage;
3899
4346
  exports.InternalMastraMCPClient = InternalMastraMCPClient;
3900
4347
  exports.MCPClient = MCPClient;
4348
+ exports.MCPOAuthClientProvider = MCPOAuthClientProvider;
3901
4349
  exports.MCPServer = MCPServer;
4350
+ exports.createIntrospectionValidator = createIntrospectionValidator;
4351
+ exports.createOAuthMiddleware = createOAuthMiddleware;
4352
+ exports.createSimpleTokenProvider = createSimpleTokenProvider;
4353
+ exports.createStaticTokenValidator = createStaticTokenValidator;
4354
+ exports.extractBearerToken = extractBearerToken;
4355
+ exports.generateProtectedResourceMetadata = generateProtectedResourceMetadata;
4356
+ exports.generateWWWAuthenticateHeader = generateWWWAuthenticateHeader;
3902
4357
  //# sourceMappingURL=index.cjs.map
3903
4358
  //# sourceMappingURL=index.cjs.map