Consumer-Facing Neutral API¶
Date: 2026-03-11 Status: Implemented v1 gateway baseline Scope: Single domain contract exposed through HTTP/OpenAPI and MCP bridge adapters
Purpose¶
This layer exposes runtime execution to external users without coupling clients to internal binding protocols.
The same domain operations are now available through:
- HTTP/OpenAPI adapter
- MCP tool bridge adapter
Both adapters call the same runtime stack, with gateway-mediated operations for skill discovery/list/attach and neutral API operations for execution.
Architecture¶
Core modules:
runtime/engine_factory.py-
Shared runtime construction used by CLI and customer-facing adapters.
-
customer_facing/neutral_api.py - Protocol-neutral domain facade.
-
Operations: health, describe_skill, execute_skill, execute_capability, async runs, run status/cancel, checkpoints, resume.
-
gateway/core.py - Agent-facing gateway layer.
- Operations: list_skills, discover, attach, diagnostics, reset_diagnostics_metrics.
-
Includes attach target validation and diagnostics persistence metadata.
-
customer_facing/http_openapi_server.py - HTTP adapter with v1 routes and OpenAPI spec endpoint.
- Reuses
runtime/openapi_error_contract.pyfor deterministic error mapping. -
Optional API-key authentication (
x-api-key) and in-memory per-client rate limiting. -
customer_facing/mcp_tool_bridge.py - MCP-oriented tool adapter over the same neutral operations.
- Includes stdio loop for lightweight bridge hosting.
v1 HTTP Routes¶
Base version: /v1
GET /v1/healthGET /v1/skills/{skill_id}/describeGET /v1/skills/listGET /v1/skills/governanceGET /v1/skills/diagnosticsPOST /v1/skills/discoverPOST /v1/skills/{skill_id}/attachPOST /v1/skills/{skill_id}/executePOST /v1/capabilities/{capability_id}/executePOST /v1/capabilities/{capability_id}/explainPOST /v1/skills/{skill_id}/execute/asyncPOST /v1/run_async(alias) andPOST /run_async(alias)GET /v1/runsGET /v1/runs/{run_id}POST /v1/runs/{run_id}/cancelGET /v1/runs/{run_id}/checkpointsPOST /v1/runs/{run_id}/resumePOST /v1/runs/{run_id}/approvePOST /v1/runs/{run_id}/denyPOST /v1/runs/{run_id}/replayPOST /v1/runs/{run_id}/forkGET /run_status/{run_id}andGET /v1/run_status/{run_id}(legacy aliases)POST /run_cancel/{run_id}andPOST /v1/run_cancel/{run_id}(legacy aliases)GET /openapi.jsonGET /v1/metricsGET /v1/metrics/prometheus
Async run status model:
- Canonical (
/v1/runs/*):pending,running,waiting_for_human,waiting_for_signal,replaying,completed,failed,canceled. - Legacy aliases project
canceledtofailedfor compatibility with older clients.
Checkpoint and resume contract:
GET /v1/runs/{run_id}/checkpointsreturns checkpoint list +checkpoint_head.POST /v1/runs/{run_id}/resumeaccepts optionalcheckpoint_id.- Current slice behavior: resume mode is
checkpoint_resume; the runtime restores the checkpointed state and continues execution from the remaining steps. POST /v1/runs/{run_id}/approveaccepts optionalapproverandnotes, then resumes a run that is waiting for human approval.POST /v1/runs/{run_id}/denyaccepts optionalapproverandnotes, then cancels the run canonically.POST /v1/runs/{run_id}/replayaccepts optionalcheckpoint_id, creates a new replay run, and continues from the restored checkpoint state.POST /v1/runs/{run_id}/forkaccepts optionalcheckpoint_id, creates a new pending run, and preserves source linkage without re-executing.
Async launch idempotency:
POST /v1/run_asyncandPOST /run_asyncaccept optionalidempotency_key(orx-idempotency-keyheader).- Repeating the same async launch with the same
skill_id+idempotency_keyreturns the existing run instead of creating a duplicate. - Reusing the same
idempotency_keywith a different async request payload returns409 idempotency_conflict. - Server-side TTL for idempotency keys is configured with
AGENT_SKILLS_IDEMPOTENCY_TTL_SECONDS(default86400). After expiry, the same key can create a new run.
Idempotency observability:
- Runtime counters are available via
GET /v1/metrics: runtime.idempotency.createdruntime.idempotency.reusedruntime.idempotency.conflictruntime.idempotency.expired- Prometheus exposition is available via
GET /v1/metrics/prometheuswith normalized names: agent_skills_runtime_idempotency_created_totalagent_skills_runtime_idempotency_reused_totalagent_skills_runtime_idempotency_conflict_totalagent_skills_runtime_idempotency_expired_total
Security model (configurable):
GET /v1/healthandGET /openapi.jsoncan remain unauthenticated.- All execution/describe routes can require
x-api-key. - Protected routes can enforce request rate limits with
429responses.
OpenAPI spec file:
docs/specs/consumer_facing_v1_openapi.json
MCP Tool Surface¶
Exposed tools:
runtime.healthskill.describeskill.listskill.discoverskill.diagnosticsskill.metrics.resetskill.attachskill.executecapability.executecapability.explainskill.governance.list
Bridge entrypoint:
tooling/run_customer_mcp_bridge.py
Error Contract¶
Both adapters map runtime exceptions through:
runtime/openapi_error_contract.py
Error payload shape:
{
"error": {
"code": "string",
"message": "string",
"type": "string"
},
"trace_id": "string"
}
Runbook¶
Run HTTP/OpenAPI server¶
python tooling/run_customer_http_api.py --host 127.0.0.1 --port 8080
Run with API key + rate limit:
python tooling/run_customer_http_api.py --host 127.0.0.1 --port 8080 --api-key local-dev-key --rate-limit-requests 20 --rate-limit-window-seconds 60
Run MCP bridge (stdio)¶
python tooling/run_customer_mcp_bridge.py
Example stdio request line:
{"id":"1","method":"tools/call","params":{"name":"skill.execute","arguments":{"skill_id":"agent.plan-from-objective","inputs":{"objective":"Build a plan"}}}}
Example attach request line (generic sidecar attach):
{"id":"2","method":"tools/call","params":{"name":"skill.attach","arguments":{"skill_id":"agent.trace","target_type":"output","target_ref":"<existing-trace-or-output-ref>","include_trace":true,"inputs":{"goal":"Trace current orchestration","events":[],"trace_state":{},"trace_session_id":"session-1"}}}}
Verify both adapters¶
python tooling/verify_customer_facing_neutral.py
Verify HTTP controls (auth + throttling)¶
python tooling/verify_customer_http_controls.py
Verify HTTP/MCP parity snapshot¶
python tooling/verify_customer_facing_parity_snapshot.py
Inspect idempotency telemetry¶
JSON metrics snapshot:
curl -s http://127.0.0.1:8080/v1/metrics
Prometheus counters only:
curl -s http://127.0.0.1:8080/v1/metrics/prometheus | grep idempotency
Operational interpretation:
- High
createdwith lowreusedis expected for unique client requests. - Rising
reusedconfirms retries are being deduplicated successfully. - Rising
conflictindicates clients are reusing keys with changed payloads and should be investigated. - Rising
expiredindicates key cleanup is active; if too high, review retry windows andAGENT_SKILLS_IDEMPOTENCY_TTL_SECONDS.
Design Invariants¶
- Consumer-facing contract is protocol-neutral and stable.
- Internal provider protocols (pythoncall/openapi/mcp/openrpc) remain behind binding resolution.
- Adding or changing internal protocols must not require external API contract changes.
- Trace propagation uses
x-trace-idheader ortrace_idbody field. - Skill ranking is heuristic; product agents should apply selection policy and not rely exclusively on top-1 score.
Product Agent Orchestration Pattern¶
Recommended policy for product-facing agents:
- Discover candidates for the primary user intent.
- Execute primary skill.
- Attach optional sidecar skills (monitoring/control/reporting) when requested by user policy.
- Return both business output and execution trace/control summary.
This pattern treats sidecar skills as a normal classified skill category (role=sidecar,
invocation=attach|both) and avoids hard-coded behavior for any single skill id.
Next Steps¶
- Replace in-memory rate limiting with distributed/shared limiter for multi-instance deployment.
- Replace static API key with pluggable authn/authz provider.
- Replace stdio MCP bridge with full MCP server transport integration.
- Extend parity snapshots across more capabilities and representative error cases.