
Attest Mcp
io.github.chudah1Published by GitHub account chudah1v0.1.0Updated Oct 24, 2026
Credential enforcement middleware for MCP servers — verifies scoped tokens on every tool call
Overview
Middleware that wraps an existing MCP server so every tool call is gated by a scoped Attest credential, with scope mapping and audit events.
- What you can do
- Wrap an existing MCP server so each tool call is checked against a scoped Attest credential before the handler runs
- Verify credential signature and expiry offline against the issuer's JWKS, then check revocation
- Map tool names to resource:action scopes automatically, with per-tool or global overrides
- Return structured, machine-readable errors to the calling agent when a call is blocked
- Expose the resolved scope registry for credential issuers and read verified claims inside handlers
- Fire audit events for every tool call, with callbacks for violations and delivery failures
- When to use it
- For developers who already run an MCP server and want per-tool authorization, delegation tracking and an audit trail without rewriting their tool handlers. It also suits staged rollouts, where violations are logged first and blocking is enabled later.
- Requirements
- Runs as a local npm package added to your own MCP server project, alongside the MCP SDK. Needs an Attest issuer server reachable at the configured issuer URI (ATTEST_BASE_URL in the manifest), network access for JWKS and revocation lookups, and credentials issued by that server.
Installation
In SourceWeft
- Open Attest Mcp in the dashboard and add it to a workspace.
- Enable the server for the chats that should use its tools.
Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.
Other MCP clients
Follow the launch instructions in the repository.
README
@attest-dev/sdk/mcp
Attest credential enforcement middleware for Model Context Protocol servers.
Two lines to protect every tool call on an existing MCP server.
Install
@attest-dev/sdk ships both the core client and the MCP middleware as a single package with two entry points.
Two-line integration
withAttest patches server.tool() in place and returns the same server object typed as the original. Everything else — connect(), close(), transports — works unchanged.
How it works
On every tool call, before the handler executes:
- Extract credential from
extra.authInfo.token(MCP auth middleware path),extra.meta.attest_token, orextra.meta.authorization - Verify RS256 signature + expiry offline against the issuer's JWKS (fetched once, cached for 1 hour by default)
- Map tool name → scope using
scopeForTool()and check the credential'satt_scopecovers it - Check revocation via
GET /v1/revoked/{jti}(10-second in-process cache) - Pass through to the original handler on success
- Return a structured error on any failure — agent frameworks receive
isError: truewith a typed JSON payload - Fire audit event to
POST /v1/audit— async, never blocks the tool call
Scope mapping
Tool names are automatically mapped to resource:action scope strings. The first word (before the first _) becomes the action; the remainder becomes the resource.
Action aliases — these raw prefixes map to canonical actions:
Multi-word resources keep their underscores in the scope string. get_weather_forecast → weather_forecast:read. This is intentional — the full noun phrase is preserved as the resource. When issuing credentials, use the same string: scope: ["weather_forecast:read"].
Override at registration — pass a AttestToolOptions object as the final argument to server.tool(). This is the preferred way to set scope for non-standard tool names because the scope lives next to the tool definition:
The options object is consumed by withAttest and stripped before the MCP SDK sees the arguments — the SDK is unaware of it.
Override globally using toolScopeMap in options (applies to all tools that don't declare requiredScope explicitly):
Scope discovery endpoint
getAttestScopes() returns the live scope registry — every tool name mapped to its resolved scope string. Wire it to an HTTP endpoint so credential issuers can query what scopes a server requires without out-of-band coordination.
Response:
An orchestrator agent issues credentials by fetching this endpoint first:
No spreadsheet of scope strings. No manual sync between server and client. The server is the source of truth.
What an agent receives when blocked
isError: true signals the MCP framework that this is an error response. The reason field is machine-readable:
Options
Reading credential claims inside a tool handler
After withAttest has already verified the credential, use getAttestContext to read the claims without re-verifying:
Gradual rollout
Add Attest to an existing server without blocking traffic until you're confident the mapping is correct:
Flip requireCredential to true once violation rates drop to zero in your monitoring.
Audit logging
Every tool call — pass or fail — fires a POST /v1/audit to your Attest server. Delivery is async and never blocks tool execution.
Audit delivery is best-effort with structured error surfacing. For guaranteed delivery, configure onAuditError and implement your own retry queue.
Failures surface in priority order — nothing is ever silently dropped:
onAuditError(error, event)— you handle it (retry queue, fallback log, pager)onViolation({ reason: "audit_failure", ... })— surfaces to your existing monitoring ifonAuditErroris not setconsole.warn— last resort if neither callback is configured
Non-2xx responses from the audit endpoint are treated as delivery failures, not silent successes.
The audit event carries: event_type, jti, att_tid, att_uid, agent_id (mcp:<toolName>), scope, and an outcome meta field (allowed, scope_violation, blocked_revoked).
Query the audit chain for a task tree:
Full example
License
Apache-2.0
Source: sdks/typescript/mcp/README.md at commit defffb9
Tools
0Version history
1- v0.1.0LatestSep 16, 2026
Score
50% of the applicable checks met
How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.
- Reached in the last 30 days25 pts
Keep the endpoint answering the MCP handshake; SourceWeft probes it every 30 days.
- Auth declared10 pts
Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.
- Tools listed15 pts
Answer tools/list without a credential, or ship a package that starts in a sandbox without one.
- DescriptionRequired15 pts
- Logo10 pts
- Documentation15 pts
- License10 pts