@offlinemediainc/partners-mcp
v0.5.1
Published
MCP server for the Partners submission API
Readme
Partners MCP Server
Thin MCP server that exposes the existing /api/llm/** HTTP endpoints as Claude/LLM tools. Everything lives under packages/partners-mcp/ so it can be removed or published without touching the Next.js app.
Prerequisites
- Node 18+ (Claude Desktop uses stdio transport)
- Env vars:
API_URL– base URL for the Partners app (fallbacks toNEXT_PUBLIC_SITE_URL, thenhttp://localhost:3000).LLM_MCP_API_KEY(optional) – API key generated via/admin/settings/api-keys, sent asx-api-key.LLM_MCP_API_TOKEN(optional) – legacy bearer token if you don't want per-client keys.
Run locally
pnpm --filter @offlinemediainc/partners-mcp devClaude Desktop claude_desktop_config.json example:
{
"mcpServers": {
"partners-submissions": {
"command": "npx",
"args": ["-y", "@offlinemediainc/partners-mcp"],
"env": {
"API_URL": "https://partners.yourdomain.com",
"LLM_MCP_API_KEY": "partners_abc123"
}
}
}
}Tools registered:
| Tool | Description |
| --- | --- |
| list_submissions | Search existing submissions (prevent duplicates). |
| get_submission | Fetch a submission snapshot. |
| duplicate_submission | Clone an existing submission (clears event link, resets stage to draft). |
| create_submission | Create a new submission via the LLM API. |
| link_submission_contact | Link or create a HubSpot contact + partner user. |
| link_submission_event | Associate a backend event ID. |
| move_submission_stage | Force move a submission to another workflow stage. |
| list_submission_stages | Return workflow stage metadata. |
Workflow stages
| Stage ID | Label | Status |
| --- | --- | --- |
| submission_received | Submission Received | Active |
| internal_assessment | Internal Assessment | Active |
| concept_development_admin | Concept Prep | Active |
| concept_development | Concept Development | Active |
| draft_event_admin | Draft Prep | Active |
| draft_event | Draft Event | Active |
| waiting_launch | Launch Prep | Active |
| event_live | Event Live | Active |
| event_recap_admin | Recap Prep | Active |
| event_recap | Event Recap (Terminal) | Completed |
Stages mirror /api/llm/metadata/stages. activeOnly: true means “exclude submissions currently at event_recap.” Use the stage filter or the metadata endpoint when you need to reason about specific transitions.
Tool reference
Each MCP tool proxies a locked-down HTTP route under /api/llm/**. All responses wrap data in { success, data } envelopes when called directly; the MCP server unwraps that for the client.
list_submissions
Search existing submissions before creating a new one; call this whenever you need to avoid duplicates.
Inputs
search– free text for restaurant/ contact name / contact email.contactEmail– exact case-insensitive match (takes precedence oversearch).limit– integer between 1 and 50 (defaults to 25).activeOnly– whentrue, filters out submissions whose workflow stage isevent_recap.stage– workflow stage ID (submission_received…event_recap). OverridesactiveOnlyif both are supplied.
Returns
{
"submissions": [
{
"id": "uuid",
"restaurantName": "Lady Justice",
"contactName": "Avery",
"contactEmail": "[email protected]",
"createdAt": "2025-03-18T17:30:00Z",
"eventId": 1234,
"workflowStage": "draft_event_admin"
}
]
}Possible errors
LLM_SUBMISSION_INVALID_STAGE– badstagefilter; check/api/llm/metadata/stages.LLM_SUBMISSION_LIST_FAILED– backing Supabase query failed (see hint for details).
Example
{
"tool": "list_submissions",
"input": {
"search": "Lady Justice",
"activeOnly": true
}
}get_submission
Fetch a single submission snapshot. The payload mirrors the internal /api/submissions/{id}/workflow-data route, so you get:
submission– core submission row +formData(now includesguest_flow), concept/example info, HubSpot flags, etc.draftEventData,assessmentData,conceptData,questionsAndAnswers– workflow context.hubspotDeal,offerData,eventData– linked external systems.submissionContacts– full contact objects (with profile + auth info).linkedContacts– simplified view kept for backwards compatibility.
Inputs
submissionId(string UUID)
Returns
{
"submission": {
"id": "uuid",
"restaurant_name": "Lady Justice",
"contact_name": "Avery",
"contact_email": "[email protected]",
"created_at": "2025-03-18T17:30:00Z",
"event_id": 1234,
"workflowState": "draft_event_admin",
"status": "draft_event_admin",
"hasHubspotDeal": true,
"hubspotDealId": "123456789",
"hubspotPipelineName": "Events",
"hubspotStageName": "Planning",
"hasCaseStudy": false,
"guestFlow": "6:30pm - First 40 guests\n7:00pm - Next 40 guests",
"formData": {
"...": "...",
"guest_flow": "6:30pm - First 40 guests\n7:00pm - Next 40 guests"
}
},
"assessmentData": { "...": "..." },
"conceptData": { "...": "..." },
"draftEventData": {
"guest_flow": "6:30pm - First 40 guests\n7:00pm - Next 40 guests",
"...": "..."
},
"questionsAndAnswers": [
{ "question": "How many guests?", "answer": "120" }
],
"hubspotDeal": {
"dealId": "123456789",
"pipelineName": "Events",
"stageName": "Planning"
},
"offerData": { "...": "..." },
"eventData": { "...": "..." },
"submissionContacts": [
{
"id": "contact-link-id",
"user_id": "user-uuid",
"contact_role": ["owner"],
"user_profiles": {
"full_name": "Avery Justice",
"hubspot_contact_id": "987654321"
},
"auth_users": {
"email": "[email protected]",
"phone": "+1-555-0100"
}
}
],
"linkedContacts": [
{
"submissionContactId": "contact-link-id",
"userId": "user-uuid",
"name": "Avery Justice",
"email": "[email protected]",
"contactRole": ["owner"],
"linkedAt": "2025-03-18T18:00:00Z"
}
],
"workflowId": "workflow-uuid",
"stateData": { "chatgpt_link": "https://chat.openai.com/..." },
"chatgptLink": "https://chat.openai.com/..."
}Possible errors
LLM_SUBMISSION_ID_REQUIRED– missing/blanksubmissionId.LLM_SUBMISSION_NOT_FOUND– invalid ID or removed submission.
duplicate_submission
Clone an existing submission for quick retries. Copies form data, contacts, concept/example links, and workflow tree; clears event links, resets workflow to draft_event_admin, and returns a fresh submission snapshot.
Inputs: submissionId
Returns
{
"submission": { /* same schema as get_submission */ }
}Possible errors: LLM_SUBMISSION_ID_REQUIRED, LLM_SUBMISSION_DUPLICATE_FAILED (see message for which copy step failed).
create_submission
Create a new submission record (and kick off its workflow). Always call list_submissions first to avoid duplicates.
Inputs
restaurantName(required)restaurantAddress(optional, defaults to a placeholder)contact→{ name, email, phone? }(name + email required)source– identifier for the workflow/agent ('n8n','internal-llm', etc.)createdBy– human-friendly run ID or operator badge for auditingnotes,metadata,formFields(free-form records stored with submission)sessionReplayLink– optional URLeventId– numeric backend event ID to link immediately after creation
Returns
{
"submissionId": "uuid",
"workflowStage": "submission_received",
"submission": { /* snapshot as in get_submission */ }
}Possible errors
LLM_SUBMISSION_INVALID_PAYLOAD– missing required fields;hintexplains which one.LLM_SUBMISSION_CREATE_FAILED– downstream creation failure (see message).
link_submission_contact
Link an existing HubSpot contact (and optional partner user) to a submission. If the link already exists, the response reflects its status without error.
Inputs
submissionIdhubspotContactId– HubSpot primary key (required)contactRole– optional array of role strings (e.g.,['owner', 'operator'])profileOverride– optional{ firstName, lastName, email, phone, company }to sync profile details if HubSpot lacks them.
Returns
{
"submissionId": "uuid",
"contact": { /* linked contact summary */ },
"linked": true,
"imported": false,
"message": "Linked contact owner to submission"
}Possible errors: LLM_SUBMISSION_ID_REQUIRED, LLM_CONTACT_ID_REQUIRED, and the HubSpot-specific errors surfaced by linkHubspotContactToSubmission (LLM_CONTACT_NOT_FOUND, LLM_CONTACT_SYNC_FAILED, etc.).
link_submission_event
Associate a backend (Rails) event record with a submission. Validates that the event exists, isn’t already linked elsewhere, and matches ownership rules from the backend.
Inputs: submissionId, numeric eventId
Returns
{
"submissionId": "uuid",
"eventId": 9876,
"previousEventId": 1234
}previousEventId helps audit changes if a submission already pointed at a different event.
Possible errors: LLM_SUBMISSION_ID_REQUIRED, LLM_EVENT_ID_REQUIRED, LLM_EVENT_INVALID_ID, LLM_EVENT_NOT_FOUND, LLM_EVENT_VERIFICATION_FAILED, LLM_EVENT_LINK_FAILED.
move_submission_stage
Force-move a submission to any valid workflow stage (admins only). Use sparingly and provide a concise audit reason so humans can follow along later.
Inputs
submissionIdtargetStage– one of the IDs listed abovereason– short explanation stored in the workflow’s historymetadata– optional object (e.g.,{ actorRunId, confidence })
Returns
{
"submissionId": "uuid",
"from": "draft_event_admin",
"to": "waiting_launch"
}Possible errors: LLM_SUBMISSION_ID_REQUIRED, LLM_STAGE_REQUIRED, LLM_STAGE_REASON_REQUIRED, LLM_SUBMISSION_INVALID_STAGE, LLM_WORKFLOW_NOT_FOUND, LLM_STAGE_UPDATE_FAILED.
list_submission_stages
Discover legal workflow stages plus their allowed transitions. Feed this into your prompts so the agent knows what moves are legal.
Returns
{
"stages": [
{
"id": "draft_event_admin",
"label": "Draft Prep",
"order": 4,
"allowedTransitions": ["draft_event"]
},
...
]
}No inputs required. Errors are rare—if Supabase is unavailable the MCP server will return a generic LLM_MCP_ERROR with the upstream hint.
All commands proxy to the locked-down HTTP API, so removing the mcp-server/ folder detaches the MCP integration cleanly.***
