VibeHost
Guides

MCP server

Wire any MCP-compatible client to VibeHost. The MCP server is the path for coding agents (Claude Code, Codex, Cursor, Antigravity, Grok Build) and chat clients (ChatGPT, Claude Desktop, Gemini Spark) that don't have a native CLI tool channel.

VibeHost ships a remote Model Context Protocol server at https://api.vibehost.com/mcp. Any MCP-compatible client can connect and use tools for the whole platform.

Where MCP fits

VibeHost has two integration channels. Which one fits depends on what is driving the work.

  • Coding agents run in your editor or terminal. They have native MCP support and can also shell out to the vibehost CLI when a script gives better control than tool calls. Ten of them have a first-class integration, each with its own guide:
  • Chat clients (ChatGPT, Claude Desktop, Gemini Spark) don't have a CLI channel. They reach VibeHost only through MCP, and setup is one URL. See Chat clients below.

The rest of this page covers the MCP server itself: tool list, OAuth, error codes. Setup steps are not repeated here. Each client's guide is the one place they are written down.

What your agent can do

There is one tool per action, grouped by area below. The Tools → CLI cheatsheet below lists every tool with its read / write / destructive marking. Tools that change publicly-visible internet state carry openWorldHint: true so the client can prompt for confirmation.

Workspaces & apps

list_workspaces, list_apps, create_app, get_app, delete_app.

Deployments

deploy, get_deployment, get_logs, get_screenshot, promote, rollback, request_deployment_download, plus check_blobs_missing and request_upload for getting file bytes to the server before a deploy.

Channels

list_channels, create_channel.

Custom domains

add_custom_domain, list_custom_domains, verify_custom_domain, remove_custom_domain.

Redirect rules

list_redirect_rules, add_redirect_rule, remove_redirect_rule, bulk_import_redirect_rules.

App access

list_app_grants, grant_app_email_access, grant_app_team_access, revoke_app_email_access, revoke_app_team_access, set_app_visibility, set_app_password, clear_app_password, get_app_password_info, create_share_link, list_share_links, revoke_share_link.

Folders

move_app_to_folder, set_folder_visibility, list_folder_grants, grant_folder_email_access, update_folder_email_grant, remove_folder_email_grant, grant_folder_team_access, update_folder_team_grant, remove_folder_team_grant.

Members & invitations

list_workspace_members, remove_workspace_member, invite_to_workspace, list_workspace_invitations, revoke_workspace_invitation, list_teams, list_team_members, remove_team_member, invite_to_team, list_team_invitations, revoke_team_invitation.

Server-side files (no-egress clients)

create_file, edit_file, read_file, list_files, delete_file. They write a deploy's files into a server-side draft tree when your client can't upload blobs directly. See Choosing an upload path.

Choosing an upload path

A deploy needs your files on the server. There are two ways to get them there. The right one depends on whether your client's environment can make outbound HTTPS requests directly to api.vibehost.com, not on whether it has a local filesystem.

Both procedures were run end to end against the live API, and the field names below are the ones those calls returned. Every tool answers { ok: true, data } or { ok: false, error: { code, message } }; the fields named here sit inside data.

Manifest path, for clients that can reach the API

File bytes go straight to api.vibehost.com over short-lived signed URLs, outside the MCP channel. It carries any file type, images and fonts included, and the server keeps one copy of each distinct file. Use it whenever you can reach the API.

  1. create_app(workspace, name) returns id. Every later call takes it as appId. The app starts with visibility: "private".
  2. Hash each file with SHA-256 and build the manifest: one { path, sha256, size } per file.
  3. check_blobs_missing(shas) returns missing, the hashes the server does not hold yet. Blobs are shared across apps, so a file that any earlier deploy uploaded is not listed.
  4. When missing is not empty, request_upload(appId, shas: missing) returns uploads, one { sha, uploadUrl, expiresAt } per hash, together with method: "PUT" and contentType: "application/octet-stream". The URLs expire after five minutes. It refuses an empty shas, so skip this step and the next when nothing is missing.
  5. PUT each file's raw bytes to its uploadUrl with that content type. This is a plain HTTP request, not a tool call. The response is an ordinary HTTP one with the same envelope: 201 and { ok: true, data: { sha256, size, wasNew: true } }, or 200 with wasNew: false when the server already held those bytes. Both mean the blob is there. Bytes that do not hash to the sha in the URL are refused with 400 and VALIDATION_FAILED.
  6. check_blobs_missing(shas) again. missing is now [].
  7. deploy(appId, manifest) returns id, status and url. A manifest entry whose blob was never uploaded is refused with VALIDATION_FAILED.
  8. Read status: it is one of starting, healthy, failed or superseded. A static deploy came back already healthy, with finishedAt set. Only while it is starting, call get_deployment(deploymentId) with the id from step 7 every few seconds. healthy means it is live, failed comes with error, and superseded means a newer deploy to the same channel replaced this one. Stop polling on any of the three.
  9. status: "healthy" is the confirmation. Fetch url as well only when the app is public and has no password: the app from step 1 is private, and an anonymous request to a private app is not answered with the site, which says nothing about the deploy. When you do fetch it, every file other than HTML is served byte for byte. An HTML page can have VibeHost's badge markup added before </body>, as it was on the free workspace used for the run, so compare an HTML page by content rather than by checksum.

