MCP — connect an AI assistant
Status: Public preview. Tools, resources, and prompts are stable; new entries land additively and are announced in this documentation.The Scripe MCP (Model Context Protocol) server lets MCP-compatible hosts — Claude, ChatGPT, Cursor, agent frameworks — read and write Scripe workspace data on behalf of an authenticated user. It exposes the same capabilities as the REST API plus the irreversible verbs REST deliberately lacks (publish to LinkedIn now, permanent deletion, settings writes), each gated behind a two-phase confirmation that hands your host an exact preview of what will happen before anything executes. This page gets a host connected and explains the concepts every integration needs (auth, workspaces, projects, limits). The full tool catalogue and the contracts behind it live on MCP tools & contracts.
TL;DR
https://mcp.scripe.io/mcp URL and approve the consent screen. That’s
the whole setup.
1. Endpoints and discovery
https://mcp.scripe.io/) also answers Streamable
HTTP for clients that probe the resource URL directly.
Discovery is RFC 9728 protected-resource metadata:
https://mcp.scripe.io), the authorization
server, and the supported scope vocabulary (the same
closed scope list as the rest of the API). Hosts
that follow the spec discover the authorization server automatically;
for older hosts, paste the URLs manually.
2. Authentication
OAuth 2.1 only. The MCP transport accepts OAuth access tokens (scripe_oat_*) exclusively — scripe_sk_* API keys are a REST-only
credential and fail the auth challenge here.
You don’t need to build anything for the common case: every major MCP
host registers itself via
Dynamic Client Registration,
sends the user through the Scripe consent screen, and stores the
resulting tokens. A 401 with WWW-Authenticate: Bearer … resource_metadata=… is the signal that makes hosts (re)run the flow.
Two things happen on the consent screen that matter later:
- The user picks the workspace the grant is pinned to (its default workspace).
- The user can pick a default project for the grant (auto-selected when the workspace has exactly one). See §2.2 for which tools fall back to it.
scope_missing teaching error when
called — re-consent with the wider scope set to unlock it. The
sensitive scopes (posts:publish, settings:write, *:destroy) must
be requested by name; the consent screen then lists them under Full
access — the user’s one choice is Read-only vs Full access, there are
no per-scope toggles. See Scopes.
For headless automation that genuinely cannot complete an interactive
consent, use the REST API with an API key instead.
2.1 Session-resumable but token-bound
The server stores session state in Redis keyed off the sha256 of the access token. If the token rotates (refresh), the old session is automatically retired and a new one is created on the next request. Hosts do not need to do anything special: just keep presenting the freshest access token they have.2.2 The default project, and which tools fall back to it
With a default project pinned at consent, most tools let the model omitprojectId and the server fills it in — which is what lets a prompt
like “draft me a LinkedIn post about X” work without a discovery
round trip.
projectIdis optional on 32 tools: the seven idea tools (attach_media_to_ideaincluded) andgenerate_post_ideas, the content writes (create_note,generate_post,create_post_draft,create_source_text,create_source_file,add_to_knowledge_base,create_media_asset), and the project-scoped reads (list_posts,list_notes,list_sources,list_knowledge,get_knowledge_doc,get_project,get_settings,get_engagement_policy,get_usage,list_post_statuses,search_media,get_personal_dna,list_profile_lists, and the single-workspace analytics tools). Pass it explicitly to override the default.- Two tools take no
projectIdat all:get_cross_workspace_analytics_overviewandget_cross_workspace_analytics_reportaggregate every project the caller can read in each workspace, so a project id would be a workspace-scoped id at a workspace-crossing call — they reject it. - Eleven tools still require it: the calendar family
(
list_calendar,get_next_free_slot,get_posting_times,update_posting_schedule), the irreversible or admin-gated writes (publish_post,update_tone_of_voice,update_engagement_policy,update_personal_dna,delete_knowledge_doc), and the spend-capped generators (generate_image,generate_carousel). Omitting it there is a schema rejection before the call runs, never a write to the default project. - If the default is unset and the argument is omitted on a tool that
allows it, the tool returns
invalid_requestwith a hint to either passprojectIdor set a default at consent time. - The active default is surfaced on
get_workspace/scripe://workspace/measdefaultProject: { id, name } | null, and the server instructs the model to echo the project name when it acts on the default (“Using the Marketing project — passprojectIdto override.”). - Only the idea family,
get_personal_dnaandlist_profile_listsecho the resolution: every such response — includingdelete_idea’s preview — carriesproject: { id, name, fromDefault }. WhenfromDefaultistruethe model is told to name the project back to the user, because the idea board is more visible than a note and landing on the wrong one should be obvious and correctable.nameis the display name the dashboard shows; it isnullonly when none resolves. - The REST endpoints are unchanged:
projectIdstays required there, since an HTTP caller has no connection-pinned default.
2.3 When the connection stops working
Every auth failure answers with the cause and the repair, because they need different repairs and only one of them is “re-authorize”.
The last two are the ones worth handling explicitly: neither is fixed
by re-authorizing, and both used to answer
401 — which makes a host
throw away a working token and send the user back through consent for a
problem consent cannot solve.
The body is the OAuth shape (RFC 6749 §5.2) with Scripe’s code beside
it, and the WWW-Authenticate challenge always keeps its
resource_metadata pointer:
3. Workspaces and projects
Every tool call runs against a single active workspace.- Default. The active workspace starts as the one pinned at consent.
- The model can switch.
switch_workspace({ workspaceId })moves the active workspace to any workspace the consenting user can reach, mid-conversation —list_workspacesreturns the reachable set with ids, anisDefaultmarker and areachmarker. Switching to a workspace they cannot reach fails withworkspace_unavailable. The semantics of the switch are below. - Hosts can pin. A
Scripe-Workspace-Idheader on the MCP connection selects a workspace for the whole connection — useful in hosts like Cursor where an agency runs one MCP entry per client workspace off the same login. Aswitch_workspacecall takes precedence over the header. - Two grants, checked live. Reach is the user’s current Clerk
workspace membership (60-second cache) or the billing-based
agency-owner grant: the client workspaces an agency they own pays for,
which is what the Scripe dashboard has always honoured. Both are
re-checked on every request, so a new client workspace becomes
reachable immediately with no re-consent, and a revoked membership
stops access within ~60 seconds.
list_workspacesmarks each entryreach: "member" | "agency_owner", and inside a workspace reached by the agency grant the owner is treated as an admin — the same rights the dashboard gives them. See Workspaces § Two ways a workspace is reachable. - Plan follows the default workspace. Gating inherits the pinned
workspace’s plan, so client workspaces on cheaper plans still work
under an agency’s API-enabled grant. The pinned workspace’s plan is
the plan of whoever pays for it, so a client workspace an agency
manages carries the agency’s plan even when it is the pinned one —
how it is resolved, and which workspace a
plan_not_eligiblenames, is onplan_not_eligible.
- It outlives the conversation that made it — up to 12 hours, or
until something switches back. A later conversation on the same
connection inherits it. The
switch_workspaceresponse carriesexpiresInSeconds,defaultWorkspaceId(to undo), anddefaultProjectCleared. - While the connection is off its default workspace, every tool
result carries a second text block naming the active workspace and
how to switch back. It rides on failures too — a
not_foundfor an id the user just named is what an inherited switch looks like from the model’s side. The notice is acontentblock only;structuredContentis unchanged, so it never breaks a typed client. - The pinned default project is cleared while switched (it belongs
to the workspace you left), so pass
projectIdexplicitly. Omitting it fails with aninvalid_requestthat names the active workspace and tells you to calllist_projects.
defaultWorkspaceId drops the override immediately
and restores the default project.
Analytics is the exception to one-workspace-per-call.
get_cross_workspace_analytics_overview (and its report twin) fan out
over every reachable workspace in a single call, attributing each row to
the workspace it came from — so “how are all my clients doing” does not
need a switch at all. See
MCP tools § 1.17.
Everything else stays single-workspace by design.
The server’s instructions tell the model to announce the workspace and
project it acts on, so users can correct a wrong guess before more than
one thing lands in the wrong place.
REST API keys stay single-workspace. On the REST API, aScripe-Workspace-Idheader that disagrees with the key’s workspace is rejected withworkspace_unavailable. Multi-workspace access requires an OAuth token — which is all the MCP transport accepts anyway.
4. What the model can and cannot do
The short version of the tool contracts, across the 76 tools:- Reads are free. 39 tools advertise
readOnlyHint: true, across workspaces, projects, notes, posts, ideas, calendar, sources, knowledge, media, analytics, settings, positioning, community profile lists, usage, and jobs — includingdraft_positioning_from_website, which crawls a site and drafts text but persists nothing. - Writes are scoped and metered. 37 write tools (including the
data-neutral
switch_workspace) mirror the REST writes plus the MCP-only verbs. Job-creating tools stream progress. - Engaging with another person’s post is not on this API, by
decision.
add_profile_to_listputs somebody on a watchlist so Scripe collects their posts; liking, commenting and reposting stay an explicit human click in the Scripe dashboard, and the data model admits nothing else. See §2.16. - Per-post auto-engagement mirrors the post editor.
get_post_engagementsreads a post’s queued likes/comments/reposts plus the actors it could engage from;update_post_engagementscomposes that timeline under the editor’s own limits (Advanced+ plan, same-workspace LinkedIn-connected actors with a non-OFF policy for the action, the editor’s content and timing bounds). Composing is not consent: each acting brand’s engagement policy resolves fail-closed when the post is scheduled, and granting aREQUESTapproval is not available on this API — approvals happen in the Scripe dashboard or the mobile app. The write is single-phase, and removing a row is irreversible: it also deletes the tracked links bound to that row and their click history. Read the timeline withget_post_engagementsbefore changing it. - Irreversible actions are two-phase.
publish_post,schedule_post,delete_post,delete_idea,delete_knowledge_doc,delete_source,delete_media_asset, permanentdelete_note,update_tone_of_voice,update_engagement_policy, andupdate_posting_schedulenever execute on the first call: they return a proposal and a confirmation token (~5-minute TTL), and only a second call replaying that token executes. The positioning writes (update_positioning,update_global_tone_of_voice,update_personal_dna) are conditionally two-phase: filling an empty field lands directly, replacing text a person already wrote confirms. What the server enforces is the binding: the token is tied to the action, the acting user, the subject, and the proposed change — confirming with different arguments than you proposed fails.publish_postalone also binds a hash of the post’s content, media, and engagement flags, so only there does editing the subject between the two calls invalidate the token. - Two-phase is not a human gate on MCP. Both calls are
model-initiated — the host receives the token from phase one and can
replay it immediately, and the server cannot tell an approved replay
from an auto-approved one. What two-phase buys here is argument-drift
protection (plus content-drift for
publish_post), the TTL, and an audit trail; it is not evidence that a person saw the action. Surfacing the proposal and getting an explicit yes is your host’s obligation (see mcp-tools.md §2.2). The real consent boundary on MCP is the OAuth scope:posts:publish,settings:writeand the*:destroyscopes must each be granted by name, are implied by no alias, and cannot be held by an API key. - Some things stay in the dashboard. Turning autonomous engagement
fully
ON, reshare publishing, review decisions (approving or rejecting a post), reconnecting LinkedIn, and project creation are deliberately not available from any agent surface.
5. Limits and quotas
- Rate limits — the same per-credential buckets as REST, keyed by
the OAuth access token: reads 120/min, writes 30/min, and the job
bucket 10/min — the job-creating tools (
generate_post,generate_image,generate_carousel,create_source_file,add_to_knowledge_base) plus two that create no job but run a model synchronously:draft_positioning_from_website(which also crawls a site) andgenerate_post_ideas. The two cross-workspace analytics tools (get_cross_workspace_analytics_overview,get_cross_workspace_analytics_report) draw from their own tighteranalytics_fanoutbucket, also 10/min — each call fans out per reachable workspace and costs roughly 20x (overview) or 8x (report) a plain read’s database work. Exceeding a bucket returnsrate_limited. - Agent publishes — 5 per user per rolling 24 hours, counted across
every project and both agent surfaces (MCP and the in-app chat).
Dashboard, editor, and scheduled publishes do not count. See
publish_post. - AI budget — job-creating tools draw from the same weekly usage
limit and daily spend cap as the REST job endpoints
(Jobs § AI budget); exhaustion returns
usage_limit_exceeded/spend_cap_exceeded.get_usagereports both meters and acanGenerateverdict before either fires — check it once before a batch. - Sessions — 100 active per workspace, oldest evicted first; idle sessions retire after 30 minutes (resuming is invisible to the host — see §2.1).
- Progress notifications — capped at 60 per request, at most one per ~750 ms.
- Streamed body cap — 8 MB per response; larger results return
payload_too_large. Use the paginatedlist_*tools rather than dumping everything at once. - Plan — the API (REST and MCP alike) is gated on the plan’s
apiAccessentitlement: Advanced and Business carry it, as do Enterprise, the grandfathered Agency tier, and admin-assigned Trial workspaces. Everything else is rejected with403 plan_not_eligible. Some tools carry their own plan gate on top and returnplan_not_eligiblewith an upgrade pointer rather than being hidden.
6. Host setup walkthrough
Claude (Desktop or claude.ai): Settings → Connectors → Add custom connector, URLhttps://mcp.scripe.io/mcp, then approve the consent
screen in the browser tab that opens. The connector turns green and the
Scripe tools, resources, and prompts appear.
ChatGPT: add the same URL in the connector settings (Streamable
HTTP).
Cursor: add a remote MCP server with the same URL; older Cursor
builds fall back to the SSE endpoint automatically. For the
one-connection-per-workspace agency pattern, add multiple entries with a
Scripe-Workspace-Id header each.
Anything else that speaks MCP: point it at the Streamable HTTP URL;
the OAuth flow is standard DCR + PKCE, so no Scripe-specific host code
is needed.
Try it with a prompt like “What’s on my Scripe calendar next week?” —
the model will call list_calendar and answer from the result. The
slash-command prompts (weekly_planning, repurpose, inbox_zero) are
listed in MCP tools § Prompts.
7. Operational notes
- Tool payloads are versionless — REST’s
Scripe-Api-Versionpin and its no-breaking-changes promise do not extend here; MCP takes no version header, so tool result shapes may change between releases. Read them dynamically rather than compiling against them. See Conventions § Versioning. - Audit log — every tool call appears in the workspace audit log,
on success and on failure, recorded as method
MCPwith pathmcp/<tool_name>, the OAuth client, the acting user, and the resulting status code. - Email notifications — the user receives an email when a new MCP consent is granted.
- Revocation — users disconnect from Settings → Developer → Connected apps; revoking the consent kills the tokens and with them every session.
- Status page — status.scripe.io surfaces the MCP endpoint and the OAuth server as separate components.
Need help?
- Email [email protected] for integration help.
- Email [email protected] for suspected token leaks — MCP and REST tokens are treated identically.