mandaire.com · For your life .app · For your product .dev · Architecture & trust .org

Connect your AI to your Mandaire.

Add one MCP URL to Claude Desktop, ChatGPT, or Gemini. Authenticate once. Your AI reaches everything: your full life graph, communications, calendar, synthesis, all through one verb shape. No endpoint proliferation. No per-feature integrations.

Connect in under two minutes.

Mandaire exposes a single MCP server. Add it to Claude Desktop, ChatGPT, or Gemini. No SDK to install. No API key to request.

# Claude Desktop, add to claude_desktop_config.json { "mcpServers": { "mandaire": { "command": "mcp-remote", "args": ["https://mcp.mandaire.com/mcp"] } } } # ChatGPT, Settings > Connectors > + Add > paste: https://mcp.mandaire.com/mcp # Claude.ai, add as MCP server URL in Settings > Integrations: https://mcp.mandaire.com/mcp

OAuth runs automatically on first connect. You will be redirected to Mandaire to authorize. No key to manage.

Access requires a Mandaire account. Request access →

OAuth 2.1 with Dynamic Client Registration. No API key request.

Endpoint: https://mcp.mandaire.com/mcp
Transport: Streamable HTTP (MCP spec v2025-03-26)
Auth: OAuth 2.1 with Dynamic Client Registration (DCR)

Your client registers dynamically. Send a DCR POST /register, receive a client_id, exchange for a bearer token, attach to every request. The full OAuth metadata is at https://mcp.mandaire.com/.well-known/oauth-authorization-server.

For Claude.ai and Claude Desktop: add https://mcp.mandaire.com as an MCP server URL. The auth flow runs automatically on first connect.

Five tools registered. Four operational for your AI.

Everything your AI needs to help you: read about a person, search your communications, write back what it learns, query your open calendar, retrieve synthesis. All through mandaire(). One permission grant covers all routine access.

mandaire Everything. All reads and all writes. health Liveness check. Returns ok + DB reachability. server_status Full from_kind catalog + DB row counts. get_protocol_spec Machine-readable wire spec.

A fifth tool, tool_telemetry, is registered for Mandaire-internal observability. Your AI does not call it directly.

SELECT, INSERT, UPDATE, DELETE. One verb, one envelope, every operation.

