Soloft MCP reference
A Model Context Protocol server that lets AI assistants such as Meta Muse read and, with the user's explicit OAuth consent, update a Soloft workspace: clients, projects, invoices, expenses, bookings, content and portfolio.
Overview
| Item | Value |
|---|---|
| MCP endpoint | POST https://mcp.soloft.io/mcp |
| Transport | Streamable HTTP, one JSON response per POST (no SSE stream). GET and DELETE return 405. |
| Protocol versions | 2025-06-18, 2025-03-26, 2024-11-05 (the requested version is echoed if supported) |
| Capabilities | tools only. No resources, prompts or sampling. |
| Authorization server | https://auth.soloft.io |
| Access | Read and write, limited to the scopes the user approves. Write tools create or update workspace records. No tool sends email or messages to a third party, creates a payment link, takes a payment or deletes data, and none changes billing, integrations, team access or account settings. |
| Plan | Any Soloft account can connect. Tool calls require an active trial or subscription (see Errors). |
| Units | Amounts are integer cents plus a currency code. Dates are ISO-8601 in the workspace timezone. Ids are UUID strings. |
Authentication
OAuth 2.1 authorization code flow with PKCE (S256 only) and RFC 8707 resource indicators. API keys are not supported. Every request to /mcp needs Authorization: Bearer <access_token>.
Discovery
| Document | URL |
|---|---|
| Protected resource metadata (RFC 9728) | https://mcp.soloft.io/.well-known/oauth-protected-resource |
| Authorization server metadata (RFC 8414) | https://auth.soloft.io/.well-known/oauth-authorization-server |
An unauthenticated or invalid request returns 401 with WWW-Authenticate: Bearer resource_metadata="https://mcp.soloft.io/.well-known/oauth-protected-resource".
Endpoints
| Purpose | URL |
|---|---|
| Authorization | GET https://auth.soloft.io/authorize |
| Token | POST https://auth.soloft.io/token (form-encoded) |
| Revocation | POST https://auth.soloft.io/revoke |
| Client registration | POST https://auth.soloft.io/register (RFC 7591) |
Flow
- The client sends the user to
/authorizewithresponse_type=code,client_id, a registeredredirect_uri,scope,state,code_challenge(S256),code_challenge_method=S256andresource=https://mcp.soloft.io. - The user signs in to Soloft and approves a consent screen that lists each requested permission in plain language.
- Soloft redirects to
redirect_uriwithcodeandstate(orerror=access_denied). - The client POSTs to
/tokenwithgrant_type=authorization_code,code,redirect_uri,client_id,code_verifierand the sameresource.
{
"access_token": "…",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "…",
"scope": "crm:read projects:read invoices:read"
}
Token lifetimes and rules
- Authorization codes last 5 minutes and are single use. Access tokens last 1 hour; refresh tokens last 30 days and rotate on every use. Reuse of a rotated refresh token revokes the whole token family for that grant.
- Strict audience: a token must be bound to
resource=https://mcp.soloft.io. Tokens without it are rejected by/mcpwith 401. - Public clients authenticate with PKCE only (
token_endpoint_auth_method: none). Confidential clients may useclient_secret_basicorclient_secret_post. - Redirect URIs are matched exactly against the client's registered list. Tokens and codes are stored only as hashes.
- Users can disconnect an app at any time in Soloft under Settings → Connected apps. Deleting the account also ends all tokens.
Scopes
tools/list returns only the tools whose scope the token holds, further narrowed to the modules enabled for the workspace's business type. Read scopes never allow writes.
| Scope | Grants |
|---|---|
profile:read | Business profile: name, timezone, currency, plan state and which integrations are connected (as booleans only) |
crm:read / crm:write | Contacts, activity log and quick notes / create and update contacts, log activity, add notes |
projects:read / projects:write | Projects, tasks and milestones / create and update them |
invoices:read / invoices:write | Invoices, templates and revenue summary / create and edit draft invoices, create templates, mark an invoice paid |
expenses:read / expenses:write | Expenses and summaries / log and update expenses, create categories |
calendar:read / calendar:write | Booking links and bookings (names only, no booker emails or join links) / edit booking links and weekly availability |
content:read / content:write | Content calendar, social handles, sponsorships / create and update content items, social handles and sponsorships |
portfolio:read / portfolio:write | Brands, products and KPIs / update products, create brands and products, checklists and decisions |
commerce:read / commerce:write | Orders, catalog items and events (maker, reseller and event-vendor workspaces) / create and edit catalog items, events and suppliers, move an order through non-restocking statuses |
inventory:read / inventory:write | Stock, raw materials, recipe costs, production runs, suppliers and purchase orders (stock for maker, reseller and event-vendor workspaces; materials, recipes and suppliers for maker and reseller) / create suppliers and draft purchase orders. Nothing here changes stock quantities. |
feedback:read | Customer feedback posts per product (creator workspaces) |
portal:read | Client portal threads for a project |
automations:read / automations:write | Email sequences and enrollments / create inactive draft sequences and add or edit their steps |
Tools that change stock quantities (creating or cancelling orders, inventory adjustments, production runs, purchase-order receipt) are not available over MCP; do those in Soloft.
Protocol
JSON-RPC 2.0 over POST /mcp. Supported methods: initialize, ping, tools/list, tools/call. Notifications without an id return 202. Request bodies are limited to 256 KB and must be application/json. Requests carrying a browser Origin header that is not on the allowlist are rejected with 403.
POST /mcp
Authorization: Bearer <access_token>
Content-Type: application/json
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18"}}
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{
"name":"soloft_list_invoices","arguments":{"status":"overdue","limit":5}}}
Tool results are returned as a text content item plus structuredContent holding the same JSON, with isError: false. Every tool carries title and annotations (readOnlyHint, destructiveHint, idempotentHint), and arguments are validated against the tool's JSON Schema.
Tools
All names are prefixed soloft_. List tools return at most 100 rows (default 20). Arguments not in a tool's schema are rejected. tools/list is the authoritative list for a connection; the tables below group tools by scope.
Read tools Read
| Tool | Scope | Returns |
|---|---|---|
soloft_get_profile | profile:read | Business profile, plan state, connected-integration flags |
soloft_get_today | any of crm/projects/invoices/calendar/portal :read | Tasks due, today's bookings, overdue invoices, stale leads (no activity in 14 days), portal threads awaiting a reply. Sections the connection lacks are named, not guessed. |
soloft_list_contacts, soloft_get_contact | crm:read | Contacts with stage, tags, last activity; one contact with activities, tasks, projects and invoices |
soloft_list_activities, soloft_list_notes | crm:read | Activity log and quick notes |
soloft_list_projects, soloft_get_project, soloft_list_tasks | projects:read | Projects with status, budget and due date; project detail with milestones and tasks; filtered tasks |
soloft_list_invoices, soloft_get_invoice, soloft_list_invoice_templates | invoices:read | Invoices by status, client and date; one invoice with line items; saved templates |
soloft_get_revenue_summary | invoices:read | Month-to-date, year-to-date, outstanding and overdue totals per currency |
soloft_list_expenses, soloft_get_expense_summary | expenses:read | Expenses; totals by category and profit and loss |
soloft_list_booking_links, soloft_list_bookings | calendar:read | Meeting types and availability; upcoming and past bookings |
soloft_get_content_calendar, soloft_list_social_handles, soloft_list_sponsorships | content:read | Content items by week, status and platform; handles; sponsorships |
soloft_get_portfolio_summary, soloft_get_product | portfolio:read | Brands and products with stage and KPIs |
soloft_list_orders, soloft_get_order, soloft_get_order_revenue, soloft_list_catalog_items, soloft_list_events | commerce:read | Orders and order revenue, catalog items, events |
soloft_get_inventory, soloft_list_raw_materials, soloft_get_recipe_cost, soloft_list_production_runs, soloft_list_suppliers, soloft_list_purchase_orders | inventory:read | Stock levels, materials, recipe cost, production runs, suppliers, purchase orders |
soloft_list_feedback, soloft_get_feedback_post | feedback:read | Feedback board posts and comments per product |
soloft_get_project_portal | portal:read | Portal messages and approvals for a project |
soloft_list_sequences, soloft_list_enrollments | automations:read | Email sequences, steps and who is enrolled |
Write tools Write
None of these is destructive. Insert tools accept an optional client_request_id; repeating a call with the same id returns the original record instead of creating a duplicate.
| Tool | Scope | Effect |
|---|---|---|
soloft_create_contact, soloft_update_contact | crm:write | Create or edit a contact. Nothing is sent to the contact. |
soloft_log_activity, soloft_add_note | crm:write | Record a call, meeting or note in the workspace |
soloft_create_project, soloft_update_project | projects:write | Create or edit a project |
soloft_create_task, soloft_update_task, soloft_create_milestone, soloft_update_milestone | projects:write | Create or edit tasks and milestones, including marking them done |
soloft_create_draft_invoice, soloft_update_draft_invoice | invoices:write | Create or edit a draft invoice. It is never sent and no payment link is created; the user reviews and sends it in Soloft. |
soloft_mark_invoice_paid | invoices:write | Marks a sent, viewed or overdue invoice as paid, as the Mark paid button does. Nothing is emailed. Drafts and cancelled invoices are refused; an already-paid invoice is returned unchanged. The tool description instructs the assistant to confirm with the user first. |
soloft_create_invoice_template | invoices:write | Save a reusable set of line items |
soloft_log_expense, soloft_update_expense, soloft_create_expense_category | expenses:write | Record or correct an expense; add a category (an existing name is returned, not duplicated) |
soloft_create_content_item, soloft_update_content_item | content:write | Create or edit a content-calendar item. Nothing is published. |
soloft_update_product, soloft_create_product, soloft_create_brand | portfolio:write | Update a product's stage and KPIs; create a product or brand |
soloft_update_booking_link, soloft_set_availability | calendar:write | Edit a meeting type; replace a booking link's weekly availability windows. No booking is created or cancelled and nobody is notified. |
soloft_create_sequence_draft, soloft_add_sequence_step, soloft_update_sequence_step | automations:write | Create an inactive email sequence and edit its steps. Nothing is sent or enrolled; sequences that are active or have live enrollments are refused, and activation happens in Soloft. |
soloft_add_social_handle, soloft_update_social_handle, soloft_create_sponsorship, soloft_update_sponsorship | content:write | Record or edit a social handle or a sponsorship deal. Nothing is posted or invoiced. |
soloft_toggle_checklist_item, soloft_seed_checklist, soloft_add_decision | portfolio:write | Tick a product checklist item, add checklist items, log a product decision |
soloft_create_catalog_item, soloft_update_catalog_item | commerce:write | Create or edit a catalog item (new items get an empty stock record; quantities are not changed) |
soloft_create_event, soloft_update_event | commerce:write | Create or edit a sales event, including ending or reopening it |
soloft_update_order_status | commerce:write | Move an order between pending, processing, shipped and delivered. Cancelled or refunded orders are refused because reopening them needs stock changes. No customer email is sent. |
soloft_create_supplier, soloft_update_supplier, soloft_create_purchase_order | commerce:write | Create or edit a supplier; create a draft purchase order (receiving it and adding stock is done in Soloft) |
Write behavior
- Writes apply immediately to the user's own workspace and are the same changes the user could make in the app.
- No tool deletes data. No tool sends email, SMS or portal messages, publishes content, creates payment links or moves money.
- Every write is scoped to the workspace bound to the token; ids belonging to another workspace are treated as not found.
- Updates that cannot apply (for example marking a draft invoice paid) return a stable error code and change nothing.
Errors
| Condition | Response |
|---|---|
| Missing, invalid, expired or wrong-audience token | HTTP 401 + WWW-Authenticate header |
| Rate limit exceeded | HTTP 429 with Retry-After |
Malformed JSON, non-object body, bad jsonrpc or method | HTTP 400, JSON-RPC error -32700 / -32600 |
| Unknown method | JSON-RPC error -32601 |
| Unknown tool name | JSON-RPC error -32602 |
| Token lacks the tool's scope | Tool error insufficient_scope |
| Trial ended and no active subscription | Tool error subscription_inactive with the upgrade URL. Tokens stay valid; no reconnect is needed after subscribing. |
| Daily write limit reached | Tool error write_limit_reached |
| Invalid tool arguments | Tool error invalid_params describing the constraint |
| Record not found or not payable, concurrent change | Tool error such as not_found, not_payable, conflict |
| Unexpected server error | Tool error internal_error with a generic message |
A tool error is a normal JSON-RPC result with isError: true:
{"jsonrpc":"2.0","id":3,"result":{
"content":[{"type":"text","text":"{\"error\":\"insufficient_scope\",\"message\":\"This connection lacks the invoices:write permission.\"}"}],
"structuredContent":{"error":"insufficient_scope","message":"This connection lacks the invoices:write permission."},
"isError":true}}
Limits
- 60
tools/callrequests per minute per user; 120 requests per minute per client IP on/mcp. - 200 successful write calls per user per rolling day.
- List tools return at most 100 rows. Request bodies are limited to 256 KB.
Testing
Reviewer access with a sample workspace is provided in the submission notes. To verify the server without OAuth, an unauthenticated call should return 401 with the discovery header:
curl -i -X POST https://mcp.soloft.io/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
A health check is available at GET https://mcp.soloft.io/mcp/health.
Data & privacy
- Tools read and write only the authenticated user's own workspace; every query is filtered by the owner bound to the token, never by tool arguments.
- Single-record tools may return a client's contact details; list tools return names, ids and contact email; booking lists never return booker emails or join links. Payment-provider tokens, calendar and integration credentials, and other secrets are never exposed.
- Soloft keeps an audit log of each tool call (which app, which tool, when and the outcome) without arguments or results. Nothing from the connector is shared with third parties. See the privacy policy and terms.
- Support and security contact: founder@tenderlab.studio.