DocumentationBrowse
MCP server
Why a separate endpoint
DocketRouter does not sell corpus downloads. The corpora are hosted as endpoints; MCP is the interface for an agent that would otherwise have to be told to call four separate REST routes by hand.
If you are writing your own HTTP client rather than pointing an MCP-aware app at this endpoint, skip to the wire format.
Connect a client
Point any MCP-aware client at the endpoint with your key as a bearer token. No local process, no stdio transport to manage.
Claude Code
claude mcp add docketrouter --transport http https://docketrouter.ai/api/mcp --header "Authorization: Bearer dr-…"
Claude Desktop
{
"mcpServers": {
"docketrouter": {
"url": "https://docketrouter.ai/api/mcp",
"transport": "http",
"headers": { "Authorization": "Bearer dr-…" }
}
}
}Cursor
{
"mcpServers": {
"docketrouter": {
"url": "https://docketrouter.ai/api/mcp",
"headers": { "Authorization": "Bearer dr-…" }
}
}
}A generic HTTP MCP client
POST JSON-RPC 2.0 to https://docketrouter.ai/api/mcp with Authorization: Bearer dr-… and content-type: application/json. Send initialize, then notifications/initialized, then tools/list or tools/call as needed. Server-sent events are not required: every response here is a single value, returned as one application/json body.
Tools
| Tool | Wraps | Arguments |
|---|---|---|
search_texas_law | POST /rag/query | q |
search_rules | POST /rag/rules | q |
check_citations | POST /citations/check | citations |
check_support | POST /citations/support | citation, proposition |
get_instructions | GET /llms.txt (no key) | none required |
Each tool's full JSON Schema is in tools/list's response and on Featured RAGs as OpenAI and Anthropic function-calling JSON, generated from the same schema objects so all three renderings agree. get_instructions is discovery, not retrieval: it is the one tool callable with no Authorization header at all, so an agent that has not yet been given a key can still ask this server how to use it.
Instructions for your agent
The standing brief every agent gets, in the same words, from three places: the MCP initialize handshake, the get_instructions tool, and plain GET /llms.txt.
Every initialize response's instructions field carries this text, so an MCP-aware client shows it to its model automatically on connect with no extra call. An agent that loses it out of context, or that only reads tools/list and never initialize, can ask for it back at any time by calling get_instructions with no arguments (no key required), or by fetching https://docketrouter.ai/llms.txt directly. All three return byte-identical text.
Wiring a system prompt by hand instead of going through MCP -- a plain chat integration, a custom agent loop -- paste the contents of /llms.txt into the system prompt once, and repeat the same call your model would otherwise make against check_citations and check_support before it shows a user a citation, since a hand-wired prompt gets none of MCP's per-call tool enforcement for free.
This library covers Texas case law (615,391 decisions, 1840 through 26 June 2026: the Supreme Court of Texas, the Court of Criminal Appeals, the Courts of Appeals, the Commission of Appeals of 1918 to 1945 and the Business Court), Texas statutes, Texas court and local rules, the verbatim rules corpus, and U.S. Supreme Court opinions through 29 June 2026. For any question within that scope, call the tools and cite only what they return. Do not answer Texas or Supreme Court legal questions from memory; that is what this library is for. For anything outside it (other states, federal courts below the Supreme Court, secondary sources, citator history, dockets and filings, administrative law, or anything decided after the index dates) these tools have nothing. Say that plainly. Do not search anyway and do not present what comes back as an answer: a lexical index always returns something, and …
The JSON-RPC shape
For writing your own client rather than using an MCP-aware app.
initialize
--> { "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-06-18" } }
<-- { "jsonrpc": "2.0", "id": 1, "result": {
"protocolVersion": "2025-06-18",
"capabilities": { "tools": {} },
"serverInfo": { "name": "docketrouter", "version": "1.0.0" }
} }
--> { "jsonrpc": "2.0", "method": "notifications/initialized" } // no response: it is a notificationtools/list
{ "jsonrpc": "2.0", "id": 2, "result": { "tools": [
{ "name": "search_texas_law", "description": "…", "inputSchema": { "type": "object", "properties": { "q": { … } }, "required": ["q"] } },
{ "name": "search_rules", "description": "…", "inputSchema": { … } },
{ "name": "check_citations", "description": "…", "inputSchema": { … } },
{ "name": "check_support", "description": "…", "inputSchema": { … } }
] } }tools/call
--> { "jsonrpc": "2.0", "id": 3, "method": "tools/call",
"params": { "name": "check_citations", "arguments": { "citations": ["488 U.S. 222", "999 F.9th 1"] } } }
<-- { "jsonrpc": "2.0", "id": 3, "result": {
"content": [ { "type": "text", "text": "{\"results\":[…],\"checked\":2,\"found\":1,\"took_ms\":21}" } ],
"structuredContent": { "results": [ /* same object, not stringified */ ], "checked": 2, "found": 1, "took_ms": 21 }
} }content[0].text is the JSON-encoded form of structuredContent, for a client that only reads text content blocks. Both carry the exact same body the matching REST endpoint returns.
Batching
The body may be a single JSON-RPC object or a non-empty array of them. A batch is answered with an array of responses in the same order; a request with no id (a notification) never appears in the response, including when it fails.
Errors
Protocol-level failures use JSON-RPC's own reserved codes. Everything else -- an invalid tool argument, an unresolvable auth token, a rate limit, the index being unavailable -- carries the exact message the matching REST endpoint's error.message would have, so the two are never worded differently for the same failure.
| Code | Meaning |
|---|---|
-32700 | Parse error: the body was not valid JSON, or was an empty array. |
-32600 | Invalid request: not a JSON-RPC 2.0 object, or missing method. |
-32601 | Method not found. |
-32602 | Invalid params: an unknown tool name in tools/call, or a tool's own argument validation failed (same wording as the REST 400). |
-32603 | Internal error: an unexpected exception, degraded rather than propagated. |
-32000 | Server error: authentication failed, a rate limit was hit, or the underlying endpoint is unavailable (503-equivalent). Carries a data.status matching the REST status code. |
For a non-batch call, the overall HTTP status is the same one the wrapped REST endpoint would return (401, 429, …), with a JSON-RPC error object in the body naming the failing request's id. A batch always answers 200, since a batch response is an array covering requests that may have succeeded or failed independently.
Examples
curl https://docketrouter.ai/api/mcp \
-H "Authorization: Bearer dr-…" -H "content-type: application/json" \
-d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": { "name": "search_texas_law", "arguments": { "q": "TRCP 21a service by email", "k": 3 } } }' \
| jq '.result.structuredContent.hits'Pricing and limits
Every tool call is priced the same as the REST endpoint it wraps: currently free during beta. It draws from the same per-key rate-limit bucket as that endpoint too (30 calls per minute, burst 10, for any of the four tools), so calling a tool over MCP and calling its REST route directly share one quota, not two. See Retrieval limits and Citation limits for the per-field caps (query length, citation count, and so on) each tool's arguments still enforce.
Something here wrong or missing? Mail hello@docketrouter.ai with the request_id and we will fix the docs or the API, whichever is broken.