dhaga.docs
Using Dhaga

Connect an AI assistant (MCP)

Connect Claude, ChatGPT, Cursor or any MCP client to read your Dhaga network and write back to it — twenty-four tools, and no AI credits for the reading half.

Ask Dhaga answers questions inside Dhaga. MCP is the other direction: point an assistant you already use at your graph and let it ask on its own, in the middle of whatever else you were doing.

  • "Who did I meet at the conference last month?"
  • "Save the recruiter who referred me, and remind me to follow up Friday."
  • "Which employers haven't got back to me?"

The endpoint is /api/mcp on your instance. Any client that speaks the Model Context Protocol can connect to it — Dhaga doesn't ship a per-client integration, because the client discovers the tools, their inputs and their descriptions at connect time.

What it costs

On a hosted instance, MCP is part of Pro and Power. Both ways in are gated the same way — the connector and the token reach the same endpoint, so there is no cheaper door. A request from an account without the plan is refused with a 403 and "error": "plan_required", which most clients surface as a connection error; reconnecting or logging in again won't clear it, upgrading will.

Self-hosted enterprise deployments have no plans, so nothing here is gated.

Connecting a client

There are two ways in, and both reach the same tools. Which one you use depends on the client, not on what you're allowed to do.

As a connector (log in, no keys)

Dhaga is its own OAuth 2.1 authorization server, so a client like claude.ai or ChatGPT can add it the way it adds any other connector: you paste the URL, Dhaga shows you a normal login and a consent screen, and the client gets a token of its own. Nothing is copied around by hand, and you can only ever grant access to your own account.

Add a custom connector in the client and give it one thing — the endpoint, https://www.dhaga.app/api/mcp (self-hosted deployment? use your own address followed by /api/mcp instead).

Everything else is negotiated. The client fetches Dhaga's discovery documents (/.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server), registers itself, and sends you to log in.

Step by step, in claude.ai or ChatGPT:

  1. Open the client's connector settings and add a custom connector. In claude.ai that is Settings › Connectors › Add custom connector; in ChatGPT it is under Settings › Connectors with developer mode on.
  2. Name it anything (for example "Dhaga") and paste your endpoint, https://www.dhaga.app/api/mcp (self-hosted: your own address followed by /api/mcp). Leave any client ID or secret fields empty — the client registers itself.
  3. Click Connect. A Dhaga login page opens in your browser. Sign in with the account that owns the network you want to share (a Pro or Power account on a hosted instance).
  4. Approve the consent screen. The client returns to its own page, and the Dhaga tools show up in the connector's tool list.

Staying connected

The client keeps its own token, so closing or signing out of the Dhaga website does not disconnect it. The token lasts an hour and the client renews it silently with a refresh token that stays valid for 90 days of use, so you sign in again only on the first connect or after three months idle.

If a connector you added before 13 October 2026 keeps asking you to reconnect, disconnect it and add it once more: connections made earlier were issued without a refresh token and cannot renew. A personal access token is the alternative: it has no expiry and never asks for a login.

With a personal access token

For local clients — Claude Code, Cursor, anything running on your own machine — the simpler path is the API key you already have. It's the same personal access token the mobile app uses, created under Settings › Account › API keys (see Settings). On a hosted instance, creating a new token needs Pro or Power; self-hosted enterprise deployments have no plans and no gate.

Claude Code, in one line:

claude mcp add --transport http dhaga https://www.dhaga.app/api/mcp \
  --header "x-api-key: YOUR_TOKEN"

Any client that takes a raw HTTP MCP server config wants the same two things — the URL and the header:

{
  "mcpServers": {
    "dhaga": {
      "type": "http",
      "url": "https://www.dhaga.app/api/mcp",
      "headers": { "x-api-key": "YOUR_TOKEN" }
    }
  }
}

A token is as good as your account, so treat it like a password: it goes in the client's config, not in a note, a repo, or a chat message. Revoke it in Settings and every client holding it stops working immediately.

Verification status

The production personal-access-token path was tested on 9 August 2026 with the official MCP SDK and a dedicated load-test account. The client initialized over Streamable HTTP and exercised all ten tools the server advertised at the time: six reads and four writes. The contact, note and follow-up created by the test were read back, the follow-up was closed, and every temporary token was revoked. The thirteen tools added since (contact curation, lists and Pages, below) have only been exercised by mocked automated tests, not against a live client or a real database.

The OAuth connector path still needs a live certification pass in claude.ai and ChatGPT. Its discovery, authorization and plan-gate behavior remain covered by the automated suite, but that is not the same as completing a real consent flow.

