model context protocol

Use doora as a tool inside Claude.

The doora MCP server gives Claude Desktop, Cursor, Continue, Zed — anything that speaks Model Context Protocol — a set of tools to look up addresses by doora handle, encode + decode DIGIPINs, and validate handle shapes. One npm install, one config line, and your agent can address India.

2 minutes

Quickstart with Claude Desktop

Drop this block into your Claude Desktop config and restart the app. On macOS, the file is at ~/Library/Application Support/Claude/claude_desktop_config.json.

{
  "mcpServers": {
    "doora": {
      "command": "npx",
      "args": ["-y", "doora-mcp"]
    }
  }
}

No API key needed for the launch — just paste, restart Claude, and ask "resolve demo@home". The tools appear in the bottom-of-chat tools panel.

1Install — nothing

The npx command does the install for you on first run. No clone, no local build.

2Configure — nothing

No env var, no API key, no signup. Just the four lines above. (See key scopes for higher rate limits and per-merchant attribution.)

3Use — natural language

"Look up demo@office" / "decode 422-36L-P8FM" / "is praneeth@flat-2 a valid handle?" — Claude picks the right tool.

cursor / continue / zed

Other MCP clients

Every MCP-capable client runs servers the same way — a command + args + env. Drop the equivalent of the block above into your client's config; common locations:

  • Cursor — Settings → MCP → Add new MCP Server. Use type command, command npx, args -y doora-mcp.
  • Continue — add to ~/.continue/config.json under the "experimental": { "modelContextProtocolServers": [...] } array.
  • Zed — settings → context_servers. Same shape;command = npx.

surface

Tools the server exposes

Seven tools in three camps: pure-local computation (no network), authenticated reads, and the create-handle tools that let an agent help a user without ever needing an API key.

ToolWhat it doesNetworkArgs
resolve_handleLook up address for a username@label handle. Returns coordinates, DIGIPIN, building/floor/unit (per the owner's visibility flags), verification, and an audit id. DOORA_API_KEY is optional during the launch window — see Authentication below.yeshandle: string
start_handle_creationStart a session so the user can create a new doora handle in their browser. Returns a claim_url to show them. Anonymous — no DOORA_API_KEY required.yes(none)
check_handle_statusPoll the result of start_handle_creation. Returns pending / complete (with the new handle string) / expired. Anonymous — no DOORA_API_KEY required.yessession_id: string
encode_digipinConvert lat/lng (in India) to a 10-character DIGIPIN, formatted XXX-XXX-XXXX (12 chars with separators). Pure local computation.nolat: number, lng: number
decode_digipinConvert a DIGIPIN back to lat/lng centroid (~2m of the original encoded point).nodigipin: string
list_demo_handlesStatic list of handles a test key can resolve. The LLM calls this when a 404 needs an alternative to suggest.no(none)
validate_handle_shapeRegex-check a string is a structurally valid handle. Useful before resolve_handle for clean error messages.noinput: string

create handles from an agent

Letting users create handles

A chat window can't render a map or capture a pin drop, so start_handle_creation hands the user a one-time URL to open in their browser. They finish the claim visually there; the agent polls check_handle_status until the new handle is ready.

  1. User asks Claude (or any MCP client): "Create me a doora handle for my home."
  2. Claude calls start_handle_creation. The tool returns a claim_url on www.doora.to/claim?session=… and a session_id.
  3. Claude shows the URL to the user. They open it, sign in to doora (or sign up — free), pick a location on a map, fill the address fields, and save.
  4. Claude calls check_handle_status every few seconds. While the user is in the browser the tool returns pending. Once they finish, it returns complete with the new username@label.
  5. Claude continues the conversation with the new handle — for example by calling resolve_handle on it, or referencing it in subsequent prompts.
No API key required for this flow

start_handle_creation and check_handle_status are anonymous — they don't need DOORA_API_KEY in the env. The user authenticates in their own browser; the agent never sees their credentials.

The session grants no capability on its own

A session_id can't be used to create a handle. The only thing it does is link 'this agent request' to 'this browser-side claim outcome'. A leaked session_id reveals at most the resulting handle string (which is public anyway).

Hard rate limit

3 sessions per hour per IP. Creating a handle is a deliberate human action — anything past that is a script and gets 429s.

15-minute TTL

Each session expires 15 minutes after creation. Past that, polling returns 'expired' — the agent should offer to start a fresh session, not retry the old one.

examples

Prompts to try

A few prompts that exercise the tools cleanly. Each one is what a real user would type to Claude with the doora MCP server attached:

"Where does demo@home live?"

Calls resolve_handle('demo@home'). Returns building, floor, unit, coords, DIGIPIN.

"What's the DIGIPIN for the Taj Mahal? (lat 27.1751, lng 78.0421)"

Calls encode_digipin(27.1751, 78.0421). Returns the 10-character DIGIPIN (XXX-XXX-XXXX).

"Decode 422-36L-P8FM and open it in a map app."

Calls decode_digipin then assembles a universal map URL from the result.

"Is 'praneeth_home' a valid doora handle?"

Calls validate_handle_shape. Tells the user it's not (underscores aren't allowed) and what a valid one looks like.

"What demo handles can I test against?"

Calls list_demo_handles. Returns the four-line table.

api keys

Authentication + key scopes

DOORA_API_KEY is optional during the launch window — leave it out and the server falls back to a shared anonymous bucket. Configure your own to get higher rate-limit budgets and per-merchant audit attribution. Three modes:

(no key)

Launch-window default. Resolves any public handle, shares a global rate-limit bucket with everyone else who hasn't configured a key. Fine for trying it out + low-volume use.

dk_test_…

Sandbox key. resolve_handle works only against the demo@* handles. Safe to paste in a shared config file, ideal for development.

dk_live_…

Production key. Resolves any real handle, gets your own rate-limit budget, attributes every call to your merchant in our audit log. Treat like a database password.

The handoff/exchange path that powers the merchant widget is a different surface — that one is consent-gated, and a test key works there too. The MCP server only exposes direct resolution today.

trust model

Trust model — MCP

Tools are stateless (no cookies, no cross-call caching) and no agent ever holds a write credential. Reads (resolve_handle) are audit-logged — attributed to your dk_… key when one is set, or to the shared anonymous bucket otherwise. Handle creation goes through the device flow — the agent asks doora to mint a session, but the actual claim happens in the user's own browser, authenticated as themselves; the agent only sees the resulting handle string. There is no edit_handle or delete_handle — agents can't modify or remove existing handles. Test keys are firewalled to the demo handles. Full posture at /docs/trust.

limits

Rate limits

MCP shares the per-key budget with the REST surface — default 60 RPM, ask via /contact to lift. Over-limit returns 429 with a Retry-After hint, which Claude surfaces as a readable tool error.

common issues

Troubleshooting

  • "DOORA_API_KEY environment variable not set" — add "env": { "DOORA_API_KEY": "dk_…" } alongside the "command" in your MCP client config, then restart the client.
  • "handle not found" against demo@home — your key is test-mode and the demo user isn't seeded on this deploy yet. Use a real handle with a live key, or ask us via /contact.
  • "too many requests" — past your 60 RPM. Backoff per the Retry-After header.

Built something cool with doora-mcp? Tell us.