Vibeos Mcp
FreeMaintainedDrive a vibeOS desktop from your own MCP client (Claude Code, Cursor, Codex).
About
Drive a vibeOS desktop from your own MCP client (Claude Code, Cursor, Codex).
README
Drive a vibeOS desktop from your own MCP client — Claude Code, Cursor, Codex — instead of pasting an API key into the browser. vibeOS supplies the tools and the machine; your agent supplies the model.
claude mcp add vibeos -- npx vibeos-mcp
That is the whole install. The first time your agent needs the desktop it
answers with a link — https://vibeos.sh/app#pair=… — open it, read the
warning, accept, and the desktop is paired: its tools appear in the agent
without a restart. The token behind the link is minted on your machine and
remembered in ~/.vibeos-mcp/token.json for seven days (the desktop remembers
it for the same seven — reloads and closed tabs keep the pairing); the token
rides the link's fragment, so it stays out of the HTTP request — access logs,
proxies, Referer never see it. It is still sent, once, in the pairing hello
over the websocket to whichever relay is selected, so that relay's operator sees
it (vibeos.sh on the default relay; you, on a self-hosted one). npx vibeos-mcp forget
drops it. Starting from the desktop instead — Settings › Capabilities › Pair
— still works: paste the command it shows, which carries --token. Forget this
agent in the desktop ends a pairing at once.
Whoever opens the link hands their desktop to your agent, root included, so
the warning is on the page before anything dials. Clients that do not show a
server's instructions get a single pair_desktop tool that returns the link
until a desktop is paired.
Why a relay exists
An MCP client spawns a subprocess or POSTs to an endpoint. A browser tab can do
neither — it cannot listen, only dial out. So both ends dial the same relay and
it pairs them by token and copies frames, parsing nothing beyond the first frame.
(An unconnected socket still holds a pairing slot and can ping; two sockets that
present the same token pair with each other, so a token is a shared secret,
not a name — it reaches no desktop but the one whose tab holds it.)
The default relay is wss://2yetm9bvy2.execute-api.us-east-1.amazonaws.com/prod
(API Gateway + DynamoDB: pairing is durable, so it does not matter which server
each side lands on), with wss://vibeos.sh/api/mcp/relay as fallback. The
Capabilities pane's command carries --relay <url> when the tab is on a
non-default one.
Security
The token is root on the desktop. The tool set includes edit_file on
system/os.js and vm_exec, so anything holding it can rewrite the OS and run
commands in the VM. It is sent as the first frame over the websocket, never in a URL, because URLs
reach access logs, proxies and Referer headers — the websocket reaches only
the relay operator, who sees it either way.
The relay sees the token and every tool call in plaintext. Assume the operator of the relay you select (vibeos.sh by default) can read what your agent does on your desktop, and pick a self-hosted relay if that is not acceptable.
Frame contract
Between the tab and this package. The relay does not interpret any of it.
| Direction | Frame | Meaning |
|---|---|---|
| both → relay | {"hello":"tab"|"agent","token":"<64 hex>"} |
first frame, pairs the socket |
| agent → tab | {"want":"tools","agent":"<client name>","sampling":bool,"pairing":bool} |
sent on every (re)connect, and again after MCP initialize once the client's name and capabilities are known; sampling says whether the client's model can power apps; pairing that the package minted its token and no desktop has taken the link yet |
| tab → agent | {"ask":N,"ai":{"prompt","images?":[{mime,base64}],"json?","system?"}} |
an app's api.ai call when the tab has no model of its own; answered via MCP sampling (sampling/createMessage) when the client supports it |
| agent → tab | {"ask":N,"result":"…"} or {"ask":N,"error":"…"} |
the model's text, or why not (client cannot sample, 120 s deadline) — ask ids are the tab's, distinct from call ids |
| tab → agent | {"tools":[{name,description,parameters}],"instructions":"…"} |
TOOL_SCHEMAS verbatim, plus the tab's map of the OS (~6 KB): passed to the client as the MCP server's instructions in the initialize result |
| agent → tab | {"id":N,"tool":"name","input":{...}} |
a call |
| tab → agent | {"id":N,"result":...} or {"id":N,"error":"..."} |
its answer; any {mime,base64} or {mimeType,data} object in a result, at any depth, reaches the MCP client as image content and {mime:"text/plain",text} as a text block, each replaced in the JSON by a marker; never a data: url in text |
| relay → either | {"paired":true|false,"instance":"<id>"} |
the relay's answer to the hello: is the other side already there, and which relay function instance this is (tab and agent must land on the same one) |
| agent → relay | {"ping":N} |
every 30 s; a relay that names its instance must answer within 10 s or this side redials |
| relay → agent | {"pong":N,"instance":"<id>"} |
the relay's answer, never forwarded to the tab |
| relay → either | {"bye":4001|4003,"reason":"…"} |
sent just before a normal close by a relay that cannot send custom close codes (API Gateway); means exactly what the close code would |
| relay → either | {"error":"peer not connected","code":4002} |
the other end is gone |
| tab → relay | {"revoke":true} |
byte-exact; the relay closes both ends 4003 and forgets the token |
| tab → relay | {"ping":<ms>} |
every 30 s, answered by the relay the same way |
The tab also sends {"tools":...} unsolicited when it connects. That is not
enough on its own: this package usually pairs after the tab, and reconnects
roughly every 800 s when the serverless function reaches its limit — so it asks
on every connect and the tab must answer want.
A self-hosted desktop (the vibeOS container) runs its own relay: start the
agent with --relay wss://<that host>/… and the pair link carries it as
&relay=. A link with no &relay= means the public default; a container
desktop refuses a link whose relay is not its own, naming the --relay to use,
rather than silently pairing through infrastructure you do not run.
--relay (or VIBEOS_RELAY) takes a comma-separated list: a relay that cannot
be reached at all falls through to the next; one that was open and dropped is
redialed as is. A call whose frame exceeds 128 KB is refused with an error naming
the size (API Gateway closes the sender above that); the tab does the same for
results. An API Gateway {"message":"Internal server error",…} frame (a
throttled lambda) fails in-flight calls loudly rather than vanishing.
Seeing the desktop
read_desktop returns the open windows (top first, with geometry), the dock
and the machine state as structured text; {window} gives one window's body as
text (password and hidden values never), {window, dom:true} its sanitised
outerHTML, and {screen:"image"} the machine's screen (the VM's VGA canvas) as
a JPEG at most 1024 wide and under about 110 KB for the relay, with the text
console's rows alongside. The desktop's own windows are DOM, which the tab
cannot rasterise without a large vendor library, so they come back as text, not
pixels. list_themes, list_files and search_file over a directory or glob
(in a worker, 3 s deadline) complete the picture; the chat log and machine
snapshots under system/ are the person's and are not readable.
initialize waits up to 3.5 s (VIBEOS_INIT_WAIT_MS) for the tab's first tools
frame so the instructions can ride the initialize result — MCP has no second
chance to send them. A tab that pairs later gets them to the next client that
connects; the current one works from the tool descriptions, and stderr says so once.
Failure behaviour
Calls fail; they never hang. An MCP client waiting forever is indistinguishable from one doing slow work, and the user cannot tell the difference.
- Relay drops mid-call (the ~800 s cut): in-flight calls reject with a note that the desktop may still have run the tool. The socket redials in place.
- The tab never answers (its main thread is busy — a catastrophic regex in
search_filemeasured about 50 s): the call fails at a deadline, 120 s by default (VIBEOS_CALL_TIMEOUT_MS), ortimeout_s+ 30 s forvm_exec, with a note that the desktop may still finish it. - No tab paired:
tools/listanswers within 2 s with an empty list — Claude Code gives up on a slow list and shows the server as broken — and sendsnotifications/tools/list_changedthe moment the tab's schemas arrive. Atools/callin the gap errors with the relay instance id, so it can be compared with the one the Capabilities pane shows. - The tab's socket dies with a call in flight: the relay tells this side
4002at once (not on the next send), so the call fails with the same note. - A second
vibeos-mcpon the same token (close code 4001): final for the first one. Stop one, or pair a new token. - The pairing outlives the tab. The token is remembered in that browser for
seven days:
reload_os, a hand reload or a closed tab only open a gap — calls in it fail withpeer not connected— and a tab back on the same token answers again; this package reconnects on its own. A second tab of the same browser offers Take over here rather than dialing over the first, and the first is told. Forget this agent in the pane, or the expiry, ends it: a connected package reads revoked (4003); one that dials afterwards finds no tab. - Revoked in Settings (close code 4003): final. No redial; every later call
and
tools/listsay the desktop revoked the token.
Development
node e2e.mjs # real relay, fake tab, this package over real MCP stdio
Install Vibeos Mcp in Claude Desktop, Claude Code & Cursor
unyly install vibeos-mcpInstalls into Claude Desktop, Claude Code, Cursor & VS Code — handles npx, uvx and build-from-source repos for you.
First time? Get the CLI: curl -fsSL https://unyly.org/install | sh
Or configure manually
Run in your terminal:
claude mcp add vibeos-mcp -- npx -y vibeos-mcpStep-by-step: how to install Vibeos Mcp
FAQ
Is Vibeos Mcp MCP free?
Yes, Vibeos Mcp MCP is free — one-click install via Unyly at no cost.
Does Vibeos Mcp need an API key?
No, Vibeos Mcp runs without API keys or environment variables.
Is Vibeos Mcp hosted or self-hosted?
Self-hosted: the server runs locally on your machine via the install command above.
How do I install Vibeos Mcp in Claude Desktop, Claude Code or Cursor?
Open Vibeos Mcp on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.
Related MCPs
GitHub
PRs, issues, code search, CI status
by GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
by mcpdotdirectAmap Maps Mcp Server
MCP server for using the AMap Maps API
by duxiaohuiSupabase
Database, auth and storage
by SupabaseEverything
Reference / test server with prompts, resources, and tools.
Git
Tools to read, search, and manipulate Git repositories.
Sequential Thinking
Dynamic and reflective problem-solving through thought sequences.
Time
Time and timezone conversion capabilities.
Compare Vibeos Mcp with
Not sure what to pick?
Find your stack in 60 seconds
Author?
Embed badge for your README
Browse similar
All development MCPs
