Quickstart
Scaffold an app, run it against your local Chataway, call its tool from a chat and iterate — in about ten minutes.
At the end of this page you'll have an app running in your own copy of Chataway, marked DEV, with a tool the agent can call and a panel in the dock.
#Before you start
- Chataway for Mac, running and signed in. Download it if you haven't.
- Node.js 20 or newer.
- Developer mode on: in Chataway open Settings → Developer and switch on Developer mode. This writes a local developer token that the CLI uses to talk to your running Chataway. It never leaves your Mac.
#1. Create the app
npx create-chataway-app my-appIt asks four questions — app name, your publisher slug, the app id, a one-line description — and suggests answers for each. (Pass --yes to accept them all.)
? App name (My App)
? Publisher slug (chataway.co → Developer) (your-company) acme
? App id (com.acme.my-app)
? One-line description (My App for Chataway.)
✔ Created My App in my-appThe app id is permanent once you publish: reverse-DNS under a domain you own, like com.acme.my-app. The publisher slug is the one you'll create on chataway.co when you publish — any placeholder is fine for now.
cd my-app
npm installYou get a working app:
my-app/
├── plugin.json # the manifest: what the app is and may do
├── server/index.ts # your remote MCP server — the app's tools
├── web/ # the panel's source (Vite + @chataway/apps/bridge)
│ ├── index.html
│ ├── main.ts
│ └── style.css
├── ui/ # the built panel (generated from web/)
├── assets/
│ ├── icon.png # a placeholder 512×512 icon in your accent colour
│ └── mark.svg # single-colour glyph for the dock
├── vite.config.ts
├── README.md # becomes your store description
├── CHANGELOG.md
└── package.json#2. Look at the manifest
Open plugin.json. The parts that matter now:
{
"remote": {
"mcpUrl": "https://mcp.acme.com/mcp",
"domains": ["mcp.acme.com"]
},
"grants": ["files:receive", "host:media"],
"contributes": {
"tools": [
{
"id": "inspect_images",
"name": "Inspect images",
"description": "Report the size, dimensions and format of one or more images the user provides. …",
"files": {
"images": {
"accept": ["image/png", "image/jpeg", "image/webp"],
"multiple": true,
"prepare": { "op": "resize", "width": 1024, "height": 1024, "fit": "contain", "format": "png" }
}
},
"confirm": { "title": "Send {{count}} image(s) to My App?", "action": "Send" }
}
]
}
}remote.mcpUrlis where your tools will live in production — anhttpsURL you'll deploy to. During development,chataway devoverrides it withchataway.devMcpUrlfrompackage.json(http://localhost:8787/mcp).contributes.toolslists the tools Chataway may offer the agent. Only tools listed here are ever registered, even if your server has more. Thedescriptionhere is what the agent reads.- The example tool already uses a file param with
prepare, and aconfirmcard. Your first tool explains each piece.
#3. Look at the server
server/index.ts is a normal MCP server over Streamable HTTP, built with the official SDK and @chataway/apps/server:
const app = createAppServer({ manifest });
app.tool('inspect_images', {
input: {
images: fileParam({ multiple: true, description: 'The images to inspect' }),
detail: z.enum(['short', 'full']).optional(),
},
annotations: { readOnlyHint: true },
}, async ({ images }) => {
const rows = images.map(f => ({ name: f.name, bytes: f.size, ...imageSize(f.bytes) }));
return {
text: `Inspected ${rows.length} image(s).`,
json: { images: rows },
card: card.notice({ title: `Inspected ${rows.length} image(s)`, body: /* a Markdown list */ '' }),
};
});
await app.listen({ port: 8787 });createAppServer checks your server against the manifest when it starts: it refuses to run if a declared tool has no implementation, or if you implement one the manifest doesn't declare.
#4. Run it in Chataway
npm run dev✔ plugin.json is valid (dev)
[server] [com.acme.my-app] MCP server on http://localhost:8787/mcp — tools: inspect_images
[ui] built in 180ms
✔ Linked My App (com.acme.my-app) into Chataway at http://localhost:2222 [DEV]
ℹ tools are served from http://localhost:8787/mcp (dev override of remote.mcpUrl)
ℹ Open Chataway → Apps → My App. Edits reload automatically. Ctrl-C to unlink.npm run dev runs chataway dev, which:
- validates your folder (store-only rules are warnings here),
- starts your MCP server (
npm run server) and the panel build in watch mode (npm run watch:ui), - links the folder into your running Chataway as a DEV app, using the local developer token,
- re-validates and re-links whenever
plugin.jsonchanges, and streams the App inspector feed.
Press Ctrl+C to stop everything and unlink.
#5. Enable it in a project
In Chataway, open a project and go to Apps. My App is listed with a DEV badge. Switch it on.
Chataway shows the screen your users will see: your icon and name, and the sentences from permissions. Accept it. A new tab with your mark appears in the dock — that's your panel, with the Home and Activity views from the manifest and Chataway's own Settings view next to them.
#6. Call it from a chat
Start a new chat in that project — any provider works — attach an image or two, and ask:
What size are these images?The agent picks inspect_images. Chataway asks whether the files may go to My App (the hand-off consent card, once per project), resizes them, then shows your confirm card: "Send 2 image(s) to My App?". Tap Send. Your notice card appears with the results, and the agent answers.
Open My App → Inspector to see the whole call: arguments, what was sent to your server, the raw response, timing.
#7. Iterate
- Server code — edit
server/index.ts; the server restarts on save. Ask again. - Manifest — edit a tool's
descriptionor add a tool;chataway devre-validates and re-links. New chats see the change. - Panel — edit
web/; it rebuilds intoui/and the panel reloads. For fast UI work,npm run uiopens the panel in your browser against a mock host with a palette and view switcher.
#8. Check it against the store
npm run validateThis builds the panel and runs chataway validate --store: exactly the checks the store runs on upload. When it passes and your server is deployed at remote.mcpUrl, you're ready to publish.
#Troubleshooting
"Chataway is not reachable". Chataway must be running. If it listens on a port other than 2222, pass --port: npx chataway dev --port 2323.
"Developer mode is off" / no developer token. Switch on Settings → Developer → Developer mode. The CLI reads the token from Chataway's data folder; pass --data-dir if you run Chataway with a custom one.
The agent doesn't see the tool. Start a new chat. Then check the Inspector: tools your server lists but the manifest doesn't declare are ignored, and declared tools your server doesn't list are skipped — both are logged.
The panel is blank. The panel runs in a sandbox with a strict Content Security Policy. Anything loaded from a host that isn't in remote.domains is blocked — bundle your assets into ui/. See Panels.