Skip to main content
Glama
README.md
---
title: Hatun MCP β€” Governed Agent Gateway
emoji: πŸͺ’
colorFrom: indigo
colorTo: blue
sdk: docker
app_port: 7860
pinned: false
license: apache-2.0
short_description: Source-bound MCP gateway with signed governance receipts
---

[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
[![Doctrine v11 LOCKED](https://img.shields.io/badge/Doctrine-v11_LOCKED-d4a444.svg)](https://github.com/szl-holdings/lutar-lean)
[![CI](https://github.com/szl-holdings/hatun-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/szl-holdings/hatun-mcp/actions/workflows/ci.yml)
[![SLSA](https://img.shields.io/badge/SLSA-L1_honest_%C2%B7_L2_roadmap-22c55e.svg)](https://slsa.dev/spec/v1.0/levels)

<div align="center">

# πŸͺ’ hatun-mcp

**The great context protocol** β€” *hatun* (Quechua) = "big / great".

The **one signed MCP endpoint** that aggregates the SZL backend services β€” the
**a11oy** command platform (and its live immune, companion and llm-router organs)
plus **killinchu** (drones & vessels) β€” under PURIQ governance and re-exposes their
tools to any MCP client.

[Hatun Gateway](https://a-11-oy.com/wires) Β·
[GitHub Org](https://github.com/szl-holdings) Β·
[LLM Router](https://github.com/szl-holdings/szl-router)

</div>

> **Canonical runtime source.** This repository owns the Python gateway, governed tool
> catalog, Streamable HTTP transport, and container contract. The standalone Hatun Hugging
> Face publisher was retired on September 3, 2026; `hf-deploy` must not be recreated.
> The public product experience is [Hatun Gateway](https://a-11-oy.com/wires), which is not
> itself proof that this package's `/mcp/` endpoint or a newly added tool is deployed.
> See the [surface contract](docs/HATUN_SURFACE_CONTRACT.md). The monorepo copy at
> [`platform/packages/hatun-mcp`](https://github.com/szl-holdings/platform/tree/main/packages/hatun-mcp)
> is a **non-canonical embedded copy** (it carries `CANONICAL.md` pointing here) that exposes a
> smaller tool set for local imports and must not diverge from this server's contracts. Folding
> the platform copy in is a later founder step; repos are **not deleted** here. Ξ› = **Conjecture
> 1** (advisory) is preserved verbatim.

<!-- szl:header v1 -->
<!-- Native identity: szl-brand@4309f6fce89179331b0b3ed8c99494e76c5be256; Orbit v2 icon. -->
<p>
  <a href="https://github.com/szl-holdings">
    <img src="https://raw.githubusercontent.com/szl-holdings/szl-brand/4309f6fce89179331b0b3ed8c99494e76c5be256/kit/logos/orbit-v2/suite/szl-icon-32.svg" alt="SZL Holdings β€” Orbit v2" width="32" height="32" />
  </a>
</p>

**Control before action. Evidence after.**

[Product](https://a-11-oy.com) Β· [Proof](https://a11oy.net) Β· [GitHub organization](https://github.com/szl-holdings)
<!-- /szl:header -->

<div align="center">

`receipts.in ≑ receipts.out`

</div>

---

## What this is

A **real, operational** Model Context Protocol server built on the official `mcp`
Python SDK (`mcp.server.fastmcp.FastMCP`). Every tool call is governed by the PURIQ
formula:

1. **Authenticate** the client (SZL API key β†’ `client_id`); anonymous calls are declined.
2. **Yuyay-13 gate** on the input (input-as-data; OWASP MCP06 injection defense).
3. **Reputation** factor `Hatun_MCP(client) ∈ [0,1]`.
4. **2-person Yuyay gate** for state-changing tools (e.g. `killinchu_cue`, `halt_drone`).
5. Call the **real organ backend** within a latency budget.
6. Mint a **Khipu receipt** on success **and** failure (append-only sha256 DAG).
7. Return a **DSSE-signed** response β€” the client receives the **receipt hash**.

### Choose your route

| Audience | Start here | Evidence boundary |
|----------|------------|-------------------|
| **Developer** | [Run locally](#run-locally-stdio), then inspect `tools/list` | Local startup proves only the local process; backend reachability is separate |
| **Integrator** | [MCP client setup](#mcp-client-setup) | The checked-in client configuration is **SAMPLE** and does not include credentials |
| **Evaluator** | [Hosted health contracts](#evaluate-the-hosted-contract), then [Tests](#tests) | `/healthz` is liveness; `/readyz` is signed-release readiness; neither is an uptime guarantee |

### KANCHAY status contract

- **LIVE**: a runtime-backed route with source and observation time. It never means perpetual
  availability.
- **PARTIAL**: Hatun is locally ready, but one or more required upstream organ observations are
  missing, stale, or degraded.
- **SAMPLE**: checked-in configuration, payload, or transcript for reuse; not observed runtime
  evidence.
- **SIMULATED**: a mocked backend or hermetic test fixture. CI intentionally uses these where
  external services would make tests nondeterministic.
- **UNAVAILABLE**: the dependency or readiness check cannot produce a usable result. Preserve the
  reason and do not substitute sample data.

### Tools exposed

- **26 static tools** registered at import (verifiable: `tools/list` returns 26 with
  `HATUN_MCP_DISABLE_DYNAMIC=true`):
  - **20 `szl_*` tools** β€” `szl_a11oy_code_chat`, `szl_a11oy_operator_reason`, `szl_a11oy_sentinel_scan`,
    `szl_anatomy_3d_render`, `szl_doctrine_lookup`, `szl_drone_lookup`,
    `szl_formula_evaluate`, `szl_github_estate_snapshot`, `szl_khipu_verify`, `szl_killinchu_cue`,
    `szl_killinchu_detect`, `szl_lean_verify`, `szl_puriq_evaluate`,
    `szl_companion_reason`, `szl_immune_scan`, `szl_thesis_query`, `szl_wayra_recent`,
    `szl_yachay_dome_predict`, `szl_yuyay_score`, and **`szl_lambda_quorum`** (Byzantine Ξ› verdict).
    > Two tools were **renamed 2026-06-16** to honest organ names β€”
    > `szl_immune_scan` (was the retired codename scan tool) and
    > `szl_companion_reason` (was the retired codename reason tool). See
    > [`DEPRECATED.md`](DEPRECATED.md) for the old→new mapping. The old names are
    > **not** served (they are not registered in `tools/list`).
  - **6 governance tools** β€” `yuyay_gate_check`, `khipu_append_and_verify`,
    `dsse_sign`, `mesh_quorum_status`, `puriq_master_tool`, `governance_pacbayes_bound`.
- **Service-derived tools** registered *dynamically* at startup from each backend
  service's live catalog at `/api/<service>/v1/mcp/tools`, named `<service>_<tool>`. The
  dynamic count is **probe-dependent**: it equals 26 + (whatever the reachable services
  publish), and is 0 extra when dynamic registration is disabled or all services are
  unreachable.

### Public GitHub estate evidence

Call `szl_github_estate_snapshot` with `{}` to observe the fixed public
`szl-holdings` organization. This is a structural inventory tool, not an LLM
answer or a merge bot. It accepts no organization, URL, token, cursor, or other
argument. GitHub access uses tokenless, redirect-disabled GETs to a fixed
origin; environment credentials and proxies are not inherited by that client.

The observer reports repository identities and default-branch names, bounded
open-PR base/head SHAs, draft state, and check runs queried at those exact head
SHAs. Citations include request paths and parameters, response byte lengths and
SHA-256 digests. A canonical snapshot digest is included in the Khipu receipt
and its DSSE payload; a configured P-256 key signs that receipt. Without a key,
the envelope remains explicitly `PLACEHOLDER` with no signatures. A digest or a
complete inventory is not an independent witness or a cryptographic signature.

Hard per-call limits: 15 seconds, 20 requests, 3 repository pages (300 records),
50 open PR search results, 100 check runs per PR, 1 MiB of accepted response-body
data per response and 4 MiB accepted across the call. Exhaustion stops queued
requests and further body acceptance. These are application acceptance limits,
not a measurement of wire traffic or transport prefetch; already-delivered but
rejected bytes are not counted as accepted. Overflow, timeouts, rate limits, malformed
records, stale PR search evidence, and absent or unrecognized check results
produce explicit gaps and `INCOMPLETE` or `UNAVAILABLE`. Zero checks are
`UNKNOWN`, never success. `COMPLETE` means the declared observation scope was
covered; failed CI may still be completely observed. It does not mean the
estate is operational or safe to merge.

This v1 deliberately does not attest private repositories, resolved default
branch heads, legacy commit-status contexts, reviews, protection rules, merge
eligibility, HF publication, model quality/training, or deployed runtime health.
Its multiple requests are not an atomic provider snapshot. Each response is
input data, not instructions. Normal Hatun authentication and receipt handling
still apply, including optional operator-configured `SZL_RECEIPT_SINK`
forwarding; the GitHub observer performs no provider mutations.

> **Naming note.** The three previously-codenamed backends were **purged**; their
> capabilities are now served directly by the **live honest a11oy organs** on
> `a-11-oy.com`: the **immune** organ (egress policy/gates inspector β€” *Hukulla*),
> the **companion** organ (operator / reasoning console), and the **llm** organ
> (open-LLM tier router). Hatun-MCP addresses them by these honest role names; the
> live routes are published in `/openapi.json`.

### Recorded reachability snapshot (HONESTY OVER CHECKLIST)

The table below records repository evidence dated **2026-06-16**. It is not a current health
probe. `/healthz` and `/readyz` establish Hatun's local process, receipt chain, and signer only;
they do not probe the upstream organs. Before presenting any row as currently **LIVE**, make a
separate bounded, read-only probe of that row's listed route (or a documented non-mutating
readiness route), and record the response status, source, and observation timestamp. For
POST-only or state-changing surfaces, use a pre-authorized non-mutating contract probe or a
timestamped receipt; never trigger an action merely to claim availability. If any required
upstream observation is missing, stale, or unusable, present that row as **PARTIAL** or
**UNAVAILABLE**.

| Backend organ | Catalog route | Recorded state (2026-06-16) |
|-----------------|---------------|-------------------|
| a11oy β€” **llm** open-LLM tier router | `GET /api/a11oy/v1/llm/tiers` | **LIVE (200)** β€” `llm_tiers` derived from the live tier catalog |
| killinchu | `/api/killinchu/v1/mcp/tools` | **LIVE** β€” 4 tools (cue/halt_drone are 2-person) |
| a11oy β€” **companion** operator / reasoning console | `/api/a11oy/v1/companion/{ask,act,recommend}` | **LIVE (200)** β€” 3 tools derived from live action routes (no JSON `/v1/mcp/tools` catalog) |
| a11oy β€” **immune** (Hukulla) egress policy inspector | `GET /api/a11oy/v1/immune/gates` | **LIVE (200, gates-derived)** β€” gates + `screen`/`verdict`; the immune *screen* is the signed `/immune/verdict` route (there is no separate `/screen`) |
| a11oy β€” command / flagship | `/api/a11oy/v1/mcp/tools` | Registers a11oy-flagship tools when the JSON catalog is exposed, else one honest `a11oy_status` tool. **Self-heals** on the next server restart once the catalog returns 200 β€” no code change, no fabricated stubs. |

> **Purge note (2026-06-16).** The three previously-codenamed backends were purged
> (their old routes now 404). Hatun-MCP was repointed to the **live honest a11oy
> twins** above and verified 200 before wiring. No tool is ever pointed at a 404;
> where a sub-route does not exist (e.g. `/immune/screen`) the tool maps to the
> closest real route (`/immune/verdict`) and the mapping is disclosed in the
> adapter docstring and the catalog `reason`.

### Byzantine quorum + BLS aggregate

`szl_lambda_quorum` fans a governance-critical Ξ› verdict out to the five backend services
and decides under a **Byzantine n β‰₯ 3f+1 quorum (n=5, f=1)**: β‰₯ 4 services must be reachable
and β‰₯ 3 must agree. Participating receipts are **BLS12-381 aggregated** (`py_ecc`;
honest sha256 Merkle-root fallback if the BLS backend is absent). If any organ's
policy route is not live, quorum degrades gracefully (n=4 still satisfies n β‰₯ 3f+1)
and discloses the degradation in `governance.quorum`.

---

## Run locally (stdio)

```bash
pip install -r requirements.txt
python -m hatun_mcp.server          # stdio mode for Claude Desktop / Codex
```

## Run hosted (Streamable HTTP)

```bash
uvicorn hatun_mcp.server_http:app --host 0.0.0.0 --port 7860
# MCP endpoint:  http://127.0.0.1:7860/mcp   (legacy SSE at /sse)
# Process liveness: http://127.0.0.1:7860/healthz
# Signed-release readiness: http://127.0.0.1:7860/readyz
```

The DSSE signing key is injected at runtime via the `HATUN_MCP_SIGNING_KEY` (PEM)
operator-managed secret; without it the signer runs in honest `PLACEHOLDER` mode (clearly
labeled, never a fake signature).

`/healthz` proves that the process and local receipt chain can answer. `/readyz`
is the fail-closed investor/deployment contract: it returns `200` only when the
receipt chain verifies and a non-placeholder signing key is active; otherwise it
returns `503` with the failing check named. The public server card advertises only
the API-key scheme that this server actually implements.

### MCP manifest attestation

`GET /.well-known/mcp-manifest-attestation` returns a cached integrity binding for
the exact raw bytes served by `GET /.well-known/mcp` (all server-card aliases serve
those same bytes). It contains a deterministic
[`https://in-toto.io/Statement/v1`](https://in-toto.io/Statement/v1) with SHA-256
subject digest and the custom predicate type
`https://szlholdings.com/attestations/mcp-manifest/v1`. When the existing P-256
signing key is configured, the statement is carried in a real DSSE envelope whose
`payloadType` is `application/vnd.in-toto+json`. Without that key, the response is
explicitly `signing.state=UNSIGNED` with `dsseEnvelope=null`; an empty signature is
never presented as signed.

The artifact is built once at process start and accepts no caller-supplied signing
payload. It does not mint a Khipu receipt. Its scope is byte integrity only: it does
not attest runtime tool parity, upstream availability, or the behavior behind the
card. The MCP server card and this well-known route are an SZL **DRAFT extension**,
not a claim of a ratified MCP discovery standard. `keyid` is only a hint; verifiers
must establish trust in the P-256 public key out of band (the same-origin `/pubkey`
route is not an independent trust anchor). Transparency-log inclusion remains
explicitly unavailable.

## Evaluate the hosted contract

```bash
curl -i http://127.0.0.1:7860/healthz
curl -i http://127.0.0.1:7860/readyz
curl -s http://127.0.0.1:7860/api/console-state
```

`/api/console-state` (`hatun_mcp/state.py`) is the read behind the human console at `/`.
The commands above target your locally started server. For a deployment, use
its admitted operator-provided origin, not the retired standalone Space host.
It is assembled in-request from this process only: the tool catalogue is enumerated from
the LIVE FastMCP registry (not a hand-maintained list), the receipt depth and head hash
come from the live Khipu chain, and `card_parity` reports a MEASURED comparison between
the published server card and that runtime registry. Anything it cannot read is returned
with an honest label (`UNAVAILABLE`) and no number β€” there is no seeded snapshot, so the
console shows `UNAVAILABLE` rather than a stale value when a reading fails. It mints no
receipt and attests nothing beyond the reading itself.

Record the response status and observation time. Report `healthz=200` as Hatun process liveness
only. Report `readyz=200` as Hatun's repository-defined local receipt-chain and signer readiness
only. These checks do not establish killinchu or a11oy organ availability. Before labeling any
upstream row **LIVE**, separately run a bounded, read-only probe of its listed route (or a
documented non-mutating readiness route) and record the route, response status, source, and
observation timestamp. For POST-only or state-changing surfaces, require a pre-authorized
non-mutating contract probe or timestamped receipt instead of triggering an action. If Hatun is
ready but an upstream observation is missing, stale, or unusable, report that row as **PARTIAL**
or **UNAVAILABLE**. Never fall back to the sample client configuration.

---

## MCP client setup

### Claude Desktop
The snippets below are **SAMPLE local-server configurations**. Start the HTTP
server first, configure an accepted API key, and replace `szl_YOUR_KEY`. For a
remote deployment, substitute the admitted operator-provided HTTPS MCP URL.
Drop `examples/claude-desktop-config.json` into
`~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) /
`%APPDATA%\Claude\claude_desktop_config.json` (Windows), replacing `szl_YOUR_KEY`:

```json
{
  "mcpServers": {
    "hatun-mcp": {
      "command": "npx",
      "args": ["-y", "mcp-remote",
        "http://127.0.0.1:7860/mcp/",
        "--header", "Authorization: Bearer szl_YOUR_KEY"]
    }
  }
}
```

### Codex (`~/.codex/config.toml`)
```toml
[mcp_servers.hatun-mcp]
command = "npx"
args = ["-y", "mcp-remote", "http://127.0.0.1:7860/mcp/",
        "--header", "Authorization: Bearer szl_YOUR_KEY"]
```

### Continue (`~/.continue/config.json`)
```json
{
  "experimental": {
    "modelContextProtocolServers": [
      {
        "transport": {
          "type": "stdio",
          "command": "npx",
          "args": ["-y", "mcp-remote",
            "http://127.0.0.1:7860/mcp/",
            "--header", "Authorization: Bearer szl_YOUR_KEY"]
        }
      }
    ]
  }
}
```

---

## Tests

```bash
HATUN_MCP_DISABLE_DYNAMIC=true python -m pytest tests/ -q
# tests/test_server.py    β€” list tools, call a tool, assert response shape
# tests/test_github_estate_snapshot.py β€” bounded public GitHub observer, mocked provider
# tests/test_quorum.py    β€” quorum math + threshold edge cases + BLS aggregate
# tests/test_adapters.py  β€” mocked organ endpoints, adapter wiring + honest gaps
# tests/test_governance.pyβ€” Khipu chain, Yuyay gate, PURIQ factor (pre-existing)
# tests/test_http_routes.py β€” exact-byte card attestation + public HTTP contracts
```

The server tests also import the estate regression class so the existing
explicit-file hosted CI gate exercises it. They verify real in-memory MCP
discovery/calls, argument rejection, canonical digest binding, and an ephemeral
P-256 signature. Provider responses in these tests are **SIMULATED**; passing
them does not publish the new tool or establish current GitHub/HF health.

`python tests/proof_inmemory.py` exercises the real in-memory MCP protocol and
checks exact static runtime/server-card name parity. The proof disables dynamic
catalog probing and receipt-sink forwarding in its own process. CI also runs it
from an unrelated working directory to verify the documented direct command.

## The Ouroboros loop (doctrine cross-reference)

hatun-mcp does **not** implement the estate's Ouroboros bounded-recursion kernel itself; its
PURIQ orchestrator is a **bounded, single-pass per-tool-call flow**, not recursion. This
section is a **doctrine cross-reference** plus an honest note on how that flow embodies the
loop's receipt-closed identity (`receipts.in ≑ receipts.out`, already carried in this README).

The canonical definition is the receipt-closed kernel
[`szl-holdings/ouroboros` β†’ `src/loop-kernel.ts`](https://github.com/szl-holdings/ouroboros/blob/main/src/loop-kernel.ts) (`runLoop`): *bounded recursion with measurable convergence* that MUST terminate on one
of four exit conditions β€” `converged | consistent | aborted | budgetExhausted` β€” and emits a
governance receipt for every run. **The trace is the product.**

How hatun-mcp embodies that primitive (in `hatun_mcp/puriq.py`):

- **Bounded & terminating.** Each tool call is a finite pipeline β€” Yuyay-13 gate β†’ mesh
  quorum (Byzantine n β‰₯ 3f+1) β†’ HUKLLA tripwire β†’ Khipu append β†’ DSSE-sign β†’ compose the
  master-formula scalar β€” that always terminates within a latency budget and returns a
  receipt hash. There is no unbounded loop.
- **Receipt-closed.** Every call mints a Khipu link on an append-only sha256 DAG and the
  client receives the **receipt hash**. That is this repo's live realization of the header
  identity `receipts.in ≑ receipts.out` β€” a **metaphor (doctrine, not math)**, where each
  signed receipt is fed back into the DAG as an auditable input.

**Honesty (Doctrine v11 Β· 749/14/163):** Ξ› is consumed here as an input scalar in [0,1] and
is **Conjecture 1** β€” advisory, *never* a proven theorem. This is a *bounded, terminating*
governance flow β€” it makes **no** perpetual-motion or zero-cost claim.

---

<sub>

**Doctrine v11 LOCKED β€” 749 / 14 / 163 Β· Ξ› = Conjecture 1 (NOT a theorem) Β· SLSA L1 honest Β· L2 verified-provenance on roadmap (L3 not claimed)**
`receipts.in ≑ receipts.out`

Signed-off-by: Yachay &lt;yachay@szlholdings.ai&gt;
Co-Authored-By: Perplexity Computer Agent &lt;agent@perplexity.ai&gt;

</sub>

Maintenance

ActivityActive
ResponsivenessSlow