Max MCP Server
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Max MCP Serverget my workspace profile settings"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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.
154 tools registered.
Tool | Purpose |
| Add prospects to a prospect list by their UUIDs. |
| 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. |
| 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. |
| 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. |
| Append more leads to an existing Apollo list (async). Re-runs the saved search for additional results. |
| 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 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 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 a campaign (soft delete) — hidden from default views but restorable. |
| Archive a chat (soft delete). Messages remain, use archived=true filter to see them. |
| 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). |
| Delete multiple organizations. Set deleteProspects=true to cascade-delete linked prospects. |
| Delete multiple prospects by IDs. |
| 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. |
| Import multiple organizations. Deduplicates by domain. Returns imported/existing/failed counts. |
| Import multiple prospects at once. Deduplicates by email. Returns imported/existing/failed counts. |
| 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 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| Drain the dead-letter queue. Returns the number of entries removed. Use after manually replaying or after resolving the upstream issue. |
| 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 a new campaign in draft state. Requires name, included_lists, and accounts. Won't send until launched. |
| 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 a new organization/company record. |
| Create a single prospect. Deduplicates by email — returns existing row if email already exists. |
| Create an empty platform prospect list. For Apollo-sourced lists, use apollo_create_list instead. |
| 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 }] }. |
| 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[] }] }. |
| 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 }] }. |
| Fetch a single CRM contact by email (the dedup identity). Returns null if not found. |
| Fetch a single HubSpot deal by id, including its full properties and associated company/contact ids. Returns null if not found. |
| 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. |
| 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. |
| List HubSpot owners (sales reps) for the workspace. Returns id, email, firstName, lastName, teams. Use to map deals/assignments to people. |
| List deal pipeline stages (optionally scoped to one pipeline). Returns id, label, displayOrder, pipelineId, isWonStage, isLostStage. |
| 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:[...] }. |
| 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[] }] }. |
| 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. |
| Report whether HubSpot is connected for this workspace. Attempts a cheap HubSpot read; returns { connected: bool, ... }. |
| Create-or-update a company in the connected CRM, matched by domain — never creates a duplicate. Requires a read + write HubSpot connection. |
| 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. |
| 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. |
| Permanently delete a campaign and all its workflow executions. Prefer archive for soft removal. |
| Delete an organization. Linked prospects get organization_id = null. |
| Delete a prospect permanently. |
| Delete a prospect list (prospects themselves are NOT deleted). |
| 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 a LinkedIn or email account from the workspace. Use hosted_auth_link to reconnect. |
| 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. |
| 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. |
| 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. |
| 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. |
| 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. |
| Use AI to generate a personalized message for a prospect. Charges credits. Specify channel and prompt. |
| Use AI to generate a campaign workflow from natural language. Charges credits. Provide a prompt or structured fields. |
| Get full details of a connected account — provider, channel, config, sync status. |
| Get daily/weekly sending limits and current usage for a specific account. |
| 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 full details of a campaign by ID — workflow, scheduling, accounts, prospect lists, stats. |
| Engagement summary for a campaign: open/click/reply/bounce rates plus per-link click detail (top links, distinct URLs clicked). |
| Per-prospect breakdown — where each lead is in the workflow and message event history. |
| 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. |
| Map of workflow node ID → execution count. Useful for funnel visualization. |
| Aggregate performance stats — email open/reply rates, LinkedIn connection/reply rates, execution counts. |
| Get full details of a single Unibox chat/conversation thread. |
| 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). |
| Workspace-wide aggregate stats — execution counts, email/LinkedIn rates, completion percentage. |
| 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. |
| Return the workspace's daily enrichment quota usage: {cap, used_today, remaining}. Check this before a large bulk enrichment to confirm there's headroom. |
| 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}. |
| 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. |
| Link clicks for a campaign grouped by url, with total click counts and unique-prospect counts. Sorted by clicks desc. |
| Get full details of an organization — domain, industry, employee count, funding, social URLs. |
| Get full profile of a prospect — name, title, company, LinkedIn, email, location, enrichment data. |
| Chronological log of message events for a prospect across all campaigns (newest first). |
| Chronological email engagement timeline for a prospect (oldest first) — every open, click, reply, and bounce. |
| Get full details of a prospect list by ID — status, search config, result counts, timestamps. |
| 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[]}. |
| 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. |
| 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). |
| Fetch workspace profile settings (company info) for the authenticated workspace via DigitalCrew API. Uses the MCP connection Authorization: Bearer token when set. |
| Generate a short-lived URL for the user to connect a LinkedIn or email account via Unipile hosted auth. |
| Create a new prospect list and import prospects in one call. Each row needs an email (for dedup). |
| Launch a draft campaign — transitions draft → active and creates workflow executions for each prospect. |
| Cancel/withdraw a sent LinkedIn invitation by its invitation_id (from list_invitations_sent). |
| Comment on a LinkedIn post. |
| Create a LinkedIn post from the connected account. |
| 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. |
| Get all recent LinkedIn messages across all conversations. |
| Get a LinkedIn company profile by its identifier or slug. |
| Get messages in a specific LinkedIn conversation. |
| Get the profile of the connected LinkedIn account. Useful to confirm the account is active. |
| Get a full LinkedIn profile by slug (public identifier). Returns provider_id and profile data. |
| Get recent posts by a LinkedIn user. |
| List first-degree LinkedIn connections of the connected account. |
| List LinkedIn conversations (inbox). |
| List pending LinkedIn invitations received from others. |
| List pending LinkedIn invitations you have sent. |
| React to a LinkedIn post. |
| Reply in an existing LinkedIn conversation. |
| Search LinkedIn for people by keywords. |
| Send a LinkedIn connection request. ALWAYS call find_profile first to get provider_id. |
| Send a LinkedIn direct message to start a new conversation. Requires existing connection or InMail credits. |
| List all connected LinkedIn and email accounts — name, email, status, daily limits. |
| List all outreach campaigns. Filter by status, search by name, paginate and sort. |
| Get all messages in a conversation — body, direction (in/out), timestamp, status. |
| List LinkedIn and email conversations — filter by channel, prospect, account, or archived status. |
| 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 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 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 all organizations/companies — search by name or domain, filter by industry/country. |
| List all prospects in a specific list — paginated, searchable, sortable. |
| List all prospect lists — name, status, result counts, and search criteria. |
| List prospects with rich filtering — search, status, org, titles, countries, industries, pagination, sorting. |
| 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. |
| 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. |
| 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 }. |
| 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 }. |
| Fetch a Notion page object plus all of its child blocks (paginated). Returns { page, blocks }. |
| 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? }. |
| Search the connected Notion workspace for pages matching a free-text query. Returns [{ id, title, url }]. |
| Pause an active campaign — stops dequeueing new actions (in-flight calls finish). |
| 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. |
| 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 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 a prospect list by their UUIDs. |
| Restore an archived campaign back to draft status. |
| Resume a paused campaign — transitions paused → active. |
| Preview filter results without creating a list — search by titles, countries, industries, employee count, etc. |
| 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 a manual reply in an existing Unibox chat. Channel (email/LinkedIn) is inferred from the chat. |
| 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 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}. |
| Fire a Unipile account-connected webhook event. Use to test the handler that creates/updates an account record when Unipile finishes connecting. |
| 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. |
| 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. |
| Fire a Unipile LinkedIn messaging event (message_received, message_read, message_delivered, etc.). Use to test reply detection and campaign execution advancement on LinkedIn. |
| Fire a Unipile mail_received webhook. Use to test inbound email handling — reply detection, chat thread creation, and campaign execution advancement. |
| 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 an active or paused campaign permanently — cannot be resumed. |
| Update account sender name, timezone, and working hours. |
| Update daily or weekly sending cap for a specific rate-limit row. |
| Partial update of a campaign — name, description, workflow, lists, accounts, scheduling. |
| 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 metadata — title, read state, archived status, prospect link. |
| Update an organization's fields (partial update). |
| Update a prospect's fields (partial update). |
| Update a prospect list (only list_name and status are editable). |
| Create or update workspace profile settings for the authenticated workspace via DigitalCrew API (PUT upsert). Uses the MCP connection Authorization: Bearer token when set. |
| 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.
Related MCP server: Salesforce MCP Server
Requirements
Node.js (see Next.js 15 requirements)
pnpm (see
packageManagerinpackage.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 |
| Yes | Base URL of the Digital Crew API (no trailing slash) |
| Yes (prod) | Shared secret; callers send |
| No | Separate key required for admin tools when |
| No | Set |
| No* | Used only when |
| No | Set |
| No | Set |
| No | Per-gateway-key chat limit (default 20) |
| No | Daily OpenRouter call cap per key (default 500) |
| 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 devThe MCP endpoint is:
Local:
http://localhost:3000/mcpProduction:
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:3000Note: SSE is currently disabled in
app/mcp/route.ts(disableSse: true). The olderscripts/test-client.mjs(SSE) is not aligned with the default setup unless you enable SSE and Redis per Vercel’s MCP pattern.
Stack
Next.js App Router
mcp-handler(Vercel MCP adapter)Zod for tool input schemas
Deploying on Vercel
Enable Fluid compute for efficient execution.
Adjust
maxDurationinapp/mcp/route.tsif your plan allows (e.g. up to 800s on Pro/Enterprise).SSE: If you switch
disableSsetofalse, attach Redis and setREDIS_URLas required by the adapter. See also the Next.js MCP template.
Learn more
Digital Crew — AI workforce platform
Max — Outreach control center — Max product site
Model Context Protocol — MCP specification
License
Private project ("private": true in package.json). Use and deployment are governed by your Digital Crew agreements.
This server cannot be installed
Maintenance
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
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/Digital-Crew-Technologies/max-mcp-server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server