Sponsor MCPSafe10 founding slots
Registry/Sso MCP Server

Sso MCP Server

Verified

OAuth 2.1 and enterprise SSO for remote MCP servers without API keys in client configs.

MIT
go
automation
9stars
19.3Kdownloads
Scanned Oct 2, 2026
RepositoryGITHUB
Automated scan (limited checks) · 2026-10-02

This grade comes from an automated MCPSafe scan of the server's JavaScript, TypeScript and Python source on 2026-10-02. It read the whole repository (144 files). It checks for leaked credentials, shell or code injection from function inputs, unsafe deserialization, malware signatures, install-time scripts, and weak password handling. It does not check for path traversal, server-side request forgery, prompt injection in tool descriptions, network exposure, SQL injection, cross-site scripting, or vulnerable dependencies. It has not been manually reviewed, and the code may have changed since the scan.

A+100%
Scanned 10/2/2026

Excellent Security

Vulnerabilities Found
0Crit
0High
0Med
0Low
0Info

Trust Score 70

Review required
Trust grade B

Trust is stricter than security: it combines reviewed security, MCP capability risk, supply-chain metadata, quality, popularity, freshness, and penalties for unknowns.

Security
100
MCP risk
48
Supply chain
92
Maintenance
50
Docs
100
Freshness
55
Risk traits
Runs shell commands
Network/API access
Requires secrets
Database access
Remote transport
Why it scores well
  • • No reviewed severe vulnerabilities published
  • • Good documentation surface
  • • Solid supply-chain metadata
What reduces confidence
  • • Stale or missing recent scan
  • • MCP risk traits: Runs shell commands, Network/API access, Requires secrets

Security ScoreA+

100
out of 100

Vulnerabilities Found

0
Crit
0
High
0
Medi
0
Low
0
Info
Last scanned: 10/2/2026

Quality ScoreC

68
out of 100
50
Maint.
44
Popular
100
Docs
78
Compat
Maintenance50%
Popularity44%
Documentation100%
Compatibility78%

Maintenance50

Unknown
Commit Frequency65%
Release Frequency35%
Issue Response45%

Popularity44

9
Stars
19.3K
Downloads
0
Forks
Stars Score27%
Downloads Score77%
Forks Score0%

Documentation100

README Quality100%

Available Documentation

API DocsExamplesChangelog

Compatibility78

MCP Spec Compliance80%
Transport Support75%

Features

TypeScript

Supported Transports

STDIO

Vulnerabilities(0)

This grade comes from an automated scan. Individual findings from automated scans are not published.

READMESso MCP Server

View source

mcp-sso

OAuth 2.1 for your MCP server, so nobody pastes a shared API key into a client config.

