Skip to content

feat: OAuth support#330

Open
sazap10 wants to merge 16 commits intomainfrom
oauth_server_bugsnag
Open

feat: OAuth support#330
sazap10 wants to merge 16 commits intomainfrom
oauth_server_bugsnag

Conversation

@sazap10
Copy link
Contributor

@sazap10 sazap10 commented Feb 13, 2026

Goal

To introduce support for Dynamic Request-Level Authentication (OAuth) across the SmartBear MCP Server. Previously, the server required static, server-level credentials to be provided via environment variables at startup, making it unsuitable for multi-tenant environments or usage behind an API gateway where the active user context changes per request. This change allows the server to extract authentication tokens dynamically from incoming HTTP request headers and implements RFC 9293 OAuth discovery.

Design

To achieve request-level authentication without polluting the @modelcontextprotocol/sdk signatures, the solution leverages Node's AsyncLocalStorage to propagate the HTTP request headers deep into the tool execution layer.

  1. AsyncLocalStorage for Request Context: A new requestContextStorage wraps the execution of incoming MCP stream/HTTP requests. Tool implementations can call getRequestHeader() deep within their business logic to pull context tokens securely and dynamically.
  2. RFC 9293 OAuth Discovery Flow: The HTTP transport layer now exposes the /.well-known/oauth-protected-resource endpoint. When a client connects without credentials, the server responds with a 401 Unauthorized and includes a WWW-Authenticate header, pointing the client to the Authorization Server to begin the OAuth flow.
  3. Lazy API Client Initialization: Client ConfigurationSchema definitions (e.g., BugSnag, Qmetry, Zephyr, Swagger, etc.) have been updated to mark authentication fields as .optional(). The isConfigured() checks have been relaxed to allow the tools to successfully mount to the server at startup, deferring the credential requirement until the actual tool is invoked (at which point it checks the dynamic request context or falls back to the static config).

Changeset

  • Core/Transport:
    • Added src/common/request-context.ts implementing AsyncLocalStorage to store IncomingMessage headers.
    • Updated src/common/transport-http.ts to wrap handleRequest and handlePostMessage in requestContextStorage.run().
    • Added the /.well-known/oauth-protected-resource metadata endpoint.
    • Added the WWW-Authenticate header to 401 initialization failures.
    • Added the OAUTH_AUTHORIZATION_SERVER_URL config variable.
  • Client Implementations:
    • Updated BugsnagClient to use a dynamic apiKey resolver function instead of a hardcoded string.
    • Updated ReflectClient, ZephyrClient, SwaggerClient, PactflowClient, QmetryClient, and CollaboratorClient to extract tokens via getRequestHeader().
    • Modified ConfigurationSchema in all clients to mark api_key / token / username / login_ticket as .optional().
    • Updated isConfigured() logic across all clients to ensure they mount when static credentials are omitted.

Testing

  • Validated that npm run build, npm run format, npm run lint, and npm run test pass successfully.
  • Added specific unit tests verifying that API configurations fetch tokens dynamically via the function resolver.
  • Verified that tools successfully mount to the clientRegistry even when no authentication environment variables are supplied at startup.

How to test with local development

  • Run mcp server with:
PORT=4000 MCP_TRANSPORT=http MCP_CLIENTS=bugsnag ALLOWED_ORIGINS=http://localhost:6274,http://localhost:8080,http://localhost:8081 OAUTH_AUTHORIZATION_SERVER_URL=http://localhost:7070 node dist/index.js
  • Run oauth server
  • Run bugsnag with oauth support

@github-actions
Copy link
Contributor

github-actions bot commented Feb 13, 2026

🎯 Coverage Target Met!

📈 Coverage Metrics

Metric Coverage Target Status
Lines 93.3% 80%
Functions 86.1% 80%
Branches 85.8% 80%
Statements 93.3% 80%

📊 Test Statistics

  • Total Lines: 20,663 / 22,151
  • Total Functions: 328 / 381
  • Total Branches: 953 / 1111
  • Total Statements: 20663 / 22151

🔍 Files Needing Coverage

File Coverage
src/reflect/prompt/sap-test.ts 9.5%
src/qmetry/client/testsuite.ts 10.3%
src/common/prompts.ts 16.7%
src/qmetry/client/issues.ts 20.1%
src/swagger/client/api.ts 30.4%

📝 Report generated on Node.js v24.13.1 • View workflow • Coverage by Vitest + v8

@sazap10 sazap10 changed the title feat(bugsnag): automate oauth flow on startup if unauthenticated feat(bugsnag): OAuth support for bugsnag Mar 10, 2026
@sazap10 sazap10 changed the title feat(bugsnag): OAuth support for bugsnag feat: OAuth support Mar 24, 2026
@sazap10 sazap10 marked this pull request as ready for review March 25, 2026 11:50
@sazap10 sazap10 requested review from a team as code owners March 25, 2026 11:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant