[go: up one dir, main page]

Skip to content

MCP server

Upload, attach, and search files from any MCP client.

Connect #

The hosted server gives an agent upload tools without a local shell or checkout. With it an agent can:

The server works with any MCP client that supports remote servers over HTTP. Add it to yours:

claude mcp add --transport http uploads https://agents.uploads.sh/mcp

The client opens a browser to sign in on first use.

Have the CLI? uploads install registers the server with Claude Code, Codex, and Grok if they are installed.

Using claude.ai or Cowork? Add uploads from the Claude directory instead.

Authentication #

The server accepts two kinds of bearer token.

  • OAuth (recommended). Clients that support MCP authorization find the authorization server on their own. You sign in at uploads.sh and choose a default workspace for the grant. If you belong to more than one workspace, a call that passes repo runs in the workspace that repo is linked to, and every tool takes an optional workspace to pick one. whoami lists them.
  • Workspace token. Send the personal token that uploads login creates (upk_…, or up_<workspace>_… from older CLIs), or a ups_<workspace>_… service token that a workspace admin creates for CI. The server uses the workspace the token was created for.

Send the token in the Authorization header:

Authorization: Bearer upk_…

Most tools need one scope. The tool reference shows it next to each tool.

Scope Grants
files:read List, search, and read files, metadata, usage
files:write Upload, promote, unstage, set metadata, galleries, feeds. Also delete a gh/ file the server stamped as this token’s. Older uploads need files:delete
files:delete Delete any file, and purge expired ones

Client developers: discovery, client registration, and token details are in auth.md.

Stage, then promote #

An agent usually takes screenshots before the pull request exists. The hosted server has no git checkout, so pass repo and branch to put to stage each file for that branch:

put arguments
{
"contentUrl": "https://cdn.example/settings-after.png",
"repo": "acme/web",
"branch": "feat/settings",
"state": "after"
}

After the agent opens the PR, call promote with the same branch. It copies the staged files into the PR and updates the attachments comment:

promote arguments
{ "repo": "acme/web", "pr": 12, "branch": "feat/settings" }

PR already open? Pass pr and repo to put to attach in one call.

If the GitHub App is installed, it promotes staged files when the PR opens, and promote is safe to call again.

unstage clears that branch without deleting the files. Pass repo and branch, or keys. Set dryRun to preview. A file marked unstaged is skipped by promote.

Upload from a sandbox #

An agent in a cloud sandbox has its screenshots on disk but no CLI. Sending them through put means typing every byte as base64. Call sign_upload_url instead. It returns a short-lived URL and a curl line per file, and the agent uploads straight from disk:

sign_upload_url arguments
{ "files": [{ "filename": "settings-after.png" }], "repo": "acme/web", "pr": 12 }
Run in the sandbox
curl -sS --fail-with-body -T 'settings-after.png' 'https://agents.uploads.sh/u/…'

Each upload answers with the same JSON as put, including markdown. With pr or issue, call comment once after the uploads to update the attachments comment. URLs expire after 15 minutes and take files up to the workspace’s upload limit (each URL’s maxBytes, at most 100 MB), so videos work too. Files over 25 MiB stream straight to storage, so they get no video poster or PDF preview, and they skip AI labels and hold checks. Smaller uploads through the hosted server get both.

The sandbox has to reach agents.uploads.sh. If curl fails with a 403 from a proxy, add agents.uploads.sh to the environment’s allowed domains.

Hosted vs. local #

The CLI also runs an MCP server on your machine with uploads mcp. It reads local files and fills in repo and branch from git. Most tools are the same on both servers.

Server Only this server has
Hosted sign_upload_url, promote, list_activity, list_repo_files, repo_link_status
Local attach, staged, screenshot, doctor, report

To register the local server instead, see Skill vs. MCP.

Tools #

Every tool on the hosted server, as tools/list returns it. Tools marked “Hosted only” are not on the local server.

Upload and attach

putUpload file

Can overwrite or deletefiles:write

