feat(shared): add 12 net-new DCR MCP catalog entries fixes NV-7959#11437
Conversation
Context7 PRM lists context7.com while AS metadata declares clerk.context7.com; allow that pattern when OAuth endpoints stay on the product host, and surface upstream DCR error_description for clearer failures. Co-authored-by: Cursor <cursoragent@cursor.com>
…method Added support for the `token_endpoint_auth_method` in the MCP OAuth discovery service. This includes parsing the method from the response and ensuring it is correctly returned in the registration response. Updated tests to verify the behavior when the authorization server downgrades to a public client. Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Removed outdated comments in the MCP OAuth controller and updated the discovery service to better handle authorization server metadata. Enhanced the token exchange process to interpret error outcomes more effectively, ensuring clearer logging and error messages. Updated tests to validate new behaviors and ensure compliance with the latest OAuth standards. Co-authored-by: Cursor Agent <cursoragent@cursor.com>
…entation vetting Updated the onboarding process for DCR OAuth MCP catalog entries to include a documentation vetting step. Added criteria for aborting onboarding if providers require whitelist or manual approval. Adjusted descriptions and updated the MCP_SERVERS catalog to reflect changes in provider management for 'monday.com' and 'monday-com'. Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Updated the MCP OAuth discovery service to support the well-known gateway pattern for PlanetScale and Monte Carlo, improving issuer validation. Enhanced onboarding documentation to include steps for appending blocked providers to the `blocked-mcp-servers.md` file. Adjusted MCP server configurations to reflect a shift from managed to DCR authentication mode for several providers. Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Updated the GenerateMcpOAuthUrl use case to improve session rotation logic based on catalog scopes. Introduced new functions to resolve DCR authorize scopes and check scope matches, ensuring better handling of OAuth sessions. Modified the MCP_SERVERS configuration for PostHog to include curated OAuth scopes, optimizing authorize URL requests within Slack button limits.
Added new MCP server entries for GitLab, Grafana Cloud, New Relic, Buildkite, Axiom, Better Stack, LaunchDarkly, Pulumi, Railway, Replicate, and Semgrep, each supporting DCR OAuth mode. Enhanced the MCP OAuth discovery service to accept sibling-subdomain patterns for New Relic, improving issuer validation. Updated the GenerateMcpOAuthUrl use case to construct authorization URLs more effectively.
…mentation Added new entries for Fly.io, Pylon, Omni Analytics, and Sourcegraph to the blocked MCP servers list, detailing their OAuth capabilities and reasons for blocking. Enhanced the onboarding documentation to clarify candidate selection criteria and re-probing conditions for DCR entries. Updated MCP server configurations to reflect a shift from managed to DCR authentication mode for Omni Analytics and Pylon.
…nd enhance descriptions Modified several MCP server entries to transition from provider-managed to DCR authentication mode, including AirOps, Apollo.io, Bitly, CB Insights, Contentsquare, Day AI, and Scite. Updated descriptions for clarity on each service's capabilities and use cases.
Move GitLab, Grafana, New Relic, Buildkite, Axiom, Better Stack, LaunchDarkly, Pulumi, Railway, Replicate, Semgrep, and Sourcegraph to a stacked catalog-only PR for easier review. fixes NV-7959 Co-authored-by: Cursor <cursoragent@cursor.com>
Onboard Axiom, Better Stack, Buildkite, GitLab, Grafana Cloud, LaunchDarkly, New Relic, Pulumi, Railway, Replicate, Semgrep, and Sourcegraph after live PRM/DCR probes. Stacked on fix/mcp-oauth-dcr-polish for OAuth discovery polish. fixes NV-7959 Co-authored-by: Cursor <cursoragent@cursor.com>
📝 WalkthroughWalkthroughThis PR adds test coverage for MCP OAuth discovery and issuer-matching validation across delegated, parent-domain, and gateway issuer patterns, validates endpoint-domain binding constraints, and extends the MCP server catalog with 12 new DCR-enabled entries (GitLab, Grafana Cloud, New Relic, Buildkite, Axiom, Better Stack, LaunchDarkly, Pulumi, Railway, Replicate, Semgrep, and Sourcegraph). ChangesOAuth Discovery and Validation Test Coverage
MCP Catalog Extensions
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Take next's issuer-match and token-exchange-outcome refactor while keeping the 12 net-new DCR catalog entries from this branch. Co-authored-by: Cursor <cursoragent@cursor.com>
✅ Deploy Preview for dashboard-v2-novu-staging canceled.
|
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
apps/api/src/app/agents/mcp/oauth/dcr-provider-strategies/__fixtures__/_shared/replay.ts (1)
44-61: ⚡ Quick winUse
interfacefor backend request/response contracts.
SafeJsonRequestArgs,SafeJsonResponse, andSafeRawResponseshould beinterfacedeclarations in backend TS files per repository rule.As per coding guidelines: "
**/*.{ts,tsx}: On the backend: useinterfacefor type definitions; on the frontend: usetypefor type definitions".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/api/src/app/agents/mcp/oauth/dcr-provider-strategies/__fixtures__/_shared/replay.ts` around lines 44 - 61, Replace the three exported type aliases with exported interface declarations: change SafeJsonRequestArgs, SafeJsonResponse<T>, and SafeRawResponse from "export type" to "export interface" while preserving their property names, optional modifiers (e.g., method?), generic parameter on SafeJsonResponse<T>, and the exact property types (headers as Record<string, string | string[] | undefined>, body Buffer for SafeRawResponse, etc.); keep the exports and shapes identical so consumers are unaffected.apps/api/src/app/agents/mcp/oauth/dcr-provider-strategies/__fixtures__/_shared/types.ts (1)
1-14: ⚡ Quick winUse
interfacefor backend type definitions.These exported backend contracts are declared as
type; convert them tointerfaceto match project conventions.As per coding guidelines: "
**/*.{ts,tsx}: On the backend: useinterfacefor type definitions; on the frontend: usetypefor type definitions".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/api/src/app/agents/mcp/oauth/dcr-provider-strategies/__fixtures__/_shared/types.ts` around lines 1 - 14, Change the backend type aliases to interfaces: replace the exported type declarations DcrFixtureManifest and DcrFixtureSet with exported interface declarations that preserve the exact shape (fields mcpUrl, issuer, optional registrationEndpoint, tokenEndpoint for DcrFixtureManifest; manifest, prm, asMetadata, optional dcrRegisterResponse, tokenExchangeResponse for DcrFixtureSet). Ensure the export names remain identical and keep the same property types (string, optional string, and Record<string, unknown>) so callers of DcrFixtureManifest and DcrFixtureSet continue to work.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@apps/api/src/app/agents/mcp/oauth/dcr-provider-strategies/__fixtures__/_shared/replay.ts`:
- Around line 10-15: The helper readOptionalJsonFile currently swallows all
errors; change it to only return undefined for a missing-file error (errno/code
'ENOENT') and rethrow any other exceptions so parse/permission errors surface;
locate readOptionalJsonFile and its call to readJsonFile<T>(filePath) and wrap
the readJsonFile call in a try/catch that checks the error code (or errno) and
returns undefined only when it's ENOENT, otherwise rethrow the caught error.
- Around line 71-77: The urlMatches function is too permissive because the
reverse-prefix check candidate.startsWith(`${url}/`) causes broad issuer URLs to
match narrower endpoints; fix urlMatches by removing that reverse-prefix
condition and normalizing trailing slashes before comparison: trim any trailing
'/' from both url and candidate, then return true only when they are exactly
equal or when url is a longer path that starts with candidate + '/' (i.e., url
=== candidate || url.startsWith(candidate + '/')). Ensure this change is applied
inside the urlMatches function.
In
`@apps/api/src/app/agents/mcp/oauth/dcr-provider-strategies/resolve-default-dcr-token-exchange-outcome.ts`:
- Line 1: The default resolver resolveDefaultDcrTokenExchangeOutcome is applying
provider-specific remaps via mapTokenExchangeErrorCode without any
mcpId/provider context, causing GitHub-only codes (e.g.,
application_suspended/app_blocked → mcp_github_org_block) to leak to other
providers; change it so the default resolver is provider-agnostic by either
returning a generic fallback like mcp_token_exchange_failed (or another neutral
code) from resolveDefaultDcrTokenExchangeOutcome, or update the call sites to
pass mcpId/provider and move the GitHub-specific remapping into the GitHub DCR
strategy (not in resolveDefaultDcrTokenExchangeOutcome) so
mapTokenExchangeErrorCode is only used with provider-aware logic in the per-mcp
strategy.
In `@apps/api/src/app/agents/mcp/oauth/mcp-oauth-discovery.service.spec.ts`:
- Around line 270-347: Add negative tests for the delegated-issuer,
parent-domain, and sibling-subdomain relaxed-match patterns mirroring the
existing "MCP well-known gateway" off-domain rejection test: for each pattern
(delegated-issuer / Clerk, parent-domain / Vercel, sibling-subdomain / New
Relic) stub safeJsonStub to return metadata whose issuer is on a different
domain than the requested MCP host and assert that
service.discoverAuthorizationServer rejects or throws (i.e., does not accept the
off-domain issuer) — follow the same structure and assertions used by the
existing gateway negative test but applied to the test cases named
"delegated-issuer", "parent-domain", and "sibling-subdomain" to ensure
domain-boundary validation cannot regress.
---
Nitpick comments:
In
`@apps/api/src/app/agents/mcp/oauth/dcr-provider-strategies/__fixtures__/_shared/replay.ts`:
- Around line 44-61: Replace the three exported type aliases with exported
interface declarations: change SafeJsonRequestArgs, SafeJsonResponse<T>, and
SafeRawResponse from "export type" to "export interface" while preserving their
property names, optional modifiers (e.g., method?), generic parameter on
SafeJsonResponse<T>, and the exact property types (headers as Record<string,
string | string[] | undefined>, body Buffer for SafeRawResponse, etc.); keep the
exports and shapes identical so consumers are unaffected.
In
`@apps/api/src/app/agents/mcp/oauth/dcr-provider-strategies/__fixtures__/_shared/types.ts`:
- Around line 1-14: Change the backend type aliases to interfaces: replace the
exported type declarations DcrFixtureManifest and DcrFixtureSet with exported
interface declarations that preserve the exact shape (fields mcpUrl, issuer,
optional registrationEndpoint, tokenEndpoint for DcrFixtureManifest; manifest,
prm, asMetadata, optional dcrRegisterResponse, tokenExchangeResponse for
DcrFixtureSet). Ensure the export names remain identical and keep the same
property types (string, optional string, and Record<string, unknown>) so callers
of DcrFixtureManifest and DcrFixtureSet continue to work.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 0842eac4-3407-4b4f-ba7d-1613f3e02ae9
📒 Files selected for processing (14)
apps/api/src/app/agents/mcp/oauth/dcr-provider-strategies/__fixtures__/_shared/replay.spec.tsapps/api/src/app/agents/mcp/oauth/dcr-provider-strategies/__fixtures__/_shared/replay.tsapps/api/src/app/agents/mcp/oauth/dcr-provider-strategies/__fixtures__/_shared/types.tsapps/api/src/app/agents/mcp/oauth/dcr-provider-strategies/__fixtures__/example/as-metadata.jsonapps/api/src/app/agents/mcp/oauth/dcr-provider-strategies/__fixtures__/example/dcr-register-response.jsonapps/api/src/app/agents/mcp/oauth/dcr-provider-strategies/__fixtures__/example/manifest.jsonapps/api/src/app/agents/mcp/oauth/dcr-provider-strategies/__fixtures__/example/prm.jsonapps/api/src/app/agents/mcp/oauth/dcr-provider-strategies/__fixtures__/example/token-exchange-response.jsonapps/api/src/app/agents/mcp/oauth/dcr-provider-strategies/dcr-provider-strategy-registry.tsapps/api/src/app/agents/mcp/oauth/dcr-provider-strategies/dcr-provider-strategy.spec.tsapps/api/src/app/agents/mcp/oauth/dcr-provider-strategies/dcr-provider-strategy.tsapps/api/src/app/agents/mcp/oauth/dcr-provider-strategies/resolve-default-dcr-token-exchange-outcome.tsapps/api/src/app/agents/mcp/oauth/mcp-oauth-discovery.service.spec.tspackages/shared/src/consts/providers/mcp-servers.ts
Align with NV-7959 (#11435): issuer matching and token exchange now live in mcp-oauth-issuer-match and token-exchange-outcome. Co-authored-by: Cursor <cursoragent@cursor.com>
Cover delegated-issuer, parent-domain, and sibling-subdomain patterns in mcp-oauth-issuer-match.spec.ts per security review feedback. Co-authored-by: Cursor <cursoragent@cursor.com>
New DCR MCP servers (12)
Stacked on #11435 (
fix/mcp-oauth-dcr-polish) — catalog-only follow-up after OAuth discovery polish lands.axiombetter-stackbuildkitegitlabgrafanalaunchdarklynew-relicpulumirailwayreplicatesemgrepsourcegraphSummary
oauth.mode: dcr, each vetted via live PRM/DCR probes (see.cursor/skills/onboard-dcr-mcp/).Linear: NV-7959
Test plan
pnpm --filter @novu/shared buildMade with Cursor
What changed
This PR adds 12 new MCP catalog entries with Dynamic Client Registration (DCR) authentication support. The entries are for Axiom, Better Stack, Buildkite, GitLab, Grafana Cloud, LaunchDarkly, New Relic, Pulumi, Railway, Replicate, Semgrep, and Sourcegraph. Each entry includes the provider's name, description, URL, and DCR oauth configuration. The changes build on PR
#11435, which introduced issuer-relaxation and DCR flow improvements required for these providers.Affected areas
shared: The
mcp-servers.tscatalog was extended with 12 newMcpServerentries configured for DCR authentication, adding them to both popular and "all others" sections.api: Two test suites were enhanced. The
mcp-oauth-discovery.service.spec.tsnow includes tests validating issuer pattern scenarios (delegated-issuer, parent-domain, sibling-subdomain, MCP gateway patterns), plus a test confirming issuer normalization with trailing slashes. Themcp-oauth-issuer-match.spec.tsadds negative test cases verifying that issuer-relaxation patterns are rejected when the OAuth token endpoint moves to an unauthorized domain.Testing
New unit tests were added to validate issuer-discovery edge cases: delegated issuer on product origin, parent-domain issuers, sibling-subdomain acceptance, MCP gateway patterns, and trailing-slash normalization. Tests also verify that invalid endpoint-to-domain binding is rejected. Manual verification is planned for staging to confirm OAuth connect works for GitLab, New Relic, and one additional provider after PR
#11435is deployed, and to verify all 12 entries appear in the agent MCP picker with correct DCR wiring.Greptile Summary
This PR adds 12 net-new MCP catalog entries with
oauth.mode: dcr(Dynamic Client Registration) tomcp-servers.ts, covering providers like GitLab, Grafana Cloud, New Relic, LaunchDarkly, Axiom, and others. Accompanying tests extend the OAuth discovery and issuer-match specs with acceptance and rejection scenarios for delegated-issuer, parent-domain, sibling-subdomain, and MCP gateway patterns.mcp-servers.ts): 12 newMcpServerentries spread across the popular and all-others sections, each with a vetted DCR OAuth config.mcp-oauth-discovery.service.spec.ts): New positive tests validate the service processes Clerk delegated-issuer, Vercel parent-domain, PlanetScale MCP gateway, New Relic sibling-subdomain, and Monte Carlo trailing-slash patterns; one new rejection test verifies the service throwsmcp_no_as_metadatawhen an OAuth endpoint moves to an unauthorized domain.mcp-oauth-issuer-match.spec.ts): Three new negative cases confirmisAcceptableIssuerMatchrejects each pattern variant when the token endpoint is swapped toevil.example.Confidence Score: 5/5
Safe to merge; purely additive catalog entries and tests with no changes to runtime logic.
All 12 catalog additions follow the established McpServer schema, and the PR description notes each endpoint was vetted via live PRM/DCR probes. The new tests correctly exercise both acceptance and rejection paths for each issuer-relaxation pattern, and the negative cases in both spec files explicitly guard against adversarial token-endpoint substitution.
No files require special attention.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[User selects DCR MCP provider] --> B[Load catalog entry from mcp-servers.ts] B --> C[discoverAuthorizationServer\ncatalog URL] C --> D{PRM probe\n/.well-known/oauth-protected-resource} D -->|200 with authorization_servers| E[Fetch AS metadata] D -->|404 / no PRM| F[Try direct AS discovery\n/.well-known/oauth-authorization-server] E --> G[isAcceptableIssuerMatch\ndiscovery URL x issuer x endpoints] F --> G G -->|exact match| H[Proceed to DCR registration] G -->|relaxed match\nparent-domain / sibling / delegated| I{All OAuth endpoints\non trusted domain?} G -->|no match| J[throw mcp_no_as_metadata] I -->|yes| H I -->|evil.example in token endpoint| J H --> K[POST registration_endpoint\nDynamic Client Registration] K --> L[Store client_id + client_secret] L --> M[OAuth PKCE Authorization Code Flow]Reviews (3): Last reviewed commit: "test(api): add off-domain rejection case..." | Re-trigger Greptile