Skip to content

MCP Reference (agents)

Agent integration

The Model Context Protocol (MCP) surface lets AI agents query AOI. It exposes a single tool, aoi_dsl_query, which executes DSL. Learning DSL is learning MCP - the tool is the whole surface.

Beta - subject to change

The MCP surface may change during the beta. Breaking changes are announced in the Changelog.

Endpoints

Endpoint Transport Auth
POST /mcp Streamable HTTP (protocol 2025-06-18) Bearer JWT, Auth0 token, or MCP_API_KEY — plus mcp:access

POST /mcp is the only MCP endpoint.

  • GET /mcp returns 405 (no SSE); DELETE /mcp ends a session (Mcp-Session-Id).
  • GET /mcp/capabilities returns a capabilities document.
  • GET /.well-known/oauth-protected-resource (unauthenticated) returns RFC 9728 metadata naming the authorization server. An unauthenticated POST /mcp answers 401 with a matching WWW-Authenticate challenge.
  • Send the token in the Authorization header. A ?token= query parameter is rejected on MCP paths.

Removed in 0.16.0-beta

POST /mcp/v1 and GET /mcp/v1/capabilities now return 404. Use POST /mcp and GET /mcp/capabilities. The request body is unchanged, but two response-side differences require client changes:

  1. Tool-execution errors are HTTP 200 with the error in the JSON-RPC body. /mcp/v1 returned HTTP 400. Clients that branch on HTTP status must branch on the presence of error instead.
  2. The success envelope is nested one level deeper. See Tool response shape below.

Methods

  • initialize
  • notifications/initialized
  • tools/list
  • tools/call
  • ping

initialize returns serverInfo { "name": "AOI MCP Server" } plus agent instructions.

The aoi_dsl_query tool

The single tool executes any DSL command - LIST / GET / COUNT / TITLECONVERT / COMPANYCONVERT / CLUSTERCONVERT / SUBMIT / HELP and more. Because DSL is the superset, title and company resolution are expressed inline.

Arguments

Arg Required Values
query yes Any DSL command string

format retired in 0.16.10

The format argument (standard | compact | detailed) is gone. There is one response shape. Sending it is not an error — it is ignored — so existing callers keep working without a change.

Inline resolution examples

TITLECONVERT "nurse" EXPAND CLUSTER
TITLECONVERT "manager" WHERE COMPANY IS "McDonalds"
COMPANYCONVERT "MSFT"
GET AI_IMPACT FOR CLUSTER "187"
SUBMIT TITLECONVERT TITLE "warehouse" CHOICE "41-2031.00"
SUBMIT TITLECONVERT VALUE '{"query_info":{"title":"warehouse"},"picked":{"onet_code":"11-3071.00","cluster_id":42}}'

After TITLECONVERT returns rows and a person chooses one, record the pick with SUBMIT TITLECONVERT (do not convert again). Keyword CHOICE or VALUE with the same JSON as POST /api/titleconvert. Same commands as the DSL reference.

Removed in 0.14.2 (breaking for MCP clients)

The dedicated aoi_title_convert and aoi_company_convert tools were removed. Use the DSL commands above through aoi_dsl_query. There is no compatibility shim - discover the surface dynamically via tools/list.

Connecting

Initialize a Streamable HTTP session (service key):

curl -s https://aonav.ai/mcp \
  -H "Authorization: Bearer $MCP_API_KEY" \
  -H 'Content-Type: application/json' \
  -X POST \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize",
       "params":{"protocolVersion":"2025-06-18","capabilities":{},
                 "clientInfo":{"name":"my-agent","version":"1.0"}}}'

Call the tool:

curl -s https://aonav.ai/mcp \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -X POST \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
       "params":{"name":"aoi_dsl_query",
                 "arguments":{"query":"LIST COMPANIES LIMIT 10"}}}'

See the DSL reference for the full command surface available through the tool.

Tool response shape

A successful tools/call carries the DSL result three ways, all identical:

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [{"type": "text", "text": "{\"success\":true,\"count\":3,\"data\":[...]}"}],
    "structuredContent": {"success": true, "count": 3, "data": []},
    "isError": false,
    "_meta": {"tool": "aoi_dsl_query", "query": "...", "record_count": 3}
  }
}
  • structuredContent (MCP 2025-06-18) is the DSL envelope as data. Prefer it — no parsing.
  • content[0].text is the same envelope as a JSON string, for clients that read text content only. One parse:
data = json.loads(response["result"]["content"][0]["text"])
  • _meta is server bookkeeping (tool name, echoed query, timestamp, record_count). Never read it for data; count in the envelope is the answer.

The envelope's top-level fields are exactly those POST /api/dsl returns in its data object — including baseline, data_status_note, dsl_suggestions, total, snapshot and mermaid. That parity is enforced by a test, because a field-by-field copy here used to drop them.

Changed in 0.16.10 (was double-wrapped)

content[0].text used to parse to another MCP envelope, so reaching the data took two json.loads. It now parses straight to the DSL envelope. A client that unwraps defensively — parse content[0].text, and if the result still has a content key unwrap again — works against both shapes and needs no change.

Differs from /mcp/v1

The deprecated /mcp/v1 shim still places an inner envelope at result (result["content"][0]["text"] → DSL envelope, plus metadata). It is frozen for one remaining caller; use /mcp.

Tool-execution failures carry no result; check for error first:

{"jsonrpc": "2.0", "id": 2, "error": {"code": -32602, "message": "...", "suggestion": "..."}}