Clients that only speak stdio

Some MCP clients still launch a local process and talk over stdin/stdout rather than HTTP. Bridge those with mcp-remote, which runs as the local process and forwards to the endpoint:

{
  "mcpServers": {
    "dhaga": {
      "command": "npx",
      "args": [
        "-y", "mcp-remote",
        "https://www.dhaga.app/api/mcp",
        "--header", "x-api-key:YOUR_TOKEN"
      ]
    }
  }
}

That's a community bridge, not something Dhaga ships — prefer a direct HTTP connection wherever the client offers one.

Installing the skills

MCP hands your client the tools. A skill hands it the judgement about when and how to use them — search before you need a contact id, quote the receipt a fact came from, don't invent a connector that isn't there. Dhaga publishes three, and one command installs them:

npx skills add https://www.dhaga.app
  • dhaga-setup — connecting a client: connector or personal access token, the config each one wants, and what a 401 usually means.
  • dhaga-network — the reading half: search first, cite the note a fact came from, work out a warm intro path, run a weekly review, and say "not in your network" rather than answering from what the model happens to know about a similarly-named public figure.
  • dhaga-capture — the writing half: look for the person before creating them, keep to what you actually said, set a due date only when you gave one, and leave a follow-up open unless you confirmed it's done.

Self-hosted? Install from your own instance instead — npx skills add https://your-dhaga-host — because the skills are plain static files served from /.well-known/skills/, like any other asset.

Installing them is optional. The tools behave the same either way; the skills only change how well a client uses them.

Both the command and your endpoint are copyable from Settings › Account › Connect your AI assistant, which already knows the URL of the instance you're signed in to.

What the assistant can read

Nine read tools (six here, three for Pages), and none of them spend AI credits; the two list reads are under Lists:

  • dhaga_search — hybrid keyword + semantic search across contacts and note text. It returns the people plus the snippets that matched, so the assistant can tell you why someone came back rather than asserting it. Capped at 20 results.
  • dhaga_list_contacts — browse with filters (name, company, tag, starred) and pagination, up to 100 per page. Each row has the name, title, company, sector, tags, starred flag, custom fields, and the bare email addresses, phone numbers and links (without their labels or notes). It does not include notes, facts, job history or follow-ups; dhaga_get_contact adds those.
  • dhaga_get_contact — one person in full: details, job history, the facts extracted from their notes (each with the sourceNoteId receipt it came from), up to 25 recent notes, and their open follow-ups.
  • dhaga_list_follow_ups — everything you're on the hook for, across the whole network, soonest-due first.
  • dhaga_find_warm_path — up to three introduction paths to a person or company, through people you already know.
  • dhaga_list_upcoming_dates — birthdays and anniversaries in the next N days (30 by default, 365 at most), resolved in your own timezone.
  • dhaga_page_search — find your Pages by title and text.
  • dhaga_page_read — one page as Markdown.
  • dhaga_page_query_database — the rows of a Pages database, with filters.

Dhaga tells the connected model, at connect time, to treat your graph as the only source of truth about who you know — and to say so plainly when the answer isn't there rather than filling the gap from what it happens to know about a similarly-named public figure.

What it can write

Seven tools add things:

  • dhaga_add_note — attach a note to a contact.
  • dhaga_create_contact — save a person. Only a name is required; title, company, emails, phones, links, location and a first note are all optional.
  • dhaga_create_contacts — save several people in one call (up to 25). Each entry takes the same fields as dhaga_create_contact. Every person is saved on its own, so one that fails does not lose the rest, and the result says per person whether it was created, failed, or wasn't attempted because the call ran out of time. If you are importing more than one person, have your assistant use this rather than calling dhaga_create_contact over and over — many separate calls are slower and are the most likely thing to be turned away (see below).
  • dhaga_create_follow_up — open a reminder (what to do, plus an optional due date).
  • dhaga_close_follow_up — mark one done or dismissed.
  • dhaga_page_create — create a page, optionally inside another, from Markdown.
  • dhaga_page_append — add Markdown to the end of an existing page. It never edits or removes text that is already there, and it respects the page's sharing permissions.

A note written by an assistant goes through exactly the same path as one you type in the app: it queues background extraction, and every fact or follow-up derived from it keeps a source_note_id receipt. Delete the note later and its derived facts go with it, as always.

Cleaning up contacts

