@arcadeai/mcp-remote

中文文档
0.1.0发布于 2026/07/14SEE LICENSE IN LICENSEES Module近 30 天下载 2766

Local stdio-to-remote bridge for Arcade MCP gateways, with managed OAuth and local file materialization.

原始使用文档
文档目录13 节
  1. Status
  2. Usage
  3. Headless hosts
  4. Authentication
  5. Token storage
  6. Other environment variables
  7. How it works
  8. File uploads
  9. Uploading to tools that fetch a URL
  10. File downloads
  11. File access policy
  12. Development
  13. License

@arcadeai/mcp-remote

A local stdio bridge to an Arcade MCP gateway.

Arcade serves MCP gateways over Streamable HTTP. Some MCP clients only speak the stdio transport, and, more importantly, a local process can do work that a cloud gateway cannot: it can read files from the machine. This bridge runs locally, presents a stdio MCP server to your client, and relays every request to the remote gateway over HTTP.

The headline capability this unlocks is local file materialization: the model emits a short file path, the bridge reads the bytes and substitutes them before the request leaves the machine, and files returned by tools are written to disk and handed back as a path. Large base64 blobs never enter the model's context window.

Status

The bridge relays MCP traffic to the gateway, authenticates with browser-based OAuth (Dynamic Client Registration + PKCE, silent refresh), rewrites file-carrying tool parameters so the model passes a local path (uploads), and materializes files returned by tools to disk so the model gets a path instead of a blob (downloads). On macOS the OAuth tokens are kept in the Keychain; file reads are guarded by a credential-directory blocklist and an optional allowlist.

Not yet done: the arcade connect --local integration and the public npm publish (the package is prepared but intentionally unpublished).

Usage

Point your MCP client at the bridge. The first time it connects to a gateway you have not authorized yet, the bridge opens your browser to sign in, caches the tokens, and then serves; every later start is silent. (Set ARCADE_AUTO_LOGIN=0 to disable this and require an explicit login first.)

To authorize ahead of time, so the first spawn never has to wait on you, run once in a terminal:

npx @arcadeai/mcp-remote login <gateway-slug-or-url>

Either way, point your MCP client at the bridge:

// in your MCP client config (e.g. claude_desktop_config.json)
{
  "mcpServers": {
    "arcade": {
      "command": "npx",
      "args": ["-y", "@arcadeai/mcp-remote", "<gateway-slug-or-url>"]
    }
  }
}

