Hosted service offline; see undercurrentholdings.com/status
API Reference

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.

EnvironmentEndpoint
Productionhttps://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/mcp

Replace YOUR_API_KEY with an AEGIS API key.

Health Check

The /health route is public (no authentication required):

curl https://mcp.aegis.undercurrentholdings.com/health

Local 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.10

Or in one step:

claude mcp add aegis -- aegis-mcp-server

Add 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_id argument)

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_ID in 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, so aegis_list_decisions and aegis_get_decision can return it. audit_chain reports the chain append only; decision_stored reports 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, expect false in 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 reports in-memory when the database is unavailable. Without DATABASE_URL, development stages persist to a local SQLite file, .aegis/aegis.db in the working directory, and report persisted for it.
  • Fail-closed: for an authenticated call in fail-closed stages (any AEGIS_STAGE other than development, dev, test, local, ci, e2e, or unset/empty, compared trimmed and case-insensitively — so production, staging, and unknown values such as prod fail 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 without AEGIS_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 /ready probe 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:

ParameterTypeDescription
proposal_summarystringBrief description of the proposed action
estimated_impactstringImpact level: low, medium, high, critical

Optional parameters:

ParameterTypeDefaultDescription
risk_scorenumber0.0Alias for risk_proposed (0.0-1.0)
risk_proposednumber0.0Predicted risk after change (0.0-1.0)
risk_baselinenumber0.0Current risk level (0.0-1.0). Gate triggers when risk doubles vs baseline with 95% Bayesian confidence.
profit_proposednumber0.0Expected profit/performance after change
profit_baselinenumber0.0Current profit/performance metric
novelty_scorenumber0.5How unprecedented the proposal is (0.0-1.0). Gate passes when novelty >= ~0.74. Logistic inflection at 0.7.
complexity_scorenumber0.5Normalized complexity where HIGHER = SIMPLER (0.0-1.0). Hard floor at 0.5: below this the proposal HALTs and CANNOT be overridden.
quality_scorenumber0.7Overall proposal quality (0.0-1.0). Must be >= 0.7.
quality_subscoresarray[number][0.7, 0.7, 0.7]Detailed quality subscores. None may be zero.
agent_idstring"unknown"Identifier of the calling agent
session_idstring(auto UUID)Session identifier for correlation
phasestring"plan"Lifecycle phase: plan, commit, work
shadow_modebooleanfalseShadow mode: gates evaluate but results are advisory only. Requires Professional tier or higher.
telemetry_urlstring(none)HTTPS URL for structured telemetry events
drift_baseline_dataarray[number](none)30+ historical observations for KL divergence drift detection
requires_human_approvalbooleanfalseWhether this proposal requires human approval
time_sensitivebooleanfalseWhether this proposal is time-sensitive
reversiblebooleantrueWhether this proposal is reversible
metadataobject{}Additional key-value metadata
change_typestring"feature"Type of change: feature, refactor, bugfix, config. Non-feature types skip novelty and profit gates.

Utility auto-synthesis: If utility_result is not provided (the common case), the MCP server auto-synthesizes it from profit_baseline/profit_proposed using 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:

FieldTypeDescription
statusstringDecision: proceed, pause, halt, escalate
confidencenumberMinimum confidence across all gates
gates_passednumberCount of passing gates
gates_failednumberCount of failing gates
failing_gatesarray[string]Names of failed gates
rationalestringHuman-readable decision rationale
next_stepsarray[string]Recommended next actions
constraintsarray[string]Active constraints
decision_idstringUnique decision UUID
timestampstringISO 8601 UTC timestamp
override_eligiblebooleanWhether override is possible
override_requiresarray[string]Roles needed for override
gatesobjectPer-gate detail (value, threshold, confidence, message)
audit_hashstringSHA-256 hash of the decision record (same canonicalization as REST)
chain_hashstringSHA-256 chain hash linking this entry to the previous one
chain_previousstringChain hash of the previous entry (genesis hash for the first)
audit_chainstringpersisted (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_storedbooleantrue 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:

ParameterTypeDescription
action_descriptionstringBrief description of the action to risk-check
risk_scorenumberEstimated risk score (0.0-1.0)

Optional parameters:

ParameterTypeDefaultDescription
thresholdnumber0.5Risk threshold. Score below this is considered safe.
agent_idstring"unknown"Identifier of the calling agent

Response fields:

FieldTypeDescription
safebooleantrue if risk is below threshold
risk_scorenumberThe evaluated risk score
thresholdnumberThe 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:

FieldTypeDescription
gatesarrayArray of 6 gate definitions with name, type, threshold, description
configobjectFull 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:

  1. aegis_check_thresholds — understand gate configuration
  2. aegis_get_scoring_guide(domain) — get derivation formulas for your domain
  3. aegis_evaluate_proposal(...) — submit with derived values

Parameters:

ParameterTypeRequiredDescription
domainstringNoDomain for tailored guidance. One of: trading, cicd, moderation, agents, generic. Defaults to generic if omitted or unrecognized.

Response fields:

FieldTypeDescription
domainstringThe domain returned
domain_descriptionstringBrief description of the domain context
parametersobjectPer-parameter guidance with description, derivation, formula, example, and range_guide
common_mistakesarray[string]Domain-specific pitfalls (e.g., complexity inversion, novelty pass direction)
worked_exampleobjectComplete scenario with input JSON and expected_gates results

Available domains:

DomainUse CaseKey Metric
tradingAlgorithmic trading, portfolio changesVaR / position limit
cicdCI/CD pipelines, deploymentsError rate, rollback rate
moderationContent moderation, safety filtersFalse positive rate
agentsAutonomous AI agentsTask failure rate
genericAny 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:

FieldTypeDescription
signatures.hybridbooleanEd25519 + ML-DSA-65 hybrid available
signatures.bip322booleanBIP-322 Schnorr available
signatures.ed25519booleanEd25519 (deprecated) available
encryption.hybrid_kembooleanX25519 + ML-KEM-768 + AES-256-GCM available
post_quantum.ml_dsabooleanML-DSA-65 signatures available
post_quantum.ml_kembooleanML-KEM-768 encryption available
key_management.aws_kmsbooleanAWS KMS envelope encryption available
key_management.hsm_pkcs11booleanPKCS#11 HSM key wrapping available
schema_signing.availablebooleanAMTSS schema signer package installed
default_providerstringActive signature provider: bip322-simple, ed25519, or none
kek_providerstringBest available KEK provider: kms, hsm, or environment
documentation_urlstringLink 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:

ParameterTypeRequiredDefaultDescription
monthstringNoCurrent monthYYYY-MM filter for decision history
limitintegerNo50Maximum decisions to return (max 200)
customer_idstringNoFrom auth / env varIgnored 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:

FieldTypeDescription
countintegerNumber of decisions returned (length of decisions)
decisionsarrayArray of decision records

Each entry in decisions is the cleaned persisted decision record — the internal storage keys (pk, sk, entity_type, ttl) are stripped, and Decimal values 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 malformed month; 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:

ParameterTypeRequiredDefaultDescription
decision_idstringYes--UUID of the decision to retrieve
monthstringNo--Accepted for compatibility and ignored; lookup is by decision_id for the authenticated customer
customer_idstringNoFrom auth / env varIgnored 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:

FieldTypeDescription
decision_idstringUnique decision UUID
statusstringDecision outcome: proceed, pause, halt, escalate
confidencenumberMinimum confidence across all gates
proposal_summarystringBrief description of the proposal
estimated_impactstringImpact level: low, medium, high, critical
timestampstringISO 8601 UTC timestamp
rationalestringHuman-readable decision rationale
next_stepsarray[string]Recommended next actions
gatesobjectPer-gate evaluation details (value, threshold, confidence, message)
constraintsarray[string]Active constraints
override_eligiblebooleanWhether override is possible
override_requiresarray[string]Roles needed for override
audit_hashstringSHA-256 hash of the decision record for tamper detection
chain_hashstringAudit-chain hash of this entry
chain_previousstringAudit-chain hash of the previous entry
change_typestringChange type submitted with the proposal
phasestringLifecycle phase submitted with the proposal
quality_subscoresarray[number]Quality subscores submitted with the proposal
agent_idstringCalling agent identifier
gate_resultsobjectPer-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:

ParameterTypeRequiredDefaultDescription
proposal_idstringYes--Unique proposal id (1-64 chars)
cycle_nintegerYes--1-indexed meta-improver cycle counter (>= 1)
sourcestringYes--"track-c" (current) or "track-d" (forward-compat)
verdictstringYes--One of PROCEED | PAUSE | ESCALATE | HALT
confidencenumberYes--Model-derived confidence on the verdict (0.0-1.0)
gate_scoresobjectYes--Per-gate score dict
tier_b_votersarrayYes--Per-voter records (array of objects)
tier_b_consensusobjectYes--Consensus summary dict
proposal_diffstringYes--Human-readable diff text
customer_idstringNoFrom auth / env varIgnored 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:

FieldTypeDescription
proposal_idstringThe recorded proposal id
chain_hashstringSHA-256 chain hash of the new entry (64 hex chars)
sequenceinteger1-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:

ParameterTypeRequiredDefaultDescription
limitintegerNo100Maximum proposals to return (max 200)
offsetintegerNo0Pagination offset
customer_idstringNoFrom auth / env varIgnored 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:

FieldTypeDescription
countintegerNumber of proposals returned (length of proposals)
proposalsarrayProposal 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:

ParameterTypeRequiredDefaultDescription
customer_idstringNoFrom auth / env varIgnored 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:

FieldTypeDescription
validbooleanWhether the chain verifies end-to-end
entry_countintegerNumber of entries walked (an empty chain is valid with entry_count 0)
errorstring | nullNull 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_impact is validated as a string enum
  • The validate_float() helper preserves legitimate falsy values like 0 and 0.0

On this page