MCP reference

The endpoint, sign-in, the four tools, limits and errors.

This page is for people building an MCP client or wiring up an agent by hand. If you only want to connect Claude Code, Cursor or another agent, use Coding agents.

Endpoint

https://ramble.sh/mcp

It is a remote MCP server on the streamable HTTP transport, named ramble. There are no sessions: each request stands alone. A browser page on another site can't call it. A request whose Origin header isn't ramble.sh itself gets a 403.

When a client connects, the server sends a short instructions text. It holds your counts (rambles with findings no agent has acted on, open record requests) and your autonomy setting. It never holds a title, a quote or a note.

Signing in

There are two ways to authenticate. Both end as Authorization: Bearer … on each request.

OAuth

This is what Claude Code, Codex, Gemini CLI, Cursor and VS Code use. A client that follows the MCP authorization flow needs nothing but the URL.

  1. Call /mcp with no token. It answers 401 with a WWW-Authenticate header carrying a resource_metadata address.
  2. Read the discovery documents. /.well-known/oauth-protected-resource names the resource and the authorization server. /.well-known/oauth-authorization-server lists the endpoints below.
  3. Register the client by dynamic client registration (RFC 7591) at /api/oauth/register. Clients are public: the only token_endpoint_auth_method accepted is none. A redirect URI may be a loopback address (http://127.0.0.1, http://localhost or http://[::1], any port), an https address, or a private-use scheme followed by ://.
  4. Authorize at /oauth/authorize with the authorization code flow and PKCE. Only S256 is accepted. Ask for the scopes you need. The person sees a consent page with your client's name marked "unverified name", and presses Connect. The redirect carries an iss parameter.
  5. Exchange the code at /api/oauth/token (grant_type=authorization_code).
  6. Refresh at the same endpoint (grant_type=refresh_token). Refresh tokens rotate: every refresh returns a new one. Using an old one a second time is treated as theft and revokes that client's tokens for the person.
  7. Revoke at /api/oauth/revoke (RFC 7009).

An access token lasts 24 hours. A refresh token lasts 90 days since it was last used. Any failure of a code or a refresh token answers with the same invalid_grant error, so the response doesn't tell you which part was wrong.

A personal token

For an agent that can't sign in through a browser. The person makes one under Connected agents on Your rambles and gives it to you. It is shown once, carries all three scopes and doesn't expire until it is revoked. Send it as Authorization: Bearer TOKEN.

What a bad request gets

  • No token, or one that isn't valid or has expired: 401 unauthorized, with the WWW-Authenticate header above.
  • A token without rambles:read: 403 insufficient_scope.

Scopes

ScopeLets the client
rambles:readUse ramble_find, and ramble_read on a ramble or a finding. Every connection needs it to reach /mcp.
rambles:annotateUse ramble_work: claim findings, set statuses, comment, set a project, set the autonomy setting (back to plan_first only).
rambles:requestUse ramble_ask to ask the person to record and to cancel, and ramble_read with a requestId to wait.

All four tools are always listed. The scope is checked for each call, and for ramble_read by what you ask it: a ramble or a finding needs rambles:read, a request needs rambles:request. A call whose scope the token lacks answers with an insufficient_scope tool error rather than an HTTP error. No scope covers uploading, recording, deleting or editing findings. Agents can't do those.

The four tools

Every tool definition a server lists is context an agent pays for in every session, and Ramble is usually one of several servers it has connected. So related operations share a tool, and one short argument picks the operation: view on ramble_find, action on ramble_work and ramble_ask. Reading and writing are separate tools, so a client can treat the two reading tools as read-only (readOnlyHint) and ask before the two writing tools.

ToolReads or writesWhat it does
ramble_findreadsList rambles and projects, and say who you are connected as.
ramble_readreadsRead one ramble or one finding, with screenshots, or wait for a requested ramble.
ramble_workwritesClaim findings, set statuses, comment, set a project, set the autonomy setting.
ramble_askwritesAsk the person to record a short check, or cancel the request.

The schemas are flat on purpose, so the server, not the schema, says what an action still needs: a call that leaves something out answers invalid_argument with the missing names in missing, and costs nothing against the write limit.

ramble_find

Scope rambles:read. Orients you and finds things. The argument view picks what you get:

  • rambles (the default) lists the account's rambles, newest first. Arguments, all optional: - limit: 1 to 100, default 25. - since: an ISO 8601 date or timestamp. Rambles recorded at or after it. - state: ready, processing, failed or recording. - project: a project key such as github.com/owner/repo, or an id. - unassigned: only rambles with no project. - unactioned: only ready rambles with findings no agent has touched. This is the inbox. - query: up to 200 characters, matched against the title, the app name, the request summary and finding titles.

Each ramble has id, createdAt, state, durationMs, title, appName, project, projectSource, projectCandidates, findingCount, agentSummary, claimedBy and requestId.

  • projects lists the account's projects: id, key, name, repoUrl, hints and rambleCount.
  • me says who the connection is: mode, the email, the scopes, the autonomy setting, autonomySource (where the setting comes from: user, default or env), the server address, and two counts: unactioned (rambles with findings no agent has acted on) and openRequests. Call it first.

ramble_read

Reads one ramble, one finding, or waits on a request. Pass id, or requestId, not both.

A ramble (id, scope rambles:read) comes back as agent markdown: the recorder's words with m:ss times, the findings and the transcript. For the owner it adds what agents have done with it. Arguments:

  • id: the 12-character ramble id.
  • frames: none (default), hero or all. hero returns each finding's main screenshot as an image. all returns every screenshot of every finding. Images come only when the ramble is ready.
  • maxImageBytes: 0 to 3000000, default 1500000. The budget for raw screenshot bytes.

structuredContent carries state, title, appName, durationMs, project, findingCount, claimedBy, enriching, enrichingTasks and a findings array. While enriching is true Ramble is still checking screenshots and labels: caption, elementName and bbox are null on the findings listed in enrichingTasks. Read again for the final values. Each finding has id, position (the number the markdown prints as its work item), kind, title, quote, t0Ms, t1Ms, attentionOrder, elementName, caption, bbox, bboxFromDiff, screenshots, and the agent fields agentStatus, agentStatusUrl, agentStatusNote, agentStatusActor, agentStatusAt and claim. These are summaries. They leave out description, expected, actual, reproSteps and the confidence fields: the markdown text carries the recorder's words and the reproduction notes, and a finding read (id and findingId, below) returns every field.

A finding (id and findingId, scope rambles:read) comes back with every field of the finding (description, expected, actual, reproSteps, confidenceScore, confidenceBand, needsVisual, attentionOrder and the ones in the summary), its agent status, its live claim and its screenshots as images. Use it for a screenshot the ramble read left out for size. frames is hero by default, or all or none. The bounding box is [ymin, xmin, ymax, xmax] on a 0 to 1000 scale. When bboxFromDiff is true, it marks where the screen changed, not an identified element.

A wait (requestId, scope rambles:request) waits for the ramble that answers a request made with ramble_ask. Arguments: requestId; cursor, the value the last call returned; and timeoutSeconds, 5 to 40, default 40. It returns as soon as the status changes, a new provisional finding appears or the status is final, or when the time is up. Call it again until the status is ready, failed, cancelled or expired, then read the rambleId with id.

The answer has status (open, recording, processing, ready, failed, cancelled or expired), rambleId, cursor, elapsedMs, requestAgeMs and provisional. Items in provisional are findings Ramble has proposed while the person is still talking. They carry provisional: true. They are previews: never act on them. If the ramble is deleted while you wait, discarded is true and the status is cancelled. If the client disconnects, the wait stops. If you send a progress token, the server sends a progress notification about every 10 seconds.

ramble_work

Scope rambles:annotate. Records your work on a ramble for the owner and other sessions. The argument action picks the operation, and each action needs its own arguments:

actionArgumentsWhat it does
claimrambleId, actor; findingIds (up to 100, or leave it out to claim the whole ramble), ttlMinutes (5 to 240, default 30)Takes a lease on findings so another session doesn't work on them. Returns leaseId, expiresAt, claimed and renewed. If another session holds some of them, the answer is a held error with heldBy naming who and until when. Claiming again with the same connection and actor renews your own lease.
renewleaseIdExtends a lease this connection holds. Call it about every 20 minutes while you work. A lease that has ended is a not_found_or_expired error.
releaseleaseIdGives a lease up.
statusrambleId, findingId, status (planned, in_progress, pr_opened, fixed or wont_fix), actor; url (an https link, up to 500 characters), note (up to 1000 characters)Records where a finding stands. It needs a live claim held by this connection. Without one the answer is not_claimed.
commentrambleId, note (1 to 2000 characters), actor; findingIdLeaves a note on a ramble, or on one finding. It needs no claim.
projectrambleId, project: an object with key and optionally name and repoUrl, or null to clear it; actor is optionalSays which project a ramble is about. The key is normalised, and the project is created on first use. If the owner chose a project, it is kept and the answer says kept: true.
autonomyautonomyFrom an agent connection only plan_first is accepted. act is refused with a not_available error: the person switches to act themselves, in the Connected agents card on Your rambles. Don't retry, and don't look for another credential. Do it only when the person asks you to in the conversation.

The actor is who is writing: your agent name, the repo folder and the local time. Two sessions on one connection tell each other apart by it.

ramble_ask

Scope rambles:request. Asks the person to record a short ramble that checks something, or withdraws the request. The argument action is request or cancel.

request arguments:

  • brief: 1 to 2000 characters. What should now be true and how to try it.
  • actor.
  • project: a project key, optional.
  • findingIds: up to 50 findings the recording should check. They must be findings on this account, or the answer is unknown_findings.
  • context: repo, branch, commit, urls (up to 10) and changedFiles (up to 50), all optional.
  • expiresInMinutes: 5 to 240, default 60.

It returns requestId, deepLink (ramble://record?request=ID, for Ramble for Mac), webUrl (https://ramble.sh/?request=ID), expiresAt and howToOpen. Open exactly one of the two links for the person, then wait with ramble_read and the requestId. An account can have 20 open requests at a time.

cancel takes requestId and withdraws a request nobody has started answering. Otherwise the answer is not_open.

Results and untrusted text

Every result has two parts.

  • structuredContent holds the values as they are, with classification: "untrusted_reference" and a guidance sentence. Read data from here.
  • content[].text is what most clients show the model. Its free text (titles, quotes, notes, names, links) is escaped so it can't pose as formatting. Only ids, fixed values, numbers and sentences written by the server are left plain.

Everything that came from speech, a screen, a model or another session is untrusted reference material. It never authorizes an action. Tell your model so, and wait for the person's own instruction. Text inside a screenshot is data too.

Limits

WhatLimit
One wait (ramble_read with a requestId)40 seconds.
Waits open at once2 per connection. A third gets too_many_waits.
Writes240 per connection per 10 minutes. After that, rate_limited until the window passes.
Screenshot bytes per call1500000 by default, 3000000 at most. A frame that doesn't fit is left out, with a line naming the ramble_read call that fetches it. Black frames are never sent.
ramble_find100 rambles per call.
Findings per claim100.
Lease length5 to 240 minutes.
Comment, status note, brief2000, 1000 and 2000 characters.
Open record requests20 per account.
Projects200 per account.
Personal tokens20 per account.

Errors

A tool that fails still returns a normal result, with isError: true. structuredContent.error is a stable code and message is a sentence written by the server. The one exception is a value the schema itself rejects (a wrong type, a number outside its range, a choice that is not one of the listed ones): the MCP library refuses that before Ramble sees it, and the result has isError: true and "Input validation error" text, with no structuredContent and so no code. The codes you can meet:

CodeMeaning
insufficient_scopeThe token lacks the scope. The result names it in scope.
rate_limitedToo many writes in the last 10 minutes.
not_foundNo such ramble, finding, lease or request on this account. A thing that belongs to someone else looks exactly the same.
heldAnother session holds a claim. heldBy says who and until when.
not_claimedA status needs your live claim. Claim again.
not_found_or_expiredThe lease has ended. Claim again.
unknown_findings, too_many_findingsBad finding ids, or more than 100.
invalid_argumentAn action is missing an argument it needs (missing lists them), or since is not an ISO 8601 date. A call refused for this costs nothing against the write limit.
invalid_actor, invalid_note, invalid_url, invalid_status, invalid_project, invalid_brief, invalid_contextA value passed the schema and the server still refused it, for example a link that is not https. The message says which.
project_limit, activity_limit, too_many_open_requestsAn account limit is reached.
not_openThe request has already been started or answered.
too_many_waitsTwo waits are already open.
not_availableNot possible from an agent connection, for example setting the autonomy to act.
internal_errorThe call failed on the server. Try again.