The single argument is either a gateway slug (expanded to https://api.arcade.dev/mcp/<slug>) or a full gateway URL.

To sign out: npx @arcadeai/mcp-remote logout [<gateway-slug-or-url>] (omit the argument to clear all cached credentials).

Headless hosts

On a machine with no browser, add --no-browser (or set ARCADE_NO_BROWSER=1) to login. It prints the authorization URL instead of opening a browser; open that URL on any machine that can reach the loopback callback, for example by forwarding the callback port over SSH:

ssh -L 9908:localhost:9908 you@remote-host

The device-code grant is not offered, because the Arcade authorization server does not support it.

Authentication

By default the bridge uses OAuth tokens cached by login, refreshing the access token silently when it expires. No API key is required, so project members who cannot mint API keys can still connect.

Alternatively, pass a bearer credential via the environment. This overrides the cached login and is handy for CI. The Engine accepts it in Authorization for both shapes:

VariableMeaning
ARCADE_API_KEYAPI key. Sent as Authorization: Bearer <key>.
ARCADE_USER_IDPaired with ARCADE_API_KEY (sent as the Arcade-User-Id header).
ARCADE_TOKENAn OAuth access token. Sent as Authorization: Bearer <token>.

Token storage

On macOS the OAuth tokens are stored in the login Keychain (through the security tool, so there is no native module to compile), and the on-disk file keeps only the non-secret client registration. On other platforms, or when ARCADE_USE_KEYCHAIN=0, tokens are written to credentials.json (0600) in the cache directory instead. A file written before the Keychain was in use still works and is migrated on the next token write.

Other environment variables

VariableDefaultMeaning
ARCADE_ENGINE_URLhttps://api.arcade.devEngine base used to expand a slug.
ARCADE_OAUTH_CALLBACK_PORT9908Loopback port for the login redirect.
ARCADE_LOGIN_TIMEOUT_SECONDS600How long login waits for the callback.
ARCADE_MCP_REMOTE_DIR~/.arcade/mcp-remoteWhere the client registration (and, without a keychain, tokens) is stored (0600).
ARCADE_USE_KEYCHAINon (macOS)Set 0 to keep tokens in the file instead of the OS keychain.
ARCADE_NO_BROWSER(unset)Set 1 for headless login (same as --no-browser).
ARCADE_DOWNLOAD_DIR~/Downloads/arcadeWhere files returned by tools are saved (also --download-dir).
ARCADE_ALLOWED_DIRS(unset)Colon-separated dirs uploads may read from; unset means anywhere but credential dirs.
ARCADE_MAX_FILE_BYTES26214400 (25 MB)Max in-memory upload/download size.
ARCADE_MAX_DOWNLOAD_BYTES262144000 (250 MB)Max assembled chunked-download size.
ARCADE_DOWNLOAD_CHUNKEDonSet 0 to disable chunked-download assembly.
ARCADE_PUBLIC_UPLOAD_BASE(unset)Public base URL that lets URL-fetching tools accept a local file path.
ARCADE_UPLOAD_PORT9909Local port the upload host binds (behind your tunnel).
MCP_REMOTE_LOG_LEVELinfodebug | info | warn | error.

All diagnostics are written to stderr; stdout carries only MCP protocol traffic, as the stdio transport requires.

How it works

MCP client  <--stdio-->  @arcadeai/mcp-remote  <--Streamable HTTP-->  Arcade gateway

The bridge connects outbound to the gateway as an MCP client, reads its capabilities and identity during initialization, and then presents an inbound stdio server that mirrors them. Most requests and notifications pass through untransformed; tools/list and tools/call are the seam where file parameters are rewritten (uploads) and returned files are materialized (downloads).

For auth, the outbound client carries an OAuth bearer token. On a 401 the SDK follows RFC 9728 discovery from the gateway to its authorization server, dynamically registers a public client (PKCE S256), and, during login, drives the browser + loopback authorization-code flow. In serve mode it only ever uses the cached tokens (refreshing silently); it never opens a browser.

File uploads

When a tool takes a file as bytes, the model should never see or produce base64. On tools/list the proxy detects file-carrying parameters and, at tools/call, reads the file locally and substitutes its contents before forwarding. The model only ever emits a short local path.

Detection favors precision; an ambiguous parameter is left untouched (today's behavior, base64 through the model):

  • Curated registry for known shapes: Gmail attachments[].source (the item already accepts a file:// URI, which becomes a data: URI, with filename/mime_type filled from the file), and base64 workbook/attachment parameters (presented to the model as a local-path parameter instead).
  • Guarded heuristic for new tools: a string parameter is rewritten only when its name looks like a bytes field (*_base64, file_content*, file_bytes, attachment*) and its description carries a base64/binary signal. This excludes plain-text content parameters.
  • Explicit marker: a parameter with contentEncoding: base64 is treated as authoritative.

Pass an absolute path or a file:// URI. Files up to 25 MB are read (override with ARCADE_MAX_FILE_BYTES), and file contents are never logged.

Uploading to tools that fetch a URL

Some tools don't accept bytes at all: they take a public URL that the gateway fetches server-side (for example GoogleDrive_UploadFile's source_url, Resend_SendEmail attachments, Fireflies_UploadRecording). A remote gateway can't reach a file on your machine, so this is opt-in: set ARCADE_PUBLIC_UPLOAD_BASE to a URL that reaches this machine (an SSH tunnel or an ngrok/cloudflared tunnel to ARCADE_UPLOAD_PORT, default 9909). With it set, you may pass a local path to those tools and the bridge hosts the file under an unguessable token and substitutes the public URL; the local server binds 127.0.0.1 only, so nothing is exposed except through your tunnel. Without it set, passing a local path returns a clear error explaining how to enable hosting.

File downloads

When a tool returns a file, the proxy saves it to the download directory (~/Downloads/arcade/ by default, or --download-dir / ARCADE_DOWNLOAD_DIR) and replaces it with the local path, so the bytes never reach the model's context. It handles three shapes in a tool result:

  • a resource_link URL — downloaded (best-effort, unauthenticated) and saved;
  • a data:<mime>;base64,... URI anywhere in the result (a text block or a structured field);
  • a base64 value under a bytes-looking field name, with the extension resolved from a sibling filename, then magic bytes, then .bin;
  • an http(s) URL under a media/file field (image_url, file_url, download_url, a bare image/photo, a nested *_image_url), which the proxy fetches and saves. Page and profile URLs are deliberately excluded so the proxy does not fetch every URL a tool returns.

Some tools return a large file as a "fetch me in chunks" reference rather than inline bytes (Google Drive's DownloadFile does this above ~5 MB, pointing at DownloadFileChunk). The proxy recognizes that shape, pulls every chunk through the gateway, streams them into one file on disk, and returns the same local-path result a small file's direct download gets, so the model makes a single call and never sees the chunking. Assembled downloads are capped at ARCADE_MAX_DOWNLOAD_BYTES (250 MB default).

A small per-tool registry adds the file producers whose output arrives under a field the generic rules miss (for example Figma_ExportImage, Firecrawl_ScrapeUrl, GoogleSlides_GetSlideThumbnail); incidental images like avatars and listing thumbnails are deliberately left alone. When a file URL is on the gateway's own host, it's fetched with the bearer; every other host is fetched unauthenticated, so the token never leaks. Provider-private attachment URLs (Microsoft Graph, Zendesk, Confluence, Linear) therefore can't be materialized by the bridge, since it holds the Arcade token, not the provider's; those need a gateway-side tool that returns bytes.

Anything the proxy can't confidently identify is left untouched. Set ARCADE_DOWNLOAD_MATERIALIZE=0 to disable all of it, ARCADE_DOWNLOAD_LINKS=0 to stop fetching resource_link and media URLs, or ARCADE_DOWNLOAD_CHUNKED=0 to disable chunked-download assembly.

File access policy

Reads for uploads are confined. A built-in blocklist always refuses credential directories (~/.ssh, ~/.aws, ~/.gnupg, ~/.kube, ~/.docker, ~/.config/gcloud, and the proxy's own ~/.arcade), and symlinks are resolved before the check so a link cannot smuggle a read past it. Set ARCADE_ALLOWED_DIRS (colon-separated) to additionally confine reads to specific roots. Downloads are written only inside the download directory; a gateway-supplied filename is reduced to its basename so it cannot escape.

Development

npm install
npm run build     # tsc -> dist/
npm test          # unit + end-to-end passthrough against an in-process gateway

The end-to-end tests stand up real servers in-process: one spawns the built bridge as a child over stdio and asserts that initialize, tools/list, tools/call, and error propagation all cross the bridge intact; another runs a mock authorization server and secured gateway to exercise the full OAuth login (DCR + PKCE + loopback), silent serve, and silent token refresh; a third drives the bridge against an echo gateway to confirm that base64 parameters are rewritten to paths and that file bytes (raw base64 and data: URIs) are substituted before forwarding; and a fourth confirms that files returned by tools (data: URIs, base64 fields, and resource_link URLs) are saved to disk and handed back as paths. Unit tests cover MIME typing, the credential store's Keychain split (with an injected backend, so the real keychain is never touched), the streaming download size cap, and chunked-download assembly (with an injected tool caller, so no gateway is needed).

License

Proprietary - Arcade Software License Agreement v1.0. See LICENSE. Copyright (c) 2025 Arcade Technologies, Inc. All rights reserved.

版本与发布记录

@arcadeai/mcp-remote 共发布 1 个版本,首次发布于 2026/07/14;过去 12 个月发布了 1 个版本。

版本发布日期距今
0.1.0latest2026/07/143 个月前

关于 @arcadeai/mcp-remote 的常见问题

@arcadeai/mcp-remote 是什么?

Local stdio-to-remote bridge for Arcade MCP gateways, with managed OAuth and local file materialization.

如何安装 @arcadeai/mcp-remote?

在项目目录运行 npm install @arcadeai/mcp-remote,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 mcp-remote,可以用 npx mcp-remote 直接运行。

@arcadeai/mcp-remote 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/arcadeai__mcp-remote 是否存在。

@arcadeai/mcp-remote 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@arcadeai/mcp-remote 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@arcadeai/mcp-remote 可以商用吗?

@arcadeai/mcp-remote 使用 SEE LICENSE IN LICENSE 许可。许可条款写在包内的独立文件中,请阅读该文件确认可否商用。以上为概括,不构成法律意见,请以许可原文为准。

@arcadeai/mcp-remote 的下载量有多少?

根据 npm 官方下载统计,@arcadeai/mcp-remote 在 2026/10/07 – 2026/11/05 这 30 天内被下载 2,766 次,日均约 92 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@arcadeai/mcp-remote 还在维护吗?

最新版本 0.1.0 发布于 2026/07/14(3 个月前);至今共发布 1 个版本,其中过去 12 个月发布了 1 个;首次发布于 2026/07/14。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。