Deploying from Claude
The connector for Claude in a chat, and the Claude Code plugin.
On this page
- The connector
- Add the connector
- What you allow
- Disconnect
- What Claude learns when it connects
- When Claude suggests the CLI
- The tools
- Deploying from chat
- Every check, and its fix
- Listing on the community
- Things to ask
- The Claude Code plugin
- What to say
- What the skill does
- Rules it keeps
- How it signs in
- What Claude will not do
If Claude built the demo, Claude can deploy it, send it and tell you what viewers made of it. Most people start with the connector: add Roomi to Claude, on claude.ai or in the Claude app, say "deploy it to roomi" in a chat, and Claude does the rest, with nothing to install. The second way in is the Claude Code plugin, which drives the pitch CLI in your repository, for a demo with a server, a build bigger than a chat carries, or work in the repository itself. Both rehearse a deploy first, and publish only on your yes.
| Connector | Claude Code plugin | |
|---|---|---|
| Where | The Claude app and claude.ai, in a chat | Claude Code, in the demo's repository |
| Deploys | A static demo held in the conversation, with its tour and pages, through deploy_static: at most 200 files and 3 MB |
Any demo on disk, static or container, through pitch deploy: a static build of at most 5,000 files, 32 MB per file and 256 MiB unpacked |
| Needs installing | Nothing: it is added in Claude's settings | The pitch CLI, npm install -g @pitch-product/cli, with Node 24 |
| Learns the rules from | The connector itself: its instructions, deploy_guide and check_bundle |
PITCH.md, which pitch init writes, and the skill |
| Signs in | With OAuth, in the browser | The connection with OAuth, in the browser; pitch with the token pitch login stored |
| Links, activity, feedback | Yes | Yes |
| Listing on the community | Yes | Yes |
When a demo needs the CLI, the connector says so, and Claude offers to install it for you: When Claude suggests the CLI.
The connector
The connector is for the Claude app, on desktop and mobile, and claude.ai: anywhere you talk to Claude, with nothing to install. It gives Claude the platform's operations as tools, and it acts as you, on the one workspace you approve.
Add the connector
In Claude, open Settings, then Connectors, and choose Add custom connector. On a Team or Enterprise plan, an owner adds it once under Organization settings, then each person connects.
Name it Roomi, and paste the address:
https://mcp.getroomi.com/mcpLeave the advanced settings empty.
Choose Connect. Claude sends you to Roomi: sign in, check what it may do, and choose Allow.
The address is also in the app, under Settings, Connect Claude.
What you allow
The page Roomi shows before you choose Allow lists what Claude is asking for:
| Permission | What it lets Claude do |
|---|---|
pitch:read |
See your projects, their builds, viewer links, visits and feedback, and their community cards, and your rooms' brand and theme |
pitch:write |
Deploy demos, create and revoke viewer links, add pages to rooms, write community cards and ask for listings, and set your rooms' brand, logo and theme |
offline_access |
Stay connected until you disconnect it |
A connection allowed only to read is refused every tool that changes something, with This connection may only read. Reconnect and allow changes.
The page also names the app asking. If it says the app is not Claude, allow it only if you set it up yourself.
Disconnect
Remove the connector in Claude. Links you made through it stay as they are; revoke any you want closed from the app's Links tab.
What Claude learns when it connects
Claude in chat never sees the PITCH.md that pitch init writes into a repository, so the connector teaches it the rules itself, before the first deploy:
- Its instructions. When Claude connects, the connector tells it, in a few lines, what it deploys and what the files must be: a static demo only; every app's path a file among the files sent, so
index.htmlat the root; what a browser runs, not source; no hidden files,node_modulesor keys; the size limits; mock data only; that a guided tour and pages are what make a demo a pitch; to rehearse first and publish only on your yes; and, on Free, which has no viewer links, to offer to list a live demo on the community. deploy_guide, the whole guide: the build contract, the manifest, every field of it, how to write the tour, pages, and what comes after publishing. It is drawn from the same sources asPITCH.mdand these docs, with the connector's tools wherePITCH.mdhaspitchcommands. The instructions tell Claude to read it before preparing a deploy.check_bundle, which checks a demo exactly asdeploy_staticwould and sends nothing. It names every problem at once, each with its fix and the docs entry that explains it, so Claude fixes them all before it rehearses. Every check, and its fix lists them.
When Claude suggests the CLI
The connector deploys a static demo held in the chat. For some demos the pitch CLI, on your own machine, is the better route, and the connector tells Claude when:
- A container demo, one with a server. Only the CLI builds and runs one, and it rehearses every step of the tour on that running copy first.
- A build over the connector's limits: more than 200 files or 3 MB. The CLI takes at most 5,000 files, 32 MB per file and 256 MiB unpacked.
- Working in the repository with Claude Code. The CLI builds the demo from its folder, scans it with gitleaks and rehearses it on your machine.
- Links and pages from a terminal: making a viewer's link, copying it again, or adding a page, without opening a chat.
It says so where it matters: in deploy_guide, which Claude reads before preparing a deploy; in check_bundle's and deploy_static's refusal of a container demo or a build that is too big; and once, as a one-line tip, after the deploy that creates a project. A demo that fits the connector is deployed from the chat with no word about the CLI.
Installing it takes two commands, and Node 24 or later:
npm install -g @pitch-product/cli
pitch loginIn Claude Code, which has a terminal, Claude offers to run the install for you, and runs it only on your yes. pitch login opens your browser for you to approve, so you run it yourself: Claude never signs in for you. On claude.ai or in the Claude app, Claude gives you the two commands to paste into a terminal. Install the CLI has the rest.
Once it is installed, the CLI deploys, and the connector keeps working on the same project: links, activity, feedback and the community, from the chat.
The tools
The same sixteen tools are in the plugin's connection and the connector. MCP tools has every argument.
| Tool | What it does | Changes anything |
|---|---|---|
deploy_guide |
Returns the guide to building a demo the platform will take, telling its tour and adding its pages | No |
check_bundle |
Checks a static demo exactly as deploy_static would, and names every problem with its fix. It sends nothing |
No |
list_projects |
Lists your projects, with each room's address | No |
deploy_status |
Shows a project's live build and its five most recent builds | No |
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 | No |
read_feedback |
Shows viewers' answers to the next step, and their comments. It marks nothing read or resolved | No |
pipeline |
Shows every viewer link across every project, with its stage | No |
create_link |
Makes a viewer link for one named person, and returns its address this once | Yes |
revoke_link |
Revokes a viewer link. It stops opening the room at once, and cannot be undone | Yes |
get_link |
Shows a viewer link's address again, when the viewer has lost it. Each showing is recorded | Yes |
add_page |
Adds a Markdown or HTML page to a project's room, visible to every viewer at once | Yes |
deploy_static |
Rehearses a static demo held in the conversation; with commit: true, publishes it |
Yes, with commit: true |
community_card |
Shows a project's community card, and whether it is a draft, listed, blocked by a check, taken off or waiting for review | No |
set_community_card |
Rehearses the card's words and cover, with the pre-flight check's warnings; with commit: true, saves them |
Yes, with commit: true |
request_listing |
Rehearses a listing, saying whether it would be listed now and what it means; with commit: true, lists it if the checks pass |
Yes, with commit: true |
withdraw_listing |
Rehearses taking a project off the community; with commit: true, does it |
Yes, with commit: true |
appeal_listing |
Rehearses asking Roomi to review a block or a listing it took off; with commit: true, asks |
Yes, with commit: true |
list_notices |
Shows your notices: a listing taken off, a project taken down, a review decided | No |
Each tool tells Claude whether it changes something, so Claude can ask you before it uses one that does. deploy_static publishes only when called with commit: true, and its description tells Claude to do that only after you have seen the rehearsal and said yes.
Deploying from chat
deploy_static is for a static demo that exists only in the conversation: a page or a small app Claude wrote in chat. Say "deploy it to roomi", or be more particular:
Deploy this page to Roomi as a new project called corner-demo, with a tour.Claude reads deploy_guide, writes the files and drafts the manifest: the demo's apps, the tour (story) and any pages, and shows you the tour. Then it calls check_bundle with exactly what it will deploy, fixes whatever that names, and calls deploy_static without commit, which checks and packs the files and changes nothing:
{
"project": "corner-demo",
"name": "Corner",
"files": [
{ "path": "index.html", "content": "<!doctype html>..." },
{ "path": "till/index.html", "content": "<!doctype html>..." },
{ "path": "why-corner.md", "content": "# Why Corner\n\nCoaches lose an hour a day..." }
],
"manifest": {
"apps": [{ "id": "shop", "name": "Corner", "path": "/", "device": "phone" }],
"story": [
{ "id": "till", "title": "The till", "say": "Every sale lands in the books at once.", "app": "shop", "path": "/till/" }
],
"pages": ["why-corner.md"]
}
}Rehearsal only: nothing has been sent to the platform.
Project: corner-demo (new; it will be created)
Files: 2, 36 bytes (130 bytes packed)
index.html 18 bytes
till/index.html 18 bytes
Apps: shop at / (phone)
Story: 1 step: till at /till/
Pages: why-corner.md as "Why Corner"
Manifest: {"kind":"static","output":".","name":"Corner","apps":[…],"story":[…],"pages":["why-corner.md"]}
Publishing makes this the live build of the room. Call again with commit: true only after the person says yes.It shows you that, and calls again with "commit": true only on your yes. Publishing creates the project if the name is new, or replaces the live build of the one you already have, and adds the pages to the room before the build goes live. It ends with what your plan allows next, as pitch deploy --commit does: on a paid plan the room is private, and create_link makes a viewer's link; on Free, which has no viewer links, the room is a draft only your workspace opens, and Claude offers to list it on the community. If the demo went out without a tour or pages, it says to add them next. The deploy that creates a project also ends with a one-line tip on when the CLI is the better route, and how to install it; later deploys do not repeat it.
What it takes:
| Argument | What it is |
|---|---|
project |
The project's address: lower-case letters, digits and hyphens, 3 to 40 characters, starting with a letter. An existing project's, or a new one. |
name |
The demo's name, shown in the room. |
device |
The frame the room draws when there is no manifest: phone, tablet, laptop, screen or none. The default is laptop. |
files |
Each file's path from the root and its content, as text or base64. |
manifest |
Optional. pitch.json for the demo, as an object: its apps, its tour (story) and its pages. kind is static and can be left out; there is no output, since the files are the build. Without it, the demo is one app, web, at /. |
note |
A note kept with the build. Optional. |
commit |
false, the default, rehearses. true publishes. |
check_bundle takes the same arguments, less commit.
Every check, and its fix
check_bundle and deploy_static make the same checks, and say the same words: every problem at once, each with where it is, what to do about it, and the docs entry that explains it. Nothing is sent while any remains.
| Problem | The fix |
|---|---|
No index.html at the root, with no manifest |
Add index.html: without a manifest the demo is one app at /, and the room opens it there |
An app whose path is not a file among the files |
Add the page the app starts on: the path itself, or the index.html inside it (dashboard/index.html for an app at /dashboard/) |
A step whose path is not a file among the files |
Add the page, or correct the step's path. A static build serves only the files it has |
A page in pages that is not among the files |
Add it to files, or take it out of pages |
| A field of the manifest that is wrong, or one the platform does not know | As the pitch.json reference says for that field. A misspelt field, such as stroy, comes with the name it probably meant |
kind is container |
A demo with a server is deployed from your machine with pitch deploy (Container demos). The refusal says how to install it: npm install -g @pitch-product/cli, then pitch login |
| More than 200 files, or more than 3 MB together | Send fewer, or smaller images; a demo this size is deployed with pitch deploy, and the refusal says how to install it, as above |
A hidden file, such as .env, or anything in node_modules |
Leave it out: send only what the browser loads |
A path that is absolute, climbs out with .., has an empty or . segment, a backslash, or is longer than 255 bytes (or its file name longer than 100) |
Give its path from the root, with forward slashes, such as assets/app.js |
| The same path twice | Send each file once |
base64 content that is not base64 |
Encode the file's bytes as base64, or send text as utf8 |
| A file that looks like it holds a live credential: a private key, or an AWS, Stripe, GitHub, Slack, Anthropic, OpenAI or Google key | Take it out: a demo runs on mock data |
Two things are said without stopping anything: source files, such as src/App.tsx or package.json, which the room would serve as they are and never build; and a demo with no tour.
A demo on your disk, or anything with a server, goes through pitch deploy instead, which builds it, runs it and scans it with gitleaks first. deploy_static does none of that. When Claude suggests the CLI says how Claude hands the deploy over.
Listing on the community
On Free there are no viewer links, so a deployed demo is a draft only your workspace opens until it is listed on the community: public at one address, behind a card strangers see, as soon as the card and the build pass Roomi's checks. Claude can do every step of that from the conversation, the same steps as Community listing in the app, through the same routes. Ask:
List the supplier portal on the community.- The card. Claude drafts a title (up to 60 characters) and one line (up to 140) for someone who does not know the business, and a cover: a screenshot already in the demo's live build, which the platform copies itself, or an image you give it. Either way it is a PNG, JPEG or WebP image of at most 1 MB, its type read from its bytes.
set_community_cardrehearses it first and runs the pre-flight check on the project with those words. Whatever it finds that might identify a real business or client, such as a real-looking web address or a name that keeps coming up, Claude tells you, with neutral wording to use instead. The check is a guess and never stops you; the card is saved, as an unlisted draft, only on your yes. - What listing means.
request_listingrehearses next. It runs the checks without writing anything and says whether the project would be listed now, would be blocked (and why), or would wait for review, and Claude tells you what it says before you agree: anyone can open the public address, with no link and no account; five people can be in it at once; on Free, its analytics are total views only; and changing a listed card or deploying a new build checks it again, and it stays listed unless a check fails. - The checks. On your yes, the checks run for real. If they pass, the project is listed at once, and
community_cardgives its address. If one fails, it is not listed, andcommunity_cardsays which part and why; Claude can change the card and ask again, or, if you think the check is wrong, ask for a review withappeal_listing. If Roomi takes a listing off later, its comment is beside the card and in your notices (list_notices).
withdraw_listing takes a listed project off the community at once, or a waiting one out of review; it rehearses first too. The card is kept as a draft.
Things to ask
Once a demo is live, the tools that change nothing answer questions:
Who opened the Corner demo this week, and what did they say?
Which viewers are still at "sent"?
Make a viewer link for Priya.
Revoke Tom's link.
Add this briefing to the Corner room as a page.
Is the Corner demo listed yet?The Claude Code plugin
The plugin is the second way in, for Claude Code working in the demo's repository: it drives the pitch CLI there, so it deploys anything the CLI does, a container demo or a build bigger than a chat carries included. It brings three things to Claude Code: the deploy-to-pitch-product skill, which turns "deploy it to roomi" into a rehearsed deploy; the list-on-pitch-product-community skill, which drafts the project's Community card from its story and lists it on your yes (Listing on the Community); and a connection to Roomi with the same tools the connector has.
What to say
In the demo's repository, any of these starts the skill:
deploy it to roomi
put this on pitch product
ship this demo to roomi
write a tour for this demoYou can name a folder, if the demo is not the repository you are in.
What the skill does
- Finds the project. The folder you named, or the repository you are in. If it holds several likely projects, it lists them and asks which one.
- Checks the CLI. If
pitchis missing, it offers to install it withnpm install -g @pitch-product/cli, and runs that only on your yes. If a command says you are not signed in, it asks you to runpitch loginyourself, and stops. It never signs in for you. - Writes
pitch.jsonwithpitch init, if there is none. Whenpitch initcannot tell something, such as which device an app is for, it stops and names the flag that answers it; the skill asks you, and runs it again with your answer. It never passes--forceunless you asked to replace your file. - Proposes the one-origin change, if your apps each run on their own port. A room loads one origin, so they have to be served by one server first. The skill shows the change as a diff, and applies it only on your yes.
- Drafts the tour, if there is no
story, by reading the demo's routes, seed data and screens, following Writing the tour. It shows you thestoryblock and adds it on your yes. - Validates with
pitch validate. Each problem names the docs entry that explains it; the skill reads that entry before proposing a fix. - Rehearses with
pitch deploy, and shows you the rehearsal's own output, not a summary: each check, each tour stepokorFAIL, and what would go live. - Asks you one question: whether to publish. Anything but a clear yes is a no.
- Publishes with
pitch deploy --commit, and shows the room's address. - Offers a link, and asks for whom. It makes it with
create_link, orpitch linkif the connection is not there, and gives you the address in that reply, the one time it is shown. - Offers the Community. On Free, where there are no viewer links, it offers to write a Community card and list the project, and on a paid plan it offers it beside the link. On a yes, the
list-on-pitch-product-communityskill drafts the card, rehearses it withpitch community card, and saves it and asks to list it only on your yes.
If pitch deploy --commit fails, the skill shows its output and stops: it does not run it again. It checks the build with pitch validate and against the static limits first.
Rules it keeps
- A yes to publish counts only for the rehearsal it answers. If anything changes afterwards, it rehearses again and asks again.
- It changes no file of yours without asking, except what
pitch initwrites:pitch.json,pitch.README.mdandPITCH.md, and one line pointing atPITCH.mdat the end ofAGENTS.mdandCLAUDE.md. - Adding a page, making a link and revoking one each need your go-ahead in the conversation.
- It never prints, repeats or stores a token, and shows a link's address only in the reply that hands it to you.
How it signs in
The plugin's connection to Roomi signs in as the connector does, with OAuth, in your browser, the first time Claude Code connects (/mcp shows it). No pitch token leaves your machine for it. The skill's pitch commands use the token pitch login keeps in the macOS Keychain. Only against a platform running on your own machine does the connection use that token too, which Claude Code asks pitch auth header for each time it connects, so it never sits in a configuration file.
What Claude will not do
The skill's rules and the tools' own descriptions hold Claude to these:
- Publish without your yes to the rehearsal it has shown you.
- Save a community card, ask for a listing or withdraw one without your yes to its rehearsal.
- Sign in for you.
pitch loginand the connector's sign-in both happen in your browser. - Install the CLI without your yes. Where it has no terminal, it gives you the commands instead.
- Change your code except as a diff you approved.
- Repeat a link's address after the reply that hands it to you. If it is lost,
get_linkshows it again, and the showing is recorded.