Three more tools change or remove people you already have. They exist so an assistant can do housekeeping — fix a title, tag a batch, fold duplicates together — without you opening each contact.

  • dhaga_update_contact — change one contact, PATCH-style: only the fields you pass change. You can set name, title, company and location (title and company edit the current role in place; an empty string clears one), add tags with addTags, remove them with removeTags, set custom fields with customFields (matched by label, so it updates the field or adds it), and delete custom fields with removeCustomFields. Emails, phones and links work like tags: addEmails, addPhones and addLinks add (one already there is skipped), removeEmails, removePhones and removeLinks remove (one that isn't there is ignored). Matching ignores case, and entries you don't name keep their labels. A phone matches on its last 10 digits, so two numbers in different countries that share them count as the same number: adding the second is skipped and removing one deletes both. Up to 25 per list per call; a value in both an add and a remove list is left as stored. The reply lists what changed under changes, and adds that matched an existing entry under skipped. Tags you don't name are kept, and so are job history, other emails, phones and links, and notes.
  • dhaga_merge_contacts — fold 1 to 5 duplicates (sourceIds) into one survivor (targetId). Notes, facts, follow-ups and graph edges move to the survivor, emails, phones, links and tags are combined, and the duplicate records are then deleted. There is no undo.
  • dhaga_delete_contact — permanently delete one person and everything attached to them: notes, facts, follow-ups, tags and graph edges. A hard delete: no trash, no undo.

Both destructive tools are marked destructive to the client, so a well-behaved client asks you before running them. On top of that:

  • dhaga_delete_contact needs confirmName, the contact's current name (case and surrounding spaces don't matter), and refuses if it doesn't match.
  • It refuses your last remaining contact, so a connected client can never empty your whole network.
  • It refuses contacts shared through an organization.
  • It is capped at 50 deletes an hour. This is a safety net, not a hard guarantee: the counter lives in the server's memory, so it is per server instance and resets on a restart.
  • A merge counts against the same hourly budget: each contact it folds in (and deletes) spends one of the 50. If the budget runs out part-way, nothing is merged but the points already taken stay spent. It also refuses organization contacts.
  • A merge takes at most 5 duplicates per call, so cleaning a large book means many small calls you can follow, not one sweeping one.

Limits worth knowing: dhaga_update_contact adds and removes emails, phones and links, but it can't relabel an existing one or change its note; do that in the app. It also can't edit addresses or important dates.

Example: tidy up after a conference

"I met a lot of people at the conference. Tag everyone from Acme as acme-2026, fix the title on the one who changed jobs, and merge the two entries for the same person."

A well-behaved assistant works in this order:

  1. dhaga_search for "Acme" to find the people.
  2. dhaga_get_contact on each to see what is already there, including job history, notes and existing tags (the list already shows custom fields and contact methods).
  3. dhaga_update_contact per person with addTags: ["acme-2026"], and title set on the one who moved. For example, to fix a changed address: {"contactId": "…", "removeEmails": ["old@example.com"], "addEmails": ["new@example.com"]}.
  4. For the duplicate, dhaga_get_contact on both records, then asks you to confirm they are the same person, and only then calls dhaga_merge_contacts with the fuller record as targetId.

It should not delete anyone unless you asked it to delete that person by name.

Lists

Lists are Dhaga's place for things that aren't people — Reading, Shopping, ideas to try. Five tools cover them:

  • dhaga_list_lists — your lists, with id, name, parent list and how many items are still open.
  • dhaga_get_list — the items on one list, open ones by default (or done, dismissed or all), paged up to 100 at a time.
  • dhaga_create_list — make a list, or a sub-list with parentId. It fails if a live list already has that name.
  • dhaga_add_list_item — add a line. Give dueDate only when you named a date; a dated item also shows up in your plan.
  • dhaga_update_list_item — change an item's text, or tick it done or reopen it.

There is no tool to delete a list or an item, and dismissed can't be set from here; dismissing stays in the app.

What it deliberately cannot do

There is no export and no admin tool, no way to delete a list or an item, and nothing that overwrites a whole book. dhaga_create_contacts saves many people at once, but it only ever adds; the only tools that remove anything are the two guarded clean-up tools above (merge and delete). That is a decision rather than a gap.

Why the destructive half is guarded, not absent

Until October 2026 this server had no delete and no merge at all, on the reasoning that a confused or prompt-injected client must not be able to trigger the deletion cascade. Customer feedback changed that: cleaning a contact book by hand is the job people most wanted an assistant for. The guards above replaced the ban, and they are a mitigation, not a proof. A delete still cascades through the person, their notes, facts, graph edges and embeddings, and a merge can't be reversed. If you connect an assistant, read what it plans to delete or merge before you say yes.

