Skip to content

Digital-Crew-Technologies/max-mcp-server

Repository files navigation

Max MCP Server (Digital Crew)

MCP server for Max, the AI sales agent from Digital Crew. Max is built for lead qualification, cold outreach, and pipeline growth—see the product story on max.digitalcrew.tech.

This repo hosts a Next.js app that exposes MCP tools over Streamable HTTP so Max (or any MCP-capable client) can read and update workspace profile settings against the Digital Crew backend API.

What it does

The server registers MCP tools that proxy to the Digital Crew max-agent API.

Generated by pnpm docs:tools — do not edit by hand.

158 tools registered.

Tool Purpose
add_prospects_to_list Add prospects to a prospect list by their UUIDs.
agent_draft_create Stage an action (e.g. send a nudge, upsert a CRM contact, export a CSV) as a PENDING draft in the workspace for the human to approve. The draft is persisted in max-agent's agent_action_drafts table; nothing runs until the human approves. Returns { draft_id, state:'pending', review_url }. AFTER CREATING, POST THE RETURNED review_url (or a link to the agent drafts view in the workspace UI) TO THE USER'S CHAT CHANNEL SO THEY CAN APPROVE. Use this whenever an agent wants to take a write action on the user's behalf but the user hasn't pre-authorized the specific instance.
agent_draft_get Fetch one agent action draft by id, including its full payload, current state, audit trail, and any execution result. Use to inspect a specific draft before recommending approve/reject to the user.
agent_draft_list List agent action drafts in the workspace, optionally filtered by state (pending/approved/rejected/executed/failed/canceled) and/or action_type. Paginated via { limit, cursor }. Returns { data: [...], next_cursor? }. Use to show the user what's awaiting their approval or to audit recently-executed actions.
apollo_add_more Append more leads to an existing Apollo list (async). Re-runs the saved search for additional results.
apollo_create_list Create an Apollo-backed prospect list (async). Starts people search → ingestion. Poll the list status (or use wait_for_prospect_list) for progress. Auto-generates an idempotency_key if not provided so retries are safe.
approve_inbox_draft Approve a drafted reply and send it in-thread via Unipile, setting the action's status to 'approved'. Optionally pass body to send an edited reply instead of the stored draft. Returns the updated {data: InboxAutopilotAction}. Fails with 404 (draft not found), 409 (action not in 'draft' status), or 400 SendReplyError (code CHAT_NOT_FOUND | ACCOUNT_NOT_FOUND | ACCOUNT_NOT_CONNECTED | NO_PROSPECT_EMAIL | SEND_FAILED).
approve_proposal Approve a pending proposal and launch its draft campaign. SYNCHRONOUS — max-agent builds the prospect list, creates the campaign, and launches it (may take a while). Optionally pass modifications (titles, target_prospect_ids, campaign_name, campaign_description) to override the recommendation before launch; omit them to approve as-is. On success returns {data: SignalProposal (status 'launched', draft_campaign_id set), campaign_id}. Can fail with 402 (enrichment quota exceeded — proposal reverts to pending), 409 (not pending / already decided / expired), or 422 (no target prospects / no connected account).
archive_campaign Archive a campaign (soft delete) — hidden from default views but restorable.
archive_chat Archive a chat (soft delete). Messages remain, use archived=true filter to see them.
book_meeting Create a Cal.com booking and record it as a meeting. event_type_id defaults to the connection's default event type; start is a required ISO8601 UTC time; attendee_name, attendee_email, and attendee_time_zone (IANA) describe the attendee. Optionally pass prospect_id (advances that prospect to 'replied'), campaign_id, and a flat string metadata map. Returns the created {data: meeting row} (calcom_booking_uid, meeting_url, status 'scheduled', etc.). Fails with 409 (no calendar connected), 400 (invalid body / no default event type), or 4xx/502 CALCOM_ERROR (e.g. slot conflict / instance unreachable).
bulk_delete_organizations Delete multiple organizations. Set deleteProspects=true to cascade-delete linked prospects.
bulk_delete_prospects Delete multiple prospects by IDs.
bulk_enrich Queue many prospects and/or organizations for background enrichment by the cron worker (does NOT run inline). Provide prospect_ids and/or organization_ids. Returns {accepted} — how many rows were queued. Use this instead of calling enrich_prospect in a loop when enriching more than a couple of records.
bulk_import_organizations Import multiple organizations. Deduplicates by domain. Returns imported/existing/failed counts.
bulk_import_prospects Import multiple prospects at once. Deduplicates by email. Returns imported/existing/failed counts.
calendar_status Return the workspace's Cal.com connection status. Returns {data: {connected: false}} when no instance is connected, or {data: CalendarStatus} (baseUrl, username, label, defaultEventTypeId, eventTypes) when connected. eventTypes may be [] if the instance is currently unreachable. The api_key is never returned.
cancel_meeting Cancel a recorded meeting by its meetings.id UUID. If the meeting has a Cal.com booking uid it is cancelled on Cal.com first, then the row is marked cancelled. Optionally pass a reason. Returns the updated {data: meeting row} with status 'cancelled'. Fails with 404 (unknown / not in this workspace), 409 (no calendar connected, only when a Cal.com cancel is needed), or 4xx/502 CALCOM_ERROR.
claire_deep_research Multi-source background research on a named person or company. Returns recent activity, news, role context, company highlights, and proof points. Synchronous (may take 30-90s). Call this BEFORE crafting personalized outreach so the message reflects who the prospect actually is.
claire_extract_prospects_from_url Fetch a public URL (conference attendee list, team / about page, press release, panel announcement, etc.) and extract structured prospects (people / contacts) from it via Claire. Returns { prospects:[{ name, title, company, linkedin_url?, email?, ... }], source_url, extracted_count, claire_request_id? }. Use when the user pastes a URL and asks to 'find leads here' / 'build a list from this page'. Pair with create_prospect or import_prospect_list_csv to persist the result.
claire_find_competitors Identify direct competitors of a company by URL. Returns a list of competing companies with descriptions and sources. Synchronous (typically 1-3 min). Use to expand a prospect list with similar companies or to ground competitive positioning in outreach.
claire_market_watch Run a market-watch pass on a URL, optionally filtered by criteria (e.g. 'pricing', 'hiring', 'funding'). Returns a snapshot of detected signals. Use to inform outreach timing (e.g. just-funded companies) or to spot competitive moves.
claire_search Free-text research query against Claire's hub. Use for quick lookups like industry trends, funding news, or any topic where you'd otherwise google. Synchronous — waits for Claire to finish and returns the result. Use mode='lite' for fast first-pass (default), 'full' for deeper multi-source.
clear_failed_requests Drain the dead-letter queue. Returns the number of entries removed. Use after manually replaying or after resolving the upstream issue.
connect_calendar Connect the workspace's self-hosted Cal.com instance so Max can read availability and book meetings. Validates the credentials by calling Cal.com listEventTypes before storing them. Pass base_url (the instance API base, e.g. https://cal.example.com or .../api/v2), api_key, and optionally default_event_type_id. Returns {data: CalendarStatus} with connected:true, baseUrl, username, label, defaultEventTypeId, and the list of eventTypes. The api_key is never returned.
create_campaign Create a new campaign in draft state. Requires name, included_lists, and accounts. Won't send until launched.
create_intent_trigger Set up a trigger that watches a target URL for a buying signal (funding, hiring, tech_stack, news, job_change, or custom). Optionally scope it to an organization or prospect and set how often it re-polls. Returns the created {data: IntentTrigger}. Used to start monitoring for intent before any signals exist.
create_organization Create a new organization/company record.
create_prospect Create a single prospect. Deduplicates by email — returns existing row if email already exists.
create_prospect_list Create an empty platform prospect list. For Apollo-sourced lists, use apollo_create_list instead.
crm_assign_prospects Assign prospects to HubSpot owners using agent_settings.assignment_rules (or assignment_rules_override). First matching rule wins; no match → owner_id null, reason 'no rule matched', priority 'low'. Priority otherwise derives from prospect.score (>=75 high, 50–74 med, <50 low) else 'med'. Returns { assignments: [{ id|email, owner_id, owner_name, owner_email, reasoning, priority }] }.
crm_detect_forecast_changes Compare current open HubSpot deals against the workspace's deal snapshot from window_days ago (read from max-agent's crm_deal_snapshots via GET /api/v1/crm/deal-snapshots). Flags amount changes (abs delta pct > threshold), stage moves (forward/backward via pipeline displayOrder), close-date slips (> threshold days), new deals since last snapshot, and deals that disappeared. Returns { window_days, baseline_iso, current_iso, changes:[{ deal_id, dealname, owner_id, owner_name, amount, prior_amount, amount_delta_pct, stage, prior_stage, stage_movement, close_date, prior_close_date, close_date_slip_days, flag_reasons[] }] }.
crm_export_import_csv Build a HubSpot-import CSV (base64-encoded) from prospects. When dedup_against_hubspot (default true), drops prospects whose email already exists in HubSpot (concurrency <=5). Columns: Email, First Name, Last Name, Job Title, Company, Company Domain, Country, Industry, Number of Employees [, HubSpot Owner ID when include_assignment]. Returns { csv_base64, row_count, deduped_count, deduped:[{ email, existing_id }] }.
crm_get_contact Fetch a single CRM contact by email (the dedup identity). Returns null if not found.
crm_get_deal Fetch a single HubSpot deal by id, including its full properties and associated company/contact ids. Returns null if not found.
crm_list_activities List HubSpot engagements (call/email/meeting/note/task) with optional filters (deal, contact, owner, types, since). Per-type queries are merged sorted by timestamp desc. Returns id, type, timestamp, ownerId, dealId, contactId, subject, body.
crm_list_deals List deals from HubSpot with optional filters (stage, owner, pipeline, amount range, close-date range, modified-after). Returns id, dealname, amount, ownerId, stage, pipeline, closeDate, lastModified, lastActivityDate, nextStep, associated company/contact ids.
crm_list_owners List HubSpot owners (sales reps) for the workspace. Returns id, email, firstName, lastName, teams. Use to map deals/assignments to people.
crm_list_pipeline_stages List deal pipeline stages (optionally scoped to one pipeline). Returns id, label, displayOrder, pipelineId, isWonStage, isLostStage.
crm_pipeline_risk_scan Scan open HubSpot deals for risk: days inactive, days-to-close, missing fields (amount/owner/next_step/last_activity), close-date slipping, and high-value-low-activity. Computes a transparent weighted risk_score (0–100) and a PRIVATE suggested nudge draft to the owner (never sent). Uses agent_settings.risk_thresholds. Returns { scanned_count, flagged:[...] }.
crm_score_prospects Score prospects 0–100 against the workspace ICP rules (agent_settings.icp_rules): country 25, industry 25, employee-in-range 20, any title keyword 30. With no rules configured, every prospect scores 50 (reason no_icp_rules_configured). Returns { scored: [{ id|email, score, matched[], missed[] }] }.
crm_search_contacts Search the connected CRM (HubSpot) for contacts by free text (name, email, company). Returns matching contacts. Use before creating a contact to check if one already exists.
crm_status Report whether HubSpot is connected for this workspace. Attempts a cheap HubSpot read; returns { connected: bool, ... }.
crm_upsert_company Create-or-update a company in the connected CRM, matched by domain — never creates a duplicate. Requires a read + write HubSpot connection.
crm_upsert_contact Create-or-update a contact in the connected CRM, matched by email — never creates a duplicate. Use when the user asks to add or update a contact in HubSpot. Requires a read + write HubSpot connection.
crm_weekly_brief_compose Compose a structured weekly sales brief from last week's activities, current open deals, the pipeline risk scan, and per-rep aggregates. Pure data (no writes). Returns { week_ending, last_week_summary, this_week_priorities, stale_deals, deals_without_next_step, top_risks, per_rep_questions, suggested_bj_notes, action_items }. Pass the result to notion_publish_weekly_brief to draft it in Notion.
delete_campaign Permanently delete a campaign and all its workflow executions. Prefer archive for soft removal.
delete_organization Delete an organization. Linked prospects get organization_id = null.
delete_prospect Delete a prospect permanently.
delete_prospect_list Delete a prospect list (prospects themselves are NOT deleted).
disable_trigger Disable an intent trigger (sets active=false) so it stops re-polling. Returns the updated {data: IntentTrigger}, or a 404 error if the trigger is not in this workspace.
disconnect_account Disconnect a LinkedIn or email account from the workspace. Use hosted_auth_link to reconnect.
enrich_organization Run Claire deep-research on an organization and save the result onto the record. SYNCHRONOUS — waits for Claire (may take 30-180s) and returns {status} ('complete' with the research, 'skipped' if the org has no name, or 'busy' if another enrichment is already running). Counts against the workspace's daily enrichment quota; returns a 429 quota error when exhausted. Set force=true to re-enrich an already-complete record.
enrich_prospect Run Claire deep-research on a prospect and save the result onto the record. SYNCHRONOUS — waits for Claire (may take 30-180s) and returns {status} ('complete' with the research, 'skipped' if the prospect has no name, or 'busy' if another enrichment is already running). Counts against the workspace's daily enrichment quota; returns a 429 quota error when exhausted. Set force=true to re-enrich an already-complete record. Call this BEFORE crafting personalized outreach.
explorium_add_more Append more leads to an existing Explorium list (async). Re-runs the saved search for additional results. Poll the list (or use wait_for_prospect_list) for progress.
explorium_create_company_list Create an Explorium-backed company (organization) list (async). Runs a business search with the given filters → enrichment → ingestion. Returns a pending list DTO; poll its status (or use wait_for_prospect_list) until completed. Auto-generates an idempotency_key if not provided so retries are safe.
explorium_create_list Create an Explorium-backed prospect (people) list (async). Runs a people search with the given filters → enrichment → ingestion. Returns a pending list DTO; poll its status (or use wait_for_prospect_list) until completed. Auto-generates an idempotency_key if not provided so retries are safe.
generate_message_preview Use AI to generate a personalized message for a prospect. Charges credits. Specify channel and prompt.
generate_workflow Use AI to generate a campaign workflow from natural language. Charges credits. Provide a prompt or structured fields.
get_account Get full details of a connected account — provider, channel, config, sync status.
get_account_rate_limits Get daily/weekly sending limits and current usage for a specific account.
get_availability Fetch open booking slots for an event type, grouped by date. event_type_id defaults to the connection's default event type; start defaults to now and end to now + 14 days; time_zone is an optional IANA string to localize slots. Returns {data: {eventTypeId, slotsByDate: {: [{start, end?}]}}}. Fails with 409 if no calendar is connected, or 400 if no event type id is available.
get_campaign Get full details of a campaign by ID — workflow, scheduling, accounts, prospect lists, stats.
get_campaign_engagement_summary Engagement summary for a campaign: open/click/reply/bounce rates plus per-link click detail (top links, distinct URLs clicked).
get_campaign_lead_analytics Per-prospect breakdown — where each lead is in the workflow and message event history.
get_campaign_memory Read Max's durable memory for a campaign (ICP, decisions, notes). Recall this when working on one of several simultaneous campaigns so you keep them straight.
get_campaign_node_run_counts Map of workflow node ID → execution count. Useful for funnel visualization.
get_campaign_stats Aggregate performance stats — email open/reply rates, LinkedIn connection/reply rates, execution counts.
get_chat Get full details of a single Unibox chat/conversation thread.
get_circuit_status Inspect the circuit-breaker state for each upstream host the MCP server has called. Shows whether requests are being fast-failed (state=open), probing (half-open), or flowing normally (closed).
get_dashboard_kpis Workspace-wide aggregate stats — execution counts, email/LinkedIn rates, completion percentage.
get_email_tracking_events Raw per-event email tracking rows for a prospect (opens, clicks, replies, bounces) with url/ip/user_agent detail. Newest first. Optionally filter by event_types.
get_enrichment_credits Return the workspace's daily enrichment quota usage: {cap, used_today, remaining}. Check this before a large bulk enrichment to confirm there's headroom.
get_enrichment_status Check the enrichment state of a single prospect or organization. Provide exactly one of prospect_id or organization_id. Returns {enrichment_status, enrichment_updated_at, has_research}.
get_inbox_autopilot_status Return the workspace's current inbox autopilot setting: {enabled, mode ('auto_safe'|'draft_all'|'off'), daily_cap}. Use this to confirm whether autopilot is active and how it is configured before changing it or reviewing drafts.
get_link_click_details Link clicks for a campaign grouped by url, with total click counts and unique-prospect counts. Sorted by clicks desc.
get_organization Get full details of an organization — domain, industry, employee count, funding, social URLs.
get_prospect Get full profile of a prospect — name, title, company, LinkedIn, email, location, enrichment data.
get_prospect_campaign_activity Chronological log of message events for a prospect across all campaigns (newest first).
get_prospect_engagement_timeline Chronological email engagement timeline for a prospect (oldest first) — every open, click, reply, and bounce.
get_prospect_list Get full details of a prospect list by ID — status, search config, result counts, timestamps.
get_signal_history Return the detected SignalEvent rows for a single trigger — each event records whether the poll found changes, a summary, and the raw scrape. Returns {data: SignalEvent[]}.
get_signal_proposal Fetch a single signal proposal by id, including its full recommendation (campaign name/description, workflow_config, target_prospect_ids, estimated contacts/credits, matched ICP, and entity). Returns {data: SignalProposal}, or a 404 error if not found.
get_upcoming_meetings List upcoming non-cancelled meetings ordered by start time ascending. from defaults to now and to defaults to now + 30 days (both ISO8601). Returns {data: meeting row[]} (status one of scheduled|rescheduled|completed|no_show).
get_workspace_profile Fetch workspace profile settings (company info) for the authenticated workspace via DigitalCrew API. Uses the MCP connection Authorization: Bearer token when set.
hosted_auth_link Generate a short-lived URL for the user to connect a LinkedIn or email account via Unipile hosted auth.
import_prospect_list_csv Create a new prospect list and import prospects in one call. Each row needs an email (for dedup).
launch_campaign Launch a draft campaign — transitions draft → active and creates workflow executions for each prospect.
linkedin_cancel_invitation Cancel/withdraw a sent LinkedIn invitation by its invitation_id (from list_invitations_sent).
linkedin_comment_on_post Comment on a LinkedIn post.
linkedin_create_post Create a LinkedIn post from the connected account.
linkedin_find_profile Find a LinkedIn profile by name, company and/or title. Searches LinkedIn directly — always use this before any invitation or message. If multiple people share the name, returns candidates with ambiguous:true; show them to the user and ask which one. Never guess a slug or proceed when ambiguous.
linkedin_get_all_messages Get all recent LinkedIn messages across all conversations.
linkedin_get_company_profile Get a LinkedIn company profile by its identifier or slug.
linkedin_get_conversation_messages Get messages in a specific LinkedIn conversation.
linkedin_get_own_profile Get the profile of the connected LinkedIn account. Useful to confirm the account is active.
linkedin_get_profile Get a full LinkedIn profile by slug (public identifier). Returns provider_id and profile data.
linkedin_get_user_posts Get recent posts by a LinkedIn user.
linkedin_list_connections List first-degree LinkedIn connections of the connected account.
linkedin_list_conversations List LinkedIn conversations (inbox).
linkedin_list_invitations_received List pending LinkedIn invitations received from others.
linkedin_list_invitations_sent List pending LinkedIn invitations you have sent.
linkedin_react_to_post React to a LinkedIn post.
linkedin_reply_in_chat Reply in an existing LinkedIn conversation.
linkedin_search_people Search LinkedIn for people by keywords.
linkedin_send_invitation Send a LinkedIn connection request. ALWAYS call find_profile first to get provider_id.
linkedin_send_message Send a LinkedIn direct message to start a new conversation. Requires existing connection or InMail credits.
list_accounts List all connected LinkedIn and email accounts — name, email, status, daily limits.
list_campaigns List all outreach campaigns. Filter by status, search by name, paginate and sort.
list_chat_messages Get all messages in a conversation — body, direction (in/out), timestamp, status.
list_chats List LinkedIn and email conversations — filter by channel, prospect, account, or archived status.
list_failed_requests Inspect the dead-letter queue — write requests (POST/PATCH/DELETE) that exhausted all retries. Useful for forensics or manual replay. Backed by Redis if REDIS_URL is set, otherwise an in-memory ring buffer of up to 500 entries.
list_inbox_drafts List the autopilot-generated reply drafts awaiting review (status='draft') for the workspace, newest first. Returns {data: InboxAutopilotAction[]}, each row including intent, sentiment, confidence, and the proposed reply_body. Review these before calling approve_inbox_draft or reject_inbox_draft.
list_intent_signals List the workspace's intent triggers (optionally filtered by active state) so you can see what is being monitored and review recent signal activity. Returns {data: IntentTrigger[]}, each row including last_run_at and last_error. Use get_signal_history to drill into the detected events for a specific trigger.
list_organizations List all organizations/companies — search by name or domain, filter by industry/country.
list_prospect_list_members List all prospects in a specific list — paginated, searchable, sortable.
list_prospect_lists List all prospect lists — name, status, result counts, and search criteria.
list_prospects List prospects with rich filtering — search, status, org, titles, countries, industries, pagination, sorting.
list_signal_proposals List the AI-generated campaign proposals produced from detected signals, optionally filtered by status (pending, approved, rejected, modified, launched, expired). An unknown status is ignored and all proposals are returned. Returns {data: SignalProposal[]}. Review pending proposals before approving them.
meetings Read the workspace's meetings from the meeting hub. The workspace comes from your bearer token — prospect_id filters within it.
modify_proposal Adjust a pending proposal WITHOUT launching it: pass modifications (titles, target_prospect_ids, campaign_name, campaign_description) to re-select prospects and regenerate the campaign workflow. The proposal stays pending so it can be reviewed and approved later. Returns the updated {data: SignalProposal}. Fails with 409 if the proposal is not pending, or 404 if not found.
notion_append_blocks Append an array of Notion block JSON objects to an existing page (chunked into ≤100-block requests). Requires agent_settings.allow_notion_writes (default true). Returns { appended }.
notion_create_page Create a new Notion page under a parent page, with an optional body of Notion block JSON. Requires agent_settings.allow_notion_writes (default true). Returns { id, url }.
notion_get_page Fetch a Notion page object plus all of its child blocks (paginated). Returns { page, blocks }.
notion_publish_weekly_brief Render a crm_weekly_brief_compose output as a DRAFT Notion page (H1 title + H2 section per part) under the workspace's Drafts/the assistant parent. Write-gated by agent_settings.allow_notion_writes. Defaults parent to agent_settings.notion_drafts_parent_id and template to agent_settings.notion_weekly_template_id. Returns { page_id, url, status:'draft', partial_success? }.
notion_search_pages Search the connected Notion workspace for pages matching a free-text query. Returns [{ id, title, url }].
pause_campaign Pause an active campaign — stops dequeueing new actions (in-flight calls finish).
propose_times Return the n soonest available slots as a flat list — handy for offering a prospect a few concrete times. count is 1-50 (default 3); event_type_id defaults to the connection's default event type; time_zone is an optional IANA string. Returns {data: {eventTypeId, slots: [{start, end?}]}}. Fails with 409 if no calendar is connected.
prospect_list_meetings List the meetings involving one prospect, newest first — the prospect meeting feed. Same filters and shape as the meetings group's list action, with prospect_id required. Returns {data: MeetingSessionSummaryDto[], nextCursor}. Paginated: echo nextCursor back as cursor; null means the last page. prospect_id filters within your authenticated workspace.
prospect_list_tasks List the tasks about one prospect, newest first. Same filters and shape as the tasks group's list action, with prospect_id required. Returns {data: TaskDto[], nextCursor}. Paginated: echo nextCursor back as cursor; null means the last page. prospect_id filters within your authenticated workspace.
reject_inbox_draft Reject a drafted reply so it will not be sent, setting the action's status to 'rejected'. No message is sent. Returns the updated {data: InboxAutopilotAction}. Fails with 404 (draft not found) or 409 (action not in 'draft' status).
reject_proposal Reject a pending proposal so it will not be launched. Returns the updated {data: SignalProposal (status 'rejected')}. Fails with 409 if the proposal is not pending.
remove_prospects_from_list Remove prospects from a prospect list by their UUIDs.
restore_campaign Restore an archived campaign back to draft status.
resume_campaign Resume a paused campaign — transitions paused → active.
search_prospect_lists Preview filter results without creating a list — search by titles, countries, industries, employee count, etc.
send_booking_link Compose the rep's public Cal.com booking link so it can be shared with a prospect. Does NOT call Cal.com. Optionally pass prospect_id for context. Returns {data: {url, username}}. Fails with 409 if no calendar is connected.
send_chat_message Send a manual reply in an existing Unibox chat. Channel (email/LinkedIn) is inferred from the chat.
send_new_email Compose and send a brand-new email to any recipient, outside any chat or campaign. Sends from the workspace's connected email account. Use send_chat_message instead to reply within an existing conversation.
set_inbox_autopilot Set the workspace's inbox autopilot configuration. enabled is the master kill switch (false = do nothing). When enabled, the autopilot reads each inbound email reply, drafts a suggested reply in the rep's voice, and NOTIFIES the user to review/approve it — it NEVER auto-sends; a human approves every reply (via list_inbox_drafts + approve_inbox_draft). mode: 'off' suppresses entirely; 'auto_safe' and 'draft_all' both draft-and-notify (auto-send is disabled in v1). daily_cap is reserved for a future auto-send mode. Returns the saved {data: inboxAutopilot}.
simulate_account_connected Fire a Unipile account-connected webhook event. Use to test the handler that creates/updates an account record when Unipile finishes connecting.
simulate_account_status Fire a Unipile account-status webhook. Use to test status changes (OK, CREDENTIALS, ERROR, CONNECTING, STOPPED). Useful for verifying that disconnected accounts are flagged correctly.
simulate_email_tracking Fire a Unipile mail_opened or mail_link_clicked tracking event. Pass label as 'execution_state_id:node_id' to link the event to a campaign execution and increment open/click counts.
simulate_linkedin_messaging Fire a Unipile LinkedIn messaging event (message_received, message_read, message_delivered, etc.). Use to test reply detection and campaign execution advancement on LinkedIn.
simulate_new_email Fire a Unipile mail_received webhook. Use to test inbound email handling — reply detection, chat thread creation, and campaign execution advancement.
simulate_new_relation Fire a Unipile new_relation event (LinkedIn invitation accepted). Use to test that the connection record is updated and any waiting campaign executions are advanced.
stop_campaign Stop an active or paused campaign permanently — cannot be resumed.
tasks Read tasks and propose new ones. Agents propose, humans dispose: you can create suggestions and drive accepted work forward, but only a human can approve or reject a suggestion. The workspace comes from your bearer token.
update_account Update account sender name, timezone, and working hours.
update_account_rate_limit Update daily or weekly sending cap for a specific rate-limit row.
update_campaign Partial update of a campaign — name, description, workflow, lists, accounts, scheduling.
update_campaign_memory Record/update Max's durable memory for a campaign. Pass a partial 'memory' object (top-level keys merge; send the full array to change decisions/notes). Use it to remember ICP, decisions, and progress per campaign.
update_chat Update chat metadata — title, read state, archived status, prospect link.
update_organization Update an organization's fields (partial update).
update_prospect Update a prospect's fields (partial update).
update_prospect_list Update a prospect list (only list_name and status are editable).
update_workspace_profile Create or update workspace profile settings for the authenticated workspace via DigitalCrew API (PUT upsert). Uses the MCP connection Authorization: Bearer token when set.
wait_for_prospect_list Poll a prospect list (typically Apollo-backed) until its status becomes completed, failed, or cancelled — or the timeout elapses. Use this after apollo_create_list / apollo_add_more so the agent doesn't need to manage polling itself.

