MCP tools
Every tool the connector gives Claude, with its arguments.
On this page
- The connector
- Signing in
- What each grant allows
- From Claude Code
- Every tool, at a glance
- Tools
- deploy_guide
- check_bundle
- list_projects
- deploy_status
- create_link
- revoke_link
- get_link
- viewer_activity
- read_feedback
- pipeline
- add_page
- deploy_static
- community_card
- set_community_card
- request_listing
- withdraw_listing
- appeal_listing
- list_notices
- list_workspaces
- list_members
- list_invitations
- invite_member
- revoke_invitation
- set_member_role
- remove_member
- public_address_status
- get_brand
- set_brand
- remove_logo
- set_room_theme
- reset_room_theme
- When a tool fails
The Roomi connector gives Claude the platform's operations as MCP tools: list your projects, check a deploy, make and revoke viewer links, read what viewers did and said, add a page, publish a static demo written in the conversation, with the guide and the check that get it right the first time, and list a project on the community. This page is the reference for each tool, its arguments and what it returns. Deploying from Claude is the guide.
The connector
The connector is an MCP server that speaks Streamable HTTP, at one address:
https://mcp.getroomi.com/mcpAdd it in Claude as a custom connector with that address. It holds no credential of its own and runs nothing of yours: every tool is a call to the HTTP API, made as you.
Signing in
The connector is an OAuth 2.1 protected resource, and the platform is its authorization server. When you add it, Claude does the rest:
- Claude asks the connector, is told to sign in, and reads where from
/.well-known/oauth-protected-resource/mcp. - Claude registers itself with the platform and sends you to the app to sign in.
- The app asks you to approve Claude for the workspace you are in. The grant is for that workspace only.
- Claude receives a token issued for the connector and nothing else, valid for an hour, and a refresh token to renew it without asking you again.
The connector accepts only a token the platform signed for it. A token from
pitch login is refused there, and the connector's own token is refused by
the API, so neither can stand in for the other. The connector never passes
your token on: it reaches the platform over a private channel, as the person
the token names, and the platform checks again that you still belong to the
workspace.
What each grant allows
| Scope | Allows |
|---|---|
pitch:read |
Every tool that changes nothing. |
pitch:write |
The tools that change something: create_link, revoke_link, get_link, add_page, deploy_static, set_community_card, request_listing, withdraw_listing, invite_member, revoke_invitation, set_member_role, remove_member, set_brand, remove_logo, set_room_theme and reset_room_theme. A rehearsal of one of them needs it too. get_link is here because the address it shows opens the room. |
Claude asks for both. A connection granted pitch:read alone gets a 403
with insufficient_scope when it calls a tool that changes something: its
message is "This connection may only read. Reconnect and allow changes."
From Claude Code
Claude Code can add the connector by its address, as any Claude client does.
The roomi plugin for Claude Code instead sends the token pitch login stored, read with pitch auth header;
only a connector running on your own machine accepts that token.
Every tool, at a glance
| Tool | What it does | Changes anything |
|---|---|---|
deploy_guide |
Returns the guide to building a demo the platform takes | No |
check_bundle |
Checks a static demo as deploy_static would, sending nothing |
No |
list_projects |
Lists your projects and their rooms | No |
deploy_status |
Shows a project's live build and recent builds | No |
viewer_activity |
Shows a project's links and their visits | No |
read_feedback |
Shows what viewers answered and commented | No |
pipeline |
Shows every link, with its stage | No |
create_link |
Makes a link for one viewer | Yes |
revoke_link |
Stops a link opening the room | Yes, and it cannot be undone |
get_link |
Shows a link's address again, when the viewer has lost it | Yes: each showing is recorded |
add_page |
Adds a page to a room | Yes |
deploy_static |
Publishes a static demo held in the conversation | Yes with commit: true; otherwise a rehearsal |
community_card |
Shows a project's community card and where its listing stands | No |
set_community_card |
Writes the community card, with the pre-flight check's warnings | Yes with commit: true; otherwise a rehearsal |
request_listing |
Lists a project on the community if its checks pass, or says what stopped it | Yes with commit: true; otherwise a rehearsal |
withdraw_listing |
Takes a project off the community, or out of review | Yes with commit: true; otherwise a rehearsal |
appeal_listing |
Asks Roomi to review a block, or a listing it took off | Yes with commit: true; otherwise a rehearsal |
list_notices |
Shows your notices: a listing taken off, a project taken down, a review decided | No |
list_workspaces |
Lists your workspaces: your own and each team, and which one this connection works in | No |
list_members |
Lists the people in a workspace, with their roles | No |
list_invitations |
Lists a team's invitations still waiting | No |
invite_member |
Invites an email into a team, and gives you the link to send | Yes with commit: true; otherwise a rehearsal |
revoke_invitation |
Stops an invitation's link working | Yes with commit: true; otherwise a rehearsal |
set_member_role |
Makes someone in a team an owner or a member | Yes with commit: true; otherwise a rehearsal |
remove_member |
Takes someone out of a team | Yes with commit: true; otherwise a rehearsal |
public_address_status |
Says whether a project's public address is turning new visitors away after a rush, and until when | No |
get_brand |
Shows the brand and the room theme, and what the room shows of each | No |
set_brand |
Sets the brand's name, accent colour and logo, checking the colour reads | Yes with commit: true; otherwise a rehearsal |
remove_logo |
Removes the brand's logo | Yes with commit: true; otherwise a rehearsal |
set_room_theme |
Sets the room theme, checking every colour reads | Yes with commit: true; otherwise a rehearsal |
reset_room_theme |
Removes the room theme | Yes with commit: true; otherwise a rehearsal |
When Claude connects, the connector also sends it instructions: what it
deploys, what the files must be (every app's path a file among them, so an
index.html at the root), the limits, that a tour and pages make the pitch,
and to call deploy_guide before preparing a deploy and check_bundle
before rehearsing; and that on Free, which has no viewer links, it offers to
list a live demo on the community. Deploying from Claude
says why.
Every tool describes itself to Claude with the words quoted below, and says whether it changes anything. The tools that change nothing are marked read-only; Claude asks you before it calls any of the others.
Tools
deploy_guide
Returns the guide to building a demo Roomi will take: the build contract (every app path needs an index.html among the files), the manifest, how to write the guided tour, pages, the rules, and when to suggest installing the pitch CLI instead. Call it before preparing a deploy or writing a story. Changes nothing.
Read-only. Takes no arguments.
{}Returns the guide as Markdown. It is drawn from the same sources as the
PITCH.md that pitch init writes and as these docs: the rules, the loop
(check_bundle, a rehearsal, your yes, then commit: true), what the files
must be, the manifest and every field it takes, how to write the tour, pages,
and what to do once the demo is live. Where PITCH.md names a pitch
command, the guide names the tool. One section names the CLI itself: when it
is the better route (a container demo, a build over the connector's limits,
work in a repository with Claude Code, links and pages from a terminal), the
two commands that install it, and that Claude offers to run the install where
it has a terminal and gives the commands where it has none.
check_bundle
Checks a static demo exactly as deploy_static would, and sends nothing anywhere. Changes nothing. It returns every problem at once, each with its fix and the docs entry that explains it: an app or a story step whose page is not among the files (an app at / needs index.html), too many files or bytes, a hidden file or node_modules, a live credential, a manifest field that is wrong or unknown. Call it with exactly what you will give deploy_static, and fix everything it names before rehearsing.
Read-only. It takes deploy_static's arguments, less commit: project,
name, device, files (each files[].path, files[].content and
files[].encoding), manifest and note, with the same limits.
{
"project": "corner-teaser",
"name": "Corner teaser",
"files": [{ "path": "src/App.tsx", "content": "export default () => <h1>Corner</h1>" }]
}1 problem, so deploy_static would refuse this. Nothing was sent. Fix every one, then check again:
- files: there is no index.html at the root. Without a manifest the demo is one app at /, and the room opens it at index.html: add index.html, the page the demo starts on
https://getroomi.com/docs/claude#every-check-and-its-fix
Also worth knowing:
- files: src/App.tsx look like source, not a build. The room serves files as they are and builds nothing: send what the build writes, or plain HTML, CSS and JavaScript
https://getroomi.com/docs/claude#every-check-and-its-fix
- manifest.story: there is none, so the room offers no guided tour. A viewer meets the demo without a word from you
https://getroomi.com/docs/storyA demo that needs the CLI is refused with the two commands that install it.
More than 200 files or 3 MB ends: To install it on the publisher’s machine: npm install -g @pitch-product/cli, then pitch login; a manifest with kind: container adds then pitch deploy in the demo’s folder.
With no problem, it says so, with the demo's apps, its tour and its pages, and anything worth knowing. Every check, and its fix lists what it checks.
list_projects
Lists the projects in the signed-in workspace, with each one's room URL. Changes nothing.
Read-only. Takes no arguments.
{}Returns the projects as JSON: each one's id, slug, name, visibility,
liveBuildId, roomUrl and createdAt. The id is what every other tool's
projectId takes.
deploy_status
Shows a project's live build and its recent builds. Changes nothing.
Read-only.
| Argument | Type | Required | Limits |
|---|---|---|---|
projectId |
string | yes | 1 to 100 characters: the id from list_projects |
{ "projectId": "3f2b8c1e-5a47-4d9e-8b21-6c0f9e7a4d53" }Returns project (as list_projects gives it), live (the live build, or
null when nothing is live) and recent (the five newest builds). Each build
has its id, kind, manifest, sizeBytes, note, live and createdAt.
To deploy, Claude uses pitch deploy through the plugin, or deploy_static
for a static demo in the conversation.
create_link
Creates a new viewer link to a project's room, for one named person. This changes something: it issues a working link that opens the room. Give the URL to the person straight away and write it nowhere else; if it is lost, get_link shows it again.
Changes something. Not idempotent: each call makes another link.
| Argument | Type | Required | Limits |
|---|---|---|---|
projectId |
string | yes | 1 to 100 characters |
viewerName |
string | yes | 1 to 80 characters: who the link is for |
viewerEmail |
string | no | an email address, kept with the link; nothing is sent to it |
expiresInDays |
integer | no | 1 to 365. Without it, the link does not expire |
{ "projectId": "3f2b8c1e-5a47-4d9e-8b21-6c0f9e7a4d53", "viewerName": "Priya Shah", "expiresInDays": 30 }Returns the link's id, who it is for, when it expires, and the URL, as text:
Link for Priya Shah (link id a91d4e6f-2c38-4b7a-9e05-d7b3f1c8e240, expiring 2026-10-29T10:00:00.000Z):
https://corner-for-bullring.getroomi.app/l/…
Give it to Priya Shah. If it is lost, get_link with this link id shows it again.The URL appears in that one result and nowhere else; no list carries it. On the Free plan the platform refuses every private link; see Plans and limits.
revoke_link
Revokes a viewer link. This changes something and cannot be undone: the link stops opening the room at once. What the viewer already did stays on record.
Changes something, destructively. Revoking a link that is already revoked leaves it revoked, with the time of the latest revocation.
| Argument | Type | Required | Limits |
|---|---|---|---|
linkId |
string | yes | 1 to 100 characters: the link id from viewer_activity or pipeline |
{ "linkId": "a91d4e6f-2c38-4b7a-9e05-d7b3f1c8e240" }Returns the link as JSON, with revokedAt set: id, viewerName,
viewerEmail, expiresAt, revokedAt and stage.
get_link
Shows the full URL of an existing viewer link again, for the publisher to copy and resend when the person has lost it. This changes something small: each showing is recorded in the project's history, under the person signed in, and it needs the write permission, because the URL opens the room. A link made before Roomi kept addresses cannot be shown; the reply says to reissue it in the app.
Changes something: the project's history records each showing. Idempotent otherwise: the same link gives the same URL every time.
| Argument | Type | Required | Limits |
|---|---|---|---|
linkId |
string | yes | 1 to 100 characters: the link id from viewer_activity or pipeline |
{ "linkId": "a91d4e6f-2c38-4b7a-9e05-d7b3f1c8e240" }Returns the URL as text, as create_link does. A revoked or expired link is
refused, and so is one made before 1 October 2026, whose reply says to
reissue it in the app: the platform kept only a hash of its key then.
viewer_activity
Shows a project's viewer links and their visits: who opened the room, when, and the seconds spent on each app, page and file. Changes nothing.
Read-only.
| Argument | Type | Required | Limits |
|---|---|---|---|
projectId |
string | yes | 1 to 100 characters |
{ "projectId": "3f2b8c1e-5a47-4d9e-8b21-6c0f9e7a4d53" }Returns links (each as revoke_link returns one) and visits. A visit has
its id, linkId, viewerName, startedAt, endedAt and time: seconds
per thing the viewer looked at, keyed app:<id> or page:<id>, as
Room events explains.
read_feedback
Shows what viewers said back on a project: their next-step answers (meeting, yes, later, colleague, question, no) and their comments. Changes nothing; it does not mark anything read or resolved.
Read-only.
| Argument | Type | Required | Limits |
|---|---|---|---|
projectId |
string | yes | 1 to 100 characters |
{ "projectId": "3f2b8c1e-5a47-4d9e-8b21-6c0f9e7a4d53" }Returns responses and comments. A response has its id, linkId,
viewerName, step (meeting, yes, later, colleague, question or
no), detail (what the viewer added, such as when to get back to them, or
their question) and createdAt. A comment has its id, linkId,
viewerName, appId and screen (where it was pinned), body,
resolvedAt and createdAt.
pipeline
Shows every viewer link across every project with its stage (sent, opened, engaged, yes, later, no). Changes nothing.
Read-only. Takes no arguments.
{}Returns every link in the workspace, each as revoke_link returns one, with
its projectId and projectName. Analytics and the
pipeline says how a stage is decided.
add_page
Adds a page (Markdown or HTML) to a project's room. This changes something: the page is visible to every viewer of the room at once.
Changes something.
| Argument | Type | Required | Limits |
|---|---|---|---|
projectId |
string | yes | 1 to 100 characters |
title |
string | yes | 1 to 80 characters |
kind |
string | yes | markdown or html |
content |
string | yes | the page itself, up to 2,000,000 characters |
{
"projectId": "3f2b8c1e-5a47-4d9e-8b21-6c0f9e7a4d53",
"title": "Why Corner",
"kind": "markdown",
"content": "# Why Corner\n\nCoaches lose an hour a day to…"
}Returns the page's id, title and kind. A page with the same title as one
the room already has replaces it. Pages says how the room shows
each kind.
deploy_static
Publishes a static demo (HTML, CSS, JavaScript, images) that is in this conversation to a project's room, creating the project if its slug is new. Without commit it is a rehearsal and changes nothing: it checks and packs the files and says exactly what would go live. With commit: true it changes something: the files become the live build every viewer of the room sees. Call with commit: true only after showing the rehearsal and hearing a clear yes. Limits: 200 files, 3 MB, every app's path a file among the files (index.html for an app at /), no server code. Call deploy_guide first, and check_bundle until it passes: this refuses with the same problems. A demo is half-deployed without its guided tour (the manifest's story) and pages (the room's Read more): add them, and on a paid plan create_link makes a viewer's link next; on Free, which has no viewer links, offer to list the room on the community with request_listing. A repository on disk, or a demo with a server, is deployed with
pitch deployon the publisher's machine instead.
Changes something with commit: true; without it, it only checks and packs.
| Argument | Type | Required | Limits |
|---|---|---|---|
project |
string | yes | the project's slug: 3 to 40 lower-case letters, digits and hyphens, starting with a letter. An existing slug from list_projects, or a new one to create |
name |
string | yes | 1 to 80 characters: the demo's name, shown in the room |
device |
string | no | the frame the room draws the demo in when there is no manifest: phone, tablet, laptop, screen or none. The default is laptop |
files |
array | yes | 1 to 200 files, each an object with path, content and encoding |
files[].path |
string | yes | 1 to 255 bytes, relative to the demo's root, such as index.html |
files[].content |
string | yes | the file's content |
files[].encoding |
string | no | utf8 (the default) or base64, for an image or any other binary file |
manifest |
object | no | pitch.json for the demo: its apps, its tour (story) and its pages, each page a Markdown or HTML file among files. kind is static and may be left out; output is not used, since the files are the build |
note |
string | no | up to 200 characters, kept with the build |
commit |
boolean | no | false (the default) rehearses; true publishes |
{
"project": "corner-teaser",
"name": "Corner teaser",
"files": [
{ "path": "index.html", "content": "<!doctype html><title>Corner</title><link rel=\"stylesheet\" href=\"style.css\"><h1>Corner</h1>" },
{ "path": "style.css", "content": "h1 { font-family: system-ui; }" },
{ "path": "why.md", "content": "# Why Corner\n\nCoaches lose an hour a day to paperwork." }
],
"manifest": {
"apps": [{ "id": "teaser", "name": "Corner teaser", "path": "/", "device": "phone" }],
"story": [
{ "id": "hello", "title": "Corner, at a glance", "say": "One screen that tells a coach what matters today.", "app": "teaser" }
],
"pages": ["why.md"]
}
}Before anything is sent, the files and the manifest are checked, exactly as
check_bundle checks them, and every problem is returned at once with its
fix: Every check, and its fix. Without
a manifest, the demo becomes a one-app static build: pitch.json with
kind: static, the one app web at /, named name and framed as device.
The manifest's pages are not files of the demo: they are added to the room's
Read more, each titled from its first heading.
Without commit, it returns the plan and changes nothing:
Rehearsal only: nothing has been sent to the platform.
Project: corner-teaser (new; it will be created)
Files: 2, 121 bytes (205 bytes packed)
index.html 91 bytes
style.css 30 bytes
Apps: teaser at / (phone)
Story: 1 step: hello
Pages: why.md as "Why Corner"
Manifest: {"kind":"static","output":".","name":"Corner teaser","apps":[…],"story":[…],"pages":["why.md"]}
Publishing makes this the live build of the room. Call again with commit: true only after the person says yes.With commit: true, it creates the project if the slug is new, uploads the
files, registers the build with its manifest, adds the pages, makes the build
live, and returns the same plan headed Live: build <id> of <slug>. It ends
with the next steps the plan allows, read as pitch deploy --commit reads
them: on a paid plan the room is private, and create_link makes a viewer's
link; on Free, which has none, the room is a draft, and the next step is to
list it on the community with set_community_card and request_listing. It
gives the room's address only once the project is listed, since until then
it opens to nobody. A demo published without a tour or pages is told to add
them next. The deploy that creates a project ends with one more line, a tip
said once and never on a later deploy: for a container demo, a build over 200
files or 3 MB, or work in a repository with Claude Code, the pitch CLI
deploys from the person's own machine, installed with npm install -g @pitch-product/cli and then pitch login.
community_card
Shows a project's community card (its title, one line and cover) and where its listing stands: an unlisted draft, listed (with its public address), blocked by a check (with the field and the reason), taken off by Roomi (with its comment), or waiting for review; any review asked for, and whether one can be. Also what the pre-flight check found when listing was last asked for. Changes nothing.
Read-only.
| Argument | Type | Required | Limits |
|---|---|---|---|
projectId |
string | yes | 1 to 100 characters: the id from list_projects |
{ "projectId": "3f2b8c1e-5a47-4d9e-8b21-6c0f9e7a4d53" }Returns the card and its state, as text:
Title: A portal for a trade supplier
One line: Orders, invoices and deliveries, in one place.
Cover: PNG, 48213 bytes
Listing: listed since 2026-10-07T09:12:00.000Z. Public on the community at https://trade-portal.getroomi.app: anyone can open it, with no link and no account.
withdraw_listing takes it off the community; set_community_card changes the card, which is checked again and stays listed unless a check fails.Blocked, it names each thing a check found, by its field and what it is, such
as "The build ships a program or an installer (downloads/setup.exe)", and
never the list it was checked against. Taken off, it gives Roomi's
comment as you see it beside the card in the app. Either way it says what to
change, and, when a review can be asked for, that appeal_listing asks for
one. With no card yet, it says so, and that set_community_card writes one.
set_community_card
Writes a project's community card: what strangers see on the community, separate from the pitch, so written for someone who does not know the business. Without commit it is a rehearsal and changes nothing: it runs the pre-flight check on the project with these words, which names anything that might identify a real business or client (a domain, a recurring name, an image named like a logo), and says what saving would do. With commit: true it changes something: it saves the card, and the cover if one is given. A listed card is checked again as it now reads: it stays listed, or is blocked if a check fails. A new cover is held back from the community until its own check passes. A cover is either coverFile, an image already in the project's live build (a screenshot the demo ships), copied on the platform; or cover, an image's bytes as base64. Tell the person what the check found and suggest neutral wording; call with commit: true only after they have seen the rehearsal and said yes. It never asks for listing: request_listing does.
Changes something with commit: true; without it, it only reads the card and
runs the check.
| Argument | Type | Required | Limits |
|---|---|---|---|
projectId |
string | yes | 1 to 100 characters |
title |
string | yes | 1 to 60 characters, trimmed |
line |
string | yes | 1 to 140 characters, trimmed |
coverFile |
string | no | an image in the project's live build to make the cover, by its path from the build's root, such as img/screenshot.png: no leading /, no . or .. segments, up to 255 characters. Not with cover |
cover |
object | no | a cover image sent as its bytes, with type and data, when it is not in the build. Not with coverFile. Without either, the cover stays as it is |
cover.type |
string | yes, with cover |
image/png, image/jpeg or image/webp |
cover.data |
string | yes, with cover |
the image's bytes as base64 (a data: URL's prefix is allowed), at most 1 MB once decoded |
commit |
boolean | no | false (the default) rehearses; true saves |
{
"projectId": "3f2b8c1e-5a47-4d9e-8b21-6c0f9e7a4d53",
"title": "A portal for a trade supplier",
"line": "Orders, invoices and deliveries, in one place.",
"coverFile": "img/screenshot.png"
}A coverFile is read by the platform from the project's live build, found by
the build's own record, so the path names only a file inside it: its type is
read from its bytes, never its name, and it must be a PNG, JPEG or WebP image
of at most 1 MB, as an uploaded cover must. A container build is never
unpacked, so it has no file to take. The rehearsal reads it and says what it
is; only commit: true copies it, on the platform, into the card's cover,
stored and served from the card's row like an uploaded one. Nothing is
uploaded.
A cover is checked before anything is sent, as the app checks a file you
choose, and by its bytes too: they must be the image type says. A cover
that is not base64, is over 1 MB, or is not that image is refused with what
is wrong, and the platform hears nothing.
Without commit, it returns what would change and what the pre-flight
check finds in the project with these
words:
Rehearsal only: nothing has been saved. Show the person this, and call again with commit: true only on their clear yes. A yes does not carry over once anything changes.
Title: A portal for a trade supplier
One line: Orders, invoices and deliveries, in one place.
Cover: a new PNG image, 48213 bytes, copied from the live build's img/screenshot.png
Saving writes it as an unlisted draft: nothing is public until request_listing lists it.
What the check found, with these words:
- A real-looking web address: northwind-traders.co.uk (found in index.html)
These are guesses, and none of them stops a listing. Tell the person what each might reveal about a real business or client, and suggest neutral wording for the card (or a change to the demo) that names nobody real. Change nothing without their yes. Roomi may look at a listing these name.With commit: true, it saves the words through the same route as Save the
card in the app, then the cover: a cover's bytes through the same route
as Upload cover, a coverFile through the platform's copy, which stores it
through the same data layer,
and returns the card as it now is. Words already saved are not saved again,
so sending only a cover changes only the cover. A listed card is checked
again as it now reads: it stays listed if the checks pass, and is blocked,
off the community, if one fails; the rehearsal says so first. A new cover on
a listed card is held back, with a placeholder on the community, until its
own check passes. A card waiting for review takes the new words, and those
are what Roomi reads. A listed card can be changed 10 times a day
in a workspace.
request_listing
Lists a project on the community: public at one address, behind its card. Roomi checks the card and the build first; if they pass, it is listed at once. Without commit it is a rehearsal and changes nothing: it runs the checks dry and says "would be listed now", "would be blocked" (and why) or "would wait for review", runs the pre-flight check, and says what listing means: anyone can open the address with no link and no account, 5 people at once, and on Free its analytics are total views only. With commit: true it changes something: the checks run for real, and the project is listed, blocked, or (after Roomi took it off before) sent for review. At most 3 a day for a workspace. Explain what listing means to the person and call with commit: true only on their yes. Needs a saved card (set_community_card) and a live build.
Changes something with commit: true; without it, it only reads and runs the
checks dry.
| Argument | Type | Required | Limits |
|---|---|---|---|
projectId |
string | yes | 1 to 100 characters |
commit |
boolean | no | false (the default) rehearses; true asks to list it, and lists it if the checks pass |
{ "projectId": "3f2b8c1e-5a47-4d9e-8b21-6c0f9e7a4d53" }Without commit, it runs the checks without writing or counting anything,
and says which of three things committing would do; then the card, what
listing means (the same points the app lists under Community listing, and
on Free that listing is how anyone outside the workspace sees the demo), and
what the pre-flight check finds:
Rehearsal only: nothing has been listed. Show the person this, and call again with commit: true only on their clear yes. A yes does not carry over once anything changes.
Project: trade-portal
Title: A portal for a trade supplier
One line: Orders, invoices and deliveries, in one place.
Cover: PNG, 48213 bytes
Listing: not listed. The project is an unlisted draft: only this workspace can open its room (Preview as viewer, in the app).
Would be listed now: the checks pass, so committing makes it public at https://trade-portal.getroomi.app at once.
Roomi's content check of the card's words runs only on the real request, so it can still block what this rehearsal passes.
What listing means, for the person to read before they agree:
- Once listed, the project is public at https://trade-portal.getroomi.app: anyone can open it, with no link and no account.
- Analytics there count visitors and never say who they are; on Free they are total views only.
- 5 people can be in it at once; anyone after that is told it is full.
- Roomi checks the card and the build when listing is asked for, and lists them at once if they pass; a card that fails a check is not listed, and the reason is said beside it.
- Changing a listed card or deploying a new build checks it again: it stays listed unless a check fails. A new cover shows once its own check has passed.
- Roomi may take a listing off the community, with a comment saying why; asking again then waits for review.
- This workspace is on Free, which has no private viewer links: listing is how anyone outside it sees the demo, and what it shows of them is total views.
- withdraw_listing takes it off the community at any time.
What the check found: nothing that looks like a real business."Would be blocked" names what a check found, by its field and what it is, and says to change it first. "Would wait for review" says why: Roomi took the project off the community before, or has taken two or more of the workspace's listings off. The content check of the card's words is not switched on yet, so for now the word list and the build's checks decide, and they decide the same in the rehearsal as in the real request.
With commit: true, it does what List on the community does in the app:
the checks run, and it answers "Listed: the checks passed, and the project is
public at …", "Not listed: a check blocked it." with what the check found, or
"Sent for review: nothing is public until Roomi lists it." What
the pre-flight check warns about never blocks. Without a saved card, or with
nothing live in the project, it is refused and says which. A listed project
has nothing to ask for. A workspace can ask 3 times a day, and one person 5
times.
withdraw_listing
Takes a project off the community, or out of review. Without commit it is a rehearsal and changes nothing: it says what would stop. With commit: true it changes something: a listed project’s public address stops answering at once, for everyone, and listing it again runs the checks again. The card is kept, as an unlisted draft. Call with commit: true only on the person’s yes.
Changes something with commit: true. A project taken off is listed again
only by asking again, which runs the checks again.
| Argument | Type | Required | Limits |
|---|---|---|---|
projectId |
string | yes | 1 to 100 characters |
commit |
boolean | no | false (the default) rehearses; true withdraws |
{ "projectId": "3f2b8c1e-5a47-4d9e-8b21-6c0f9e7a4d53", "commit": true }It does what Unlist does in the app. A project that is neither listed nor waiting for review has nothing to withdraw, and it says so without changing anything.
appeal_listing
Asks Roomi to review a block by its checks, or its decision to take a listing off, in the person's own words. Without commit it is a rehearsal and changes nothing: it says whether a review can be asked for now, and what would be sent. With commit: true it changes something: the review is asked for. One at a time; at most 3 in 30 days for a workspace. If Roomi agrees, this exact card and build are listed; any later change is checked again. Write the message with the person, and call with commit: true only on their yes.
Changes something with commit: true; without it, it only reads the card.
| Argument | Type | Required | Limits |
|---|---|---|---|
projectId |
string | yes | 1 to 100 characters |
message |
string | yes | why the person thinks it is wrong, in their words: 10 to 1,000 characters, trimmed |
commit |
boolean | no | false (the default) rehearses; true asks for the review |
{
"projectId": "3f2b8c1e-5a47-4d9e-8b21-6c0f9e7a4d53",
"message": "setup.exe is an empty placeholder; the demo never offers it for download.",
"commit": true
}It does what Ask for a review does in the app, and returns where the listing now stands:
Asked for. Roomi reads it and answers in a notice (list_notices).
Listing: blocked by Roomi's checks, so not listed. The build ships a program or an installer (downloads/setup.exe). A review was asked for on 2026-10-07T10:04:00.000Z, and Roomi has not decided yet.
Change what the check found with set_community_card, then request_listing asks again.A review can be asked for while the card is blocked, or while it is off the community after Roomi took it off. With one already waiting, it says to wait for its answer; past 3 in 30 days, or with nothing to review, it says so, and changes nothing. The answer arrives as a notice, and beside the card: upheld, the card and build that were reviewed are listed as they were; declined, the card stays off, with Roomi's note.
list_notices
The person's own notices in this workspace, newest first: a listing taken off by Roomi, a project taken down, a review decided. Changes nothing, and marks nothing read.
Changes nothing. It takes no arguments.
- 2026-10-07T11:30:00.000Z (unread): The review of “A portal for a trade supplier” is decided: it is listed again. The file is empty, as you said, so the card is listed as it was.Each notice is its title and then what Roomi wrote: for a listing taken off, the comment; for a review, the note on the decision.
The same notices the app shows under Notices, the last fifty. Notices are in the app only: none is sent by email yet.
list_workspaces
Every workspace the person belongs to: their own (never called a team), and each team, with their role in it (owner or member) and its id. Marks the one this connection works in: list_projects, deploy_static, create_link and the other project tools act there. The team tools here take a workspace id and act in that one. Changes nothing.
Changes nothing. It takes no arguments.
The project tools work in the workspace you were in when you connected Claude. To work in another, switch to it in the app and connect again.
list_members
Everyone in a workspace the person belongs to, with each one's role (owner or member), email and id. Their own workspace has only them. Changes nothing.
Changes nothing.
| Argument | Type | Required | Limits |
|---|---|---|---|
workspace |
string | yes | 1 to 100 characters: a workspace id from list_workspaces |
{ "workspace": "8d1c6e2a-4b7f-4e09-9a31-2f5d7c8b0e64" }list_invitations
A team's invitations still waiting: each email, who invited them, when it expires, and its id. Only the team's owners see them. Changes nothing.
Changes nothing. A member of the team is told that only its owners can.
| Argument | Type | Required | Limits |
|---|---|---|---|
workspace |
string | yes | 1 to 100 characters: a team's id from list_workspaces |
invite_member
Invites one email into a team, as a member. Nothing is emailed: the result is a link for the person to send themselves, which works once, only for someone signed in with that email, for 7 days. A waiting invitation holds one of the plan's seats. Without commit it is a rehearsal and changes nothing: it says what would happen, or why it cannot (someone in the team already, or invited already). With commit: true it changes something: the invitation is made. Only the team's owners can.
Changes something with commit: true. A team at its plan's seats, counting
the invitations waiting, is refused.
| Argument | Type | Required | Limits |
|---|---|---|---|
workspace |
string | yes | 1 to 100 characters: a team's id from list_workspaces |
email |
string | yes | an email address, at most 254 characters |
commit |
boolean | no | false (the default) rehearses; true invites |
{ "workspace": "8d1c6e2a-4b7f-4e09-9a31-2f5d7c8b0e64", "email": "tesh@daedalus.example", "commit": true }Returns the link to send. Only someone signed in with that email, verified as theirs, can use it: the link alone is not the key.
revoke_invitation
Revokes an invitation still waiting, so its link stops working at once. Without commit it is a rehearsal and changes nothing. With commit: true it changes something. Only the team's owners can.
Changes something with commit: true.
| Argument | Type | Required | Limits |
|---|---|---|---|
workspace |
string | yes | 1 to 100 characters: a team's id |
invitation |
string | yes | 1 to 100 characters: an invitation id from list_invitations |
commit |
boolean | no | false (the default) rehearses; true revokes |
set_member_role
Makes someone in a team an owner or a member. Owners also manage the team's billing, plan, people, name, brand and deletion; both roles see viewers and feedback. A team always keeps an owner. Without commit it is a rehearsal and changes nothing. With commit: true it changes something. Only owners can.
Changes something with commit: true. The team's only owner is never made a
member.
| Argument | Type | Required | Limits |
|---|---|---|---|
workspace |
string | yes | 1 to 100 characters: a team's id |
person |
string | yes | 1 to 100 characters: a person's id from list_members |
role |
string | yes | owner or member |
commit |
boolean | no | false (the default) rehearses; true changes the role |
remove_member
Takes someone out of a team: from their next request they reach none of its projects, viewers or feedback. Nothing they wrote is deleted, and they can be invited again. Not yourself: leaving is done in the app. Without commit it is a rehearsal and changes nothing. With commit: true it changes something. Only owners can.
Changes something with commit: true.
| Argument | Type | Required | Limits |
|---|---|---|---|
workspace |
string | yes | 1 to 100 characters: a team's id |
person |
string | yes | 1 to 100 characters: a person's id from list_members |
commit |
boolean | no | false (the default) rehearses; true removes them |
public_address_status
Whether a project's public address (the room its Community listing opens) is turning new visitors away right now, and until when. A sudden rush of new visitors, far beyond a busy room, trips it; it opens again by itself a few minutes later. People already in the room keep their seats, and private viewer links and the members’ preview are never affected. Also lists every time it happened, newest first. Changes nothing.
Changes nothing. It reads the project's room history, the same record the app's Activity tab shows.
| Argument | Type | Required | Limits |
|---|---|---|---|
projectId |
string | yes | 1 to 100 characters: a project id from list_projects |
{ "projectId": "6f2a9c1e-3b8d-4f70-a5e2-9d1c4b7e0a38" }get_brand
Shows the workspace's brand (its name in the room, its accent colour and whether it has a logo) and its room theme, and what the room shows of each now and why: a plan without its own branding shows neither, and the room theme is a team plan's. Also any saved colour that does not read now, which the room draws as its own instead. Changes nothing.
Changes nothing. It takes no arguments.
set_brand
Sets the workspace's brand for its rooms: the name the room calls the publisher, the accent colour, and the logo. Any left out stays as it is. Without commit it is a rehearsal and changes nothing: it checks the accent with the platform's own contrast guard (an accent no viewer could read is refused, with the nearest shade that would pass) and the logo's bytes, and says what would change. With commit: true it changes something. A logo is a PNG, JPEG or WebP image of at most 256 KB, 16 to 2048 pixels a side and no more than 8 times as wide as it is tall, sent as base64; an SVG is refused: convert it to a PNG first. Changing the brand is an owner's to do in a team.
Changes something with commit: true; otherwise a rehearsal.
| Argument | Type | Required | Limits |
|---|---|---|---|
name |
string or null | no | 1 to 80 characters; null uses the workspace's own name |
accent |
string or null | no | #rrggbb; null uses the room's own |
logo |
object | no | A new logo, replacing any there is |
logo.type |
string | yes, with logo |
image/png, image/jpeg or image/webp |
logo.data |
string | yes, with logo |
The image's bytes, base64-encoded: at most 256 KB |
commit |
boolean | no | false (the default) rehearses; true saves |
{ "name": "Northwind", "accent": "#1f6b4a", "commit": true }It does what Save brand and Upload logo do in the app, by the same routes, and an accent is refused by the same check: the rehearsal says each pair that fails in words, and the nearest shade of the colour that would pass. Your brand in the room has the rules.
remove_logo
Removes the brand's logo: rooms show the initial of the name instead. Without commit it is a rehearsal and changes nothing; with commit: true it changes something, and the logo is deleted.
Changes something with commit: true, and the logo cannot be brought back:
upload it again.
| Argument | Type | Required | Limits |
|---|---|---|---|
commit |
boolean | no | false (the default) rehearses; true removes it |
set_room_theme
Sets the workspace's room theme, whole: the tour's colour, the highlight (the ring over the demo), the attention colour, the background, the surface and the text as #rrggbb, the corners (square, soft or round) and the typeface (system, serif or rounded). A token left out is the room's own. Light themes only. Without commit it is a rehearsal and changes nothing: the platform's own contrast guard checks every pair the room would draw, and it says in words what reads and what does not (with the nearest shade that would), and the colours the room would draw. With commit: true it changes something: the theme is saved. It is kept whatever the plan, and shown on a team plan.
Changes something with commit: true; otherwise a rehearsal.
| Argument | Type | Required | Limits |
|---|---|---|---|
tour |
string or null | no | #rrggbb; unset or null follows the accent |
highlight |
string or null | no | #rrggbb; unset or null follows the accent |
attention |
string | no | #rrggbb |
background |
string | no | #rrggbb, light |
surface |
string | no | #rrggbb, light |
text |
string | no | #rrggbb |
radius |
string | no | square, soft or round |
font |
string | no | system, serif or rounded |
commit |
boolean | no | false (the default) rehearses; true saves |
{ "tour": "#6a3fb5", "background": "#fdf8ef", "font": "serif", "commit": false }The tokens and their checks are in Your brand in the room.
reset_room_theme
Removes the room theme: rooms are drawn in the room's own look again (the brand's accent, name and logo are kept). Without commit it is a rehearsal and changes nothing; with commit: true it changes something, and the saved theme is gone.
Changes something with commit: true.
| Argument | Type | Required | Limits |
|---|---|---|---|
commit |
boolean | no | false (the default) rehearses; true removes it |
When a tool fails
A tool that fails returns an error result Claude can read, never a broken connection. What it says:
| Cause | What the tool says |
|---|---|
| The platform does not accept who you are, or you have left the workspace | "The platform refused this connection. Disconnect Roomi in Claude and connect it again." (through the plugin: "The platform refused the token. Run pitch login again.") |
| A plan's limit | "That is past what the plan allows." and the platform's own words, which name the limit and the plan that allows more |
The platform answered 403 |
"That belongs to a workspace this sign-in cannot reach." From a team tool: "Only the team's owners can do that: ask one of them." |
| An id that is not in this workspace, including another workspace's | "Not found in this workspace." |
| A refusal on the merits (a slug taken or not allowed, an upload refused) | "The platform refused it (409)." or "(422)", and the platform's reason |
| The platform did not answer | "The platform API at … did not answer." |
| Any other status | "The platform API answered" and the status |
deploy_static's files or manifest |
every problem, each with its fix, as check_bundle says them |
set_community_card's cover |
what is wrong with it: not base64, over 1 MB, or not the image its type says; for a coverFile, no such file in the live build, not a PNG, JPEG or WebP image, or over 1 MB |
request_listing with no card, or nothing live |
which of the two, and the tool or step that fixes it |
A listing limit (429): 3 requests a day for a workspace or 5 for a person, or 10 changes to a listed card |
"Not now: a limit was reached." and the platform's words, which say which limit and when to try again |
Errors lists every refusal the platform makes.