mcp-svid-notes
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-svid-notessearch my notes for the SPIFFE demo"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-svid-auth
Proof of concept. An AI agent authenticates to MCP servers with its SPIFFE workload identity instead of a static API key.
Documentation: https://tanya-ok.github.io/mcp-svid-auth/
Problem
MCP client configs often hold long-lived static keys:
{ "env": { "NOTES_API_KEY": "sk-live-..." } }Issue with static keys | Effect |
Long-lived | A leaked key works until someone rotates it by hand |
Not bound to a workload | Any process that reads the file can use it |
Not bound to a server | One key often opens several servers |
No per-caller identity | Audit logs cannot say which agent acted |
Related MCP server: MCP Credentials Broker
What this POC shows
Claim | Where |
An agent gets a short-lived access token using only its JWT-SVID |
|
Tokens are bound to one MCP server via the RFC 8707 |
|
A token issued for server A is rejected by server B |
|
Scopes are enforced per tool, with a 403 |
|
Every decision is written as one JSON audit line with the SPIFFE ID |
|
The MCP server never forwards the incoming token |
|
A stdio MCP server can start with a refreshed short-lived token instead of a static key |
|
Targets the MCP authorization spec, revision 2026-07-28 (the current revision as of 2026-09-26): Protected Resource Metadata (RFC 9728), resource indicators (RFC 8707), audience validation, no token passthrough.
Client authentication follows draft-ietf-oauth-spiffe-client-auth-02: assertion type urn:ietf:params:oauth:client-assertion-type:jwt-spiffe, advertised as spiffe_jwt in token_endpoint_auth_methods_supported (section on authorization server metadata, checked against the -02 text).
Architecture
sequenceDiagram
participant SA as SPIRE agent<br/>(Workload API)
participant AG as agent/research
participant AZ as authz
participant A as notes-a (MCP)
participant B as notes-b (MCP)
AG->>A: GET /.well-known/oauth-protected-resource/mcp
A-->>AG: resource, authorization_servers
AG->>AZ: GET /.well-known/oauth-authorization-server
AZ-->>AG: issuer, token_endpoint
AG->>SA: FetchJWTSVID(aud = issuer)
SA-->>AG: JWT-SVID (5 min)
AG->>AZ: POST /token client_credentials<br/>client_assertion = JWT-SVID<br/>resource = notes-a
AZ->>SA: FetchJWTBundles
AZ->>AZ: verify SVID, check policy.yaml
AZ-->>AG: access token (aud = notes-a, 5 min)
AG->>A: tools/call + Bearer token
A->>A: verify sig, iss, exp, aud, tool scope, write audit line
A-->>AG: result
AG-->>B: same token replayed
B-->>AG: 401 invalid_token (aud mismatch)Components
Module | Command | Role |
|
| Token endpoint. JWT-SVID client assertion in, audience-bound JWT access token out. Publishes JWKS and RFC 8414 metadata. |
|
| MCP server over Streamable HTTP. Tools |
|
| Discovers the authorization server, fetches its SVID, gets a token, calls tools. |
|
| Starts a local stdio MCP server with |
|
| SPIRE server and agent 1.15.3 (pinned by digest), registration entries, the three services, four scenarios. |
Token request, as sent by the agent:
Parameter | Value |
|
|
|
|
| JWT-SVID, |
| the SPIFFE ID (optional, must match the SVID |
| canonical URI of the MCP server (required) |
| required, single-space separated, must be a subset of the policy grant |
Repeated parameters are rejected with invalid_request. Error descriptions are fixed strings; exception details go to the audit log only.
The MCP server accepts only access tokens with header typ at+jwt, algorithm ES256, and claims iss, sub, aud, exp, iat, scope. Request bodies are capped at 1 MiB (413). Bodies that are not valid JSON-RPC get 400.
Quickstart
Requires Python 3.12+ and uv.
uv sync
make check # ruff, mypy strict, pytest (offline, no SPIRE needed)
make demo # needs a running Docker daemon
make downOffline tests use a local key as a stand-in for SPIRE (LocalSvidIssuer). mcp-svid-authz --static-jwks accepts a JWKS file for the same purpose.
Optional local anonymization denylist: put one term per line in tests/denylist.txt (git-ignored). tests/test_no_identifiers.py then fails on any match in the tree or history.
Demo scenarios
# | Caller | Target | Expected |
1 |
| notes-a | token with |
2 |
| notes-b | token with |
3 |
| notes-a | valid SVID, not in |
4 |
| token for notes-a sent to notes-b | 401 |
Audit line example:
{"timestamp":"2026-09-26T18:07:11.742+00:00","component":"mcp_server:notes-b","spiffe_id":"spiffe://example.org/agent/research","tool":"notes.write","decision":"deny","reason":"insufficient_scope: needs notes:write"}Denials before the signature check prefix the SPIFFE ID with unverified:.
Threat model notes
Threat | Mitigation here | Gap |
Static key leak | No static keys. SVID and access token both live 5 min. | A stolen access token works until expiry. |
Token replay to another server |
| None within one issuer. |
JWT-SVID replay to authz | SVID | No |
Workload impersonation | SPIRE attestation. | Unix attestor is UID based. Any process under a registered UID gets that identity. |
Over-broad access | Allowlist per SPIFFE ID, resource and scope. Per-tool scope check. | Policy is a local file. No versioning or review flow. |
Confused deputy | Server never forwards the incoming token. | No delegation chain for downstream calls. |
Algorithm confusion | SVIDs: asymmetric algorithms only, | |
Stale token in a stdio child | Token passed by 0600 file, refreshed. Wrapper deletes it and stops the child on expiry. |
|
Supply chain | Dependency majors bounded, images pinned by digest, |
|
Non-goals
Not production software. No HA, no rotation of the authz signing key, no rate limits, plain HTTP inside the compose network.
Not a replacement for an MCP gateway such as agentgateway. It shows the token flow a gateway could implement.
No user delegation. The agent acts as itself (
client_credentials). No token exchange oractclaim chain yet.No X.509-SVID or WIT-SVID client authentication. JWT-SVID only.
The compose stack uses the unix attestor with a shared PID namespace. It is weak and fits a single-host demo only.
No async JWKS fetch and no refetch on unknown
kid. The MCP server caches the authz JWKS for 60s with a blocking fetch.No client-side issuer allowlist. The agent trusts the authorization server named in Protected Resource Metadata.
No
jtireplay tracking for JWT-SVIDs or access tokens.No
tools/listfiltering. Every caller sees all tools; the scope check applies attools/call.
Status
Item | State |
Offline tests | Pass ( |
| Pass on 2026-09-26, Docker Desktop on macOS. All four scenarios behave as in the table above. |
stdio wrapper | Minimal. Children must re-read |
License
This server cannot be deployed
Maintenance
Related MCP Connectors
Governed MCP gateway: one endpoint for your tools, with credential custody and audit log.
MCP server for mandates, delegation, policy-gated execution, credential grants, and audit.
Prove end users to agents and apps: login-links, OIDC clients, and API keys over remote MCP
- StytchOAuthdev.stytch.mcp
The Stytch MCP server is a reference implementation that demonstrates remote MCP server authentication and authorization using Stytch Connected Apps. It provides OAuth 2.1-compliant authorization (including PKCE), Dynamic Client Registration, and validates Stytch-issued access tokens to enable AI agents to securely interact with external services through permissioned access, supporting scopes like openid, email, profile, and manage:project_data.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceAn MCP server for OAuth 2.0 authentication supporting Device Code and Client Credentials flows, enabling secure token management for MCP applications.-
- AlicenseAqualityFmaintenanceProvides secure OAuth2-based credential management for MCP servers, allowing agents to obtain short-lived token references without exposing raw secrets.719 npmMIT
- AlicenseNot gradedqualityAmaintenanceEnables clients to access multiple backend MCP servers through a single endpoint, with OAuth 2.1 authorization, namespaced tools, and secure credential management.1MIT
- AlicenseNot gradedqualityBmaintenanceEnables teams to securely store and retrieve credentials, view decrypted secrets or live OAuth tokens, and connect to third-party MCP servers through a unified MCP endpoint.MIT