Upload a file (or files) and get a public URL plus GitHub-ready markdown. Files on disk and a shell to run curl? Use sign_upload_url instead, which skips base64. Prefer the returned embedUrl in GitHub markdown. All uploads are public. Pass pr+repo to attach to a PR (stable key + managed comment, which updates itself and shows each image's screen, state and caption). A result with holdCheck: "pending" may still be held for review if it shows a secret; comment.held counts files on the PR or issue held for review; see list_held. Pass branch+repo to stage for a PR that does not exist yet; if that branch already has one open PR (repo linked to this workspace), the file attaches to it instead. This server has no git, so in a checkout read repo and branch from git and always pass them. When the bytes are already at a public HTTPS URL, pass contentUrl instead of base64 — the server fetches them. Accepts images (PNG, JPEG, GIF, WebP, AVIF), video (MP4, WebM, MOV), PDF, zip, gzip, and text (plain, markdown, CSV, JSON). SVG and XML are accepted only on storage lanes verified to serve them sandboxed (see the workspace's storage settings); HTML is rejected.

Returns workspacekeyurlembedUrlsizecontentTypereplacedmarkdownprovenancemetadatavisibilityholdCheckfileuploadsfailurescommentcommentErrorpromotionpromoteErroroptimizeframedryRunhint

ParameterTypeDescription
contentBase64stringBase64-encoded file content to upload (must be non-empty). Exactly one of contentBase64, contentUrl, or files is required.
contentUrlstringPublic HTTPS URL of an already-hosted file to fetch and re-host. Filename is optional when the URL path has a leaf. No auth headers are forwarded; private/internal hosts are rejected. Exactly one of contentBase64, contentUrl, or files is required.
filenamestringFilename for the content (drives the key and content type). Required with contentBase64; optional with contentUrl when the URL path has a leaf.
filesobject[]Multiple files in one call (max 20). Each item needs contentBase64 or contentUrl. Filename is optional with contentUrl when the URL path has a leaf. Cannot combine with contentBase64, contentUrl, filename, or key.
keystringOverride the object key. Cannot combine with pr/issue/branch.
prefixstringKey prefix for the default key layout (default: screenshots).
repostringGitHub owner/name. Required with pr/issue/branch. Optional otherwise (default: misc). This server has no git: in a checkout, read it with git remote get-url origin.
refstringOptional key segment (default: today). Cannot combine with pr/issue/branch.
prnumberAttach to this pull request (stable gh/ key). Mutually exclusive with issue. Requires repo. With branch, also promotes that branch's staged files.
issuenumberAttach to this issue (stable gh/ key). Mutually exclusive with pr and branch. Requires repo.
branchstringGit branch (git branch --show-current). Stages under it, or attaches to its open PR when the repo is linked to this workspace and exactly one is open. With pr, also promote this branch's staged files into the PR. Requires repo.
commentbooleanWith pr or issue: create or update the managed attachments comment after upload. Default true. Pass false to skip. A comment failure never fails the upload.
altstringAlt text for the markdown (default: filename).
widthnumberEmit <img width=…> markdown instead of a plain image embed.
metadataobjectQueryable tags for later search (key→value). Use path for the route (e.g. /settings), not route/page. Omit to leave existing tags; pass an object (even {}) to replace them. state, app, screen and caption have their own fields. On pr/issue/branch, gh.* is set for you.
state"before" | "after" | "empty" | "error" | "loading"The UI state this image shows. Set it whenever the image is one side of a comparison — before/after is the most useful pair in a PR, and is what makes find_files with state=after work later.
appstringWhich surface is shown: web, ios, android, cli. Worth setting only when the same route exists on more than one surface.
screenstringThe app screen shown, for views with no route (a mobile or desktop screen), e.g. "Chat". Shown under the image in the PR comment. Use path in metadata for web routes.
captionstringWhat the shot shows, in plain words (up to 140 printable ASCII characters), e.g. "Empty state after deleting the last project". Shown under the image in the PR comment.
replacebooleanOverwrite an existing object on a non-gh/ key. Default false. No effect on gh/ keys, which always overwrite.

sign_upload_urlSign upload URLs

Can overwrite or deletefiles:writeHosted only

Get short-lived upload URLs so a shell can send files straight from disk, without base64 in the tool call. Use this instead of put when the files are on a local or sandbox disk and you can run curl. Takes put's arguments without the bytes, plus files: [{ filename, alt? }] (max 20). This server has no git: in a checkout, pass repo and branch read from git. Run each returned curl line from the folder holding the file (or swap in its path); the response is the same JSON put returns, including markdown. URLs expire after 15 minutes; each takes a file up to its maxBytes (the workspace's upload limit, at most 100 MB). With pr/issue, call comment once after every upload succeeds. If curl gets a 403 from a proxy, the sandbox blocks agents.uploads.sh: ask the person to allow it, or fall back to put with contentBase64. All uploads are public.

