Connecting a client
Wire the Parcel MCP Server into Claude Code, Claude Desktop, or any MCP-aware client — Bearer token, OAuth 2.0 with PKCE, and raw JSON-RPC over Streamable HTTP.
The endpoint is https://api.landrecords.us/mcp and the transport is Streamable HTTP. There are two ways to authenticate, and every client uses one of them:
- Bearer token — send an
Authorizationheader on every request. Your token is the same UUID you use for the/protile and WFS endpoints; find it on your account page. Simplest path, and what you want for servers, scripts, and agents. - OAuth 2.0 authorization code + PKCE — for clients that run the MCP spec's OAuth flow before speaking JSON-RPC. You paste your API token into a login form once and the client stores the result.
Both end up in the same place: the access token is your API token, validated against your plan on every request.
Setting the Authorization header
This is where most failed connections come from, so it is worth being exact. The header name is Authorization. The value is the word Bearer, a single space, then your API token verbatim:
Authorization: Bearer 3f2b1c9e-7a41-4d88-9b02-e5c6d7a81234Four rules cover every client:
Beareris required, and it is case-insensitive —bearer <token>works, a bare<token>with no scheme does not, and neither does any other scheme name.- The token goes in raw. It is a plain UUID from your account page — no
lr_prefix, no quotes, no base64, no URL-encoding. Anything that isn't a well-formed UUID is rejected, which in practice means a truncated copy/paste. - Every request, not just the handshake. The server is stateless — each JSON-RPC call is authenticated on its own, so the header belongs in the client's default headers, not in a one-time login step. MCP clients configured with a
headersblock do this for you. - There is no
?token=query parameter. The tile and WFS routes accept one;/mcpdoes not. The header is the only way in.
Every one of those failures returns the same opaque response, so the body will not tell you which mistake you made:
{ "error": "invalid_token", "error_description": "Authentication required" }A valid token on a plan without MCP is the one auth failure that looks different — 403 Your plan does not include MCP access, checked on every call against your allowance. See Limits.
Claude Code
Pass the whole header as one quoted argument — name, colon, space, value. --header (or -H) can be repeated, but this server needs only the one:
claude mcp add --transport http landrecords \
https://api.landrecords.us/mcp \
--header "Authorization: Bearer $LR_TOKEN"Use double quotes so your shell expands $LR_TOKEN — single quotes send the literal string $LR_TOKEN and every call comes back 401. Run echo $LR_TOKEN first if you are not sure the variable is set; an empty variable produces Authorization: Bearer with nothing after it, which fails the same way.
Then /mcp inside Claude Code lists landrecords and its eight tools. To share the server with a repository instead of your user config, commit a .mcp.json at the project root:
{
"mcpServers": {
"landrecords": {
"type": "http",
"url": "https://api.landrecords.us/mcp",
"headers": {
"Authorization": "Bearer ${LR_TOKEN}"
}
}
}
}Claude Code expands ${LR_TOKEN} from the environment when it reads the file, so keep the token there rather than inline — .mcp.json is checked in, and the token grants full read access to your plan's quota. Note the ${…} braces: a bare $LR_TOKEN is not expanded in JSON and is sent literally.
Claude Desktop and other local-command clients
Clients that expect to launch a local process rather than call a URL can use mcp-remote as a bridge. Edit claude_desktop_config.json:
{
"mcpServers": {
"landrecords-parcels": {
"command": "npx",
"args": [
"-y", "mcp-remote",
"https://api.landrecords.us/mcp",
"--header", "Authorization: Bearer ${LR_TOKEN}"
],
"env": { "LR_TOKEN": "your-api-token" }
}
}
}Two things about that args array:
- The header is two entries —
"--header", then the entire"Authorization: Bearer …"string as one element. SplittingAuthorization:,Bearer, and the token across three entries is the usual mistake, and it arrives as no header at all. mcp-remoteitself expands${LR_TOKEN}inside the header value from theenvblock, so the token never has to appear in the args.
On Cursor and Claude Desktop for Windows, spaces inside args aren't escaped when the client shells out to npx, which mangles the header. Move the space into the environment variable and write the header with no space after the colon:
{
"mcpServers": {
"landrecords-parcels": {
"command": "npx",
"args": [
"-y", "mcp-remote",
"https://api.landrecords.us/mcp",
"--header", "Authorization:${LR_AUTH}"
],
"env": { "LR_AUTH": "Bearer your-api-token" }
}
}
}The server does not care about the space after the colon — that part is HTTP-optional — but it does need the Bearer prefix, which is why it moves into LR_AUTH rather than being dropped.
Restart the client and the parcel tools appear alongside its others. If you omit --header, mcp-remote runs the OAuth flow described below instead.
Any client that speaks remote MCP
The Streamable HTTP endpoint is https://api.landrecords.us/mcp. Any client that can set a request header connects directly with:
Authorization: Bearer <YOUR_TOKEN>No other headers are required, and no cookie or session is involved. The server runs stateless — every request is authenticated and served on its own, so there is no Mcp-Session-Id to capture and replay, and a client that sends one anyway is fine. Mcp-Session-Id is exposed through CORS for browser-based clients that expect to read it.
https://api.landrecords.us/mcp/, with a trailing slash, is the same endpoint — use whichever form your client's config prefers. The legacy SSE handshake at /mcp/sse still exists for older clients but is served from a multi-worker pool without sticky routing, so its follow-up POSTs frequently 404; prefer /mcp.
The OAuth flow
Modern MCP clients are expected to run OAuth 2.0 before speaking JSON-RPC. The server maps that flow onto your existing API token:
- The client hits
GET /mcp/authorizewith itsclient_id,redirect_uri, and PKCEcode_challenge. - The server redirects to a login form at
GET /mcp/login. - You paste your API token into the form and submit it.
- The token is validated against your account; the server issues a one-time authorization code and redirects back to the client's
redirect_uri. - The client exchanges the code (plus its PKCE verifier) at
POST /mcp/tokenand receives an access token — which is your API token. - Every subsequent MCP request carries
Authorization: Bearer <access_token>.
Practical details:
- Discovery is at
GET /mcp/.well-known/oauth-authorization-server. - Client registration is closed. Dynamic registration is disabled; instead a small set of client IDs is pre-registered, one per common
mcp-remotecallback port. The IDs arelandrecords-mcp:7075,landrecords-mcp:3333,landrecords-mcp:8080,landrecords-mcp:9000, andlandrecords-mcp:4200, each with redirect URIhttp://localhost:<port>/callback.mcp-remotedefaults to port 7075. If you need a different callback port, contact us — we would rather add a port than open registration. - Authorization codes are single-use and expire in 5 minutes.
- No refresh tokens are issued. The access token lives as long as the underlying API token; when that expires or is revoked, requests return 401 and the client runs you back through the form.
Raw JSON-RPC
Useful for debugging, health checks in CI, or a client you are writing yourself. Because the server is stateless, each call is a self-contained POST carrying the Authorization header — there is no session to establish and no handshake to replay. Streamable HTTP responses come back as Server-Sent Events, so send both accept types:
export LR_TOKEN=3f2b1c9e-7a41-4d88-9b02-e5c6d7a81234
curl -i https://api.landrecords.us/mcp \
-H "Authorization: Bearer $LR_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0", "id": 1, "method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": { "name": "curl", "version": "1.0" }
}
}'initialize reports the protocol version and server info. Listing and calling tools takes the same three headers and no session ID:
# List the tools
curl -s https://api.landrecords.us/mcp \
-H "Authorization: Bearer $LR_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
# Call one
curl -s https://api.landrecords.us/mcp \
-H "Authorization: Bearer $LR_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0", "id": 3, "method": "tools/call",
"params": {
"name": "parcel_count",
"arguments": { "where": [{ "column": "statefp", "op": "eq", "value": "51" }] }
}
}'To see whether the header is the problem, drop it: the same request without Authorization returns 401 {"error": "invalid_token", "error_description": "Authentication required"}. If you get that with the header set, the value isn't reaching the server — check shell quoting first.
If you only want to verify reachability, GET /mcp/health needs no token and no handshake.
For a friendlier programmatic path, use an MCP SDK instead of raw JSON-RPC — see Examples.
Troubleshooting
| Symptom | Cause |
|---|---|
401 invalid_token / Authentication required on /mcp | Any header problem at all: missing header, missing Bearer, an unexpanded $LR_TOKEN, a token that isn't a UUID, a token that doesn't exist on this account, or a revoked one. The Streamable HTTP endpoint deliberately does not say which — work down the four rules above, then re-mint the token on your account page |
403 Your plan does not include MCP access | The header was fine and the token is real — the plan behind it has no MCP allowance. See Limits |
| Tools list is empty, or the client shows no tools | Almost always the token. Reproduce with the tools/list curl above: if that works, the client isn't sending your header |
503 Streamable HTTP transport not running | Transient server-side startup state — retry, and tell us if it persists |
| Client reports "session terminated" and retries over SSE | The URL is wrong. https://api.landrecords.us/mcp and /mcp/ both work; anything else under /mcp (a typo'd path, a stray /v1) falls through to the REST app and 404s |
| OAuth login redirects but the client reports an invalid grant | The authorization code expired (5 minutes) or was already used — restart the flow |
The granular messages Missing or malformed Authorization header, Invalid token format, Invalid token, and Token is disabled come only from the legacy /mcp/sse endpoint. Pointing a curl -N at /mcp/sse with your header is a quick way to find out which of those applies to a token that /mcp rejects opaquely.
Parcel MCP Server
A read-only Model Context Protocol server over the nationwide parcel layer — eight structured tools an AI assistant or agent can call directly, authenticated with your existing API token.
Tool reference
Arguments, filter operators, response shapes, and worked examples for all eight parcel MCP tools.