CtrlK
BlogDocsLog inGet started
Tessl Logo

keycard/keycard

Add Keycard auth to MCP servers, REST APIs, and A2A agents

54

Quality

68%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files
name:
keycard-a2a-agent
description:
Building an A2A (Agent-to-Agent) protocol server governed by Keycard, creating a Keycard-protected AI agent service, or implementing agent-to-agent delegation chains

Build a Keycard-Governed A2A Agent

Install

Python: pip install keycardai-a2a uvicorn TypeScript: npm install @keycardai/a2a @a2a-js/sdk express

Canonical examples — read these before writing any code

  • Python: packages/a2a/examples/keycard_protected_server/main.py
  • TypeScript: @keycardai/a2a README

Keycard Console setup

  1. Zone ID from Zone Settings
  2. Applications → Create Application (A2A agents are Applications, not Resources — they have an identity, not just an audience)
  3. Generate Client ID + Client Secret — the agent authenticates to Keycard with these to verify inbound tokens and perform delegation

keycard.toml (for keycard run sessions)

[zone]
id = "<your-zone-id>"

Accessing the caller's identity in your executor

The verified user is available via context.call_context.state["access_token"] (Python) or getKeycardAuth(requestContext).token (TypeScript). Use this as the subject_token in an RFC 8693 token exchange when calling downstream services to maintain the delegation chain.

Common errors

ErrorCauseFix
401 on all A2A requestsExpected — require_authentication=True on the JSONRPC mountCallers must present a Keycard bearer token
access_token missing from executor contextKeycardServerCallContextBuilder not wiredPass context_builder=KeycardServerCallContextBuilder() to create_jsonrpc_routes (Python) or use createKeycardRequestHandler (TypeScript)
Agent card empty or missing capabilitiesAgentServiceConfig missing description or capabilitiesSet both fields in the config object
Delegation chain not propagatingCaller token not forwarded as subject_tokenRead context.call_context.state["access_token"] and pass it as subject_token in downstream token exchange
Workspace
keycard
Visibility
Public
Created
Last updated
Publish Source
CLI
Badge
keycard/keycard badge