Graph CRM MCP Server
Click on "Install 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., "@Graph CRM MCP ServerFind all contacts I haven't talked to in a month"
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.
Graph CRM
A personal, single-user CRM that visualizes contacts and their relationships as a graph. Node/Express + Prisma backend (SQLite by default), vanilla JS frontend (no build step), WebAuthn passkey login, and an MCP server exposing the full API so Claude can read/write the CRM directly.
Setup
npm install
cp .env.example .env
npx prisma migrate deploy
npm start # http://localhost:3000First visit walks you through creating a passkey (Face ID / Touch ID / security key / device screen lock). See .env.example for optional configuration (custom port, RP_ID/ORIGIN if not running on localhost:3000, etc).
npm run dev # auto-restart on file changes
npx prisma migrate dev --name <name> # after editing prisma/schema.prismaThere's no test suite or linter — see CLAUDE.md for architecture notes if you're working on this codebase.
Related MCP server: Dex MCP Server
Docker
docker build -t graph-crm .
docker run -p 3000:3000 -v graph-crm-data:/app/data graph-crmThe /app/data volume holds everything stateful: the SQLite database, the session store, and generated secrets (session secret, passkey data lives in the DB itself). Without a mounted volume, all of that is lost when the container is removed.
If you're serving this behind a domain other than localhost:3000, set RP_ID and ORIGIN (see .env.example) — WebAuthn passkeys are bound to the origin they were created on and won't work if it changes.
Swapping the database backend
The app uses Prisma, so moving off SQLite doesn't require touching any application code — only prisma/schema.prisma and the connection string. Everything else (family.js, the REST API, mcp-server.js, the frontend) talks to Prisma's generated client, not to SQLite directly.
General steps, then Postgres/MySQL specifics below:
Change the
providerinprisma/schema.prisma'sdatasourceblock.Point
DATABASE_URLat the new database.Delete
prisma/migrations/and generate a fresh initial migration against the new provider (npx prisma migrate dev --name init) — the existing migration history was written for SQLite's SQL dialect and won't replay cleanly on a different engine. There's no automatic data carry-over; if you need existing contacts, export them first (Settings → API keys → generate one → pull data via thelist_contacts/get_directoryendpoints or MCP tools, then re-import after switching).npx prisma generateto rebuild the client.
Postgres
docker run -d --name graph-crm-pg -e POSTGRES_PASSWORD=devpass -e POSTGRES_DB=graphcrm -p 5432:5432 postgres:16prisma/schema.prisma:
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}.env:
DATABASE_URL="postgresql://postgres:devpass@localhost:5432/graphcrm"rm -rf prisma/migrations
npx prisma migrate dev --name init
npm startFor a hosted Postgres (Supabase, Neon, RDS, etc.), just swap the connection string — most give you a ready-made postgresql://... URL. Neon/Supabase's pooled connection strings work fine with Prisma; if you hit prepared-statement errors behind a pooler, add ?pgbouncer=true to the URL (Prisma's documented workaround).
MySQL
docker run -d --name graph-crm-mysql -e MYSQL_ROOT_PASSWORD=devpass -e MYSQL_DATABASE=graphcrm -p 3306:3306 mysql:8prisma/schema.prisma:
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}.env:
DATABASE_URL="mysql://root:devpass@localhost:3306/graphcrm"rm -rf prisma/migrations
npx prisma migrate dev --name init
npm startProvider-specific things worth checking after switching
The
Credential.publicKeyfield isBytes— MySQL maps this toLONGBLOB, Postgres toBYTEA; both work fine with Prisma's client, no schema changes needed.ApiKey.lastUsedAtand otherDateTime?fields are nullable on all providers. Postgres storestimestamptzunder Prisma's default mapping; MySQL'sDATETIMEhas no timezone awareness — only matters if you care about exact cross-timezone display oflastUsedAt/createdAt.Both Postgres and MySQL enforce foreign keys and unique constraints the same way SQLite does here (
onDelete: Cascade,@@unique), so the cascade-delete behavior used throughoutfamily.js/server.js(deleting a contact cleans up their relationships, interactions, credentials, etc.) carries over unchanged.Running Postgres/MySQL in Docker alongside the app: add both as services in a
docker-compose.yml(not included here — this repo ships a single-containerDockerfilesince SQLite needs no separate DB service) and pointDATABASE_URLat the DB service's hostname instead oflocalhost.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/sriramsv/graph-crm'
If you have feedback or need assistance with the MCP directory API, please join our Discord server