Adapter Authentication & Secret Handling Policy¶
How credentials and secrets flow through the MCP server, LLM adapters, and HTTP API — and what each layer is responsible for.
Principle: Secrets Stay at the Edge¶
Secrets (API keys, tokens, credentials) are never stored inside capability contracts, skill YAML, or binding definitions. They live in the environment and are resolved at execution time by the binding layer.
┌──────────────────────────────────────────────────────────────────┐
│ Environment (env vars, config files, secret managers) │
│ OPENAI_API_KEY, AGENT_SKILLS_JWT_SECRET, etc. │
└──────────────────────┬───────────────────────────────────────────┘
│ resolved at runtime
┌──────────────────────▼───────────────────────────────────────────┐
│ Binding Layer (ProtocolRouter → Invoker) │
│ - openapi_invoker reads API keys from env │
│ - mcp_invoker reads server config from env │
│ - pythoncall_invoker has no secrets │
└──────────────────────────────────────────────────────────────────┘
Per-Surface Auth Model¶
HTTP API (agent-skills serve)¶
| Mechanism | Header | Implementation |
|---|---|---|
| API Key | X-API-Key |
ApiKeyStore (SHA-256 hashed) |
| JWT HS256 | Authorization: Bearer <token> |
JWTVerifier in runtime/auth.py |
| Anonymous | — | Opt-in via allow_anonymous, gets reader role |
Activation: Set AGENT_SKILLS_RBAC=1. Without it, legacy flat API key
check applies (backward-compatible).
See AUTH.md for full configuration.
MCP Server (python -m official_mcp_servers)¶
The MCP server runs as a local subprocess managed by the MCP host (Claude Desktop, VS Code, etc.). Authentication follows the MCP transport model:
| Transport | Auth Model |
|---|---|
| stdio | Implicit trust — the host process owns the subprocess. No additional auth needed. |
| SSE | Network-exposed — use AGENT_SKILLS_MCP_API_KEY env var to require a key in the Authorization header. |
Environment variables for MCP:
| Variable | Default | Description |
|---|---|---|
AGENT_SKILLS_MCP_API_KEY |
(none) | When set, SSE transport requires this key |
AGENT_SKILLS_MCP_TRUST_LEVEL |
standard |
Maximum trust level for capability execution |
Native LLM Adapters (as_anthropic_tools, etc.)¶
The LLM adapters run in-process in the caller's Python runtime. They inherit the caller's environment and have no independent auth layer.
| Concern | Policy |
|---|---|
| Adapter auth | None — the caller already has code access |
| Upstream API keys | Resolved by the binding layer from env vars |
| Secret leakage | execute_*_tool_call() returns sanitized JSON; errors never contain credentials |
Framework Adapters (LangChain, CrewAI, etc.)¶
Same model as native LLM adapters — they create tool wrappers that call
execute_capability() internally. No separate auth.
Secret Resolution Flow¶
When a binding needs an API key (e.g., OpenAI):
1. Skill YAML → step references capability "text.content.summarize"
2. Binding resolver finds best binding (e.g., openapi_text_summarize)
3. Binding metadata declares: auth: { type: "bearer", env: "OPENAI_API_KEY" }
4. openapi_invoker reads os.environ["OPENAI_API_KEY"] at call time
5. Key is placed in Authorization header for the upstream request
6. Key is NEVER logged — header redaction is active in openapi_invoker
Credential Redaction¶
All surfaces sanitize credentials before logging or returning errors:
- Audit system (
runtime/audit.py): Recursive redaction of keys containingpassword,token,secret,key,authorization,credential. - OpenAPI invoker: Header redaction for
Authorization,X-API-Key,Cookie, and related headers. - Error responses:
sanitize_error_message()truncates and strips multi-line content. No exception causes are exposed to clients.
Adding Auth to a New Surface¶
When creating a new adapter or transport:
- Determine trust model: Is it local (implicit trust) or network (explicit auth)?
- Reuse
AuthMiddlewarefor network surfaces: it handles API key + JWT + anonymous. - Never store secrets in code: Use env vars or the plugin system (
agent_skills.authentry point). - Always sanitize errors: Use
sanitize_error_message()fromopenapi_error_contract.py. - Log auth events: Use
log_event("auth.*")for auditable auth decisions.