Redeploying. Repeat from step 2. The manifest describes the whole release, not a change to the last one: a file left out of it answers 404 afterwards. Only the bytes are incremental. With one file of five changed, missing listed one hash, and that was the only PUT. url does not change, and it can keep serving the previous release for a while after deploy returns healthy: 52 seconds when measured. A first deploy is served at once.

Draft path, for clients with no HTTP egress

Some clients can't make that PUT: a sandbox with no network egress, or a chat client like ChatGPT whose only channel to VibeHost is the tool call itself. They write the files into a server-side draft tree instead, and every step is a tool call.

  1. create_app(workspace, name) returns id, as above.
  2. create_file(appId, path, content) for each file returns { path, sha256, size }. path is POSIX-relative, such as index.html or assets/app.js; an absolute path or a .. segment is refused with VALIDATION_FAILED. Calling it again on the same path overwrites the file.
  3. list_files(appId) returns files, each { path, size, mode, updatedAt }, and read_file(appId, path) returns content, to check what is there. delete_file(appId, path) removes a file, and answers DRAFT_FILE_NOT_FOUND when there is none.
  4. deploy(appId) with no manifest. The server builds the manifest from the draft tree and returns the same id, status and url. With an empty draft tree it answers VALIDATION_FAILED.
  5. Steps 8 and 9 of the manifest path.

Redeploying. edit_file(appId, path, oldString, newString) replaces an exact string, then deploy(appId) again. It answers EDIT_NO_MATCH when oldString is absent and EDIT_NOT_UNIQUE when it occurs more than once, unless you pass replaceAll: true. The draft tree persists between deploys, so nothing else is sent again. That cuts both ways: a file your new build no longer produces stays in the tree, and keeps being deployed, until you delete_file it. The same delay before url serves the new release applies. Writing a draft needs the same deployer grant as deploying.

Text files only. content is a string, stored as its UTF-8 encoding. There is no binary or base64 input, and nothing rejects binary data either. A PNG and a WOFF2 passed as strings were accepted, deployed healthy, and served with content-type: image/png and font/woff2, but with the wrong bytes: whatever string arrives is what gets served. Sent one character per byte, a 379-byte PNG came back as 528 bytes beginning c2 89 50 4e 47 where the file begins 89 50 4e 47. Sent as base64, it came back as the base64 text. No response flags either case, so a site that needs images or fonts has to go through the manifest path.

Size. A draft file cannot exceed 1,048,576 bytes: create_file with more content, or an edit_file that would grow a file past that, answers CONTENT_TOO_LARGE and leaves the file as it was. The request carrying a tool call may be up to 6,356,992 bytes (six times the file cap plus 64 KiB), so JSON escaping, which turns a newline, quote or backslash into two bytes and a control character into six, cannot push a create_file under the file cap over it. edit_file sends two strings, and together they can: if both oldString and newString are near 1 MB, edit a smaller region per call. A request past that is refused before the tool runs with HTTP 413 and PAYLOAD_TOO_LARGE, whose details.limitBytes is the limit. A file too big for the cap has to go through the manifest path.

The decision is network reachability, not filesystem: an agent that has files on disk but no egress to api.vibehost.com still belongs on the draft path.

Endpoints

WhatURL
MCP serverhttps://api.vibehost.com/mcp
OAuth 2.1 authz server metadata (RFC 8414)https://api.vibehost.com/.well-known/oauth-authorization-server
Protected resource metadata (RFC 9728)https://api.vibehost.com/.well-known/oauth-protected-resource/mcp
Dynamic client registration (RFC 7591)https://api.vibehost.com/api/v1/oauth/register

Transport: Streamable HTTP (the MCP spec's recommended transport since 2025-06).

Authentication

OAuth 2.1 authorization-code flow with mandatory PKCE (S256). The first time your agent connects:

  1. Agent calls /.well-known/oauth-protected-resource/mcp to discover the authz server.
  2. Agent registers itself dynamically at /api/v1/oauth/register (no client secret needed for public clients).
  3. Agent kicks off the auth code flow with PKCE.
  4. You approve in browser; agent gets an access token.
  5. Access token is 60 minutes, refresh token is 30 days with single-use rotation and reuse detection.

Token audience is bound to https://api.vibehost.com/mcp via RFC 8707, so a token minted for VibeHost won't be accepted by another resource server.

These have native MCP, run in your editor / terminal, and can also call the vibehost CLI directly when a script gives better control than tool calls. Setup for each one lives in its own guide.

Chat clients (MCP-only)

These don't have a CLI channel, so MCP is the only way to drive VibeHost from them. Setup is one URL each, walked through in each client's guide.

Other MCP-compatible clients

Point Continue, Cline, Roo Code, Devin, Zed, Aider, Amazon Q, VS Code (Copilot Chat), JetBrains AI Assistant, Raycast, Warp, OpenClaw (chat-app gateway), or anything else that speaks MCP at https://api.vibehost.com/mcp. Native custom-scheme redirects (claude-desktop:, cursor:, vscode:, windsurf:, raycast:, gemini:, etc.) are pre-allowlisted, so OAuth completes without extra config.

Permissions

Tool calls run through the same per-app permissions the dashboard uses. The OAuth token represents you; the agent can only do what you can do.

  • viewer grant → read-only tools work
  • deployer grant → also deploy, promote, rollback
  • admin grant → also manage settings + grants

Tools that change public state (deploy, add_custom_domain, set_app_password, etc.) carry openWorldHint: true so well-behaved clients prompt you before calling.

Privacy

The MCP server adds no extra data store. Every tool call:

  • Authenticates against the same user/token records as the dashboard.
  • Audit-logs the same way (operation, actor, resource, outcome).
  • Hashes OAuth refresh tokens at rest.

Tools that reach beyond VibeHost (verify_custom_domain queries public DNS resolvers) declare openWorldHint: true. The invitation tools (invite_to_workspace, invite_to_team) return an invite URL for you to forward; the MCP server does not email it.

The same VibeHost privacy policy applies.

Tools → CLI cheatsheet

Every registered MCP tool, with the vibehost CLI command that does the same job. Useful for "what would this prompt actually do?". The mapping is not 1:1 in either direction: the draft-file tools have no CLI counterpart, and some CLI commands (vibehost env, vibehost channel delete, vibehost app update) have no MCP tool yet.

MCP toolCLI equivalentRead/Write
list_workspacesvibehost workspace lsread
list_appsvibehost app lsread
create_appvibehost app create <name>write
get_appvibehost app inspectread
delete_appvibehost app delete --forcedestructive, open-world
check_blobs_missing(part of vibehost deploy)read
request_upload(part of vibehost deploy)write
deployvibehost deploywrite, open-world
get_deployment(none, vibehost deploy waits for the result)read
get_logsvibehost logsread
get_screenshot(none)read
promotevibehost promote <deploymentId>write, open-world
rollbackvibehost rollbackwrite, open-world
request_deployment_downloadvibehost pullread
list_channelsvibehost channel listread
create_channel(implicit via vibehost deploy --channel <name>)write
create_file(none, the CLI uploads from disk)write
edit_file(none, the CLI uploads from disk)write
read_file(none, the CLI uploads from disk)read
list_files(none, the CLI uploads from disk)read
delete_file(none, the CLI uploads from disk)destructive
add_custom_domainvibehost domain add <hostname>write, open-world
list_custom_domainsvibehost domain listread
verify_custom_domainvibehost domain verify <hostname>write, open-world
remove_custom_domainvibehost domain remove <hostname>destructive, open-world
list_redirect_rulesvibehost redirects listread
add_redirect_rulevibehost redirects add <source> <destination>write, open-world
remove_redirect_rulevibehost redirects remove <ruleId>destructive, open-world
bulk_import_redirect_rulesvibehost redirects upload <file>destructive, open-world
list_app_grantsvibehost app grants lsread
grant_app_email_accessvibehost app grants add-email <email> <role>write
revoke_app_email_accessvibehost app grants remove-email <email>destructive
grant_app_team_accessvibehost app grants add-team <team> <role>write
revoke_app_team_accessvibehost app grants remove-team <team>destructive
set_app_visibilityvibehost app visibility <value>write, open-world
set_app_passwordvibehost app password set <password>write, open-world
clear_app_passwordvibehost app password cleardestructive, open-world
get_app_password_infovibehost app password statusread
create_share_linkvibehost app share-link createwrite, open-world
list_share_linksvibehost app share-link lsread
revoke_share_linkvibehost app share-link revoke <id>destructive, open-world
move_app_to_foldervibehost folder move-app <app> <folder>write, open-world
set_folder_visibilityvibehost folder visibility <folder> <value>write
list_folder_grantsvibehost folder grants ls <folder>read
grant_folder_email_accessvibehost folder grants add-email <folder> <email> <role>write
update_folder_email_grant(none)write
remove_folder_email_grantvibehost folder grants remove-email <folder> <email>destructive
grant_folder_team_accessvibehost folder grants add-team <folder> <team> <role>write
update_folder_team_grant(none)write
remove_folder_team_grantvibehost folder grants remove-team <folder> <team>destructive
list_workspace_membersvibehost workspace membersread
remove_workspace_membervibehost workspace remove <email>destructive
invite_to_workspacevibehost workspace invite <email>write, open-world
list_workspace_invitationsvibehost workspace invitationsread
revoke_workspace_invitationvibehost workspace uninvite <id>destructive
list_teamsvibehost team lsread
list_team_membersvibehost team membersread
remove_team_membervibehost team remove <email>destructive
invite_to_teamvibehost team invite <email>write, open-world
list_team_invitationsvibehost team invitationsread
revoke_team_invitationvibehost team uninvite <idOrEmail>destructive

Example prompts

Once connected, you can talk to your agent in natural language. The agent picks the right tool(s).

What you sayWhat runs
"Deploy ./dist to a new app called my-blog"create_app, request_upload + a PUT per file, then deploy
"What apps do I have?"list_apps
"Tell me about my-site"get_app (full inspect: runtime, visibility, channels, grants)
"Make my-site public"set_app_visibility(visibility: public)
"Add reviewer@acme.com as a viewer on my-site"grant_app_email_access(role: viewer)
"Roll back my-site"rollback (production by default)
"Add www.example.com to my-site"add_custom_domain + prints CNAME instructions
"Verify www.example.com"verify_custom_domain
"Mint a share link for my-site valid 7 days"create_share_link(expiresInDays: 7)
"Show me the latest logs for my-app"get_logs
"Set DATABASE_URL on my-app to ..."No MCP tool yet. The agent runs vibehost env set if it has a shell

For destructive operations (delete_app, remove_custom_domain, revoke_share_link, ...), well-behaved clients prompt you to confirm before calling. The destructiveHint: true annotation is part of the tool definition.

Confirmation prompts

Tools annotated openWorldHint: true change publicly-visible state and should trigger a confirmation in your MCP client. Whether they do depends on the client:

  • Claude Desktop and Claude Code follow your tool permission settings. A tool you set to "always allow" runs without asking.
  • Cursor prompts on first use and remembers your answer per tool.
  • ChatGPT connectors prompt on the first call, and the user can mark them as "trusted".
  • Codex and other programmatic clients depend on the host. Some skip confirmation by design, on the assumption that the agent may act on its own.

If you're scripting against MCP and don't want a human in the loop, use a PAT against the REST API instead. Tools are just convenience wrappers around the same endpoints.

Common errors

ErrorWhat it meansFix
Bearer token requiredToken expired or never auth'dRestart MCP client to re-trigger OAuth
Tool '<name>' not foundOld MCP client schemaUpdate the MCP client
FORBIDDEN on a write toolOAuth user lacks role on the target appAsk an admin to grant deployer (or higher)
VALIDATION_FAILED from deployNo manifest was passed and the app's draft tree is emptyPass a manifest, or write files with create_file first

See also

  • For a programmatic alternative without OAuth, see Personal access tokens. A PAT drives the same REST API over HTTP.

On this page