> ## Documentation Index
> Fetch the complete documentation index at: https://docs.geckovision.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Architecture

> Gecko is a control plane, not a data plane. The invariants, the module map, and the single API-agnostic seam.

Gecko is a **control plane, not a data plane.** It holds the API's *surface*, the
generated tool defs, and *correctness metadata* — and never the data flowing through.
That single invariant is what makes the rest of the design coherent.

## The flow

Two auth relationships and two run modes, around one control-plane engine. Your
agent connects to the **MCP surface**; the engine comprehends the API once, then
serves and calls it — injecting credentials at call time and staying out of the
data path.

<Frame caption="Your agent connects to the MCP surface; Gecko comprehends the API once, then injects credentials at call time and stays out of the data path.">
  <img src="https://mintcdn.com/gecko-d0bce12a/6RZwvxSmH1JLU822/assets/architecture.svg?fit=max&auto=format&n=6RZwvxSmH1JLU822&q=85&s=590aabdb1a51c7280155bcd809063dc8" alt="Gecko flow — the agent states intent to the MCP surface; the control plane comprehends the API (ingest → catalog → tools) and the caller builds the request; access injects credentials at call time; in live mode the real API's data returns to the agent directly, never through Gecko; recorded mode synthesizes the response from the schema for $0 offline." width="1200" height="560" data-path="assets/architecture.svg" />
</Frame>

Local `gecko add` stays **zero-login**; the identity plane gates only the hosted
surface. In `recorded` mode the request is built and proven well-formed but the
response is synthesized from the schema — it never touches the provider plane. In
`live` mode `access` injects credentials and the agent gets the real API's data
**directly**; that data never passes through Gecko.

## Invariants

<CardGroup cols={2}>
  <Card title="Control plane, never data plane" icon="shield-halved">
    Stores the API surface, tool defs, and correctness metadata only — never response
    payloads, user data, or secrets.
  </Card>

  <Card title="The engine is API-agnostic" icon="plug">
    Everything API-specific reduces to data (the spec) plus one adapter seam,
    `Session.auth_headers()`. Adding an API doesn't touch ingest/catalog/tools/caller.
  </Card>

  <Card title="One code path, two modes" icon="code-branch">
    `recorded` and `live` differ only at the transport edge. The free offline
    simulation comes first; live smoke is the final check.
  </Card>

  <Card title="Auth is invisible to the agent" icon="eye-slash">
    Tool defs never expose auth headers. The agent describes intent; Gecko injects
    credentials at call time.
  </Card>
</CardGroup>

## Module map

The comprehension logic is the product and lives in the package; the MCP server, the
client, and the scripts are thin transport.

| Module                | Responsibility                                                                          |
| --------------------- | --------------------------------------------------------------------------------------- |
| `gecko/ingest.py`     | OpenAPI 3.x → normalized `Operation` / `Param` (`$ref` resolution, cycle/depth guarded) |
| `gecko/catalog.py`    | Lexical capability search (intent → endpoint)                                           |
| `gecko/tools.py`      | `Operation` → question-shaped agent tool defs (**auth hidden**)                         |
| `gecko/caller.py`     | tool + args → correct `PreparedRequest` (stdlib `urllib`)                               |
| `gecko/access.py`     | `Session.auth_headers()` — the engine/adapter seam; two-token session                   |
| `gecko/sample.py`     | deterministic schema → example (powers \$0 recorded mode)                               |
| `gecko/client.py`     | `AgentApiClient` — `search` / `list_tools` / `prepare` / `call`                         |
| `gecko/mcp_server.py` | `McpSurface` — the agent-facing MCP surface                                             |
| `gecko/validator.py`  | replay + first-call-correct check + JSONL outcome log (metadata only)                   |
| `gecko/evaluate.py`   | task-based first-call-correct scorecard (top-1 / top-5 / well-formed)                   |
| `gecko/demo.py`       | `run()` (recorded, \$0) + `live_demo()`                                                 |

## The one seam that matters

Adding a new API should not require touching ingest, catalog, tools, or the caller. The
only API-specific code is, at most, an auth adapter — an object that implements:

```python theme={null}
def auth_headers(self) -> dict[str, str]: ...
```

A public API uses the built-in no-auth adapter (returns `{}`); a paywalled API supplies
a session that returns its tokens. See [Access & auth](/access-and-auth).

## Outcome logging (control-plane safe)

The validator and the evaluation harness append **outcome metadata only** — tool name,
rank, ok/reason — to a JSONL log. They never write response payloads. Turning that
metadata into a richer, multi-API correctness signal is V2 work and is **not live
today**; see [Status](/status).

## Security posture

* Ingested spec/doc content is treated as **untrusted input**.
* URLs are validated before fetching (no SSRF — private/loopback/link-local ranges and
  non-http schemes blocked).
* Secrets come from the environment/session and are never logged or persisted; errors
  redact tokens before they're raised.
* Stdlib-first networking (`urllib`); minimal dependencies, so the engine ports
  anywhere.
