Skip to main content
Glama
technicallypete

outreacher

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


Related MCP server: atiendo24-mcp-server

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.

© 2026 Vitruvian Technology, Corp. All rights reserved.

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

F
license - not found
-
quality - not tested
D
maintenance

Maintenance

–Maintainers
–Response time
–Release cycle
–Releases (12mo)
Commit activity

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

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/technicallypete/outreacher'

If you have feedback or need assistance with the MCP directory API, please join our Discord server