Tool definitions and validation use Zod per domain under features/pilot-tools/<domain>/schema.ts.

Requirements

  • Node.js (see Next.js 15 requirements)
  • pnpm (see packageManager in package.json)

Configuration

Set these environment variables (e.g. in .env.local for local dev, or in your host’s env for production):

Variable Required Description
DIGITALCREW_API_BASE_URL Yes Base URL of the Digital Crew API (no trailing slash)
MCP_GATEWAY_SECRET Yes (prod) Shared secret; callers send X-MCP-Gateway-Key on /mcp and /chat
MCP_ADMIN_GATEWAY_KEY No Separate key required for admin tools when ENABLE_ADMIN_TOOLS=true
ALLOW_ENV_TOKEN_FALLBACK No Set true only for legacy scripts that cannot send per-request tokens
DIGITALCREW_API_TOKEN or DIGITALCREW_BEARER_TOKEN No* Used only when ALLOW_ENV_TOKEN_FALLBACK=true
ENABLE_ADMIN_TOOLS No Set true to expose dead-letter / circuit-breaker admin tools
ENABLE_WEBHOOK_SIMULATORS No Set true to expose webhook simulation tools (dev/staging only)
CHAT_RATE_LIMIT_PER_MINUTE No Per-gateway-key chat limit (default 20)
CHAT_DAILY_REQUEST_CAP No Daily OpenRouter call cap per key (default 500)
REDIS_URL No Shared dead-letter queue and circuit-breaker state across instances

*Preferred auth: Authorization: Bearer <token> on the MCP HTTP request, or bearer_token on a tool call. Precedence: tool bearer_token → MCP Authorization → env (only if fallback enabled).

Getting started

pnpm install
# Create .env.local with DIGITALCREW_API_BASE_URL (and optional token vars)

pnpm dev

The MCP endpoint is:

  • Local: http://localhost:3000/mcp
  • Production: https://<your-deployment>/mcp

Point your MCP client at that URL using Streamable HTTP transport.

Optional: smoke-test with the bundled script

scripts/test-streamable-http-client.mjs lists tools via Streamable HTTP. It expects the @modelcontextprotocol/sdk client package available in your environment (install it if the script is not runnable yet):

node scripts/test-streamable-http-client.mjs http://localhost:3000

Note: SSE is currently disabled in app/mcp/route.ts (disableSse: true). The older scripts/test-client.mjs (SSE) is not aligned with the default setup unless you enable SSE and Redis per Vercel’s MCP pattern.

Stack

Deploying on Vercel

  • Enable Fluid compute for efficient execution.
  • Adjust maxDuration in app/mcp/route.ts if your plan allows (e.g. up to 800s on Pro/Enterprise).
  • SSE: If you switch disableSse to false, attach Redis and set REDIS_URL as required by the adapter. See also the Next.js MCP template.

Learn more

License

Private project ("private": true in package.json). Use and deployment are governed by your Digital Crew agreements.

About

MCP server for Max, the AI sales agent from Digital Crew. Max is built for lead qualification, cold outreach, and pipeline growth—see the product story on max.digitalcrew.tech.

Topics

Resources

License

Stars

2 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors