Reachpad Mcp

io.github.Reachpad由 GitHub 账号 Reachpad 发布v0.4.2更新于 Oct 24, 2026

Coding agents build full-stack apps in persistent workspaces and share them by link.

已验证Streamable HTTP可网页运行云端与基础设施开发者工具安全与监控

概览

AI 生成的概览

Reachpad 的 MCP 服务器让编码智能体在持久化云端工作区中构建并发布全栈应用,通过链接分享,无需单独部署。

可以做什么
创建、读取、更新、发布和移入回收站应用,回滚到旧版本,或更改访问范围(所有者、组织内、带密码和有效期的公开链接)。按邮箱分享应用、列出和撤销分享,并以图片形式预览线上链接当前展示的内容。在文件夹中浏览和整理应用:列出、树状查看、新建、移动,以及删除空文件夹。对函数应用自带的数据库执行单条 SQL 语句读取或修正数据,并读取线上函数应用的近期日志。按名称管理组织密钥:列出、设置(轮换到绑定的版本)和移除。查看当前登录的账号与组织,并按名称、slug 或页面文字搜索应用。
适用场景
适合希望智能体直接产出可用的网页应用并给出链接、而不只是代码的场景;也适合需要对应用做版本管理、访问控制、分享、文件夹整理,或通过 SQL 做小范围数据修正时使用。团队把应用密钥集中存放在组织密钥库而非文件里时同样适用。
运行要求
需要 Reachpad 账号,以及从 reachpad.dev 获取的每用户凭据,通过环境变量 REACHPAD_IDENTITY_CREDENTIAL 提供。可作为远程端点运行,也可作为本地 npm 包(npx @reachpad/mcp)以 stdio 或 streamable HTTP 运行;REACHPAD_ENDPOINT 指定主机,默认 m1.reachpad.dev。需要能访问 Reachpad 主机的网络。
安装前请注意
该凭据对应一个账号并代表其行事,应视为机密。发布会立即生成线上链接,访问范围默认是组织内任何已登录用户;公开链接则任何拿到网址的人都能打开。设置密钥会替换其值并轮换到所有绑定它的已发布版本,移除密钥会从这些版本中删除。查询工具可以修改数据行,移入回收站会立即让链接失效。端口链接并非私密网址。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Reachpad Mcp,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Web executable,通过 Streamable HTTP。 远程服务在工作区中配置后即可从网页运行时运行。

交给智能体安装

把这段提示词粘贴到 Claude Code、Codex、Cursor 等智能体,或 SourceWeft 对话中。智能体会读取该服务器的安装指南,先向你展示发布者、端点和登录方式,经你同意后再把它添加到你的客户端。密钥不会写进任何命令。

阅读 https://sourceweft.com/mcp/io.github.Reachpad%2Freachpad-mcp/install.md,并按其中说明把这个 MCP 服务器接入我的客户端。修改任何配置之前,先告诉我它的发布者、端点和登录方式,等我确认。不要把 API 密钥写进命令或我会提交的文件;问我密钥应从哪里获取。

查看智能体所依据的安装指南

其他 MCP 客户端

把它添加到你客户端的 mcpServers 配置中。

{
  "mcpServers": {
    "reachpad": {
      "type": "http",
      "url": "https://reachpad.dev/mcp"
    }
  }
}

README

@reachpad/mcp

reachpad lets Claude, Codex and other coding agents build full-stack apps in persistent workspaces and share them by link, without a separate deployment. A workspace is a cloud development computer an agent operates itself: a repo, a filesystem, installed dependencies and build state that all survive between calls, not an ephemeral sandbox that forgets. Processes are the exception; see below.

This is the MCP server. It lets Claude, ChatGPT, Cursor, OpenCode or your own agent create a workspace, run commands in it, fork it, and come back to it later, without a developer keeping a laptop open for them.

  • It persists. Pause it and the disk is sealed; the next call boots from that seal with the files, installs and git state intact, rather than rebuilding. Processes are the exception: a start is always a cold boot.
  • It forks. Twenty attempts from one prepared state cost a delta each, not twenty rebuilds — because the workspace is a snapshot chain, not a machine.
  • It can keep secrets out of the workspace. A brokered credential is called at the boundary on your behalf and its value never enters the workspace, the log or the store. A credential written into the workspace instead is readable there, by design.
  • It is agent-agnostic. The REST API is canonical; this server, the SDK and the CLI are translations of it. Bring your own agent.

The API is the product boundary, not a web UI: reachpad.dev.

Install

sh
npx -y @reachpad/mcp          # stdio, for a local client

In Claude Code:

sh
claude mcp add reachpad -e REACHPAD_IDENTITY_CREDENTIAL=… -- npx -y @reachpad/mcp

Two transports, one implementation

sh
npx @reachpad/mcp                                  # stdioREACHPAD_MCP_HTTP_PORT=8722 npx @reachpad/mcp      # streamable http

stdio is what a local client talks to; Streamable HTTP is what a remote connector talks to. Same tools, same behaviour — added rather than forked, because two implementations of one surface is how they drift.

The HTTP side answers 405 to GET: every tool here is request/response, and the spec permits declining the server-initiated stream rather than holding a connection open for traffic that never comes. There are no sessions — nothing is held across calls that a restart could not rebuild. And it carries no credential of its own: whatever authorizes the HTTP request is what authorizes reachpad.

It is authenticated by default. Set REACHPAD_MCP_HTTP_TOKEN and that is the bearer token; set nothing and one is generated for the run and printed on stderr, because a port that bridges to your account with run_command behind it should not be open to every other process on the machine:

reachpad mcp: streamable http on http://127.0.0.1:8722reachpad mcp: no REACHPAD_MCP_HTTP_TOKEN was set, so this one was generated for this run:
    Authorization: Bearer 3Qk…
reachpad mcp: it changes every restart. Set REACHPAD_MCP_HTTP_TOKEN to pin it, orreachpad mcp: REACHPAD_MCP_HTTP_NO_AUTH=1 to serve with no authentication at all.

Serving with no authentication is still available and is now something you say out loud: REACHPAD_MCP_HTTP_NO_AUTH=1.

Configure

variablemeaning
REACHPAD_ENDPOINTyour reachpad host. Plaintext http:// to anything but loopback is refused before a socket opens.
REACHPAD_IDENTITY_CREDENTIALyour per-user credential. It names one account and can act for no other — the server takes the identity from the credential's own record, never from the request.
REACHPAD_API_KEYoptional, per-workspace scoped and revocable. When set, run_command uses it and needs no identity exchange.
REACHPAD_MCP_HTTP_PORTserve HTTP instead of stdio.
REACHPAD_MCP_HTTP_HOSTdefault 127.0.0.1. This process bridges to a control plane with your credentials, so binding it to the world is a decision made on purpose, behind a proxy that terminates TLS.
REACHPAD_MCP_HTTP_TOKENbearer token, compared in constant time. Absent, one is generated for the run and printed on stderr — the endpoint is never unauthenticated by omission.
REACHPAD_MCP_HTTP_NO_AUTH1 serves with no authentication at all: every caller that can reach the port is authorized. An affirmative choice, not a default.
REACHPAD_MCP_ALLOWED_ORIGINScomma-separated. A request carrying an unlisted Origin is refused — a browser cannot forge it, which closes DNS rebinding. No Origin at all is a non-browser client and is allowed.

If several credentials are set, the narrowest wins, and a refused credential is never retried under a broader one — falling back would be privilege escalation nobody chose to perform.

Tools

toolwhat it does
get_credit_balance()remaining compute credits. One credit runs one standard workspace for one minute.
create_workspace(repo?, ref?, name?)a new workspace, optionally with a repository cloned into $HOME/work. Reachpad generates its display name when omitted.
list_workspaces()your workspaces and how many forks each has
get_workspace(workspace)what it boots from: its head snapshot, its log position and its fork tree
run_command(workspace, argv, cwd?, env?, timeout_ms?)one command, its exit code and its output. A paused workspace resumes to serve it.
checkpoint_workspace(workspace, name?)fork from the last sealed snapshot; the original is untouched
expose_port(workspace, port, check?)open a port to the web and get the link that reaches it. Idempotent per port.
list_ports(workspace)the ports this workspace has open, oldest first, with their links
revoke_port(workspace, port)close one port. Re-opening it later mints a different link.
delete_workspace(workspace)archive it and free the plan slot. Nothing is deleted — snapshots and history survive.

Renamed in 0.4.0. Six tools used to be spelled *_environment and took an environment argument, while the CLI, the manual and the dashboard all said workspace. There was only ever one object; now there is only one word for it. The old names and the old argument still work and are not advertised, so nothing that already calls them breaks — but write new calls against the workspace spelling.

Not here, deliberately: starting an agent inside the workspace. It is in reachpad's roadmap and not in the fleet yet — a tool that always fails costs a model a turn and teaches it to distrust the rest, so this server advertises nothing it cannot serve.

What a port link is, and is not

Worth reading before you hand one to somebody, because none of it is discoverable from the URL:

  • Anyone signed in to Reachpad who has the link can open it. It is not a private URL and not a secure one. Treat it like a preview deployment.
  • The link is an address, not a copy. Restart the app on the same port and the same link serves the new version.
  • A running process does not survive a pause. The workspace cold-boots with its files intact and nothing running, so after a pause the link answers with an error until something is listening on that port again. A visitor's request wakes a paused workspace; it does not restart anything inside it.
  • expose_port dials the port afterwards and tells you if nothing answered, because a link to a port nothing is serving looks exactly like a link that works. Pass check: false to skip it — the dial resumes a paused workspace.
  • With only REACHPAD_API_KEY, the key must be --role owner. A collaborator key is refused: listing hands back live tokens, and a token is a capability.

The rules it is built to

These are the ways a naive client of a streaming exec API misleads a model, and what this one does instead.

  • An unterminated stream is UNKNOWN, never success. If the response ends without a terminal event, the tool says so in those words. It is not a zero exit, and an agent must not retry a non-idempotent command on the strength of it.
  • Its deadline is looser than the server's, so a server-side answer always wins the race. A client that gives up first turns "your build finished" into "unknown", and the numbers are mirrored from the server rather than guessed.
  • Refusals carry remedies, not codes. At your plan limit you get the limit, your current count, and what to do — the numbers come from the server, because a limit hardcoded in a client is a lie the moment your plan changes.
  • Results are handles, not payloads. Output is tailed with the dropped byte count named. Build logs can be megabytes, and an MCP result that size destroys the caller's context window.
  • A refusal is a result, not a transport error, so the model can read it and act. Only an unknown tool is a protocol error.

Test

sh
node --test 'test/*.test.mjs'

No network and no credentials. The protocol suite spawns the shipped server as a child process and speaks real JSON-RPC over its stdio against a stub control plane on a real socket, so nothing under test is an internal import. One test runs the same arc against a real reachpad and skips when no endpoint is configured.

Repository

This repository is the source. It is deliberately separate from the reachpad backend: this code is public, it is published to npm, and its release workflow holds publishing rights — none of which belong in the same repository as the control plane. Nothing here can read the backend's source, and no job in the backend copies files out to here.

Two server constants are mirrored in src/client.js — the exec grace period and the default exec timeout. A mirrored constant normally rots; this one does not, because the backend's own CI fetches this published package and fails if they disagree. The check lives where the number would change.

来源:README.md,提交 a4ee8f2

工具

25

whoami

Who am I

Report the account and the organization this connection is signed in as: user id, email and name, plus org id and name, and `orgs`, every organization the account is in with the current one marked. Takes no arguments. Every app is created in the current organization and nowhere else, so call this before publishing when the person named an organization to publish into, and stop if the org id is not the one they named: switching is done at reachpad.dev/apps, from the organization name at the top of the sidebar, and this connection follows it.

无需输入参数。

search_apps

Search apps

Find apps in the caller's organization by text. This matches substrings of the name and the slug, and whole words on the live version's page, not meaning: search for a word that appears in the title or on the page rather than describing the app. Returns ids and live URLs.

输入参数qtypelimitcreated_byupdated_by

read_app

Read an app

Read one app before update_app: its name, type, access level and publishing mode, plus base_version_id and its files, which both describe the version the next edit builds on. Each text file carries its content, so this is how you see what an app already contains before changing it. A file marked content_omitted answer budget did not fit in this answer and read_app_file fetches it; one marked too large is past what any tool answer carries. Pass that base_version_id to update_app. version is what the link serves now, and head_version is the version base_version_id names on the apps where that is not the live one, which is every app with a version staged by publish false. pending_version is the number of that staged version, and publish_app_version is what makes it live.

输入参数app_id必填

read_app_preview

See an app

Look at the picture of what an app's link serves right now, as an image. Use it for an app whose files you cannot read: a function app's module stays with the people who develop it, so this is how you see what the app actually shows. It is the live version only, and an app published before its picture was taken, or one whose capture has not run yet, answers that there is no picture rather than an error.

输入参数app_id必填

list_app_versions

List versions

List an app's versions, newest first. Versions are immutable and numbered from 1. Earlier versions open for the app's editors; the live link follows the app's access setting. Pass the next_cursor from a previous call to get the next page.

输入参数limitapp_id必填cursor

read_app_version

Read a version

Read one version by its number, with its files and each text file's content. Use it to see what an earlier publish contained, and to copy a file back out of a version you want to return to. Earlier versions open for the app's editors; the live link follows the app's access setting.

输入参数app_id必填number必填

read_app_file

Read one file

Read one file out of an app as text, at most 256 KB. Use it for a file that read_app left out with content_omitted: "answer budget", or to fetch a single file without the rest of the app. A file that is not text, and a text file over that size, are refused rather than truncated. Reads the newest version unless you name one, which is the same version read_app lists.

输入参数path必填app_id必填number

query_app_db

Run one SQL statement