![npm](https://www.npmjs.com/package/mcp-sso) ![CI](https://github.com/acartag7/mcp-sso/actions/workflows/ci.yml) ![OpenSSF Scorecard](https://scorecard.dev/viewer/?uri=github.com/acartag7/mcp-sso) ![OpenSSF Best Practices](https://www.bestpractices.dev/projects/13556) ![license](LICENSE) ![node](package.json) ![runtime deps-blue)](docs/dependency-ledger.md)

Get started · Documentation · Threat model · Conformance matrix · Verification status

The problem

A remote MCP server needs to know who is calling. The usual answer is a static API key pasted into every client configuration. One key gives every caller the same identity, never expires, and leaks in a git add . or a support screenshot. Revoking it means rotating it for everyone.

MCP's answer is OAuth 2.1. That leaves a gap. An MCP client must identify itself to an authorization server and obtain a token that is valid for exactly your server. Identity providers such as Microsoft Entra ID and Cloudflare Access prove who the user is, but they do not speak the MCP client-facing flow: client registration, PKCE, consent, and audience-bound tokens.

mcp-sso fills that gap. It is a TypeScript library in two halves:

  • The authorization-server bridge accepts the client's identity (a Client ID Metadata Document, or POST /oauth/register), sends the user to your identity provider, shows a consent page, and mints its own access and refresh tokens for one MCP resource.
  • The resource-server verifier checks the token's signature, audience, and scope on every /mcp request, and answers with the RFC 9728 WWW-Authenticate challenge when it rejects one.

Your identity provider's tokens stay between the provider and mcp-sso. The MCP client only ever holds a token that mcp-sso minted for your server.

Install

bash
npm install mcp-sso

Node 24 or later. jose is the only runtime dependency. Fastify, Express, Hono, mysql2, ioredis, and @fastify/rate-limit are optional peer dependencies: install the ones you use.

Try it on one machine

This runs a local server that uses console pairing as the identity check. It listens on loopback and refuses a non-loopback host, issuer, or resource before it writes any state.

bash
npx mcp-sso init my-mcp-server
cd my-mcp-server
npm install
npm start

In another terminal, add it to an MCP client. With Claude Code:

bash
claude mcp add --transport http my-bridge http://127.0.0.1:3000/mcp

The server prints a one-time code. Paste it into the browser, approve, and call a tool.

[!WARNING]
Anyone who can read that code becomes the console-operator identity. Keep the server on loopback and its console private. This is a tutorial, not a production template. For real users, follow the identity-provider tutorial.

What the flow looks like

Assume Alice connects an MCP client to https://mcp.example/mcp.

  1. The client identifies itself with a Client ID Metadata Document (CIMD) or by calling POST /oauth/register.
  2. mcp-sso sends Alice to your identity provider. The provider proves who she is.
  3. Alice sees the client, the resource, and the requested scopes, and approves.
  4. The client exchanges the authorization code and its PKCE verifier for tokens. The access token is valid for https://mcp.example/mcp and nothing else.
  5. Your MCP server verifies the token's audience and required scope on every request. Refresh-token rotation detects a replayed refresh token and revokes the whole token family.
mermaid
sequenceDiagram
    participant C as MCP client
    participant B as mcp-sso
    participant I as Identity provider
    participant S as MCP server
    C->>B: CIMD identity or POST /oauth/register
    C->>B: Authorization request with PKCE
    B->>I: Sign Alice in
    I-->>B: Verified identity
    B-->>C: Consent, then authorization code
    C->>B: Authorization code and PKCE verifier
    B-->>C: Tokens for mcp.example/mcp
    C->>S: MCP request with access token
    S->>S: Verify audience and required scope

How the OAuth roles fit together explains each step and where a request can stop.

Protect a route

The bridge issues tokens. The verifier is what your /mcp handler calls. This fragment shows the shape; the complete, tested composition is `examples/fastify-sqlite/`.

ts
import { RequestAuthorizer, authorizationOccurrences, buildUnauthorizedChallenge, OAuthError, SystemClock, noopAudit } from "mcp-sso";

// `config` is the BridgeConfig you built with createBridgeConfig().
const authorizer = new RequestAuthorizer({ config, clock: new SystemClock(), audit: noopAudit });

app.post("/mcp", async (request, reply) => {
  let auth;
  try {
    // authorizationOccurrences passes the raw Authorization occurrence array into
    // RequestAuthorizer (§8.4): a framework-normalized scalar would let a duplicated
    // Authorization header silently select one of the two credentials.
    auth = await authorizer.authorize({
      authorization: authorizationOccurrences(request.raw.headersDistinct, request.headers),
      requiredScope: "mcp:read",
    });
  } catch (error) {
    const oe = error instanceof OAuthError ? error : new OAuthError("invalid_token", "Bearer token is invalid", 401);
    reply.header("www-authenticate", buildUnauthorizedChallenge(config, { scope: config.scopeCatalog, error: oe.code, errorDescription: oe.message }));
    return reply.code(oe.status).send({ jsonrpc: "2.0", error: { code: -32001, message: `${oe.code}: ${oe.message}` }, id: null });
  }
  // auth.subject, auth.scopes, and auth.credentialKind ("interactive" or "machine") are verified here.
});

RequestAuthorizer.authorize fails closed. A missing token, a wrong audience, an expired token, or a missing scope throws an OAuthError whose status is 401 or 403. There is no local or unauthenticated mode. Both the tested example and the fragment above read the header through authorizationOccurrences(request.raw.headersDistinct, ...), which passes the raw Authorization occurrence array into RequestAuthorizer so a duplicated header is rejected instead of silently collapsed. Never pass request.headers.authorization directly: the framework-normalized scalar has already discarded every occurrence but one.

What ships

| Area | Current source |

| --- | --- |

| Identity options | Cloudflare Access, Microsoft Entra ID (redirect flow plus group-to-scope ceilings), Google, generic OIDC, console pairing for local use, and claims-only website login |

| Client registration | CIMD, and POST /oauth/register in stateless or stored mode |

| Frameworks | Fastify, Express, Hono. OAuth decisions stay in the framework-free core |

| Stores | node:sqlite (one host), MySQL (shared replicas), memory (one process). One shared conformance suite, also published as mcp-sso/testing/* |

| Grants | Authorization code with PKCE S256, refresh-token rotation with family revocation, client_credentials for machine clients |

| Operations | JSONL and webhook audit sinks, Redis or Valkey rate limiting, a Fastify /mcp rate-limit helper |

| Runtime dependency | jose, and nothing else |

Not shipped: a GitHub identity port and the device authorization grant are contract-only. Neither is a release claim. The capability reference has the exact list and the deployment limits, including that one bridge protects one resource.

Why you can trust it

Security products earn trust with artifacts, not adjectives. These are the ones this project publishes.

  • A STRIDE threat model with 49 attacker-driven rows, the control for each, and the residual risk that remains.
  • A conformance matrix against MCP Authorization 2026-07-28 and CIMD draft -00, row by row, with the two deviations recorded rather than hidden.
  • A release verification matrix that binds every shipped feature to a test row, plus packed-artifact checks, plus a dated live client matrix against real identity providers and real MCP clients.
  • A dependency ledger: jose is the only runtime dependency, every pin is recorded with its publish date, and ordinary updates wait 15 days. npm publishes run only from GitHub Actions with OIDC provenance, never from a laptop.
  • Fail-closed defaults. Ambiguous configuration, a missing identity, an unknown audience, or a replayed refresh token is a hard failure, never a degraded default. Authorization codes and refresh tokens are stored as hashes and are single-use. A stolen access token is a bearer token and stays valid until exp, which is why it is short-lived (threat model row 1).

When to use something else

Use mcp-sso when your identity provider authenticates users but does not expose MCP-compatible OAuth endpoints.

| Project | Choose it if |

| --- | --- |

| mcp-sso (this repo) | Your identity provider is not an MCP authorization server: Entra ID, Cloudflare Access, most enterprise SSO. |

| `mcp-auth` | Your identity provider already exposes compatible OAuth 2.1 client registration and you only need resource-server wiring (compatibility list). |

| `mcp-oauth-server` | You need the device authorization grant today. mcp-sso ships client_credentials, not device flow. |

| `workers-oauth-provider` | Your MCP server is a Cloudflare Worker. |

Documentation

The documentation index routes by what you are trying to do. The short version:

License

MIT

Embed Security Badge

Add this badge to your README or documentation

Sso MCP Server MCPSafe Security
[![Sso MCP Server MCPSafe Security](https://api.mcpsafe.org/api/badge/acartag7-mcp-sso.svg)](https://mcpsafe.org/registry/acartag7-mcp-sso)
Need more customization options?Badge Documentation

Server Information

Source
GITHUB
Language
go
License
MIT
Transport
STDIO
Added
Aug 2, 2026
Updated
Oct 1, 2026