outreacher

outreacher

AI-powered lead qualification system with an MCP server for Claude Desktop, enabling campaign management, lead search, status updates, and CSV import.

Category
Visit Server

README

Outreacher

An AI-powered lead qualification system with an MCP server for Claude Desktop and a Next.js SaaS frontend — built within 3 days.

All dev tooling runs via Docker. You do not need Go, Node, Bun, goose, or sqlc installed locally.

Developers: see CLAUDE.md for commands, conventions, and test instructions.

[!TIP] This project demonstrates and acts as a foundation boilerplate for 2-mode operation of an AI MCP as:

  1. Single-tenant Claude Desktop Go binary (individual use)
  2. Multi-tenant Next.js SaaS + chat LLM + MCP Go server (hosted environment)

The project architecture includes:

  • AI SDK + assistant-ui chat
  • Example MCP development
  • Shared backend code for both binary and server operation
  • Claude Code (and other development agent) usage and best practices
  • Docker best practices for development and production
  • Database security role/schema best practices (adopted early for scale)

📸 Demo & Walkthrough

See the full demo with screenshots and flows here:
👉 View Demo Docs


Architecture

Claude Desktop
  └── bin/outreacher-mcp-0.1.0-linux-amd64  (Go stdio binary, WSL)
        └── localhost:5432  (standalone postgres, volume: outreacher_standalone_data)

Browser / Next.js frontend
  └── app (Next.js, :3000)
        └── mcp (:3001, Go SSE server, air hot reload)
              └── postgres (internal only, volume: outreacher_postgres-data)

Two runtime modes — standalone binary for Claude Desktop, Docker Compose for development. Important: use separate named volumes for each mode to avoid data directory conflicts.


Tech Stack

  • Next.js 15 (App Router, Bun)
  • Go MCP server (mcp/) — stdio + SSE transports, pgx/v5, sqlc, goose migrations
  • PostgreSQL 16 — app schema, named Docker volume
  • Docker Compose — dev stack with air hot reload on the Go server

DB Users

User Password Role
admin admin Owns schema, runs DDL and migrations
app app Next.js runtime — member of app_crud + direct nextauth grants
mcp mcp MCP server runtime — member of app_crud
reporter reporter Read-only access — member of app_read

Group roles (no LOGIN) — DEFAULT PRIVILEGES auto-cover new tables/sequences/types:

  • app_crud — SELECT/INSERT/UPDATE/DELETE on app schema
  • app_read — SELECT on app schema

DATABASE_URL → mcp user (MCP server) or app user (Next.js). DATABASE_ADMIN_URL → admin user (goose only).


Schema (app)

Multi-tenancy

Table Key fields
organizations id, name, slug (unique), is_system
campaigns id, organization_id, name, slug, is_default
users id, email†, slug (unique), name, is_system
organization_memberships organization_id, user_id, role (owner|admin|member)
campaign_memberships campaign_id, user_id, role (admin|member|viewer)

Domain (scoped to campaign_id)

Table Key fields
companies id, campaign_id, name, domain, industry, linkedin_url
signals id, description — global, no campaign scope
signal_keywords signal_id, keyword — global
company_signals company_id, signal_id
leads id, campaign_id, name, email†, linkedin_url†, company_id, title, status, score, location, phone
notes id, lead_id, content, created_at
contact_identifiers campaign_id, type, value — dedup key, PK is (campaign_id, type, value)

† nullable. Leads are deduplicated by contact_identifiers, not by email.

Lead status flow: new → contacted → qualified → disqualified → converted


MCP Tools

Domain tools (all accept optional campaign_id; defaults to startup campaign)

Tool Description
search_leads Filter by name, email, status, company, campaign_id
get_lead Full lead detail with company and notes
update_lead_status Advance a lead through the status flow
create_followup_note Append a note to a lead
import_csv Import from CSV text (auto-detects Gojiberry, Revli formats)
search_companies Search companies by name or domain
get_company Full company detail with signals

Campaign management (stdio binary only)

Tool Description
list_campaigns List all campaigns for the current org
get_campaign Get campaign details by id
create_campaign Create a new campaign under the current org
rename_campaign Rename a campaign's display name

Mode 1 — Claude Desktop (Go binary via WSL)

Build the binary:

npm run build:mcp
# outputs bin/outreacher-mcp-0.1.0-linux-amd64   (stdio, WSL)
#         bin/outreacher-mcp-0.1.0-darwin-arm64   (stdio, macOS Apple Silicon)
#         bin/outreacher-mcp-0.1.0-darwin-amd64   (stdio, macOS Intel)
#         bin/outreacher-mcp-0.1.0-windows-amd64.exe
#         bin/outreacher-server-0.1.0             (SSE server)

Start standalone postgres:

docker run -d --name outreacher-pg --restart always \
  -p 5432:5432 \
  -e POSTGRES_DB=outreacher \
  -e POSTGRES_USER=admin \
  -e POSTGRES_PASSWORD=admin \
  -v outreacher_standalone_data:/var/lib/postgresql/data \
  -v "$(pwd)/postgres/init:/docker-entrypoint-initdb.d" \
  postgres:16-alpine

Run migrations:

PG_IP=$(docker inspect outreacher-pg --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}')
docker run --rm \
  -v "$(pwd)/mcp:/src" -w /src \
  --add-host=postgres:$PG_IP \
  golang:1.24-alpine \
  sh -c 'go run github.com/pressly/goose/v3/cmd/goose@latest \
    -dir migrations postgres \
    "postgresql://admin:admin@postgres:5432/outreacher" up'

Claude Desktop config (Settings → Developer → Edit Config):

{
  "mcpServers": {
    "Outreacher": {
      "command": "wsl.exe",
      "args": [
        "~/Code/outreacher/bin/outreacher-mcp-0.1.0-linux-amd64",
        "--database-url", "postgresql://mcp:mcp@localhost:5432/outreacher",
        "--openai-api-key", "sk-proj-..."
      ]
    }
  }
}

Replace <user> with your WSL username. ANTHROPIC_API_KEY enables LLM-based CSV extraction for unknown formats; omit to use the built-in parsers only. Restart Claude Desktop after saving — the hammer icon confirms tools are connected.

The binary idempotently bootstraps system_default_org, system_default_user, and the Default campaign on first run.


Mode 2 — Docker Compose (dev stack)

Copy .env.example to .env and fill in API keys, then:

docker compose up --build -d
Service Host port Notes
app 3000 Next.js dev server
mcp 3001 Go SSE server, air hot reload
postgres — Internal only

First-time migrations:

docker run --rm --network outreacher_default \
  -v "$(pwd)/mcp:/src" -w /src \
  golang:1.24-alpine \
  sh -c 'go run github.com/pressly/goose/v3/cmd/goose@latest \
    -dir migrations postgres \
    "postgresql://admin:admin@outreacher-postgres-1:5432/outreacher" up'

If the mcp container exited while waiting for migrations, restart it:

docker compose restart mcp

Imports

fixtures/imports/ is gitignored — it contains live personal data used for local development and debugging only. Supported formats: Gojiberry, Revli startup contacts, Revli investor contacts, Revli startup companies, Revli investor companies.

© Copyright

© 2026 Vitruvian Technology, Corp. All rights reserved.

This code is provided under a noncommercial license for evaluation and research purposes only.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured