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.
The npx command does the install for you on first run. No clone, no local build.
No env var, no API key, no signup. Just the four lines above. (See key scopes for higher rate limits and per-merchant attribution.)
"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, commandnpx, args-y doora-mcp. - Continue - add to
~/.continue/config.jsonunder 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.
| Tool | What it does | Network | Args |
|---|---|---|---|
| resolve_handle | Look 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. | yes | handle: string |
| start_handle_creation | Start 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_status | Poll the result of start_handle_creation. Returns pending / complete (with the new handle string) / expired. Anonymous - no DOORA_API_KEY required. | yes | session_id: string |
| encode_digipin | Convert lat/lng (in India) to a 10-character DIGIPIN, formatted XXX-XXX-XXXX (12 chars with separators). Pure local computation. | no | lat: number, lng: number |
| decode_digipin | Convert a DIGIPIN back to lat/lng centroid (~2m of the original encoded point). | no | digipin: string |
| list_demo_handles | Static list of handles a test key can resolve. The LLM calls this when a 404 needs an alternative to suggest. | no | (none) |
| validate_handle_shape | Regex-check a string is a structurally valid handle. Useful before resolve_handle for clean error messages. | no | input: 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.
- User asks Claude (or any MCP client): "Create me a doora handle for my home."
- Claude calls
start_handle_creation. The tool returns aclaim_urlonwww.doora.to/claim?session=…and asession_id. - 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.
- Claude calls
check_handle_statusevery few seconds. While the user is in the browser the tool returnspending. Once they finish, it returnscompletewith the newusername@label. - Claude continues the conversation with the new handle - for example by calling
resolve_handleon it, or referencing it in subsequent prompts.
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.
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).
3 sessions per hour per IP. Creating a handle is a deliberate human action - anything past that is a script and gets 429s.
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
demouser 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-Afterheader.