mandaire( verb = "SELECT", # SELECT | INSERT | UPDATE | DELETE from_kind = "person", # what the query is about from_match = "Alex Rivera", # target (name, slug, ID) ask = "recent context", # free-text filter within the kind depth = "deep", # standard | deep | exhaustive purpose = "preparing_for_meeting", # steers payload shape fields = ["relationship", "photo_timeline"], filters = {"days": 14}, )

Defaults. verb defaults to SELECT. depth defaults to "standard" for external callers and auto-upgrades to "deep" for the data owner.

Read kinds (partial). person, email, message, event, photo, trip, note, file, topic, context, recall, open_commitments, catch_me_up, entity_state, inference_claim, open_writeback_slots.

Write kinds. ai_observation (INSERT), correction, task, state, inference_claim (UPDATE confirm/reject).

When your AI surfaces a commitment or the user confirms a fact, write it back. Mandaire compounds with every session, not just every data sync.

The full catalog is in mandaire(from_kind="system", fields=["protocol"]) or get_protocol_spec().

Every call returns the same shape. Read the fields; phrase responses accordingly.

{ "ok": true, "verb": "SELECT", "result": { ... }, "rendered_text": "Alex Rivera is ...", "confidence": 0.82, "signals": ["from_kind=person", "source=people.db"], "expectedness": "SURFACE_INTERESTING_IF_TRUE", "sources_used": ["people.db", "comms.db"], "absent_knowledge_caveats": ["Last interaction: 3 months ago"], "disclosure_applied": { "policy_version": "v0.1", "filtered": false }, "cognitive_mode": "focused", "suggestions": [] }

confidence is 0.0–1.0. Phrase hedging accordingly. Never present a 0.6 result as certain.

absent_knowledge_caveats: surface these. Gaps are facts, not noise.

expectedness: SUPPRESS (you already know), SURFACE_NOVEL, or SURFACE_INTERESTING_IF_TRUE. Use this to decide what to lead with.

disclosure_applied: confirms the policy ran. Do not re-filter; Mandaire already did.

writeback_slot_id: present on nil-result SELECTs. See below.

When Mandaire does not know something, it asks your AI to close the loop.

When a SELECT returns empty, count: 0 or found: false, the envelope includes a writeback_slot_id:

{ "ok": true, "result": { "count": 0, "results": [] }, "writeback_slot_id": "wbs_7GNSA5G27X9ZH9ZYAQ64NPEZ90" }

The slot is Mandaire saying: I do not know this yet. If you learn it from the user, close the loop.

What to do. Disclose the gap to the user. Do not invent an answer. If the user then tells you the answer, write it back and include the slot ID.

mandaire( verb = "INSERT", from_kind = "ai_observation", payload = { "claim": "Alex's new role is CTO at Inflection", "claim_source": "user_direct", "claim_tier": "factual", "confidence": 0.92, "trigger_reason": "nil_result_writeback", "entity_refs": ["[email protected]"], "field_class": "role", "source_context": "User said: 'Alex just took the CTO role at Inflection'", "source_attribution": "claude_renderer", "signals": ["user direct statement, single turn"], "writeback_slot_id": "wbs_7GNSA5G27X9ZH9ZYAQ64NPEZ90", }, purpose = "ai_writeback_from_conversation", )

The slot closes automatically. Future queries find the fact. Slots expire after 14 days if never closed.

You can query open slots:

mandaire(from_kind="open_writeback_slots", filters={"principal": "<session_token>"})

Every ai_observation declares its source, its tier, and its confidence.

1. claim_source: who provided this?

"user_direct" User stated this in the current session. confidence ≥ 0.80 "user_prior" User confirmed a prior Mandaire record. confidence 0.80–0.95 "ai_inference" You inferred it from context. confidence ≤ 0.50

If claim_source is omitted, Mandaire infers it from trigger_reason. If neither is present, the INSERT is rejected.

2. claim_tier: what validation pathway does this need?

"preference" Style, format, tone preference. Fast. Low corroboration threshold. "behavioral" Conditional pattern ("when X, Y"). Requires 3 corroborating instances. "factual" Binary true/false point fact. 5-stage pipeline before promotion.

3. confidence: how certain are you?

1.0 Verbatim user quote. 0.80–0.95 User paraphrase or confirmed record. 0.50–0.79 Strong inference; you can name the evidence chain. 0.00–0.50 Plausible synthesis or structured guess.

Overstating confidence is worse than understating. Low-confidence rows enter a 30-day review window; high-confidence rows land silently. The pipeline gates on this number.

Mandaire is your personal intelligence system. The data is yours, not your AI's.

What your AI sees. Only what your disclosure policy permits. Every SELECT passes through a policy pre-flight before the response is returned. The disclosure_applied envelope field confirms this ran.

What requires a purpose declaration. Sensitive from_kinds (correction_history, disclosure_policy, health data, personality profiles) require an explicit purpose= parameter. Without it, the call is rejected with a clear hint.

What is never exposed. Raw credentials, payment data, SSNs, passwords. PII pattern detection runs on every INSERT and rejects claims containing these patterns before they reach storage.

Write accountability. Every AI observation is audit-logged with source_attribution, trigger_reason, claim_source, and a confidence value. You can review and reject any AI-written claim.

A few lines is enough. The tool descriptions are self-documenting.

Add this to your system prompt to enable the writeback loop:

You have access to Mandaire, a personal intelligence system for <user name>. Query it BEFORE reasoning from training data about <user name>'s life, contacts, or context. When Mandaire returns a writeback_slot_id, disclose the gap to the user and close the slot if the user fills it. Never invent facts to fill a slot.

The MCP tool instructions served by get_protocol_spec() cover the full verb catalog, the from_kind list, and worked examples. You do not need to pre-load all of it. The tool descriptions are self-documenting.

Seven mistakes AIs make when connected to Mandaire. All are avoidable.

❌ Fan out multiple calls to assemble one person profile. Every sub-call is a round trip. Use one call with depth="deep".

# ❌ Four calls, four round trips mandaire(from_kind="person", from_match="Jordan Chen", fields=["name"]) mandaire(from_kind="email", from_match="Jordan Chen", ask="recent") mandaire(from_kind="message",from_match="Jordan Chen", ask="recent") mandaire(from_kind="event", from_match="Jordan Chen", filters={"days": 30}) # ✓ One call returns the full profile mandaire(from_kind="person", from_match="Jordan Chen", depth="deep")

❌ Discover a topic then fetch context in two separate calls. Use ask= in one call on the right from_kind.

# ❌ Two calls: topic discovery, then context fetch mandaire(from_kind="topic", ask="Series A") mandaire(from_kind="recall", ask="Series A context and timeline") # ✓ One call mandaire(from_kind="recall", ask="Series A context and timeline")

❌ Pre-meeting prep via three calls. Use from_kind="event" with purpose="preparing_for_meeting". The server assembles attendee context, related threads, and open commitments in one pass.

# ❌ Three calls, fragmented context mandaire(from_kind="event", from_match="board meeting") mandaire(from_kind="person", from_match="Jordan Chen", ask="recent context") mandaire(from_kind="recall", ask="board meeting topics") # ✓ One call with purpose mandaire(from_kind="event", from_match="board meeting", purpose="preparing_for_meeting")

❌ Reason from training data when Mandaire has synthesis on the topic. Call Mandaire first. Training data is stale by months; Mandaire's synthesis is current.

# ❌ Hallucination-prone "Based on what I know, Alex Rivera was involved in..." # ✓ Call Mandaire first, then reason on the result mandaire(from_kind="person", from_match="Alex Rivera", ask="current role and recent context")

❌ Pick a candidate when result.ambiguity_warning is set. Ask the user to disambiguate. Silently choosing the first result can write facts to the wrong person.

# ❌ Silently uses first result; risk of wrong-person writeback result = mandaire(from_kind="person", from_match="Sam") # ambiguity_warning is set -- two Sams in the graph # ✓ Surface the choice to the user if result.get("ambiguity_warning"): # Ask: "I found two Sams -- Sam Chen (product, 3 days ago) or # Sam Torres (investor, 2 months ago). Which one?"

❌ Call from_kind="recall" after from_kind="person" for the same entity. Check result.recall_not_required first. If True, recall adds nothing; the person call already synthesised it.

# ❌ Wasted round trip result = mandaire(from_kind="person", from_match="Jordan Chen", depth="deep") mandaire(from_kind="recall", ask="Jordan Chen") # redundant if recall_not_required is True # ✓ Check the flag result = mandaire(from_kind="person", from_match="Jordan Chen", depth="deep") if not result.get("recall_not_required"): mandaire(from_kind="recall", ask="Jordan Chen additional context")

❌ Retry a timed-out call with identical arguments. Mandaire caches SELECT results for 5 minutes. An identical retry hits the same timeout path. Use depth="standard" for the first call and upgrade only if the initial result is insufficient.

# ❌ Identical retry -- same path, same timeout risk result = mandaire(from_kind="person", from_match="Alex Rivera", depth="deep") # timeout result = mandaire(from_kind="person", from_match="Alex Rivera", depth="deep") # no help # ✓ Start at standard; escalate only if needed result = mandaire(from_kind="person", from_match="Alex Rivera", depth="standard") # if insufficient: result = mandaire(from_kind="person", from_match="Alex Rivera", depth="deep")

2.86 million edges. Sub-millisecond interactive traversal. Indexed SQLite.

Database: brain/relationship_graph.db (indexed SQLite, 2.86M edges, 2.23 GB)

Edge types: co_email (96.5%), co_calendar, co_group_chat, declared_workplace, emailed

Interactive traversal (LIMIT-100 pattern, standard MCP call):

Depth p95 (warm) Notes 1–5 hop < 1ms Covering index, no full-table scans

Full analytics (no LIMIT, batch/reporting queries):

Depth p50 (typical entity) p50 (hub entity) 1-hop COUNT 0.2ms 0.7ms 2-hop COUNT DISTINCT 50ms 443ms

Identity resolution: 5-phase ER pipeline, canonical normalization, blocking/inverted-index, multi-feature scoring (Jaro-Winkler, Levenshtein, Jaccard similarity), alias-overlap merge, UUID assignment. Entities survive alias drift across sources: same person under different email addresses, phone numbers, and display names resolves to one UUID.

Performance framing note: traversal speed comes from LIMIT short-circuiting and covering index, not native-graph-DB pointer adjacency. Do not characterize as equivalent to Neo4j or FalkorDB for analytics workloads, full-graph analytics at 2+ hops are expensive.

The common calls.

# Read about a person mandaire(from_kind="person", from_match="Jordan Chen", depth="deep") # Search emails mandaire(from_kind="email", ask="lease agreement", filters={"sender": "landlord@"}) # Upcoming events mandaire(from_kind="event", filters={"days": 7}) # Free-text recall across everything mandaire(from_kind="recall", ask="what did we decide about the Series A timing") # Catch-me-up brief mandaire(from_kind="catch_me_up") # Write back what the user just told you mandaire(verb="INSERT", from_kind="ai_observation", payload={ "claim": "...", "claim_source": "user_direct", "claim_tier": "factual", "confidence": 0.9, "trigger_reason": "user_direct_statement", }, purpose="ai_writeback_from_conversation")

Ready to connect?