dhaga_close_follow_up is not a deletion: it changes a follow-up's status, and the follow-up stays on the contact.

What it costs

Reading your graph costs no AI credits at all. There is deliberately no "ask Dhaga" tool in the MCP surface — the client on the other end is already a model, so it gets raw retrieval with receipts and does its own reasoning. You are not paying Dhaga to think on top of something that already thinks.

The one exception is notes. dhaga_add_note, and dhaga_create_contact (or each person in dhaga_create_contacts) when you give it a note, queue the normal background extraction — 1 credit, exactly as if you had written the note in the app. If you're out of credits for the month the note is still saved; only the extraction is skipped, and the assistant is told so rather than left to assume it worked. See AI credits.

There's also a plain request limit — 60 tool calls a minute, per person. An assistant working through a question makes several calls a turn and won't notice it; a client stuck in a retry loop will, and that's the point.

On top of that, Dhaga limits how many requests one person can have running at once: one write at a time (anything that adds, changes or removes something: notes, contacts, follow-ups, lists, and the update, merge and delete tools) and up to six reads, so an assistant's usual parallel lookups are fine. A request over that is turned away straight away with a "too many requests" (429) or "busy" answer rather than queued, and a single request that takes too long is stopped with a timeout error instead of hanging. A save that timed out may or may not have been saved: have the assistant check before it retries (dhaga_search for a person, dhaga_get_contact for a note, dhaga_list_follow_ups for a follow-up), then wait a few seconds and retry only what is missing. A read that times out can simply be repeated. An assistant adding a long list should send it in batches with dhaga_create_contacts, one batch at a time.

A merge or delete that times out, or fails with an unexpected error, was applied completely or not at all, never half, but you can't tell which: have the assistant re-read the contacts with dhaga_get_contact (a deleted or merged-away contact comes back not found) before trying again, and never repeat a delete or merge blindly. Updating a contact, ticking off a list item and reading lists are different: repeating them after a timeout is safe. Creating a list or adding a list item is not, because it can end up there twice, so after a timeout or an unexpected error the assistant should look with dhaga_list_lists or dhaga_get_list first.

If a whole dhaga_create_contacts call fails with an error instead of returning a result for each person, have the assistant run dhaga_search for those people before sending the list again: Dhaga does not check for duplicates, so a blind resend can save everyone twice. A very large batch can also run out of time and be rolled back whole; the assistant is told it "may not have been saved". A request over 1 MB is refused. If an assistant sends more requests in one go than Dhaga allows at once, it is told to send them separately.

Size limits when saving people. dhaga_create_contact and dhaga_create_contacts accept, for each person:

  • a name, title, company and location of up to 200 characters each;
  • a first note of up to 20,000 characters;
  • up to 20 emails, 20 phone numbers and 20 links, where an email or phone entry can be up to 320 characters and a link up to 2,048.

dhaga_create_contacts takes at most 25 people per call. Anything over a limit is rejected with a validation error that names the field, so the assistant can shorten it and try again; for more than 25 people it sends further calls, one after another.

Each person you give dhaga_create_contacts a note for queues one background extraction (1 credit when it runs). Dhaga checks your remaining credits once per batch, so if you are close to your monthly limit, later extractions in a big batch can still be skipped; the people and notes themselves are saved.

On a self-hosted enterprise deployment

MCP needs no cloud tier, and a deployment Dhaga provisions for you serves the identical /api/mcp endpoint — a first-class MCP server, not a degraded one.

One difference to know about. The strong guarantees around a multi-person save (dhaga_create_contacts works on a single database connection, each person is saved separately without losing the others, and a call that runs out of time is cut off on the spot) belong to the multi-tenant cloud build. On a deployment without it, each step may use a different database connection, a batch is not one atomic unit, and a timeout only hands control back to your assistant: the save may still finish afterwards. Have the assistant check with dhaga_search before retrying anything that timed out.

The instance URL has to match

The OAuth issuer is derived from the deployment's configured base URL. If it doesn't match the URL clients actually reach your instance on, the tokens Dhaga issues will be rejected by the client that asked for them — ask whoever administers your deployment to check it. The personal-access-token path is unaffected by this: it's a plain header.

Dynamic Client Registration today, CIMD later

The 2026-07-28 MCP spec deprecates Dynamic Client Registration in favour of Client ID Metadata Documents (CIMD). Dhaga's authorization server is better-auth's mcp plugin, and better-auth 1.6 implements DCR but does not advertise CIMD yet — so DCR is what's live at /api/auth/mcp/register, and it works with the clients shipping today. CIMD support lands here when better-auth ships it.

On this page