MCP server: run DirectoryDuck from an AI agent
Connect Claude, Cursor or any MCP client to your account. From a chat, the agent can create a directory, fill it with AI discovery, publish the listings, hand you the embed snippet, connect your own domain and get you a checkout link.
1. Create a key
In the dashboard, open Settings → API keys, name the key (for example "Claude Code") and pick its permissions. The key starts with ddmcp_ and is shown once: copy it then. It is not your directory's widget key, which can't be used here.
2. Connect your client
Server URL https://directoryduck.com/api/mcp (Streamable HTTP), with the key in an Authorization: Bearer header.
Claude Code
claude mcp add --transport http directoryduck https://directoryduck.com/api/mcp \
--header "Authorization: Bearer ddmcp_YOUR_KEY"Cursor
Add this to .cursor/mcp.json in a project, or ~/.cursor/mcp.json for every project, then restart Cursor:
{
"mcpServers": {
"directoryduck": {
"url": "https://directoryduck.com/api/mcp",
"headers": {
"Authorization": "Bearer ddmcp_YOUR_KEY"
}
}
}
}Claude Desktop
Claude's Connectors screen signs in with OAuth, which this server does not offer yet, so connect through the open-source mcp-remote bridge (it needs Node.js). In Claude Desktop, open Settings → Developer → Edit Config, add this to claude_desktop_config.json and restart Claude:
{
"mcpServers": {
"directoryduck": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://directoryduck.com/api/mcp",
"--header",
"Authorization:${DIRECTORYDUCK_AUTH}"
],
"env": {
"DIRECTORYDUCK_AUTH": "Bearer ddmcp_YOUR_KEY"
}
}
}
}Any other MCP client
Point it at the URL and send the header on every request. The server is stateless: it speaks MCP 2026-07-28 and serves 2025-era clients too; there is no session to open or close and no SSE stream to keep.
POST https://directoryduck.com/api/mcp
Authorization: Bearer ddmcp_YOUR_KEY
Content-Type: application/json
Accept: application/json, text/event-stream3. Ask for a directory
For example: "Create a directory of dog groomers in Austin, find about 15 with DirectoryDuck, publish them and give me the embed snippet." The agent calls create_directory, run_discovery, get_discovery_job until the job is done, publish_listings and get_embed_snippet.
Tools
Every id you pass must be one of your own: another account's directory or job answers as not found. Each tool needs one permission on the key.
create_directory
mcp:writeStarter and upCreate a new directory in your DirectoryDuck account. It gets a public page at https://directoryduck.com/<slug> (the slug is made from the name). Next: run_discovery to fill it with AI, or add_listing to add listings by hand. Your plan limits how many directories you can have (Starter 1, Growth 5, Pro unlimited).
namerequired | string | Directory name, e.g. "Austin Dog Groomers" |
nicherequired | string | What the directory lists, e.g. "dog groomers" |
location | string | Where, if it is local, e.g. "Austin, TX" |
run_discovery
mcp:spendStarter and upStart an AI discovery job: AI searches the web for businesses matching your query, reads each website and drafts a listing. Costs credits, like the dashboard: 10 to start, 1 for the web search, and 2 per business found. Most it can cost: small (up to 15 businesses) 41, medium (40) 91, large (100) 211 credits. Duplicates of listings you already have are skipped and cost nothing. It runs in the background for a few minutes: poll get_discovery_job with the job_id it returns, then publish_listings { all_found: true } to publish the results. One job per directory at a time.
directory_idrequired | string | The directory_id (a UUID) |
queryrequired | string | What to find, e.g. "dog groomers in Austin, TX" |
size | "small" | "medium" | "large" | How many businesses to look for: small 15, medium 40, large 100 Default "small". |
get_discovery_job
mcp:readevery plan, Free includedThe status and progress of a discovery job started with run_discovery, the credits it used, and, once it is done, how many results are ready to publish with a sample of them.
job_idrequired | string | The job_id (a UUID) |
list_listings
mcp:readevery plan, Free includedThe listings of one of your directories, in a stable order, a page at a time (pass next_cursor back as cursor). Status: prospect = found but not published, active = published on the public page; also contacted, claimed, inactive. Also returns counts per status and how many discovery results are waiting to be published.
directory_idrequired | string | The directory_id (a UUID) |
status | "prospect" | "contacted" | "claimed" | "active" | "inactive" | Only listings with this status |
limit | integer | Page size, 1-100 Default 25. |
cursor | string | next_cursor from the previous page |
add_listing
mcp:writeStarter and upAdd one listing to one of your directories. It starts unpublished (status prospect) unless publish is true; publish later with publish_listings. Your plan limits listings per directory (Starter 500, Growth 2,000, Pro unlimited).
directory_idrequired | string | The directory_id (a UUID) |
namerequired | string | Business or listing name |
website_url | string | https://... |
description | string | |
email | string | |
phone | string | |
categories | string[] | |
location | object | The business address; every part optional. Shown on the public listing page and in its structured data. |
publish | boolean | true = publish it on the public page now Default false. |
publish_listings
mcp:writeStarter and upPublish listings on the directory's public page. Either listing_ids (listings of this directory to publish), or all_found: true to publish everything found and not yet published: every prospect listing, plus every finished discovery result that has no listing yet and scored 70 or more for relevance (or was approved in the dashboard); rejected and low-relevance results are skipped. Publishing costs no credits. A directory shows at most 10 featured listings: a listing that is still featured from before is published without its Featured badge when the directory already shows 10 (featured_removed lists them).
directory_idrequired | string | The directory_id (a UUID) |
listing_ids | string[] | Listings to publish |
all_found | boolean | true = publish every prospect listing and every discovery result scoring 70+ (or approved in the dashboard) not yet published; low matches are left out |
get_embed_snippet
mcp:readevery plan, Free includedThe copy-paste snippet that puts the whole directory (search, filters, listing pages, inquiry form) on any website with one script tag, with the directory's public key filled in, plus where to paste it on common platforms.
directory_idrequired | string | The directory_id (a UUID) |
connect_domain
mcp:writeStarter and upServe one of your directories on your own domain or subdomain (e.g. directory.example.com) instead of directoryduck.com/<slug>. Returns the DNS records to set at your DNS provider; the domain goes live once DNS points at us (call it again to re-check). Starter: one domain per account; Growth and Pro: one per directory.
directory_idrequired | string | The directory_id (a UUID) |
hostnamerequired | string | The domain, e.g. "directory.example.com" |
start_checkout
mcp:billingevery plan, Free includedGet a Stripe Checkout link for a DirectoryDuck plan (Starter $39/mo, Growth $99/mo, Pro $249/mo; yearly = 2 months free). Open the link in a browser to pay; nothing is charged by this call. While paid plans are not open yet it says so instead of returning a link.
planrequired | "starter" | "growth" | "pro" | The plan to buy |
interval | "monthly" | "yearly" | Billing interval Default "monthly". |
Credits
Only run_discovery spends credits, the same credits and the same prices as discovery in the dashboard: 10 to start, 1 for the web search and 2 per business found. Duplicates of listings you already have are skipped and cost nothing. The most a job can cost: small 41 credits (up to 15 businesses), medium 91 (40), large 211 (100). A job needs at least 13 credits left to start.
Every other tool costs no credits. Publishing listings costs nothing.
Limits
- Requests per minute per key: Free 10, Starter 30, Growth 60, Pro 120. Every MCP request counts, listing the tools and the read-only tools included, so each tool call costs one. Over the limit you get HTTP 429 with a Retry-After header.
- One JSON-RPC message per request: batches (JSON arrays) are refused with HTTP 400. Request bodies are limited to 256 KiB. The server takes POST only and keeps no stream open.
- One discovery job per directory at a time.
- Your plan's directory and listing limits apply (Starter 1 directory and 500 listings, Growth 5 and 2,000 each, Pro unlimited).
- Up to 5 active keys per account.
- AI discovery pauses for everyone when DirectoryDuck's monthly AI budget is reached; run_discovery then says so and charges nothing.
Permissions and security
mcp:read: List listings, read discovery jobs and get the embed snippet.mcp:write: Create directories, add and publish listings, connect a domain.mcp:spend: Run AI discovery, which uses credits like the dashboard does.mcp:billing: Start a checkout (returns a link you open and pay in your browser).- A key acts as you. We store only a fingerprint (SHA-256) of it, so a lost key can't be shown again: rotate it (new key, same permissions, the old one stops at once) or revoke it in Settings → API keys.
- start_checkout only returns a Stripe Checkout link; you open it and pay in your browser. Nothing is charged inside the chat.
Errors
A refused tool call comes back as a tool result marked as an error, with a code and what to do next: invalid_input, not_found, scope_missing, plan_required (a Free key calling a tool that needs Starter and up), limit_reached, conflict (for example a discovery already running in that directory), refused (not enough credits, the AI budget, a domain that can't be used), unavailable or failed. A missing, wrong or revoked key is HTTP 401; over the rate limit, HTTP 429.
Prefer plain HTTP?
The public REST API reads published listings and reviews with your directory's widget key.