contacts-crm

rudijetson/contacts-crm

3.2

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.

Tools
5
Resources
0
Prompts
0

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

  1. contacts_manage - Batch add/update contacts (idempotent upsert)
  2. contacts_query - Filter by company, status, tags with pagination
  3. contacts_search - Full-text search across all contact fields
  4. contacts_followup - Find pending follow-up actions by date
  5. 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, idempotent hints

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

  1. Create a new project at https://supabase.com
  2. Go to SQL Editor and run supabase/schema.sql
  3. Get credentials from Settings → API:
    • SUPABASE_URL
    • SUPABASE_SERVICE_ROLE_KEY

3. Set Up Clerk (OAuth Provider)

  1. Create application at https://clerk.com
  2. Enable sign-in methods: Email + Google (or your choice)
  3. Important: Go to OAuth → Enable "Dynamic Client Registration"
  4. Get credentials from API Keys:
    • CLERK_PUBLISHABLE_KEY
    • CLERK_SECRET_KEY
    • CLERK_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

  1. ChatGPT Settings → Connectors → Add Custom Connector
  2. URL: https://your-project.vercel.app/api/mcp
  3. Authentication: OAuth (auto-discovered from discovery endpoint)
  4. Log in with Clerk
  5. Done! ✅

7. Connect to Claude (Optional)

  1. Claude.ai Settings → Connectors → Add custom connector
  2. URL: https://your-project.vercel.app/api/mcp
  3. OAuth Client ID/Secret: Leave blank (auto-discovered)
  4. Log in with Clerk
  5. 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 using StreamableHTTPServerTransport (Remote MCP)
  • api/oauth-discovery.ts - Serves /.well-known/oauth-authorization-server metadata
  • src/server.ts - Creates McpServer instance with registered tools
  • src/tools/*.ts - Individual MCP tool implementations

OAuth Flow

  1. ChatGPT/Claude requests /.well-known/oauth-authorization-server
  2. Discovery endpoint points to Clerk's OAuth endpoints
  3. User logs in via Clerk
  4. Clerk issues OAuth access token
  5. Token sent with each MCP request in Authorization: Bearer <token> header
  6. 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 .env and Vercel
  • Check Supabase project is active (not paused)
  • Ensure Clerk application is in production mode

Database errors

  • Run supabase/schema.sql to 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.