Skip to main content

What matters most to you in Delaware County? Take the survey →

← Back to Documentation

MCP Server

A Model Context Protocol server that gives AI assistants full access to the Campaign CRM. Built on the official MCP SDK, it wraps the REST API so that tools like Claude Desktop and Claude Code can manage contacts and track engagement through natural language.

Requires authentication. The MCP server connects to the API using a Sanctum bearer token. You'll need a running instance of the application and a valid API token.


Setup

1. Install dependencies

cd mcp-server && npm install

2. Generate an API token

php artisan app:generate-api-token

3. Configure your client

Add the server to your MCP client configuration. The server reads CONTACTS_API_TOKEN and CONTACTS_API_URL from the project's .env file automatically.

Claude Code (.mcp.json)

{
  "mcpServers": {
    "contacts": {
      "command": "node",
      "args": ["mcp-server/index.js"]
    }
  }
}

Claude Desktop (claude_desktop_config.json)

{
  "mcpServers": {
    "indigo-nebula-contacts": {
      "command": "node",
      "args": ["/absolute/path/to/mcp-server/index.js"],
      "env": {
        "CONTACTS_API_TOKEN": "your-token-here",
        "CONTACTS_API_URL": "http://localhost:8000"
      }
    }
  }
}

Remote Access

Claude.ai web and Claude Mobile via Streamable HTTP

The remote server exposes all 73 tools over HTTPS using the Streamable HTTP transport, enabling access from Claude.ai web and Claude Mobile without any local setup.

Private CRM data. This endpoint connects directly to the campaign CRM. The Sanctum bearer token is server-side only and never visible to clients, but the endpoint is publicly reachable. Consider restricting to Anthropic IP ranges if needed.

Connect from Claude.ai

  1. Go to claude.ai → Settings → Connectors
  2. Click Add Custom Connector
  3. Enter the URL: https://ryangrissinger.com/mcp
  4. Save — tools will appear in your next conversation

Claude Mobile syncs automatically once the connector is saved via the web interface.

Health check

curl https://ryangrissinger.com/mcp/health

Deployment

The remote server runs as a pm2 process on the same server as the Laravel app, proxied through Nginx.

# Start (first time)
pm2 start mcp-server/remote.js --name mcp-remote

# Check status
pm2 status mcp-remote

# View logs
pm2 logs mcp-remote

Env vars required: CONTACTS_API_TOKEN, CONTACTS_API_URL, and optionally MCP_REMOTE_PORT (default 3100).

Nginx proxy block

location /mcp {
    proxy_pass http://127.0.0.1:3100/mcp;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Connection '';
    proxy_buffering off;
    proxy_cache off;
    chunked_transfer_encoding on;
}

Tools

73 tools across 19 resource groups

Contacts

10 tools
list_contacts List all contacts with pagination
search_contacts Search by name, email, or notes
get_contact Get full details including related data
add_contact Create a new contact (accepts emails[]/phones[]/social_profiles[] arrays + county + ring)
update_contact Update an existing contact (accepts emails[]/phones[]/social_profiles[] arrays + county + ring)
delete_contact Soft-delete a contact
restore_contact Restore a soft-deleted contact
export_contacts Export all contacts as CSV
import_contacts Import contacts from CSV content
merge_contact Merge a source contact into a target (target survives, source soft-deleted); dry_run previews

Roles

3 tools

volunteer, donor, door_knocker, phone_banker, event_host, sign_location, endorser, advisor, committee_member, precinct_captain

list_contact_roles List roles for a contact
add_contact_role Add a role to a contact
remove_contact_role Remove a role from a contact

Skills

3 tools

social_media, graphic_design, web_development, writing, public_speaking, fundraising, event_planning, data_entry, canvassing, legal, accounting, photography, videography, translation

list_contact_skills List skills for a contact
add_contact_skill Add a skill to a contact
remove_contact_skill Remove a skill from a contact

Interactions

5 tools

door_knock, phone_call, text_message, email, event, meeting, social_media, mail, other

list_interactions List interactions for a contact
add_interaction Log a new interaction
get_interaction Get interaction details
update_interaction Update an interaction
delete_interaction Delete an interaction

Donations

5 tools

cash, check, credit_card, online, in_kind, other

list_donations List donations for a contact
add_donation Record a new donation
get_donation Get donation details
update_donation Update a donation
delete_donation Delete a donation

Yard Signs

5 tools

requested, placed, removed, damaged, stolen, returned

list_yard_signs List yard signs for a contact
add_yard_sign Request or place a yard sign
get_yard_sign Get yard sign details
update_yard_sign Update yard sign status
delete_yard_sign Delete a yard sign

Donor Ask Statuses

5 tools

not_asked, planned, asked, pledged, donated, declined

list_donor_ask_statuses List ask statuses for a contact
add_donor_ask_status Create a donor ask record
get_donor_ask_status Get ask status details
update_donor_ask_status Update ask status
delete_donor_ask_status Delete an ask status

