MCP Tools Reference
AEGIS MCP Server -- Model Context Protocol interface for agentic AI governance.
AEGIS MCP Server -- Model Context Protocol interface for agentic AI governance.
The AEGIS MCP server exposes governance gates as MCP tools that AI agents (Claude Code, Codex, custom agents) can call to evaluate proposals before taking action.
Hosted MCP Endpoint (not served)
The hosted MCP endpoint on GCP Cloud Run is offline (September 2026); see undercurrentholdings.com/status. The configuration below documents the interface.
| Environment | Endpoint |
|---|---|
| Production | https://mcp.aegis.undercurrentholdings.com/mcp |
Authentication uses a Bearer token (an AEGIS API key).
Claude Code (.mcp.json)
Claude Code's streamable-HTTP transport is selected with "type": "http"
(Claude Code 2.x+ resolves "http" to the MCP Streamable HTTP transport
per the 2025-11-25 spec — "streamable-http" is silently rejected and
shows as invalid in /doctor).
{
"mcpServers": {
"aegis": {
"type": "http",
"url": "https://mcp.aegis.undercurrentholdings.com/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
}
}
}Or add the server from the command line:
claude mcp add --transport http \
--header "Authorization: Bearer YOUR_API_KEY" \
aegis https://mcp.aegis.undercurrentholdings.com/mcpReplace YOUR_API_KEY with an AEGIS API key.
Health Check
The /health route is public (no authentication required):
curl https://mcp.aegis.undercurrentholdings.com/healthLocal MCP Server (stdio)
Run the MCP server locally via stdio — installable from PyPI. Its tools
call the hosted API, which is offline, so they return an error. Without an
API key the server uses sandbox mode; with AEGIS_API_KEY set in the
server's environment, the key-required tools (decision history, usage
reports, risk checks) are enabled.
Claude Code Integration
pip install "aegis-governance[mcp]" # Python >= 3.10Or in one step:
claude mcp add aegis -- aegis-mcp-serverAdd a .mcp.json to the project root (preferred — portable and version-controllable):
{
"mcpServers": {
"aegis": {
"type": "stdio",
"command": "aegis-mcp-server"
}
}
}Or add to your project's .claude/settings.json:
{
"mcpServers": {
"aegis-governance": {
"command": "aegis-mcp-server",
"args": []
}
}
}The PyPI package exposes 6 tools to Claude Code: aegis_evaluate_proposal,
aegis_quick_risk_check, aegis_health, aegis_list_decisions,
aegis_get_decision, and aegis_get_usage. The full 10-tool surface
(thresholds, scoring guides, proposal records, crypto status) was served by
the hosted endpoint (offline since September 2026). Running the server from
source (pip install -e ".[engine,mcp]" in the private repository: the extras
the MCP image needs for gate evaluation and persistence) gives in-process
gate evaluation. That server-side aegis_evaluate_proposal records each
decision the same way the REST /evaluate path does: it appends a
hash-chained audit entry and stores the decision (best-effort) for the
authenticated customer (see Audit Logging).
Starting the Server
The MCP server uses the official MCP Python SDK (FastMCP) and supports two transports:
aegis-mcp-server # stdio (default, for Claude Code)
aegis-mcp-server --transport streamable-http --port 8080 # HTTP (hosted)When the hosted service ran, the server used GCP Cloud Run with streamable-http transport. Authentication uses the MCP Python SDK's TokenVerifier contract backed by Unkey: every request to /mcp must carry an Authorization: Bearer <api-key> header, and the verified customer_id from the token is used by tools instead of any client-supplied argument. A public /health endpoint is available for container and load-balancer health checks.
Rate Limiting
The server includes a token bucket rate limiter (CoSAI MCP-T10 compliance).
- Default: 60 requests per minute
- Configurable via
AegisConfig.mcp_rate_limit - Set to 0 to disable
- Exceeding the limit returns an error in the tool response
Audit Logging
Every evaluated aegis_evaluate_proposal invocation is logged with:
- SHA-256 hash of parameters (first 16 hex chars, PII-safe)
- Decision status
- Latency in milliseconds
- Caller ID (from
agent_idargument)
A call refused for its customer id (customer_id_required,
reserved_customer_id, invalid_customer_id) is refused before evaluation: it
produces no decision, no audit log line, no telemetry, and does not consume a
rate-limit token.
Each decision is also appended to the hash-chained audit trail, using the same
shared code path as the REST /evaluate endpoint, so the same decision produces
the same audit_hash:
- Authenticated (verified Bearer token, or
AEGIS_CUSTOMER_IDin stdio mode): the entry is appended to the customer's durable chain and the response reports"audit_chain": "persisted". The decision is also stored, best-effort, soaegis_list_decisionsandaegis_get_decisioncan return it.audit_chainreports the chain append only;decision_storedreports the store. A storage failure is logged server-side, does not turn the response into an error, and sets"decision_stored": false— the chain entry exists but the decision cannot be retrieved. Until ROADMAP G136 closes, expectfalsein production: storage needs the PII-encryption keys (AEGIS_MASTER_KEK_*), which the deploy path does not yet provide. - Unauthenticated (no customer id): the entry goes to a process-local,
non-durable in-memory chain and no decision is stored. The response reports
"audit_chain": "in-memory"and"decision_stored": false. This applies only to development stages; fail-closed stages refuse the call (below). In development stages an authenticated call also reportsin-memorywhen the database is unavailable. WithoutDATABASE_URL, development stages persist to a local SQLite file,.aegis/aegis.dbin the working directory, and reportpersistedfor it. - Fail-closed: for an authenticated call in fail-closed stages
(any
AEGIS_STAGEother thandevelopment,dev,test,local,ci,e2e, or unset/empty, compared trimmed and case-insensitively — soproduction,staging, and unknown values such asprodfail closed), if the audit chain cannot be written, the tool returns{"error": "audit_persistence_unavailable", "error_class": "<class>"}instead of a decision (the REST equivalent is HTTP 503). Retry later. An unauthenticated call (no customer id, e.g. stdio withoutAEGIS_CUSTOMER_ID) in a fail-closed stage returns{"error": "customer_id_required"}and no decision: there would be no durable chain to record it in. - Reserved ids: a reserved customer id (the
/readyprobe sentinel or the Layer-D proposal namespace) returns{"error": "reserved_customer_id"}, and a whitespace-only customer id returns{"error": "invalid_customer_id"}.
Persisting requires the database extras: [engine] (includes the PostgreSQL
driver), or [persistence] for SQLite only. Without them, an authenticated
evaluation reports "audit_chain": "in-memory" in development stages and
returns audit_persistence_unavailable in fail-closed stages.
Tools
aegis_evaluate_proposal
Full governance gate evaluation via pcw_decide(). Evaluates a proposal through
all six gate checks (Risk, Profit, Novelty, Complexity, Quality, Utility)
and returns a structured decision with per-gate details and rationale.
Required parameters:
| Parameter | Type | Description |
|---|---|---|
proposal_summary | string | Brief description of the proposed action |
estimated_impact | string | Impact level: low, medium, high, critical |
Optional parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
risk_score | number | 0.0 | Alias for risk_proposed (0.0-1.0) |
risk_proposed | number | 0.0 | Predicted risk after change (0.0-1.0) |
risk_baseline | number | 0.0 | Current risk level (0.0-1.0). Gate triggers when risk doubles vs baseline with 95% Bayesian confidence. |
profit_proposed | number | 0.0 | Expected profit/performance after change |
profit_baseline | number | 0.0 | Current profit/performance metric |
novelty_score | number | 0.5 | How unprecedented the proposal is (0.0-1.0). Gate passes when novelty >= ~0.74. Logistic inflection at 0.7. |
complexity_score | number | 0.5 | Normalized complexity where HIGHER = SIMPLER (0.0-1.0). Hard floor at 0.5: below this the proposal HALTs and CANNOT be overridden. |
quality_score | number | 0.7 | Overall proposal quality (0.0-1.0). Must be >= 0.7. |
quality_subscores | array[number] | [0.7, 0.7, 0.7] | Detailed quality subscores. None may be zero. |
agent_id | string | "unknown" | Identifier of the calling agent |
session_id | string | (auto UUID) | Session identifier for correlation |
phase | string | "plan" | Lifecycle phase: plan, commit, work |
shadow_mode | boolean | false | Shadow mode: gates evaluate but results are advisory only. Requires Professional tier or higher. |
telemetry_url | string | (none) | HTTPS URL for structured telemetry events |
drift_baseline_data | array[number] | (none) | 30+ historical observations for KL divergence drift detection |
requires_human_approval | boolean | false | Whether this proposal requires human approval |
time_sensitive | boolean | false | Whether this proposal is time-sensitive |
reversible | boolean | true | Whether this proposal is reversible |
metadata | object | {} | Additional key-value metadata |
change_type | string | "feature" | Type of change: feature, refactor, bugfix, config. Non-feature types skip novelty and profit gates. |
Utility auto-synthesis: If
utility_resultis not provided (the common case), the MCP server auto-synthesizes it fromprofit_baseline/profit_proposedusing symmetric PERT three-point estimation (±20% spread) with a profit delta epsilon clamp of 0.001. This matches CLI and REST API behaviour — all three surfaces produce identical utility gate results for the same inputs.
Response fields:
| Field | Type | Description |
|---|---|---|
status | string | Decision: proceed, pause, halt, escalate |
confidence | number | Minimum confidence across all gates |
gates_passed | number | Count of passing gates |
gates_failed | number | Count of failing gates |
failing_gates | array[string] | Names of failed gates |
rationale | string | Human-readable decision rationale |
next_steps | array[string] | Recommended next actions |
constraints | array[string] | Active constraints |
decision_id | string | Unique decision UUID |
timestamp | string | ISO 8601 UTC timestamp |
override_eligible | boolean | Whether override is possible |
override_requires | array[string] | Roles needed for override |
gates | object | Per-gate detail (value, threshold, confidence, message) |
audit_hash | string | SHA-256 hash of the decision record (same canonicalization as REST) |
chain_hash | string | SHA-256 chain hash linking this entry to the previous one |
chain_previous | string | Chain hash of the previous entry (genesis hash for the first) |
audit_chain | string | persisted (entry appended to the customer's durable chain) or in-memory (no customer id, or dev-stage fallback when the database is unavailable). Decision storage is best-effort and is not reported by this field. |
decision_stored | boolean | true only when the decision was stored and aegis_get_decision can return it. false when there is no customer id or the best-effort store failed (in production, until ROADMAP G136 provides the PII-encryption keys). A persisted chain entry does not imply true. |
aegis_quick_risk_check
Quick risk threshold check (risk_score < threshold).
Warning: This is a simplified check without Bayesian evaluation. For
governance decisions, use aegis_evaluate_proposal instead.
Required parameters:
| Parameter | Type | Description |
|---|---|---|
action_description | string | Brief description of the action to risk-check |
risk_score | number | Estimated risk score (0.0-1.0) |
Optional parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
threshold | number | 0.5 | Risk threshold. Score below this is considered safe. |
agent_id | string | "unknown" | Identifier of the calling agent |
Response fields:
| Field | Type | Description |
|---|---|---|
safe | boolean | true if risk is below threshold |
risk_score | number | The evaluated risk score |
threshold | number | The threshold used |
aegis_check_thresholds
Introspect all six gate thresholds and current configuration. Call this before submitting proposals to understand gate behavior and parameter values.
Parameters: None required.
Response fields:
| Field | Type | Description |
|---|---|---|
gates | array | Array of 6 gate definitions with name, type, threshold, description |
config | object | Full AegisConfig as dict |
aegis_get_scoring_guide
Get domain-specific parameter derivation guidance. Returns formulas, range guides, common mistakes, and worked examples showing how to map domain metrics to AEGIS parameters.
Recommended workflow:
aegis_check_thresholds— understand gate configurationaegis_get_scoring_guide(domain)— get derivation formulas for your domainaegis_evaluate_proposal(...)— submit with derived values
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
domain | string | No | Domain for tailored guidance. One of: trading, cicd, moderation, agents, generic. Defaults to generic if omitted or unrecognized. |
Response fields:
| Field | Type | Description |
|---|---|---|
domain | string | The domain returned |
domain_description | string | Brief description of the domain context |
parameters | object | Per-parameter guidance with description, derivation, formula, example, and range_guide |
common_mistakes | array[string] | Domain-specific pitfalls (e.g., complexity inversion, novelty pass direction) |
worked_example | object | Complete scenario with input JSON and expected_gates results |
Available domains:
| Domain | Use Case | Key Metric |
|---|---|---|
trading | Algorithmic trading, portfolio changes | VaR / position limit |
cicd | CI/CD pipelines, deployments | Error rate, rollback rate |
moderation | Content moderation, safety filters | False positive rate |
agents | Autonomous AI agents | Task failure rate |
generic | Any domain (default) | General-purpose guidance |
aegis_crypto_status
Check availability of all AEGIS cryptographic providers (signatures, encryption, key management). Returns which providers are installed and active.
Parameters: None.
Response fields:
| Field | Type | Description |
|---|---|---|
signatures.hybrid | boolean | Ed25519 + ML-DSA-65 hybrid available |
signatures.bip322 | boolean | BIP-322 Schnorr available |
signatures.ed25519 | boolean | Ed25519 (deprecated) available |
encryption.hybrid_kem | boolean | X25519 + ML-KEM-768 + AES-256-GCM available |
post_quantum.ml_dsa | boolean | ML-DSA-65 signatures available |
post_quantum.ml_kem | boolean | ML-KEM-768 encryption available |
key_management.aws_kms | boolean | AWS KMS envelope encryption available |
key_management.hsm_pkcs11 | boolean | PKCS#11 HSM key wrapping available |
schema_signing.available | boolean | AMTSS schema signer package installed |
default_provider | string | Active signature provider: bip322-simple, ed25519, or none |
kek_provider | string | Best available KEK provider: kms, hsm, or environment |
documentation_url | string | Link to crypto documentation |
aegis_list_decisions
List governance decisions for the current customer. Returns recent decisions with status, confidence, proposal summary, and gate results.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
month | string | No | Current month | YYYY-MM filter for decision history |
limit | integer | No | 50 | Maximum decisions to return (max 200) |
customer_id | string | No | From auth / env var | Ignored when the hosted Streamable HTTP endpoint authenticates your Bearer token — the customer_id is taken from the verified token so you cannot query other customers' decisions. For stdio/local runs, defaults to the AEGIS_CUSTOMER_ID env var and falls back to this argument. |
Response fields:
| Field | Type | Description |
|---|---|---|
count | integer | Number of decisions returned (length of decisions) |
decisions | array | Array of decision records |
Each entry in
decisionsis the cleaned persisted decision record — the internal storage keys (pk,sk,entity_type,ttl) are stripped, andDecimalvalues are coerced to JSON numbers. The remaining fields reflect exactly what was stored for that decision.On failure:
{"error": "customer_id required ..."}when no customer id resolves;{"error": "invalid_customer_id"}for a whitespace-only one;{"error": "month must be YYYY-MM (year + 01-12)"}for a malformedmonth; otherwise{"error": "Failed to retrieve decisions", "error_class": "<ExceptionClass>"}(class name only, never the exception message).
aegis_get_decision
Get a specific governance decision by ID. Returns the full stored decision record: gate results, rationale, next steps, audit-chain hashes, and the proposal context recorded with it.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
decision_id | string | Yes | -- | UUID of the decision to retrieve |
month | string | No | -- | Accepted for compatibility and ignored; lookup is by decision_id for the authenticated customer |
customer_id | string | No | From auth / env var | Ignored when the hosted Streamable HTTP endpoint authenticates your Bearer token — the customer_id is taken from the verified token. For stdio/local runs, defaults to the AEGIS_CUSTOMER_ID env var and falls back to this argument. |
Response fields:
| Field | Type | Description |
|---|---|---|
decision_id | string | Unique decision UUID |
status | string | Decision outcome: proceed, pause, halt, escalate |
confidence | number | Minimum confidence across all gates |
proposal_summary | string | Brief description of the proposal |
estimated_impact | string | Impact level: low, medium, high, critical |
timestamp | string | ISO 8601 UTC timestamp |
rationale | string | Human-readable decision rationale |
next_steps | array[string] | Recommended next actions |
gates | object | Per-gate evaluation details (value, threshold, confidence, message) |
constraints | array[string] | Active constraints |
override_eligible | boolean | Whether override is possible |
override_requires | array[string] | Roles needed for override |
audit_hash | string | SHA-256 hash of the decision record for tamper detection |
chain_hash | string | Audit-chain hash of this entry |
chain_previous | string | Audit-chain hash of the previous entry |
change_type | string | Change type submitted with the proposal |
phase | string | Lifecycle phase submitted with the proposal |
quality_subscores | array[number] | Quality subscores submitted with the proposal |
agent_id | string | Calling agent identifier |
gate_results | object | Per-gate passed / value / threshold / confidence (compliance export) |
The table lists the main fields; a record may carry more (for example
customer_id,gates_passed,gates_failed,failing_gates, and drift or calibration details when present). Records stored before the G55 context enrichment may lack the proposal-context fields. On failure:{"error": "customer_id required ..."},{"error": "invalid_customer_id"}(whitespace-only id),{"error": "Decision <id> not found"}, or{"error": "Failed to retrieve decision", "error_class": "<ExceptionClass>"}.
aegis_record_proposal
Record a Layer-D meta-improver proposal (one Tier-B verdict, or a forward-compat
Track-D proposal) into the current customer's per-customer SHA-256 audit chain
(aegis_layer_d_proposals). Returns the chain hash + 1-indexed sequence. A
duplicate (customer_id, proposal_id) is rejected as a semantic error (the
proposal is already on the chain).
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
proposal_id | string | Yes | -- | Unique proposal id (1-64 chars) |
cycle_n | integer | Yes | -- | 1-indexed meta-improver cycle counter (>= 1) |
source | string | Yes | -- | "track-c" (current) or "track-d" (forward-compat) |
verdict | string | Yes | -- | One of PROCEED | PAUSE | ESCALATE | HALT |
confidence | number | Yes | -- | Model-derived confidence on the verdict (0.0-1.0) |
gate_scores | object | Yes | -- | Per-gate score dict |
tier_b_voters | array | Yes | -- | Per-voter records (array of objects) |
tier_b_consensus | object | Yes | -- | Consensus summary dict |
proposal_diff | string | Yes | -- | Human-readable diff text |
customer_id | string | No | From auth / env var | Ignored when the hosted Streamable HTTP endpoint authenticates your Bearer token — the customer_id is taken from the verified token, so callers cannot write to another customer's chain. For stdio/local runs, defaults to the AEGIS_CUSTOMER_ID env var and falls back to this argument. |
Response fields:
| Field | Type | Description |
|---|---|---|
proposal_id | string | The recorded proposal id |
chain_hash | string | SHA-256 chain hash of the new entry (64 hex chars) |
sequence | integer | 1-indexed position within the customer's Layer-D chain |
On failure returns
{error, error_class}(class-name only — never raw SQL or connection detail, per CWE-209). A duplicate(customer_id, proposal_id)is rejected rather than re-chained.
aegis_list_proposals
List the current customer's Layer-D meta-improver proposals in insertion (chain) order, paginated. Returns each proposal's verdict, confidence, gate scores, tier-B voters, and chain hashes. Use to review recorded Tier-B verdicts or audit the Layer-D chain.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
limit | integer | No | 100 | Maximum proposals to return (max 200) |
offset | integer | No | 0 | Pagination offset |
customer_id | string | No | From auth / env var | Ignored when the hosted Streamable HTTP endpoint authenticates your Bearer token — the customer_id is taken from the verified token. For stdio/local runs, defaults to the AEGIS_CUSTOMER_ID env var and falls back to this argument. |
Response fields:
| Field | Type | Description |
|---|---|---|
count | integer | Number of proposals returned (length of proposals) |
proposals | array | Proposal records in insertion (chain) order; the JSON-string columns (gate_scores, tier_b_voters, tier_b_consensus) are parsed back to native JSON |
aegis_verify_proposals
Verify the integrity of the current customer's Layer-D proposal chain. Recomputes each SHA-256 link in insertion order and reports whether the chain is intact.
Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
customer_id | string | No | From auth / env var | Ignored when the hosted Streamable HTTP endpoint authenticates your Bearer token — the customer_id is taken from the verified token. For stdio/local runs, defaults to the AEGIS_CUSTOMER_ID env var and falls back to this argument. |
Response fields:
| Field | Type | Description |
|---|---|---|
valid | boolean | Whether the chain verifies end-to-end |
entry_count | integer | Number of entries walked (an empty chain is valid with entry_count 0) |
error | string | null | Null when valid; a chain_previous/chain_hash mismatch message when tampering is detected |
Integration with MCP Clients
Claude Desktop
Add to your claude_desktop_config.json:
{
"mcpServers": {
"aegis-governance": {
"command": "aegis-mcp-server",
"args": []
}
}
}Custom MCP Clients
Connect via stdio using the MCP Python SDK:
from mcp import ClientSession
from mcp.client.stdio import StdioServerParameters, stdio_client
params = StdioServerParameters(command="aegis-mcp-server", args=[])
async with stdio_client(params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
result = await session.call_tool(
"aegis_evaluate_proposal",
arguments={
"proposal_summary": "Add caching layer",
"estimated_impact": "medium",
"risk_score": 0.2,
"complexity_score": 0.7,
},
)Security
SSRF Protection
Telemetry URLs provided via telemetry_url are validated with a
resolve-then-validate approach:
- DNS resolution is performed first to catch DNS rebinding
- Private, loopback, link-local, reserved, and CGNAT addresses are blocked
- Only
https://schemes are accepted in production
Input Validation
- All numeric parameters reject
NaN,Inf, and-Inf - Boolean parameters reject non-boolean types (prevents Python truthiness confusion)
estimated_impactis validated as a string enum- The
validate_float()helper preserves legitimate falsy values like0and0.0