Run one SQL statement against a function app's own database, to read rows and to fix data. Not for schema: a table or a column belongs in the version's migrations/NNNN_name.sql, and a statement that changes the schema is refused. Owners and editors only. Answers rows and rowCount for a select, and changes with lastInsertRowid for a write.

输入参数sql必填app_id必填params

list_secrets

List secrets

List the organization's secrets: each name, who set it, when, and the apps whose versions bind it. Names only. No tool anywhere returns a secret value.

无需输入参数。

set_secret

Set a secret

Set one organization secret, so a function can bind it by name in its manifest secrets list. Any member of the organization. Setting a name that already exists replaces the value and rotates it onto every published version that binds it. Answers the name, how many versions took the new value, and the ids of any that did not.

输入参数name必填value必填

remove_secret

Remove a secret

Remove one organization secret and delete it from every published version that binds it. A version the deletion did not reach keeps the name in the list with that version in failed, and running this again retries exactly those.

输入参数name必填

trash_app

Move an app to the trash

Move an app to the trash. Its link stops opening at once. Owner only. The app and its versions are kept and can be restored from Trash at reachpad.dev/apps/trash, which is also the only place an app is deleted for good.

输入参数app_id必填

create_app

Publish a new app

Publish new files as an app and get its URL back. Version 1 goes live at once, so show the person the content and the destination before calling this. Access defaults to org_link, which lets anyone signed in to the same organization open the link.

输入参数name必填slugtypefiles必填accessmessagemanifestvalidate

update_app

Publish a new version

Publish a new version of an existing app. Read the app first and pass the base_version_id read_app returns: it names the version this edit builds on, and a base that has moved is refused rather than overwritten. By default the new version replaces the whole file set and goes live immediately unless the app is set to review before publishing. Send mode merge to publish only the files that changed and keep the rest of that version's files, with remove for paths to drop. Two merges in a row build on each other, staged versions included. Send validate true to check the call without publishing, or publish false to stage the version and make it live later with publish_app_version.

输入参数modefiles必填app_id必填removemessagepublishmanifestvalidatebase_version_id必填

publish_app_version

Make a version live

Make one version of an app live by its number. This is how a version staged with publish false goes on the link, and how an app is rolled back: name an older number and that version is what the link serves again. Nothing is rebuilt, the pointer moves.

输入参数app_id必填number必填

set_app_access

Set who can open an app

Change who can open an app: restricted (only the owner and named people), org_link (anyone signed in to the organization), or public_link (anyone with the URL). A password and an expiry apply to a public link only. Owner only.

输入参数level必填app_id必填passwordexpires_atclear_expiryclear_password

share_app

Share an app

Give one email address access to an app as a viewer or an editor. An address without an account gets a grant that starts working the first time they sign in. The owner or an editor may use this when the owner has enabled editor-managed sharing.

输入参数role必填email必填app_id必填

list_app_shares

List shares

List the people an app is shared with, their roles, and whether the grant has attached to an account yet. The owner and editors can read this list.

输入参数app_id必填

revoke_app_share

Remove a share

Remove one share by its id, from list_app_shares. The app itself is untouched. The owner or an editor may use this when editor-managed sharing is enabled.

输入参数app_id必填share_id必填

ls

List a folder

List what is directly inside one folder: its subfolders and the apps filed in it. Omit folder_id for the top level. Each folder carries the updated_at that mv needs.

输入参数limitfolder_id

tree

Read the folder tree

The whole folder tree for the organization, nested, with the number of apps filed in each folder and the count sitting at the top level. Each node carries the updated_at that mv needs.

无需输入参数。

mkdir

Create a folder

Create a folder, at the top level or inside another one.

输入参数name必填parent_id

mv

Move an app or folder

Move an app into a folder, or move a folder under another folder. Name exactly one of app_id or folder_id as the thing being moved, and to_folder_id as the destination, or null for the top level. Moving a folder also needs base_updated_at, the value read from ls or tree, so a move made against a stale reading is refused instead of undoing someone else's.

输入参数app_idfolder_idto_folder_id必填base_updated_at

rmdir

Delete an empty folder

Delete an empty folder. This refuses while the folder still holds apps or subfolders, and it never deletes an app. Move the contents out first.

输入参数folder_id必填

get_app_logs

Read app logs

Read recent log lines from the live version of a function app. An app that serves files has no process and answers that it has no logs. Function logs are not captured yet either, and the answer says so: until they are, have the function catch its own errors and return them in the response.

输入参数limitsinceapp_id必填

版本历史

1
  1. v0.4.2最新Sep 16, 2026

评分

已满足适用检查项的 100%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通必需25 分
  • 已声明认证方式必需10 分
  • 已列出工具必需15 分
  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分