.cursor/skills/nv-onboard-dcr-mcp/SKILL.md
Use this checklist when adding a new mode: dcr entry to packages/shared/src/consts/providers/mcp-servers.ts.
Default goal: upgrade or add catalog entries — not re-audit providers already on dcr.
Read mcp-servers.ts and blocked-mcp-servers.md, then classify each target:
Catalog oauth.mode | Blocked list | Action |
|---|---|---|
provider-managed (or missing entry) | not listed | Onboard — docs gate + probes + catalog/blocker edits |
provider-managed | open blocker | Skip — already triaged; only re-probe if user says unblocked |
dcr | — | Skip — do not run full onboarding or assign batch agents |
novu-app / user-app | — | Skip — out of scope for this skill unless user asks |
Do not label work as VERIFY_ONLY in a batch onboarding run. That outcome is for explicit single-provider regression requests only (e.g. “re-probe Sentry after an outage”).
provider-managed (or net-new) + user’s category filter (e.g. category: 'code') − open blockers − already dcr.dcr entries.# Example: list provider-managed code MCPs not on the blocked list (adjust paths)
rg "id: '" packages/shared/src/consts/providers/mcp-servers.ts -A6 | rg -B5 "ProviderManaged" | rg "category: 'code'"
rg "^\| \`" .cursor/skills/nv-onboard-dcr-mcp/blocked-mcp-servers.md
dcr entry is allowedOnly if the user explicitly requests verification or there is a production incident for that id. Then: probes + short report; no catalog mode change unless probes fail (downgrade to provider-managed + blocker row).
Before curl probes or catalog edits:
Abort onboarding and report back to the user if the provider requires any of:
client_id supplied by the provider) instead of RFC 7591 DCRInclude the doc link and the specific requirement that blocked onboarding. Do not add a catalog entry or run live probes for providers that fail this gate. Append the provider to blocked-mcp-servers.md (Open blockers table).
| Situation | Action |
|---|---|
| PRM + AS metadata + DCR + token exchange all follow RFC behavior | Catalog entry only |
AS downgrades auth method at DCR (e.g. returns none) | Generic flow already handles RFC 7591 §3.2.1 |
| Issuer / PRM / gateway mismatch (Clerk, Vercel, PlanetScale-style) | Extend mcp-oauth-issuer-match.ts (review-gated) |
| Token endpoint returns non-standard JSON | Extend mcp-oauth-callback/token-exchange-outcome.ts (review-gated) |
PRM advertises oversized scopes_supported (Slack URL limits) | Pin oauth.scopes on the catalog dcr entry |
Default rule: do not touch generate-mcp-oauth-url.usecase.ts or mcp-oauth-callback.usecase.ts for a new provider unless discovery or token parsing truly cannot express the quirk generically.
Replace placeholders before running. Paste command output into the PR body.
MCP_URL="https://mcp.example.com/mcp"
curl -sS "$MCP_URL/.well-known/oauth-protected-resource" | jq .
# Also try path-suffixed well-known if the MCP URL has a path:
curl -sS "$(python3 - <<'PY'
from urllib.parse import urlparse
u = urlparse("https://mcp.example.com/mcp")
print(f"{u.scheme}://{u.netloc}/.well-known/oauth-protected-resource{u.path.rstrip('/')}")
PY
)" | jq .
Confirm: authorization_servers is non-empty, scopes_supported or challenge scope is documented.
ISSUER="https://auth.example.com"
curl -sS "$ISSUER/.well-known/oauth-authorization-server" | jq .
Confirm:
authorization_endpoint, token_endpoint, registration_endpoint presentcode_challenge_methods_supported includes S256token_endpoint_auth_methods_supported documentedREGISTER_URL="https://auth.example.com/register"
REDIRECT_URI="https://api.novu.co/v1/agents/mcp/oauth/callback"
curl -sS -X POST "$REGISTER_URL" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-d "{
\"redirect_uris\": [\"$REDIRECT_URI\"],
\"client_name\": \"Novu probe\",
\"application_type\": \"web\",
\"grant_types\": [\"authorization_code\", \"refresh_token\"],
\"response_types\": [\"code\"],
\"token_endpoint_auth_method\": \"client_secret_post\"
}" | jq .
Confirm:
client_idtoken_endpoint_auth_method if it differs from the requestRecord which scopes the PRM advertises and which scope string Novu should request on authorize. If PRM omits scopes, note that in the PR. If the list is huge, pin a curated subset on oauth.scopes in the catalog entry.
## DCR onboarding evidence
- MCP id:
- MCP URL:
- Issuer:
### PRM
(paste curl output)
### AS metadata
(paste curl output)
### DCR register probe
(paste curl output; redact client_secret if returned)
### Scope notes
(which scopes PRM advertises / which Novu will request)
### Code changes beyond catalog?
- [ ] No — catalog entry only
- [ ] Yes — discovery issuer matching because ...
- [ ] Yes — token-exchange-outcome because ...
oauth.mode: dcr (see Candidate selection)packages/shared/src/consts/providers/mcp-servers.tspnpm build --filter @novu/shared if shared types changedtoken-exchange-outcome.ts changes (review-gated)packages/shared/src/consts/providers/mcp-servers.spec.ts DCR schema test passesblocked-mcp-servers.md; if unblocked later, move the row to Resolved