Returns workspaceexpiresAtuploadsnexthint

ParameterTypeDescription
filesrequiredobject[]Files to upload, one URL each (max 20).
prefixstringKey prefix for the default key layout (default: screenshots).
repostringGitHub owner/name. Required with pr/issue/branch. Optional otherwise (default: misc). This server has no git: in a checkout, read it with git remote get-url origin.
refstringOptional key segment (default: today). Cannot combine with pr/issue/branch.
prnumberAttach to this pull request (stable gh/ key). Mutually exclusive with issue and branch. Requires repo.
issuenumberAttach to this issue (stable gh/ key). Mutually exclusive with pr and branch. Requires repo.
branchstringGit branch (git branch --show-current). Stages under it, or targets its open PR when the repo is linked to this workspace and exactly one is open (the result's next says which). Requires repo. Call promote once a PR opens for staged files.
altstringAlt text for the markdown (default: filename).
widthnumberEmit <img width=…> markdown instead of a plain image embed.
metadataobjectQueryable tags for later search (key→value). Use path for the route (e.g. /settings), not route/page. Omit to leave existing tags; pass an object (even {}) to replace them. state, app, screen and caption have their own fields. On pr/issue/branch, gh.* is set for you.
state"before" | "after" | "empty" | "error" | "loading"The UI state this image shows. Set it whenever the image is one side of a comparison — before/after is the most useful pair in a PR, and is what makes find_files with state=after work later.
appstringWhich surface is shown: web, ios, android, cli. Worth setting only when the same route exists on more than one surface.
screenstringThe app screen shown, for views with no route (a mobile or desktop screen), e.g. "Chat". Shown under the image in the PR comment. Use path in metadata for web routes.
captionstringWhat the shot shows, in plain words (up to 140 printable ASCII characters), e.g. "Empty state after deleting the last project". Shown under the image in the PR comment.
replacebooleanOverwrite an existing object on a non-gh/ key. Default false. No effect on gh/ keys, which always overwrite.

promotePromote staged attachments

Can overwrite or deletefiles:writeHosted only

Copy staged or existing files into a PR's stable gh/…/pull/… prefix, then refresh the managed comment. Pass branch and/or keys. Requires repo and pr.

Returns promotionattachedattachFailurescommentcommentErrorhint

ParameterTypeDescription
reporequiredstringGitHub owner/name. Required — this server cannot infer it from git.
prrequirednumberPull request number to promote/attach into. Never applies to issues.
branchstringStaged branch to copy into the PR.
keysstring[]Already-uploaded object keys or uploads.sh URLs to copy into the PR. One bad source does not abort the rest.
movebooleanFor keys only: delete each source after a successful copy. Default false. Ignored for the branch sweep.
commentbooleanRefresh the managed attachments comment after a successful copy. Default true. A comment failure never fails the copy.

commentSync attachments comment

Can overwrite or deletefiles:read

Refresh the managed attachments comment on a GitHub PR or issue without re-uploading. Use after deleting media. Requires repo and exactly one of pr/issue. held counts files there held for review; see list_held.

Returns postedreasonmessagefixUrlrequiredactioncountcommentUrlheld

ParameterTypeDescription
reporequiredstringGitHub owner/name. Required — this server cannot infer it from git.
prnumberPull request number. Mutually exclusive with issue.
issuenumberIssue number. Mutually exclusive with pr.

Files and metadata

listList files

Read-onlyfiles:read

List uploaded objects in the workspace, optionally filtered by key prefix. Paginate with cursor; each item includes its public URL when the workspace has one.

Returns itemscursorprefixes

ParameterTypeDescription
prefixstringKey prefix filter.
limitnumberPage size (default 100, max 1000).
cursorstringPagination cursor from a previous call.

unstageUnstage files

Writesfiles:write

Clear staged files for a branch, or specific keys, without deleting them. Sets gh.status=unstaged so they drop out of auto-promotion. Pass repo and branch, or keys. Needs files:write.

Returns dryRunrepobranchfilestruncated

ParameterTypeDescription
repostringGitHub owner/name. Required — this server cannot infer it from git.
branchstringBranch whose staged files to clear. Requires repo. Do not combine with keys.
keysstring[]Object keys to unstage. Do not combine with branch.
dryRunbooleanReport what would change without writing.

find_filesFind files

Read-onlyfiles:read

Search files by metadata (filters) and/or filename substring (name). At least one is required.

Returns itemscursortruncated

ParameterTypeDescription
filtersobjectEquality filters; all must match. Use path for the route (e.g. /settings), not route/page. Optional when name is set.
namestringCase-insensitive substring match on object keys (1–128 chars). Optional when filters is non-empty.
prefixstringKey prefix filter, combinable with filters/name.
limitnumberPage size (default 50, max 500).
cursorstringOpaque continuation from a previous call's cursor. Pass it back unchanged with the same filters/name to get the next page; a null cursor means there are no more pages.

get_metadataGet metadata

Read-onlyfiles:read

Read the queryable tags on one file. Returns { metadata } (empty when none).

Returns metadata

ParameterTypeDescription
keyrequiredstringObject key to inspect.

set_metadataSet metadata

Can overwrite or deletefiles:write

Set or delete queryable tags on an existing file. set wins over delete for the same key. Tags appear on the public file page — do not store secrets. Requires set and/or delete.

Returns metadata

ParameterTypeDescription
keyrequiredstringObject key to update.
setobjectKeys to set or overwrite. Use path for the route (e.g. /settings), not route/page.
deletestring[]Keys to remove.

list_metadata_keysList metadata keys

Read-onlyfiles:read

List metadata keys in the workspace (with counts). Pass key to list that key's values instead. Use before find_files.

Returns keystruncatedkeyvalues

ParameterTypeDescription
keystringWhen set, return distinct values for this metadata key (with counts) instead of the key list.

deleteDelete file

Can overwrite or deletefiles:delete

Permanently delete one uploaded file by key, along with its metadata. A files:write token can delete a gh/ object the server stamped as its upload. Older files and any other object need files:delete. Deleting a PR or issue attachment refreshes the managed comment.

Returns keydeleteddryRuncommentRefreshedcommentHint

ParameterTypeDescription
keyrequiredstringObject key to delete.

Held files

list_heldList held files

Read-onlyfiles:read

List uploads held for review. The classifier holds a file it thinks shows a secret (an API key, a password, a token) or sensitive personal data (an ID, a card number). A held file stays out of the managed PR comment, feeds, and galleries until a workspace member approves it with approve. A signed-in member sees the real file on its /f/ page. On hosted storage an image URL returns a Held for review card, and any other raw URL returns 404. Pass pr/issue + repo for one PR or issue, or prefix. Each item has reason (secret: api_key, pii: financial), area (an approximate box around the flagged content as fractions of the image, null when unknown), and sha (the content hash, null when unknown). approve with pr or issue sends that hash.

Returns items

ParameterTypeDescription
prnumberHeld files on this pull request.
issuenumberHeld files on this issue.
repostringGitHub owner/name. Required — this server cannot infer it from git.
prefixstringOnly keys under this prefix.

approveApprove held files

Writesfiles:write

Release uploads held for review (see list_held): pass keys, or pr/issue + repo to approve every held file there. The file returns at the same URL and the managed PR comment re-syncs. Check the file first: if it shows a real credential, rotate it and delete the file instead of approving. Same as uploads approve.

Returns files

ParameterTypeDescription
keysstring[]Held keys to approve.
prnumberApprove every held file on this pull request.
issuenumberApprove every held file on this issue.
repostringGitHub owner/name. Required — this server cannot infer it from git.

Repo activity

list_activityList recent activity

Read-onlyfiles:readHosted only

Recent GitHub-tagged activity in this workspace, grouped by pull request (default) or by repo. Pull rows are pull requests that received media tagged gh.kind=pull, newest last_media_at first. The default window is 90 days. Set all to true to lift it. Optional repo and state (open, closed, or merged) narrow those rows. A title is included only when the repo is linked to this workspace. Repo rows are distinct gh.repo values, newest metadata updated_at first. These times are not the object's uploaded-at. Untagged uploads are absent. This does not publish a feed. Pass cursor back unchanged with the same arguments for the next page. Use list_repo_files to open one repo's files, and find_files for tag or filename search in key order.

Returns byrepospullscursor

ParameterTypeDescription
by"pull" | "repo"Group the page by pull request ("pull", the default) or by repository ("repo").
repostringWhen by is pull, keep rows for this owner/repo only. Ignored values are rejected when by is repo.
state"open" | "closed" | "merged"When by is pull, keep rows in this state. Rows with no stored state drop out. Rejected when by is repo.
allbooleanWhen by is pull, include pull requests whose last media is older than 90 days. Default false. Rejected when by is repo.
limitnumberPage size. Pulls default to 20 (max 100). Repos default to 20 (max 50).
cursorstringOpaque cursor from the previous page. Send it back with the same by, repo, state, and all.

list_repo_filesList files in a repo

Read-onlyfiles:readHosted only

Files tagged with gh.repo for one repository, newest metadata updated_at first. That time is not the object's uploaded-at. repo is owner/name. Optional pr matches gh.number. Optional path matches the path tag exactly. type is a filename suffix class: screenshot, video, or other. PDFs are other. Promoted copies (gh.status=promoted) are omitted. This does not publish a feed. Pass cursor back unchanged with the same arguments for the next page.

Returns itemscursor

ParameterTypeDescription
reporequiredstringGitHub repository as owner/name. Matching is case-insensitive.
prnumberWhen set, keep files tagged with this pull-request number (gh.number).
pathstringWhen set, keep files whose path tag equals this string.
type"screenshot" | "video" | "other"Filename suffix class. screenshot is png/jpg/jpeg/webp/gif/avif, video is mp4/webm/mov, and other is everything else (including PDF).
limitnumberPage size (default 50, max 100).
cursorstringOpaque cursor from the previous page. Send it back with the same repo, pr, path, and type.

Galleries

Live links

feed_createCreate change feed

Writesfiles:write

Create a public newest-first screenshot feed (same product as CLI uploads feed create). Pass repo alone for the whole GitHub repo. Pass pr plus repo — or github as owner/repo#123 / a GitHub PR URL — to scope that same feed to one pull request. issue does the same for an issue. Optional path filters by page-path metadata. Creating the same scope again returns the existing URL. This is a live query, not a curated gallery. Anyone who knows the URL can view it. See https://uploads.sh/docs/feeds.

Returns idurlworkspacerepopathnumberkindtitlecreatedAtupdatedAtitems

ParameterTypeDescription
repostringGitHub owner/repo, for example acme/app. Required unless github is set.
printegerScope the feed to this pull request. Same product as a repo feed — one extra filter. Mutually exclusive with issue and github.
issueintegerScope the feed to this issue. Mutually exclusive with pr and github.
githubstringScope via owner/repo#number or a GitHub issue/PR URL. Supplies repo when omitted. Mutually exclusive with pr and issue.
pathstringOptional exact page-path metadata filter, for example /settings.

feed_getGet change feed

Read-onlyfiles:read

Get a workspace-owned change feed by ID, including its current newest-first screenshots and the canonical public /c/<id> URL. Each item includes pageUrl (/c/<id>/<item>) for the pager. The feed may be repo-wide or scoped to one pull request / issue (see number and kind on the result). Anyone with the URL can view the media.

Returns idurlworkspacerepopathnumberkindtitlecreatedAtupdatedAtitems

ParameterTypeDescription
feedIdrequiredstringOpaque feed ID.

Workspace

whoamiWho am I

Read-onlyAny token

Show the current uploads.sh identity: the workspace this call acts in, every workspace this connection can use (workspaces; pass one as workspace on any tool), and its scopes. A successful result also means the server is up.

Returns okworkspaceworkspacesscopesuserIdserviceTokensignedInapiUrl

usageShow usage

Read-onlyfiles:read

Workspace storage and monthly upload counters (and remaining headroom when budgets are configured).

Returns workspacebytesobjectssharedBytessharedObjectsuploadsInPeriodperiodStartupdatedAtstorageBudgetBasismaxStorageBytesstorageRemainingBytesmaxUploadsPerPerioduploadsRemainingscopesplanstorage

reconcileReconcile usage

Writesfiles:write

Rebuild usage ledger bytes/objects from storage (source of truth). Preserves the monthly upload counter. Requires files:write.

Returns workspacebytesobjectspreviouschangedusageunprefixedBucket

purge_expiredPurge expired files

Can overwrite or deletefiles:delete

Delete objects older than the workspace retentionDays setting, then reconcile. Skips if retention is unset. Requires files:delete.

Returns skippedreasonworkspaceretentionDayscutoffdeletedfreedByteskeyskeysTruncatedreconcile

Docs and changelog

search_docsSearch uploads.sh docs

Read-onlyAny token

Search the public uploads.sh documentation. Use this to answer questions about uploads.sh product workflows, including: attaching screenshots and video to GitHub PRs and issues; staging files before a PR exists; galleries; the GitHub App; comment config (.uploads.yml); hosted MCP and agent setup; screenshot capture and annotate; plans and limits; bring-your-own bucket. Same as uploads docs. Returns titles, URLs, and snippets. Pass page to fetch the full markdown of one page (slug, path, or URL). Omit query to list the catalog.

Returns urlqueryresultstotal

ParameterTypeDescription
querystringSearch query (e.g. 'stage before a PR'). A single slug like 'attach' fetches that page.
pagestringFetch one page as markdown. Accepts a slug (attach), path (/docs/agents), or https://uploads.sh URL.
limitnumberHow many search hits to return (default 5, max 50). Ignored when fetching one page.

changelogProduct changelog

Read-onlyAny token

Read recent uploads.sh product updates (platform and CLI). Returns the latest entries with titles, dates, summaries, and a link to the full changelog at https://uploads.sh/changelog. Same as uploads changelog.

Returns urlfeedentries

ParameterTypeDescription
limitnumberHow many entries to return (default 5, max 50).

Other

list_staged_mediaStaged media

Read-onlyfiles:readHosted only

Read staged media for one explicit repository and branch in the connected workspace. Read the current checkout's Git remote and branch before calling; the hosted server cannot infer them. Repository and branch match stored lowercase metadata. Excludes promoted and unstaged files. Private, held, missing, and unsupported previews are placeholders. Pass cursor unchanged with the same scope to load more; a non-null cursor means more pages even if this page is empty. Results use object-key order, not newest-first.

Returns repobranchcursoritems

ParameterTypeDescription
reporequiredstringExplicit GitHub repository owner/name.
branchrequiredstringExplicit Git branch, for example feature/settings.
cursorstring
limitinteger

resolve_media_contextResolve media context

Read-onlyfiles:readHosted only

Resolve the pull request for one explicit repository and branch. Pass the current PR number when known. Otherwise look up one open same-repository PR only when the repository is linked to this workspace. An unavailable resolution requires an explicit PR number; it does not mean that the PR has no attached files.

Returns repobranchprprResolution

ParameterTypeDescription
reporequiredstring
branchrequiredstring
printeger

list_attached_mediaAttached media

Read-onlyfiles:readHosted only

Read media attached to one explicit repository and PR in the connected workspace. Pass the displayed branch as context. PR membership uses repository and PR number, including direct attachments without branch tags. Private and held files remain placeholders. Continue with the returned cursor and the same repository and PR; never combine PRs.

Returns repobranchcursoritemspr

ParameterTypeDescription
reporequiredstring
branchrequiredstring
prrequiredinteger
cursorstring
limitinteger

list_staged_scopesList staged branches

Read-onlyfiles:readHosted only

List distinct repository/branch pairs with staged files in the connected workspace. Use an explicit pair with list_staged_media; never combine different branches. Results use repository/branch order. Pass cursor unchanged for the next page.

Returns cursorscopes

ParameterTypeDescription
cursorstring
limitinteger

open_staged_mediaUploads

Read-onlyfiles:readHosted only

Open Uploads beside the conversation to review staged files and files attached to the current PR. Read the checkout's Git remote and branch and pass repo (owner/name), branch, and the current pr number when known. Without repo and branch, show a scope picker; never assume a checkout. Refresh keeps the displayed scope until it is changed.

ParameterTypeDescription
repostringExplicit repository as owner/name.
branchstringExplicit Git branch for this tray.
printegerCurrent pull-request number, when known.

Protocol details #

These matter only if you write your own client.

  • Transport. Stateless Streamable HTTP. Each POST carries one JSON-RPC message and gets a plain JSON reply. There are no sessions and no SSE stream, so GET and DELETE return 405.
  • Headers. Every POST must send Content-Type: application/json and Accept: application/json, text/event-stream. A missing content type returns 415, and a missing Accept returns 406.
  • Spec versions. The server speaks the 2026-07-28 revision and the earlier initialize-based flow on the same endpoint.
  • Limits. The REST API’s upload limits apply, and writes are rate limited per workspace. See Plans & limits.
  • Discovery. The server card is at /.well-known/mcp/server-card.json.