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 /mcpreturns405(no SSE);DELETE /mcpends a session (Mcp-Session-Id).GET /mcp/capabilitiesreturns a capabilities document.GET /.well-known/oauth-protected-resource(unauthenticated) returns RFC 9728 metadata naming the authorization server. An unauthenticatedPOST /mcpanswers401with a matchingWWW-Authenticatechallenge.- Send the token in the
Authorizationheader. 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:
- Tool-execution errors are HTTP
200with the error in the JSON-RPC body./mcp/v1returned HTTP400. Clients that branch on HTTP status must branch on the presence oferrorinstead. - The success envelope is nested one level deeper. See Tool response shape below.
Methods¶
initializenotifications/initializedtools/listtools/callping
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].textis the same envelope as a JSON string, for clients that read text content only. One parse:
_metais server bookkeeping (tool name, echoed query, timestamp,record_count). Never read it for data;countin 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: