API Documentation & OpenAPI Spec¶
AGRO exposes a full OpenAPI 3 schema through the FastAPI app defined in server.asgi:create_app. The legacy server/app.py entry point just wires that up for older setups.
This page explains:
- Where the OpenAPI spec comes from
- How configuration flows into the API layer
- How the web UI and MCP servers use it
- How to extend the API without fighting the config system
Where to find the docs¶
When the AGRO server is running, you get three useful HTTP surfaces:
-
OpenAPI JSON
GET /openapi.json
Machine‑readable schema for all endpoints, models, and error responses. -
Swagger UI
GET /docs
FastAPI’s interactive docs. Good for poking individual endpoints. -
ReDoc
GET /redoc
Alternative, more compact view of the same OpenAPI schema.
All three are backed by the same FastAPI app and the same Pydantic models.
How configuration reaches the API¶
AGRO does not hard‑code behavior in the HTTP layer. Almost everything flows through the central configuration registry in server/services/config_registry.py.
The registry merges three sources with a clear precedence:
.envfile – infrastructure and secrets (highest precedence)agro_config.json– tunable RAG parameters, model config- Pydantic defaults – safe fallbacks
Config precedence in practice
If you set FINAL_K in both agro_config.json and .env, the .env value wins. The API layer never talks to os.environ directly; it always goes through the registry.
The API services you hit from OpenAPI are thin wrappers around this registry:
server/services/rag.py– search & answer endpointsserver/services/indexing.py– indexing control & statusserver/services/keywords.py– discriminative keyword generation & tuningserver/services/editor.py– embedded editor / devtools settingsserver/services/traces.py– trace listing and retrievalserver/services/config_store.py– reading/writingagro_config.jsonand secrets
Each of these modules pulls configuration via get_config_registry() and exposes a small, typed surface to FastAPI.
Example: retrieval endpoints and FINAL_K¶
The retrieval endpoints are wired through server/services/rag.py. The OpenAPI schema for these endpoints is generated from the FastAPI route definitions and the Pydantic models they use.
A concrete example is how top_k is resolved when you don’t pass it explicitly:
From the OpenAPI side you just see top_k: int | None. The actual default is resolved at runtime from the config registry, not baked into the schema.
This is intentional:
- The OpenAPI spec stays stable
- You can change retrieval behavior via
.env/agro_config.jsonwithout regenerating anything - Tools that call the API (including MCP clients) don’t need to know about the internal knobs
Example: indexing control¶
Indexing is exposed as a small set of endpoints that ultimately call server/services/indexing.py:start and friends.
The OpenAPI schema for the indexing endpoint only needs to describe payload (e.g. { "enrich": true }). Everything else – which repo to index, how to resolve paths, whether to enrich chunks – is driven by the config registry and environment.
Config‑aware admin & editor endpoints¶
Several admin‑style endpoints exist purely to support the web UI and devtools. They’re still part of the same OpenAPI schema, but they’re worth calling out because they lean heavily on the registry.
Editor settings¶
server/services/editor.py exposes a small JSON surface that the web UI uses to configure the embedded editor / devtools panel:
The OpenAPI schema just says “this endpoint returns a JSON object with these fields.” The actual values – and whether the editor is even enabled – come from .env / agro_config.json.
This is the pattern across the admin endpoints:
- Config store (
server/services/config_store.py) – reads/writesagro_config.json, hidesSECRET_FIELDSfrom responses, and validates againstAGRO_CONFIG_KEYS. - Keywords (
server/services/keywords.py) – exposes keyword stats and regeneration, but all the tuning knobs (KEYWORDS_MAX_PER_REPO,KEYWORDS_BOOST, etc.) are cached from the registry. - Traces (
server/services/traces.py) – lists and fetches trace files underout/<repo>/traces, withrepodefaulting toREPOfrom the environment.
Secrets and the OpenAPI schema¶
server/services/config_store.py is the main place where secrets and OpenAPI meet.
The OpenAPI schema for the config endpoints is generated from Pydantic models that exclude these fields from responses. You can still set them via the API (or .env), but you won’t see them echoed back in plain text.
Warning
Don’t rely on the OpenAPI schema as a “list of all secrets.” New providers and keys are added over time. The authoritative list is SECRET_FIELDS in config_store.py and the .env template in getting-started/environment-example.md.
How MCP servers use the OpenAPI spec¶
AGRO’s MCP integration (see features/mcp.md) doesn’t talk to FastAPI directly, but it does rely on the same Pydantic models and service layer that generate the OpenAPI schema.
The flow looks like this:
sequenceDiagram
participant MCP as MCP Client (Claude Code / Codex)
participant Server as AGRO MCP Server
participant HTTP as AGRO HTTP API
participant RAG as Retrieval Services
MCP->>Server: tool call (e.g. search_code)
Server->>HTTP: HTTP request (JSON body)
HTTP->>RAG: call service layer (rag.py, indexing.py, ...)
RAG-->>HTTP: structured result (Pydantic models)
HTTP-->>Server: JSON response
Server-->>MCP: tool result
Because everything is typed once (Pydantic models → FastAPI → OpenAPI), you get:
- A single source of truth for request/response shapes
- The same validation and defaults whether you call via HTTP, the web UI, or MCP
- Less drift between “what the docs say” and “what the MCP tools actually accept”
You don’t need to regenerate or hand‑edit the OpenAPI spec to make MCP work; you just add or adjust Pydantic models and service functions.
Extending the API safely¶
If you want to add a new endpoint or tweak an existing one, the safest path is:
- Add or modify a Pydantic model under
server/models/(or extendAgroConfigRootif it’s a config surface). - Add a small service function under
server/services/that: - Pulls configuration via
get_config_registry() - Does the work
- Returns plain dicts / Pydantic models
- Wire it into FastAPI in the ASGI app (or the relevant router).
FastAPI will:
- Infer the OpenAPI schema from your function signature and models
- Expose it at
/openapi.json,/docs,/redoc - Keep the web UI and MCP servers in sync automatically
Use AGRO to document AGRO
The AGRO repo is indexed into its own RAG engine. If you’re not sure how a particular endpoint is wired, open the Chat tab in the web UI and ask something like:
“Show me where the indexing HTTP endpoints are defined and how they call
server/services/indexing.py.”
You’ll get code‑level answers with links into the repo, which is usually faster than grepping through handlers by hand.
OpenAPI and environment‑driven behavior¶
One last point: the OpenAPI schema is intentionally static. It describes shapes, not behavior.
Behavior is driven by:
.env(infrastructure, secrets, repo selection)agro_config.json(models, retrieval knobs, evaluation settings)- The configuration registry’s precedence rules
This means:
- You can run the same OpenAPI schema against very different backends (local models vs cloud, different repos, different retrieval strategies).
- Small codebases can stick to simple BM25‑only configs without touching the API.
- When you do turn on more advanced features (learning reranker, enrichment, MCP tools), the HTTP surface doesn’t change shape – only what happens behind it.
If you need to see exactly what the current server thinks the API looks like, GET /openapi.json is the ground truth.