Skip to content

Add Agent Server REST API docs to API Reference#397

Open
rbren wants to merge 4 commits intomainfrom
add-agent-server-rest-api-docs
Open

Add Agent Server REST API docs to API Reference#397
rbren wants to merge 4 commits intomainfrom
add-agent-server-rest-api-docs

Conversation

@rbren
Copy link
Contributor

@rbren rbren commented Mar 15, 2026

  • I have read and reviewed the documentation changes to the best of my ability.
  • If the change is significant, I have run the documentation site locally and confirmed it renders as expected.

Summary of changes

This PR reorganizes the API Reference section and adds REST API documentation for the Agent Server.

Changes

1. Reorganized API Reference Structure

The API Reference section now has two subgroups:

  • Python SDK: Contains all existing SDK module documentation (openhands.sdk.agent, openhands.sdk.conversation, etc.)
  • Agent Server: New REST API documentation via OpenAPI spec

2. Added OpenAPI Spec (openapi/agent-server.json)

  • Generated from the FastAPI-based agent server in software-agent-sdk
  • Contains all REST API endpoints with schemas, parameters, and responses
  • Mintlify will automatically render these as interactive REST API docs

3. Updated scripts/generate-api-docs.py

Added a new step generate_agent_server_openapi() that:

  • Runs the openapi.py script from the agent-server package
  • Outputs the spec to openapi/agent-server.json
  • Runs automatically during doc generation

4. Updated Documentation

  • Updated scripts/README.md to document the new OpenAPI generation
  • Updated scripts/generate-api-docs.sh output messages

5. Cleanup

  • Removed duplicate API Reference from the "Remote Agent Server" guides section (was redundant)
  • Renamed agent-sdk.jsonagent-server.json for clarity

New Navigation Structure

SDK Tab
├── ...
└── API Reference
    ├── Python SDK
    │   ├── openhands.sdk.agent
    │   ├── openhands.sdk.conversation
    │   └── ...
    └── Agent Server (OpenAPI)
        └── [Auto-generated REST API docs]

- Reorganize API Reference section with two subgroups:
  - Python SDK: existing SDK module documentation
  - Agent Server: new REST API documentation via OpenAPI spec

- Generate openapi/agent-server.json from software-agent-sdk FastAPI app
- Update generate-api-docs.py to auto-generate OpenAPI spec during doc builds
- Remove duplicate API Reference from Remote Agent Server guides section
- Update scripts README and shell script to reflect new OpenAPI generation

Co-authored-by: openhands <openhands@all-hands.dev>
@rbren rbren marked this pull request as ready for review March 15, 2026 21:56
@rbren rbren requested review from enyst and xingyaoww as code owners March 15, 2026 21:56
Regenerated OpenAPI spec with fix from software-agent-sdk that adds
the 'Server Details' tag to the root '/' endpoint, preventing it from
appearing under a confusing 'API Reference' category.

Co-authored-by: openhands <openhands@all-hands.dev>
Copy link
Contributor

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taste Rating: 🟡 Acceptable → 🔴 Needs rework

The reorganization itself is solid - separating Python SDK from REST API docs makes sense. However, you've created a critical infrastructure failure that will break automated syncing.


[CRITICAL ISSUES]

🔴 Broken Sync Workflow - You renamed openapi/agent-sdk.jsonopenapi/agent-server.json but forgot to update .github/workflows/sync-agent-sdk-openapi.yml. The workflow still writes to the old filename (line 61: SCHEMA_PATH: openapi/agent-sdk.json and line 91: add-paths: openapi/agent-sdk.json). This will cause silent failures when the nightly sync runs.

🔴 Undocumented Spec Version - The OpenAPI spec in this PR contains security annotations that don't exist when generated from the current main branch of software-agent-sdk:

  • PR spec: "security": [{"APIKeyHeader": []}] on most endpoints
  • Fresh generation from main: "security": null

This indicates the spec was either:

  1. Generated from a different branch/commit of agent-sdk
  2. Manually modified (which defeats the purpose of automation)

Required Actions:

  1. Update .github/workflows/sync-agent-sdk-openapi.yml to use agent-server.json
  2. Document in the PR description which agent-sdk commit this spec was generated from
  3. If this aligns with an upstream agent-sdk PR, link to it

[IMPROVEMENT OPPORTUNITIES]

🟢 Good taste: Navigation restructuring is clean and eliminates the duplicate API Reference entry. No notes here.

🟢 Good taste: The rename from "agent-sdk.json" to "agent-server.json" is more accurate since it documents the server API, not the SDK.


VERDICT:Needs rework - The navigation changes are fine, but the infrastructure issues are blocking.

