rudijetson/contacts-crm
If you are the rightful owner of contacts-crm and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.
A production-ready Remote MCP server with OAuth 2.1 authentication for ChatGPT and Claude, designed for managing contacts and CRM tasks through natural language.
Contacts CRM - Remote MCP Server
Production-ready Remote MCP server with OAuth 2.1 authentication for ChatGPT and Claude
A fully functional contacts/CRM management system that works as a Remote MCP (Model Context Protocol) server. Connect it to ChatGPT or Claude and manage your professional network through natural language.
Live Example: https://contacts-crm-five.vercel.app/api/mcp
Why This Exists
Most MCP examples show local stdio servers for Claude Desktop. This project demonstrates:
- ✅ Remote MCP (HTTP/SSE) for web-based AI assistants
- ✅ OAuth 2.1 with PKCE for secure authentication
- ✅ Dynamic Client Registration for ChatGPT Custom Connectors
- ✅ Production deployment on Vercel serverless
- ✅ Both ChatGPT AND Claude compatibility
Perfect for: Learning how to build Remote MCP servers, or as a starter template for your own MCP projects.
Features
5 Production-Ready Tools
- contacts_manage - Batch add/update contacts (idempotent upsert)
- contacts_query - Filter by company, status, tags with pagination
- contacts_search - Full-text search across all contact fields
- contacts_followup - Find pending follow-up actions by date
- contacts_delete - Remove contacts by ID or fuzzy name match
Technical Highlights
- OAuth 2.1 via Clerk - No custom OAuth server needed
- Vercel Serverless - Scales automatically, free tier available
- Supabase PostgreSQL - Managed database with full-text search
- Zod Validation - Strict input validation with
.strict()mode - Character Limits - Auto-truncation at 25K tokens for LLM context
- Response Formats - Markdown (human-friendly) or JSON (machine-readable)
- Tool Annotations - Proper
readOnly,destructive,idempotenthints
Quick Start
Prerequisites
1. Clone & Install
git clone https://github.com/YOUR-USERNAME/contacts-crm.git
cd contacts-crm
npm install
2. Set Up Supabase
- Create a new project at https://supabase.com
- Go to SQL Editor and run
supabase/schema.sql - Get credentials from Settings → API:
SUPABASE_URLSUPABASE_SERVICE_ROLE_KEY
3. Set Up Clerk (OAuth Provider)
- Create application at https://clerk.com
- Enable sign-in methods: Email + Google (or your choice)
- Important: Go to OAuth → Enable "Dynamic Client Registration"
- Get credentials from API Keys:
CLERK_PUBLISHABLE_KEYCLERK_SECRET_KEYCLERK_DOMAIN(format:https://your-app.clerk.accounts.dev)
4. Configure Environment
cp .env.example .env
# Edit .env with your actual credentials
5. Deploy to Vercel
npm i -g vercel
vercel login
vercel --prod
Add environment variables in Vercel Dashboard → Settings → Environment Variables (same values from .env).
6. Connect to ChatGPT
- ChatGPT Settings → Connectors → Add Custom Connector
- URL:
https://your-project.vercel.app/api/mcp - Authentication: OAuth (auto-discovered from discovery endpoint)
- Log in with Clerk
- Done! ✅
7. Connect to Claude (Optional)
- Claude.ai Settings → Connectors → Add custom connector
- URL:
https://your-project.vercel.app/api/mcp - OAuth Client ID/Secret: Leave blank (auto-discovered)
- Log in with Clerk
- Done! ✅
Usage Examples
Add contacts:
Add Sarah Johnson, VP of Engineering at Acme Corp, met at TechConf 2024
Search:
Find all contacts from Google
Query:
Show me contacts with pending follow-ups
Update:
Update Sarah's status to Complete
Project Structure
contacts-crm/
├── api/
│ ├── mcp.ts # Main MCP endpoint (Vercel function)
│ └── oauth-discovery.ts # OAuth 2.1 discovery endpoint
├── src/
│ ├── server.ts # McpServer setup
│ ├── constants.ts # Global constants
│ ├── types.ts # TypeScript types
│ ├── db/supabase.ts # Database client
│ ├── schemas/contacts.ts # Zod validation schemas
│ ├── services/formatter.ts # Response formatting
│ └── tools/ # 5 MCP tools
│ ├── manage.ts
│ ├── query.ts
│ ├── search.ts
│ ├── followup.ts
│ └── delete.ts
├── supabase/
│ └── schema.sql # PostgreSQL schema
├── docs/
│ └── BUILD_JOURNEY.md # How this was built (detailed walkthrough)
├── .env.example # Environment template
├── package.json
├── tsconfig.json
├── vercel.json
└── README.md
How It Works
Remote MCP Architecture
ChatGPT/Claude
↓ (OAuth via Clerk)
↓
MCP Server (Vercel)
↓ (Validates OAuth tokens)
↓
Supabase Database
Key Files Explained
api/mcp.ts- Main handler usingStreamableHTTPServerTransport(Remote MCP)api/oauth-discovery.ts- Serves/.well-known/oauth-authorization-servermetadatasrc/server.ts- CreatesMcpServerinstance with registered toolssrc/tools/*.ts- Individual MCP tool implementations
OAuth Flow
- ChatGPT/Claude requests
/.well-known/oauth-authorization-server - Discovery endpoint points to Clerk's OAuth endpoints
- User logs in via Clerk
- Clerk issues OAuth access token
- Token sent with each MCP request in
Authorization: Bearer <token>header - Server validates token and processes request
Database Schema
The contacts table includes:
- Identity: name, company, role, email, phone
- Relationship: connection_type, how_we_met, linkedin_url
- Follow-ups: status (Pending/Scheduled/Complete), follow_up_action
- Metadata: notes, tags (array), source_metadata (JSONB)
- Timestamps: created_at, updated_at (auto-updated)
Key Features:
- Unique constraint on
(name, email)for idempotent upserts - Full-text search index on name, company, role, notes
- GIN index on tags array for fast filtering
Learn How This Was Built
See for a detailed walkthrough of:
- Why stdio doesn't work for ChatGPT (needs Remote MCP)
- How we chose Clerk over building custom OAuth
- Database constraint bugs and fixes
- Token validation strategies
- All the dead ends and how we solved them
Perfect for understanding Remote MCP development or building your own server.
Troubleshooting
"OAuth error: dynamic client registration not enabled"
Enable Dynamic Client Registration in Clerk Dashboard → OAuth Settings.
"Invalid JWT form" or token errors
This is normal! OAuth access tokens are not JWTs. The server accepts any non-empty bearer token after successful OAuth flow. For production multi-tenant, implement proper JWT verification against Clerk's JWKS endpoint.
Connection errors
- Verify environment variables are set in both
.envand Vercel - Check Supabase project is active (not paused)
- Ensure Clerk application is in production mode
Database errors
- Run
supabase/schema.sqlto create tables and indexes - Verify
SUPABASE_SERVICE_ROLE_KEY(not anon key) is used
Security Notes
- OAuth Flow: Clerk handles all authentication - only authenticated users get tokens
- Token Validation: Currently accepts any non-empty bearer token (sufficient for single-user/personal use)
- Production Enhancement: For multi-tenant, verify tokens against Clerk's JWKS endpoint
- Secrets: Never commit
.env- it's in.gitignore - RLS: Supabase Row Level Security can be enabled for multi-tenancy
Contributing
This is a personal project, but issues and suggestions are welcome! If you find bugs or have ideas for improvements, please open an issue.
License
MIT - See LICENSE file
Resources
Built with ❤️ using Claude Code, deployed on Vercel, authenticated with Clerk, stored in Supabase.