Activity Logs

3 tools
list_activity_logs List activity logs for a contact
add_activity_log Add an activity log entry
get_activity_log Get activity log details

Intelligence Profile

1 tool

Long-form markdown intelligence profile attached 1:1 to a contact (background, relationships, motivations).

set_contact_profile Create or update a contact intelligence profile

Donor Research

1 tool

Runs the FEC OpenFEC API search for a contact, computes partisan lean and giving capacity, writes findings back to the contact and the activity log.

research_contact_finances FEC search + analysis + write-back

Journal

5 tools

Campaign knowledge base. Entries support project grouping, contact linking, and community tagging.

list_journal_entries List journal entries (filterable)
get_journal_entry Get a journal entry with linked contacts
add_journal_entry Create a journal entry
update_journal_entry Update a journal entry
delete_journal_entry Delete a journal entry

Library

5 tools

Private campaign knowledge base of primers, briefings, and reference documents (markdown; team-only).

list_library_articles List library articles (filter by category/search)
get_library_article Get an article with its full body
add_library_article Create a library article
update_library_article Update a library article
delete_library_article Delete a library article

Outreach Initiatives

6 tools

Per-campaign curated contact lists with one-click "contacted" tracking. Find people with search_contacts/add_contact, then add them to a named initiative and mark outreach attempts. Initiatives are referenced by integer id.

list_initiatives List initiatives with progress (find the id)
get_initiative Get an initiative with its member list
create_initiative Create a new initiative
add_contact_to_initiative Add a contact to an initiative
remove_contact_from_initiative Remove a contact from an initiative
mark_initiative_contact Mark a member contacted / not contacted

Fundraising

3 tools

1:1 fundraising profile per contact. Tier (1/2/3/not_now) is auto-derived from donor_grade and giving_capacity unless tier_manual_override is set. Ask cycle: planned, asked_1, asked_2, asked_3, committed, received, declined, recurring.

set_fundraising_profile Create or update a fundraising profile
get_fundraising_profile Read a fundraising profile
list_fundraising_profiles List profiles with filters

Stats

1 tool

One-shot campaign CRM snapshot. Total counts and breakdowns for contacts (party, warmth, influence, top-10 community, partisan lean), interactions (by type + 7/30-day windows + open follow-ups), donations (sum, average, recency, acknowledgment, by method), and journal entries. Use for "how many" questions instead of paginating list endpoints.

contact_stats Campaign CRM snapshot — counts + breakdowns

Stakeholders

6 tools

Delaware County org/power map. 20-category enum (county departments, elected officials, appointed boards, townships, municipalities, school districts, fire/EMS, utilities, developers, nonprofits, political parties, media, etc.); tier 1–3; disposition (ally_track, neutral, adversary); relationship lifecycle (none → identified → contacted → engaged → ally). The link tool promotes a stakeholder's named primary contact to a full CRM contact with deduplication and adversary guard.

list_stakeholders List stakeholders with filters
get_stakeholder Get stakeholder details with linked contacts
add_stakeholder Create a new stakeholder
update_stakeholder Update a stakeholder
delete_stakeholder Delete a stakeholder
link_stakeholder_contact Promote primary contact to CRM contact

Parade Signups

1 tool

Add a person to a July 4, 2026 parade march roster — staff entry for phone or walk-up signups. Matches or creates the contact, writes the per-parade RSVP, and supports additional marchers with T-shirt sizes. Silent: sends no confirmation email or notification (unlike the public signup form). Re-adding the same person to the same parade replaces that parade's roster; sign someone up for multiple parades by calling it once per parade.

add_parade_signup Add someone to a parade march roster

Volunteers

2 tools

Tag a contact's volunteer activities and coverage areas. Set activities (set-replace, validated against the 14-entry taxonomy) + optional development stage (supporter/trained_lead/field_lead/host_captain). Set the Delaware County communities a volunteer covers — powers the coverage choropleth on /volunteers.

set_volunteer_involvement Set volunteer activities + optional stage
set_coverage_areas Set the Delaware County communities a volunteer covers

Committee Coverage

3 tools

Read-only access to Democratic Central Committee seat coverage (the /committee data). Query precincts by region/tier/status, pull the email-ready seated-members roster for a region, or get the 6-region coverage summary. Member contact info comes from the linked CRM contact.

list_committee_precincts List precincts by region/tier/status/search with member + CRM contact
list_committee_members Seated members (covered) for a region — email-ready roster
committee_coverage_summary Coverage rolled up by the 6 DCDP regions + party emails

Architecture

Runtime
Node.js with the official @modelcontextprotocol/sdk
Transports

stdio — Claude Desktop and Claude Code (local process)

Streamable HTTP — Claude.ai web and Claude Mobile (remote HTTPS)

Auth
Laravel Sanctum bearer token — server-side only, never exposed to clients
Source

mcp-server/index.js — stdio entry point

mcp-server/remote.js — HTTP entry point

mcp-server/tools.js — shared tool definitions and handlers