KEY INSIGHT: This is why CI/CD tests matter. You've renamed a generated file but not the generator - exactly the kind of thing that should be caught by a test that validates the workflow can still run successfully.

Keep the existing pipeline for updating the agent-server OpenAPI spec.
The docs.json restructure and openapi/agent-server.json are retained.

Co-authored-by: openhands <openhands@all-hands.dev>
@@ -104,6 +104,11 @@
"summary": "Search Conversation Events",
"description": "Search / List local events",
"operationId": "search_conversation_events_api_conversations__conversation_id__events_search_get",
"security": [
{
"APIKeyHeader": []
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@OpenHands Clone yourself agent-sdk from the same org and look up where does this come from exactly. I think we haven't done anything new like adding another API key header, so why is this newly generated in the generated openapi doc?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm on it! enyst can track my progress at all-hands.dev

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cloned OpenHands/software-agent-sdk and traced this to the existing optional session-auth dependency, not anything introduced in this PR.

  • openhands-agent-server/openhands/agent_server/dependencies.py:11 defines _SESSION_API_KEY_HEADER = APIKeyHeader(name="X-Session-API-Key", auto_error=False).
  • openhands-agent-server/openhands/agent_server/api.py:198-202 attaches that dependency to the whole /api router when config.session_api_keys is non-empty.
  • openhands-agent-server/openhands/agent_server/openapi.py generates the schema from api = create_app(), so it reads the default config from env at import time.

I also verified it locally: with Config(session_api_keys=[]), FastAPI emits no securitySchemes; with Config(session_api_keys=["test-key"]), it emits exactly this APIKeyHeader plus the per-route security block.

So this is not a new header added by the PR — it's the existing X-Session-API-Key auth showing up because the schema was generated in an environment where SESSION_API_KEY / OH_SESSION_API_KEYS_0 was set. If we want the neutral schema in docs, the generator needs to run with those vars unset.

This comment was marked as duplicate.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You’re right to push on that wording — the more precise statement is:

  • openapi.py itself does not read env inside generate_openapi_schema().
  • The env-sensitive part happens earlier, as an import side effect.

Exact chain:

  1. openhands-agent-server/openhands/agent_server/openapi.py does from openhands.agent_server.api import api
  2. openhands-agent-server/openhands/agent_server/api.py defines api = create_app() at module scope
  3. create_app() calls get_default_config() when no config is passed
  4. get_default_config() calls from_env(Config, "OH") once and memoizes it in _default_config
  5. _add_api_routes() adds the session-auth dependency only if config.session_api_keys is non-empty

So the schema is really determined by whatever env existed when openhands.agent_server.api was first imported in that process.

I verified that behavior explicitly:

  • import with no SESSION_API_KEY, then set it afterwards -> no APIKeyHeader appears
  • import with SESSION_API_KEY set, then unset it afterwards -> APIKeyHeader still appears

That means import order / process env snapshot determines the generated OpenAPI, not the later call to generate_openapi_schema().

So yes: I do think this pattern is error-prone for tooling/scripts/tests/docs generation. In the normal server process it is somewhat less surprising because config is expected to be fixed before startup, but for schema generation it’s pretty brittle because of the combination of:

  • module-level singleton app (api = create_app())
  • implicit fallback to env-backed default config
  • memoized _default_config

So I think your instinct was right: the earlier explanation was directionally right about env-sensitivity, but imprecise about where it happens. The real source is import-time app construction + cached default config, not generate_openapi_schema() itself doing a fresh env read.

Co-authored-by: openhands <openhands@all-hands.dev>
Copy link
Collaborator

@enyst enyst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OpenHands-GPT-5.4 here — after digging further, I think the right way to frame this is:

The product contract is that Agent Server has a stable set of REST endpoints / payloads, and it supports optional session-key authentication via X-Session-API-Key when the server is configured with session_api_keys.

What happened here is that the generated OpenAPI appears to be reflecting a deployment snapshot (the env/config present when the app singleton was imported), rather than a canonical product definition.

I think those are importantly different:

  • Product contract: paths, schemas, supported auth mechanisms
  • Deployment policy: whether this specific server instance has session auth enabled

In other words, the correct product definition is not:

this schema requires X-Session-API-Key because that env happened to be set during generation

It is:

Agent Server supports optional session-header auth (X-Session-API-Key), enabled by configuration

So I do think there is a design smell on the agent-server side for tooling/codegen:

  • module-level api = create_app()
  • implicit fallback to env-backed config
  • memoized _default_config

That combination makes schema generation non-deterministic for docs/tests/scripts.

My preference for published docs would be to generate a deterministic canonical schema (probably with session_api_keys=[]) and document separately that X-Session-API-Key is required only when session auth is enabled in the deployment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants