relationship-manager
Hosts the MCP server, REST API, and UI on Cloudflare Workers, with D1 for storage and cron triggers for scheduled refreshes.
Connects Fathom recorded calls to pull attendees, summaries, and action items into the relationship graph.
Connects Gmail accounts to read messages and sync them into the relationship graph, with per-account enable/park toggles and freshness tracking.
Connects Google Calendar accounts to read events and attendees, syncing them into the relationship graph with per-account controls and freshness tracking.
Imports Instagram connections, invitations, and DMs from official exports into the relationship graph.
Reads WhatsApp conversation rows from the AIOS desktop app's local database to bring chat history into the relationship graph.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@relationship-managerWho have I been most out of touch with lately?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
relationship-manager
One graph of the people you actually talk to — and an agent API on top of it.
Your relationships are spread across Gmail, WhatsApp, your calendar and recorded calls. Every tool that tries to "manage" them wants you to type data in — the one thing you will never do.
relationship-manager reads the graph you already produced: it resolves the same human across
email, phone, WhatsApp JID and meeting attendee, keeps what it learns as evidence-backed
facts, and exposes the whole thing to agents first — one remote MCP endpoint, a REST API
and a CLI generated from a single tool contract.
Humans get a calm surface for the two jobs that actually need a human: deciding what's true and deciding who to reach out to.

Ask answers from the record, shows what it drew on, and says plainly what the record does not — including which sources have gone quiet.
Demo

Walkthrough (2:26, Loom): Sync Your Contacts and Conversation Pipelines — click the preview to play.
Live app: https://relationship-manager.everyai-com.workers.dev (sign-up is closed after the owner account — the deployment is one person's real relationships, not a demo tenant).
The demo shows, in one take: sign in → Ask answers a question about the graph while streaming, cites what it drew on and refuses what the record does not say → a profile pinned into Ask → Connections showing which sources are fresh, which have stopped, and every push that ever landed.
Related MCP server: dejaview-mcp
Screenshots
Every shot below is the real app on a real graph — no mockups, and nothing personal in frame.
Today — what needs you now. Live counts, and the sources that have gone quiet, named.

Connections — sign in to your own sources. One row per toolkit, as many accounts as you like, each enabled or parked on its own line. The app never sees a password.

Connections — freshness you can argue with. Counts from the rows, the age of the newest one, and the exact command that refreshes each source locally. Gmail is stale and says by how much; LinkedIn is honest that refreshing means exporting again.

Ask — starters, not a blank box. Four questions the graph can actually answer.

Agents — one endpoint, every client. The MCP config for Claude Code, Codex and Cursor, printed with your URL and the key you just minted.

Contents
What this is
Resolves identity across sources.
person_identifiersis the authoritative index — email, phone, WhatsApp JID, LinkedIn profile, Instagram handle, meeting-attendee name. One human, one row, no duplicates invented.Keeps evidence, not vibes. Every fact carries the observations that produced it and a band scored in code:
VERIFIED(may be written),PROBABLE/POSSIBLE(offered for your call), below that (not stored at all). The model never supplies a confidence number.Prepares you.
prep_briefis deterministic and free — who they are, how you're reachable, what's on file, recent email, meetings, WhatsApp, recorded calls, LinkedIn.Knows who to reconnect with. Cohort-ranked, suppression-respecting, with the history it was derived from.
Answers from the record, not the internet.
ask_about_personanddaily_briefrun on Workers AI (@cf/zai-org/glm-5.3-flash) with the graph as the only context — and say what is missing instead of inventing it.Never sends anything. Agents can propose outreach; a human approves it. Writes are a different class of thing.
The surfaces
Today — what needs you now: facts awaiting a decision, follow-ups that have come due, sources that have gone quiet, and what agents did in the last day.
Ask — a conversation with the graph. Answers stream, name the people and the trace they came from, and carry what they were grounded on (people / facts / sources). Threads are saved; open a profile and hit Ask about them to pin that person's record to the conversation. It reads and only reads — if the record is silent, the answer says so.
People — search, filter by source or pipeline stage, and expand a profile in place to see what is known, what needs your call, and the last few things that happened.
⌘Ksearches from anywhere.Pipeline — a kanban board of where each relationship stands. Drag a card to move someone; the move is optimistic and rolls back with a message if the write is refused.
Profile drawer — clicking through opens the full record beside the list rather than replacing it: overview, history, and the deterministic brief, with the stage selector and the evidence behind every value.
Reconnect — the ranked queue with the signal that put each person there; suppressed holds stay out unless you ask.
Connections — where the graph came from and how fresh it is, and the accounts behind it. Sign in to Gmail, Google Calendar and Fathom from the screen — more than one account each, enabled or parked independently — then Sync now pulls those accounts from the cloud and a daily schedule keeps them fresh. Every source carries its real row counts, the age of its newest row, and the exact command that refreshes it locally; the push log underneath shows every write that ever landed. Freshness is computed from the newest row in the graph, so a source that stopped producing reads stale with the date — a connector cannot claim otherwise, and a count with no rows behind it says so.
Agents — the MCP endpoint, keys, the tool catalogue, the call log and the kill switch.
External apps and services
Everything this project leans on, and what it is there for. Nothing else is required to run it:
the UI ships with no runtime dependencies beyond React and lucide-react, and the connectors are
Python standard library only — no pip install, nothing to rot.
Service | Used for | Where |
Cloudflare Workers | the API, the MCP endpoint and the UI, in one Worker | |
Cloudflare D1 | the relationship graph, accounts, sessions, chat threads | |
Cloudflare Workers AI — | grounded answers for Ask, | |
Cloudflare Cron Triggers | the daily refresh of connected accounts ( |
|
Composio | connecting Gmail / Google Calendar / Fathom accounts (OAuth), and pulling them from the cloud | |
Fathom | recorded calls — attendees, summaries, action items (via Composio or its own API) |
|
Gmail, Google Calendar | messages and events, read through Composio or the local AIOS workspace |
|
conversation rows, read from the AIOS desktop app's own database — there is no API for a personal account |
| |
AIOS desktop app | the local corpus the connectors read when you would rather not connect a mailbox |
|
LinkedIn + Instagram official exports | connections, invitations and DMs — the only legitimate path, never scraping |
|
Better Auth | email + password accounts; sessions in D1, HttpOnly cookies | |
MCP clients — Claude Code, Codex, Cursor, anything MCP | agents reading and acting on the graph through one endpoint |
Deploy your own (one click)
The button clones this repository into your GitHub account, provisions the Worker, a D1 database
and the Workers AI binding, applies the schema, and deploys the whole thing — on your account, at
your own *.workers.dev URL, with Workers Builds redeploying on every push to your fork.
It asks for exactly one secret: BETTER_AUTH_SECRET — signs session cookies.
openssl rand -hex 32.
Rather your agent did it? Hand it the repository and say so. The same deployment is one idempotent command — it finds or creates the database, generates the session secret, migrates, builds and deploys — and it will not replace an existing database or rotate a secret that already works:
npm run deploy:cloudflareAGENTS.md carries the full
playbook for an agent: check the account, ask before spending it, and tell you honestly that the
graph starts empty.
Then, in order:
Open the app and create the first account. It becomes the owner, and sign-up closes behind you — a fresh deployment is one person's graph, not an open sign-up page.
Point an agent at it. Open Agents, mint a key, and the Claude Code / Codex / Cursor configs are printed with your URL and the new key already in them. Paste one, ask "who should I reconnect with this week, and why?".
Fill the graph (optional): add
COMPOSIO_API_KEYunder Settings → Variables and secrets and the Connections screen can sign in to Gmail, Google Calendar and Fathom — several accounts each — and sync them from the cloud on demand or daily. Without it, the local connectors read your own machine instead.
The graph starts empty on purpose — nothing is faked, and every row that arrives carries the evidence it came from.
Quick start (self-host)
Runs entirely in your own Cloudflare account. One Worker, one D1 database, one AI binding — there is no hosted service in the loop and no data leaves your account.
Prerequisites
Requirement | Notes |
Node |
|
Python |
|
Cloudflare | a free account with Workers, D1 and Workers AI enabled |
Tools |
|
# 1. install
git clone https://github.com/everyai-com/relationship-manager.git
cd relationship-manager
npm install
# 2. local env — copy the template and fill it in
cp .dev.vars.example .dev.vars
# 3. your own database — paste the id it prints into wrangler.jsonc
npm run db:create
# 4. schema: identity, evidence, socials, auth, pipeline, chat
npm run db:migrate
# 5. optional — seed from your own corpora (see Operations below)
npm run seed:build && npm run seed:push
# 6. secrets, then deploy
npx wrangler secret put BETTER_AUTH_SECRET # openssl rand -hex 32
npx wrangler secret put BETTER_AUTH_URL # https://<your-worker>.workers.dev
npx wrangler secret put COMPOSIO_API_KEY # optional — lets the app connect your own accounts
npm run build && npm run deployOpen the deployed app and create the first account — it becomes the owner. Sign-up closes
after that; set ALLOWED_EMAILS to let specific other people in.
# optional: let a teammate in
npx wrangler secret put ALLOWED_EMAILS # "you@example.com, them@example.com"Local development against the real API:
npx wrangler dev # the Worker + D1, on http://127.0.0.1:8787
npm run dev # the UI on http://localhost:5173, proxies /api and /mcp to 8787
BETTER_AUTH_URLis optional: when it is unset the deployment uses the origin the request arrived on, so a workers.dev URL — or a dev server on any port — works without configuration.
Connect an agent
1. Mint a key
Open Agents in the app → New key. Pick read or read,write. The plaintext rel_… key is
shown once and stored only as a SHA-256 hash. Or do it over the API:
curl -X POST https://<your-worker>/api/agents \
-H "Authorization: Bearer <human session or a write key>" \
-H "Content-Type: application/json" \
-d '{"name":"claude-code","scopes":"read,write"}'2. Point your client at /mcp
Claude Code
claude mcp add --transport http relationship-manager https://<your-worker>/mcp \
--header "Authorization: Bearer rel_..."Codex — ~/.codex/config.toml
[mcp_servers.relationship-manager]
url = "https://<your-worker>/mcp"
http_headers = { Authorization = "Bearer rel_..." }Cursor, or any JSON MCP config
{
"mcpServers": {
"relationship-manager": {
"url": "https://<your-worker>/mcp",
"headers": { "Authorization": "Bearer rel_..." }
}
}
}Clients that only speak stdio (Claude Desktop, older Cursor) — use the local bridge in
packages/cli/src/mcp-stdio.ts, which proxies to the remote endpoint:
{
"mcpServers": {
"relationship-manager": {
"command": "npx",
"args": ["tsx", "/absolute/path/to/packages/cli/src/mcp-stdio.ts"],
"env": { "REL_API": "https://<your-worker>", "REL_KEY": "rel_..." }
}
}
}One command for all of the above — skill/relationship-manager/scripts/rel-setup.sh
writes the Claude Code and Codex entries for you:
REL_API=https://<your-worker> REL_KEY=rel_... ./skill/relationship-manager/scripts/rel-setup.sh3. Give the agent the skill
skill/relationship-manager/ teaches an agent how to use the tools well, not just how to
call them: read prep_brief before recommending anything, prefer person_timeline over a
summary, be honest about staleness, respect holds, never scrape a social network, and never claim
something was sent. Drop the folder wherever your client discovers skills, or point the agent at
AGENTS.md — the same contract in one file.
4. Ask it something
"Who should I reconnect with this week, and why?"
The agent calls reconnect_queue and prep_brief, and every call lands in the Agents screen
with its arguments, result and whether it was allowed.
MCP reference
Property | Value |
Endpoint |
|
Transport | Streamable HTTP, stateless ( |
Auth |
|
Scopes |
|
Protocol |
|
Surfaces | tools only — no MCP resources or prompts |
Implementation |
|
stdio bridge |
|
Every request carries its own key, so there is no session state to leak between callers. The
server advertises the evidence law and the no-send rule in its instructions, so a client that
reads them starts with the rules.
Tools (the agent surface)
Seventeen tools, 11 read / 6 write, defined once in
packages/core/src/tools.ts. The MCP server, the REST router and
the CLI all register from that list — the tool you read about is the tool an agent calls.
GET /api/tools returns the same catalogue as JSON.
Read
Tool | Arguments | What it does |
|
| Find people by name, email, company, domain or handle. Start here when you don't have a person id. |
|
| The full record: identity, settled facts with their evidence and reasons, handles, counts. |
|
| Deterministic markdown brief to read before you talk to someone. No model spent. |
|
| Merged chronology — email, meetings, calls, WhatsApp. Ground truth for "when did we last talk". |
|
|
|
|
| Ranked reconnect list with cohort, signal and a starter draft. Holds stay hidden unless asked for. |
| — | Per-source freshness: status, last sync, row count. Call it before implying anything is current. |
|
| People grouped by the stage you placed them in. Only placed people appear. |
|
| Workers AI, grounded on that person's record only — if the record is silent it says so. |
|
| Workers AI brief from overdue follow-ups, undecided facts and the top of the queue. |
| — | Who built this, and honest live counts of what the graph holds. |
Write
Tool | Arguments | What it does |
|
| The only path for agent-derived facts. Evidence bands decide what happens. |
|
| Accept writes the value and freezes the field as human-held; dismiss means it never returns. |
|
| Move someone on the board ( |
|
| Record an outreach that already happened, and when to review it again. |
|
| Queue a draft for human approval. The end of an agent's reach. |
|
| Official LinkedIn / Instagram exports. Handles merge into existing people; profile fields arrive as suggestions. |
The evidence law
Facts are not written, they are earned. record_fact is the only write path, and it scores
the observations the agent supplies — the model never supplies a confidence number.
Band | Score | What happens |
| ≥ 0.85 and at least one primary observation | written to the record ( |
| ≥ 0.55 | stored as a suggestion ( |
| ≥ 0.3 | stored as a suggestion ( |
below | < 0.3 | not stored at all |
agent.inference (0.25) and heuristic.estimate (0.3) are deliberately weak, so an unsupported
guess cannot clear the bar. A contradiction observation caps the score at 0.45 and demotes the
claim to a suggestion. If a human has decided a field, no agent can overwrite it. Weights live in
packages/core/src/evidence.ts.
Authority, the kill switch and the log
Reads are open. Every key can read.
Writes are opt-in per key and checked in one place —
apps/api/src/tools/dispatch.ts— before anything runs. A read-only key calling a write tool gets403 denied, and the denial is logged like any other call.The kill switch. Pausing agents in the UI (
POST /api/agents/0/pause) rejects every agent write immediately, without touching keys.Nothing is sent. There is no send tool.
propose_outreachwrites a row toapprovals, where a human approves or denies it.Every call is logged — tool, arguments hash, scope decision, outcome — and shown in the Agents screen.
The CLI
rel talks to the same deployment through the same tool contract, which makes it the fastest way
for an agent (or you) to read the graph without an MCP client in the loop.
export REL_API=https://<your-worker> # defaults to http://127.0.0.1:8787
export REL_KEY=rel_...
npm run rel -- people "lucidway" # find a person, get the id
npm run rel -- person 405 # the full record
npm run rel -- prep 405 # read this before you say anything about them
npm run rel -- timeline 405 # when did we actually last talk
npm run rel -- reconnect --cohort "Researched opportunity"
npm run rel -- connections # how fresh is this data, honestly
npm run rel -- facts --status PROPOSED # what is waiting on your decision
npm run rel -- decide 812 accept
npm run rel -- board --query lucidway
npm run rel -- move 405 "Meeting"
npm run rel -- propose 405 --channel email --body "…" --rationale "…"Import your official exports:
# LinkedIn → Settings → Data privacy → Get a copy of your data → Connections
npm run rel -- import-linkedin ~/Downloads/Basic_LinkedInDataExport_2026-05-18
npm run rel -- import-linkedin ~/Downloads/Basic_LinkedInDataExport_2026-05-18 --limit 10000 # + recent DMs
# Instagram → Accounts Center → Download your information → Followers and following (JSON)
npm run rel -- import-instagram ~/Downloads/instagram-yourname-2026-09-14.zipA handle merges into the existing person — the same human never forks into two rows, and your invitations bring the message you actually wrote.
A connection or a DM is a primary observation (
linkedin.connection0.8,linkedin.message-exchanged0.85). A follow is not (instagram.you-follow0.5), so follows arrive as suggestions rather than assertions.The Connections screen reports these honestly: imported, never "live" — refreshing means exporting again.
Why exports and not scraping: LinkedIn has no API for a personal account's own connections, and Instagram's Graph API is business-only. Scraping either one breaks their terms and risks the account, so this system takes the only legitimate path.
REST API
The same tools, over plain HTTP — useful for scripts, and for any agent that speaks HTTP but not
MCP. Authentication is identical: Authorization: Bearer rel_….
# the catalogue
curl https://<your-worker>/api/tools -H "Authorization: Bearer rel_..."
# any tool, by name — the body is the tool's arguments
curl -X POST https://<your-worker>/api/tools/search_people \
-H "Authorization: Bearer rel_..." -H "Content-Type: application/json" \
-d '{"query":"lucidway","limit":5}'
curl -X POST https://<your-worker>/api/tools/record_fact \
-H "Authorization: Bearer rel_..." -H "Content-Type: application/json" \
-d '{"person_id":405,"field":"title","value":"Founder","evidence":[{"kind":"user.stated"}]}'Alongside the tool route:
Route | Purpose |
| who you are, scopes, app name, model |
| live counts, due outreach, stale sources, recent agent calls |
| keys, revocation, kill switch |
| the call log |
| approve or deny proposed outreach |
| per-source freshness and the push log |
| the sources you can connect, with every account and which are enabled |
| start a sign-in for a toolkit ( |
| poll a sign-in, enable/default an account, disconnect it |
| pull every enabled account of a source (optionally |
| Ask conversations (streaming) |
| connector ingestion — facts never travel this path |
There is no OpenAPI file; GET /api/tools and the Zod contract are the machine-readable truth.
See skill/relationship-manager/reference/tools.md
for every tool's arguments and return shape.
Keeping it fresh
The hosted app is a snapshot until something feeds it. There are two paths and they land in the
same place — the ingestion route behind POST /api/sync, where facts never travel.
1. Connect accounts in the app (cloud)
Set COMPOSIO_API_KEY as a Worker secret, then on the Connections screen press Add account
next to Gmail, Google Calendar or Fathom and finish the sign-in in the tab Composio opens. The
account appears as connected on its own line; add as many as you like — a work and a personal
mailbox, two calendars — and enable or park each one independently. Sync now pulls every
enabled account, and a daily Cron Trigger (17 13 * * *) does the same unattended.
This app never sees a password. Composio owns the OAuth flow; all that comes back is an account id, and the key never leaves the Worker.
Only what you enabled is pulled. A dashboard full of abandoned connects is listed honestly — and ignored until you tick it.
Same ingestion, same honesty. A cloud pull goes through the same
applySyncas the connectors, so it is idempotent and it moves the same freshness numbers the screen shows.
2. Push from this machine (local connectors)
connectors/ reads what only exists locally — the AIOS desktop app's own database (mail, calendar,
WhatsApp), or a direct Fathom API key — and pushes it up.
cd connectors && cp .env.example .env # REL_API, REL_KEY, and any source keys
python3 -m rel_sync.cli status # what is connected, and what is missing
python3 -m rel_sync.cli all # push everything reachableSource | Needs | What lands |
aios | the AIOS desktop app on this machine | Gmail, Google Calendar and WhatsApp rows it already ingested |
fathom (Composio) |
| recorded calls: title, attendees, times, links |
fathom (direct) |
| the same, straight from Fathom |
gmail / calendar (Composio) | the same key, with those toolkits connected | mail and events, live |
Freshness is not self-reported. The API computes it from the newest row in the graph: ≤ 3 days is
connected, older isstalewith the date, nothing dated isnot_configured. A connector cannot claim otherwise.Idempotent by design. A message already in the graph is not re-inserted, so re-running an unchanged mailbox does not inflate anyone's history.
Standard library only — no
pip install, nothing to rot. Seeconnectors/README.md.
Operations
Environment variables
Name | Where | Required | Notes |
| Worker secret | yes |
|
| Worker secret | yes | canonical origin; must match the deployed URL |
| Worker secret | no | comma-separated; without it only the first account exists |
| Worker secret | no | lets the app connect Gmail / Google Calendar / Fathom accounts and sync them from the cloud |
| local / connectors | yes | the deployment to talk to |
| local / connectors | yes | agent key — needs the |
| connectors | no |
APP_NAME and AI_MODEL are plain vars in wrangler.jsonc.
Database, migrations and seeding
Migrations live in apps/api/migrations/ (0001_init → 0006_source_accounts)
and run through wrangler's own tracker with npm run db:migrate — ordered, recorded, and safe to
re-run. npm run deploy applies them first, which is how a one-click deploy initialises its own
database. Seeding is optional and reads your own corpora from
reference/ (gitignored — a fresh clone has none), so most self-hosters skip step 5 and let the
connectors build the graph instead.
npm run db:create # once; paste the id into wrangler.jsonc
npm run db:migrate # schema, socials, auth tables, pipeline, chat
npm run seed:build # reference/ → packages/seed/out/seed-*.sql
npm run seed:push # chunked to stay inside D1 request limitsThe seed replaces graph tables (people, messages, events, meetings, person_facts,
connections, reconnect) and deliberately leaves runtime tables alone — agent keys, the call
log, approvals and outreach survive a re-seed.
Testing
npm test # unit: the evidence ledger and identity resolution
npm run typecheck
REL_API=… REL_KEY=… REL_PASSWORD=… REL_EMAIL=… npm run test:e2etests/e2e.mjs runs against a real deployment — no mocks, no staging copy. It covers auth and
session forgery, key scopes (a read key must be denied on every write tool and that denial must
reach the call log), the kill switch, the evidence law end to end (weak evidence stores nothing,
medium becomes a suggestion, strong is applied; a human decision freezes the field; a dismissed
value never returns), the reconnect queue's suppression rules, LinkedIn/Instagram imports
(including that a re-import merges rather than forks), the Workers AI tools (including that the
model says "the record doesn't say" instead of inventing), the MCP protocol (tools/list matches
the REST catalogue exactly), the ask/conversation surface (that it streams, persists and refuses to
invent), source connections (an unknown source is refused, a sign-in starts and can be abandoned
cleanly, an account with no rows behind it is never claimed as synced), and the web shell.
Current state: 80 checks passing, 0 failing against the production deployment. The suite is
re-runnable — it writes fresh fact values each run, so a second run tests behaviour rather than the
first run's leftovers. It creates one clearly-labelled person (ZZ E2E Probe) and prints the SQL
to remove it.
Architecture
flowchart LR
subgraph Sources["Sources"]
G[Gmail]
W[WhatsApp]
C[Calendar]
F[Fathom calls]
S[LinkedIn / Instagram exports]
end
subgraph Local["Local ingest (optional)"]
L[connectors → POST /api/sync]
end
subgraph Edge["Cloudflare Worker"]
A[apps/api: REST + MCP]
D[(D1: the relationship graph)]
U[apps/web: UI]
end
subgraph Agents["Agents"]
M1[Claude Code]
M2[Codex]
M3[Anything MCP]
M4[rel CLI]
end
G --> L --> A
W --> L
C --> L
F --> L
S --> A
A --- D
U --- A
M1 -->|MCP| A
M2 -->|MCP| A
M3 -->|MCP| A
M4 -->|REST| AOne definition drives all three surfaces: packages/core/src/tools.ts.
The MCP server, the REST router and the CLI register from that list, and every call — whatever
the surface — goes through one dispatch path that validates the input, checks the scope, honours
the kill switch and writes the call log.
Repo layout
apps/api Cloudflare Worker — REST + remote MCP + auth + D1
apps/web Vite + React UI (AIOS design tokens, light/dark)
packages/core evidence ledger, identity resolution, the tool contract (+ tests)
packages/seed importers: real corpora + live SQLite → migrations/seed.sql
packages/cli `rel` — talk to the deployed graph from a terminal; stdio MCP bridge
skill/ the agent skill bundle (teaches an agent how to use the tools well)
connectors/ local Python ingest → POST /api/syncLimitations
Told straight, because a demo that hides these is a worse demo:
The graph is only as fresh as its last sync. Gmail, Google Calendar and Fathom can be pulled from the cloud; LinkedIn, Instagram and WhatsApp cannot be live at all — the first two have no API for a personal account, and the third is read from the AIOS desktop app on your machine. The Connections screen shows each source's real age rather than smoothing it over.
WhatsApp needs that local app. There is no API for a personal WhatsApp account, so that source is whatever the AIOS workspace already ingested.
Ask is read-only, and slow on purpose. It cannot draft or send anything; writes go through
record_fact/propose_outreachwhere the evidence law and a human stand in the way. The model behind it is a reasoning model, so answers take seconds and sometimes longer.One graph per deployment. The first account becomes the owner and sign-up closes; there are no per-user partitions.
ALLOWED_EMAILSlets specific people in to the same graph.Deduplication is heuristic. The same human across email, phone and socials is usually resolved, but a person who appears under two unrelated addresses can still arrive as two rows — merge them by hand rather than trusting the heuristic blindly.
Freshness is a 3-day rule. A source that produced rows yesterday and then died still reads
connectedfor two more days. That is deliberate (it avoids flapping) and it is why the dates are always shown.The demo video predates the account-connecting work — it shows Connections as a health screen. The screenshots above are current.
Roadmap
Not built yet, in the order that would matter most:
More toolkits the graph can read — Outlook, Slack, Notion.
apps/api/src/composio.tsand the account model are already generic; each addition is a catalog entry plus a field mapping.Finer sync control — per-account schedules and backfill windows, instead of one daily pass for everything enabled.
A sync you can watch — the pull runs server-side today and reports counts when it finishes; streaming its progress into the Connections screen is the obvious next step.
Thread management in Ask — rename, search across conversations, and share one thread with a teammate.
Contradiction surfacing — the evidence ledger already caps contradicted claims; showing the conflicting observations side by side in the UI would make them actionable.
More official importers — the social path currently covers LinkedIn and Instagram exports.
Safety and privacy
Accounts, not a shared password. Better Auth sessions in D1; the first account is the owner and sign-up closes after that. Sessions last 30 days, refresh daily, and signing out invalidates the session server-side.
Humans and agents are different principals. A human session can do everything; an agent key carries explicit scopes, is revocable, and every call is logged.
Reads by default, writes opt-in per key, validated in one place.
Facts need evidence.
score_evidence()decides the band; belowPOSSIBLEnothing is stored.No sending. Agents draft and propose; a human approves. Outreach is logged, never fired.
Your data never enters this repo. The graph lives in your own D1 database and in local files (
reference/, generatedpackages/seed/out/) — all gitignored. The repository is the engine, the surfaces and the deploy path, and nothing else.
License
MIT — see LICENSE.
Built by Phanindra Reddy at Saphaare Labs · magicteams.ai
Part of the everyai-com agent stack: distillory · agent-ready · agentprofile · primer · plainsync · argus
This server cannot be deployed
Maintenance
Related MCP Connectors
CRM, relationship intelligence, and multi-agent orchestration for AI agents.
Your company's brain for AI agents. Cited, permission-aware knowledge across every system.
Verified, sourced, real-time intelligence layer for AI agents.
Company brain for AI agents — temporal knowledge graph search, exploration, and durable memory.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to persist and retrieve memories via a personal knowledge graph, with tools for emotional intelligence, CRM, life management, social features, self-training, and autonomous insights.40 npm5MIT
- AlicenseNot gradedqualityCmaintenanceProvides persistent knowledge graph memory for AI agents, enabling them to store, recall, and query facts about people, projects, and relationships across sessions.MIT
- FlicenseNot gradedqualityBmaintenanceEnables AI assistants like Claude to read and write contacts, relationships, and interactions in a personal CRM via a graph-based API.-
- AlicenseNot gradedqualityBmaintenanceEnables AI assistants to query and update a persistent relationship ledger, providing pre-meeting context briefs, structured post-meeting extraction, and tracking of commitments, agreements, and open loops.MIT