paseo.cafe
All plugins

Connectors for your AI apps: add them from a gallery, keep Claude and Codex in step, see what's broken and sign in.

v0.20.1 Requires Paseo >=0.9.0

Install

paseo plugin add itsjustanks/paseo-mcp --ref 9ae26e191751975fa88ac41f43ba497acdfa7be9

Git security status and fallback installation are pinned to commit 9ae26e191751.

From the plugin's README

Requires Paseo 0.8 or newer (0.1.0 is the last release for Paseo 0.7):

paseo plugin add itsjustanks/paseo-mcp

Updates are handled by Paseo:

paseo plugin update paseo-mcp

README

Paseo MCP

Manage MCP servers, project connections, credentials, and OAuth from Paseo.

In the app it is called Connectors (connectors are MCP servers): the sidebar row, its page, and the workspace and agent panels. Since 0.19.0 everything you read says "connector" where it said "server".

Install

Requires Paseo 0.8 or newer (0.1.0 is the last release for Paseo 0.7):

paseo plugin add itsjustanks/paseo-mcp

Updates are handled by Paseo:

paseo plugin update paseo-mcp

What it does

  • Adds servers from a gallery: recommended official servers, libraries you subscribe to by address or file (the MCP Gallery by default, your team's own list, a private registry), and optionally the MCP Registry, with trust badges (see Add from catalogue).
  • Shows user-level MCP servers across Claude Code, Codex, Kimi Code, and Grok as a searchable gallery: one card per server saying what it is, whether it works, which apps have it and its sign-in state, with a settings button to its own page (tools, sign-in, edit, remove).
  • Adds, edits, renames, imports, and exports definitions with masked secrets; removes a server from one editor, all editors, or everywhere including project .mcp.json files.
  • Turns servers on or off per workspace from the workspace and agent panels, where the editor has such a switch.
  • Starts Claude or Codex OAuth in the computer's default browser and shows the fallback URL.
  • Shows each Paseo workspace's project-level .mcp.json servers in a Connectors tab, available in both the workspace view and the Projects/Explorer view.
  • Tells each workspace what an agent started there loads (project, local and user-level servers), what it costs in child processes and memory, and warns when the count is heavy enough to exhaust the agent's context.
  • Checks every server's health in the background and flags problems per agent, per project, and per user config.
  • Lists the tools each server exposes, the way Claude Code's /mcp view does. A chip appears on a chat's composer only when a connector that chat loads is broken or needs sign-in (2 broken, 1 needs sign-in), and only on a running chat or one active in the last hour; a calm chat has none. Everything else that needs you shows as the sidebar row's dot, which opens a quick popover.
  • Shows, per agent, which servers the chat actually used and which it loaded without using, and turns the unused ones off for the workspace in one confirmed step. /connectors in a composer opens that panel.
  • Puts a small "needs sign-in" card in the chat when a tool call fails for lack of a sign-in, with a Connect button.
  • Shows and switches Paseo's own built-in tools (the mcp__paseo__* tools the daemon adds to agents): for the whole host, per provider, and per tool.
  • Copies every server to every AI app and account that doesn't have it (Copy to all my AI apps, which replaces Sync accounts), after a preview, never removing or overwriting anything and never copying a sign-in. Only what can be copied exactly goes; a server another app wouldn't read the same way is left for you to copy by hand, with the reason.
  • Keeps backups before config writes and preserves destination-specific credentials.

Add from catalogue

Servers → Add server opens a gallery. Search it, filter by category, and press Add on a card; Add by hand keeps the old form. Each card says who publishes it, whether it is Official, Library, Team or Community, whether it is on the Web or On this computer, whether you Sign in with your account, it Needs a key, or has No sign-in, and which library it came from.

Shelf Where it comes from
Recommended 31 official servers shipped with the plugin (shared/catalog-curated.ts), each URL copied from the vendor's docs, which the card links to. Always shown; a library never hides one (see Merging)
Libraries Every library in Libraries at the bottom of the gallery, most trusted first (see Merging). By default that is the MCP Gallery (https://raw.githubusercontent.com/itsjustanks/mcp-gallery/main/v0.2/servers.json; a saved v0.1 address, the default before 0.16.0, is read as v0.2). Add your own by address or file; the 0.12.0 team catalogue is now the library called Team
Registries A library whose address is a registry, searched once you type two letters, latest versions only, cached a day per search, up to three pages of 100. The official MCP Registry (registry.modelcontextprotocol.io) is listed but off; switch it on in Libraries. Only remote servers are added in one click. A server that only ships a package (npm, PyPI, an image) is shown, marked "Runs code on this server", with its Repository and Add by hand (the form opens with its name filled in, nothing else)

Libraries

A library is JSON in the official MCP Registry's list shape: { "servers": [ { "server": <server.json>, "_meta": { … } } ], "metadata": { "count": N } }, each server a server.json (name, description, version, remotes or packages). The gallery's curation is optional, in _meta["io.github.itsjustanks/mcp-gallery"]: { "id", "displayName", "category", "publisher", "iconUrl", "auth": "oauth" | "token" | "none", "docsUrl", "verifiedAt" }. Without it the card uses title, websiteUrl, repository and icons from server.json; a field that doesn't fit (an http icon, one over 2048 characters) is ignored on its own. The category is one of the gallery filter's names (developer, data, docs, productivity, design, crm, support, marketing, analytics, payments, automation); the older words map to the closest (database → data, documentation → docs, observability and ai → developer, websites → design) and anything else is Other. A 0.12.0 team catalogue file (a list of entries) reads as a library too, its text cleaned of hidden characters like any other.

{
  "servers": [
    {
      "server": {
        "name": "com.acme/mcp",
        "description": "Acme orders and invoices.",
        "version": "2.1.0",
        "remotes": [{
          "type": "streamable-http",
          "url": "https://mcp.acme.com/mcp",
          "headers": [{ "name": "Authorization", "value": "Bearer {ACME_TOKEN}", "isRequired": true, "isSecret": true,
                        "variables": { "ACME_TOKEN": { "description": "Acme API token", "isRequired": true, "isSecret": true } } }]
        }]
      },
      "_meta": { "io.github.itsjustanks/mcp-gallery": { "displayName": "Acme", "category": "payments", "auth": "token" } }
    }
  ],
  "metadata": { "count": 1 }
}
  • Where a library lives. An https address ending in .json is read as one document; any other address is a registry, searched through GET {address}/v0.1/servers?search=… (Read as picks either by hand). A path on the host (absolute, or starting with ~/) is a private library that is never hosted. Plain http is read only from this machine (localhost, 127.0.0.1). An address with a user name, a password or a key in its query is refused; a private address takes a header name, and its value is set in the panel, write-only, kept in $PASEO_HOME/plugin-settings/paseo-mcp/team-auth.json (0600), bound to the site of the address it was set for and never sent back to the app. It goes only to that site; if the address moves to another site, the value is deleted.
  • Reading. A library is read in the background and kept for 15 minutes; Refresh reads it again. A failed read keeps the last good copy, says why, and waits a minute before it is tried again. Until the MCP Gallery is published its read fails with "HTTP 404: nothing is published at that address (yet)", and the recommended servers stand in.
  • Merging. A server name is shown once, and a clash goes by trust, not by the order of the list: Team first, then libraries you added (by address or file), then the default public ones (MCP Gallery, MCP Registry). The order in Libraries only matters within each group. A Recommended card is never hidden. A library's copy of a Recommended server shows as the Recommended card itself, with the shipped text, docs link and auth, but only when it is exactly the same server: for a remote, the same address (query included), header names and templates, values to fill in and auth; for a package, the same command and arguments word for word (so the same version and flags) and the same environment variables. Anything less (another path, a ?via=…, an extra header, another version) is its own Library card beside the Recommended one ("whoever can change that library can change this entry"). A library card is never Official by itself.
  • Rules. Every server passes the team catalogue's rules (below): https only, no literal key, no ${…}, no control characters, exact package versions (npx name@1.2.3, uvx name==1.2.3), no runner flags or runtime variables. One that fails is refused, listed by name with the reason, and the rest still show. A package the plugin doesn't start (an image, mcpb, NuGet) is shown with Add by hand. Header, env and argument values are {PLACEHOLDER} inputs asked for when you add the server, and secret unless their name is on the short public list; a header may be a template such as Bearer {token}. A library, and the plugin's cache of it, never holds a stored value. A document is capped at 1 MB and 500 servers. A server that would not fit a card (an address over 2048 characters, more than 64 arguments, an argument over 1024) is refused with that reason, listed on the library's row.
  • Rolling back to 0.12.0. 0.12.0 can't read the version 2 catalog.json and would show no team catalogue. Paseo rewrites that file in place when it migrates, without a backup, so the plugin keeps the version 1 document beside it as catalog.v1.json first. Before downgrading, copy it back: cp $PASEO_HOME/plugin-settings/paseo-mcp/catalog.v1.json $PASEO_HOME/plugin-settings/paseo-mcp/catalog.json. The Team key needs nothing; team-auth.json also keeps it in 0.12.0's shape.

Anyone can publish to the registry. Official is kept for the Recommended shelf and for a registry entry whose every address is a recommended one, since those addresses were checked against the vendor's own docs. Every other registry entry is Community. Its card shows the namespace as plain text ("published as supabase.com", "published as github.com/") with one line: the registry checks that the publisher owns that domain or GitHub account, not who runs the service. com.supabase/mcp at mcp.supabase.com/mcp is Official; the same name at any other address is Community. Community cards say what that costs: "A third party relays your traffic and any key you give it", or "Runs code on this server" for a package. A registry server that lists no headers reads "Sign-in unclear": the registry can't tell OAuth from no sign-in, so the card says to use Connect OAuth if the server asks once it is added.

Adding asks where: My editors (the same editor configs as Apply) or One project (its .mcp.json). It shows the exact change per file with secrets masked, and writes nothing until you press Add. A server that runs locally shows the exact command line on its own line first (npx @playwright/mcp@latest). The install is bound to that preview: if the entry or the change moved since you read it (a team file edited in between), nothing is written and the sheet asks you to review again. Writes use the same writers as Import (backup, atomic write, read back). A name that is already taken is never replaced; the sheet offers the next free name or Skip. Afterwards it runs a health check, offers Open server to connect for an OAuth server, and says what the server adds to the context budget.

A card you already have is hidden (0.15.0): the gallery shows what you don't have yet, and the line under the search says how many are hidden ("8 you already have are hidden."). You have it when one of your servers is at the same endpoint, is on the same host whatever the path (anything on mcp.zapier.com, where the path carries a personal token; not a host many servers share, like vercel.app or localhost), runs the same package (npm, PyPI, OCI; any version; not a runner like mcp-remote), or is named exactly after the card's id or name, ignoring case, an -mcp ending and a prefix two or more of your servers share (ikit-notion is Notion when you also have ikit-linear; old-github isn't GitHub). Show ones I already have brings them back, each saying "You have a Zapier server already, called automations." A card at the same endpoint says Added (matched on the address or the package and its ecosystem, not the name, so an npm package never counts as a PyPI one of the same name), and its button reads Add to more and starts with only the missing places picked.

A project's .mcp.json is usually in git, so a key never goes into it. Claude Code expands ${VAR} in .mcp.json (in command, args, env, url and headers), so a secret becomes a ${VAR} reference and the sheet names the variable to set where Claude Code starts (for Paseo agents, the daemon's environment or the provider's env in Paseo's settings); if one is already set on this host, the sheet says so (by name, never the value). The plugin names every variable itself, so an entry can't choose which of your variables is sent to its server: MCP_<ID>_<INPUT> for a recommended entry (for example MCP_HEROUI_PRO_HEROUI_PERSONAL_TOKEN), and MCP_TEAM__<ID>__<HASH6>__<INPUT> or MCP_REG__… for a team or registry entry, where HASH6 comes from the SHA-256 of its address or package, so it can't reuse a recommended entry's variable. A name Claude Code uses itself (MCP_CLIENT_SECRET, MCP_TIMEOUT …) is refused. Inputs are secret by default: only one named on a short list (region, project, workspace, org, team, site, host, port, database, schema, env, locale, timezone, mode) is written as typed, and not even then when it sits in a header or env line named otherwise, anywhere in a team or registry entry's address or arguments, or when the value looks like a credential (postgres://user:pass@…, sk-…, ghp_…, a Slack webhook). Codex does not read .mcp.json, and Add project servers to agents passes the reference on unexpanded.

The 0.12.0 team file shape still reads: a list of entries, or { "entries": [ … ] }:

[
  {
    "id": "team-n8n",
    "name": "Team n8n",
    "publisher": "Your team",
    "description": "Our workflows as tools.",
    "category": "automation",
    "transport": "http",
    "url": "https://n8n.example.com/mcp/team",
    "headers": { "Authorization": "Bearer {N8N_TOKEN}" },
    "inputs": [{ "id": "N8N_TOKEN", "label": "n8n token", "secret": true }],
    "auth": "header",
    "docs": "https://wiki.example.com/n8n-mcp"
  }
]

transport is http (with url and headers) or stdio (with command, args and env); auth is oauth, header, env or none; values the user fills in are {INPUT} placeholders declared in inputs (secret and required unless they say otherwise). Addresses must be https. An entry holding a literal key, a password in its URL, a ${…} reference, an undeclared placeholder, a control character, or a header name with a . is refused and listed with the reason. A stdio entry that runs a package through npx, bunx, pnpx, npm exec, uvx or pipx run must name it plainly with an exact version (acme-mcp@1.2.3, mcp-server-fetch==1.0.0): not latest, a range, *, an address or no version, and no runner flag that picks another source or runs a shell command (-p, --registry, --index-url, --with, -c, --call). Its env may not set what changes how the runner starts: NODE_OPTIONS, NPM_CONFIG_*, UV_*, PIP_*, PYTHON*, PATH, LD_*, DYLD_*, HOME, SHELL, or a name the plugin reserves (AWS_*, ANTHROPIC_*, HTTPS_PROXY …). Names, publishers, descriptions and labels are shown without bidi controls, zero-width or control characters. An envVar on an input is ignored. The file must be under 1 MB. Copy as catalogue entry on any server card writes that server in this shape, templated so no stored value is in it: of an address only the scheme and host stay (a host's first label becomes {HOST_PREFIX} unless it is a plain word like mcp; a path that is not plain lowercase words becomes {PATH}; query keys become PARAM_1…n with their values as inputs); every header and env value is a placeholder; of a command, only the command, its package and flag names stay.

Needs setup

Some official servers can't be added in one click. A library entry says so in _meta["io.github.itsjustanks/mcp-gallery"].setup (a recommended or team entry in a setup field), and the card shows a badge, the reason in one line, and How to set it up (the vendor's own page):

kind Badge What the gallery does Examples in the MCP Gallery
byo-oauth Needs setup Set up opens a guided sheet: the vendor's steps with their links, the exact redirect address to register, the scopes to add, then the client ID and secret you copy back Gmail, Google Calendar, Drive, Docs, Sheets, HubSpot, Zoom
per-org Needs your address Set up asks for your subdomain or org and fills the address in Zendesk
approved-clients Approved apps only Only the vendor's approved AI apps can sign in. With clients, the approved apps the plugin writes to get it and the rest are skipped with why; without, it is shown, not added Dropbox (Claude Code, Codex), Slack, Canva
admin Admin setup Shown, not added: an administrator turns it on first Box, Salesforce, Microsoft 365

Fields: kind, reason (one plain line, 200 characters at most), guideUrl (https), and by kind steps (up to 10 short lines; an https address in one is shown as a link), redirectHint (the vendor's name for the redirect field), clients (claude, codex), scopes (space-separated), urlTemplate and label. It is untrusted text like the rest of a library: cleaned of hidden characters, capped, https links only, and a field its kind doesn't use is an error. A setup that doesn't check out makes the card "not installable" with the reason; it is never dropped, which would show a gated server as one click. The rules are in shared/setup.ts.

Bring your own sign-in app (byo-oauth). The vendor accepts only an OAuth client you register (no dynamic registration). The sheet shows http://localhost:33418/callback, the address Claude Code listens on when an entry's oauth.callbackPort is 33418 ("Use pre-configured OAuth credentials", code.claude.com/docs/en/mcp); register exactly that. One port for every app, so one Google client covers all five Google servers. For each Claude account you pick, the entry is { "type": "http", "url": …, "oauth": { "clientId": …, "callbackPort": 33418, "scopes": … } }. The client secret is never written to a file: Claude Code keeps it in its secure store (the macOS Keychain, or ~/.claude/.credentials.json elsewhere), and only its own claude mcp add / add-json --client-secret puts it there. So for this one server the plugin backs the file up, refuses a name that's already there, runs claude mcp add-json --scope user --client-secret -- <name> <json> for that account (CLAUDE_CONFIG_DIR set to the folder of the file it writes, unset for ~/.claude.json) with the secret in the child's environment as MCP_CLIENT_SECRET (never in its arguments, never logged, never in an answer), then reads the file back and compares it with the plan. If Claude Code fails, hangs (stopped after 30 s, killed 5 s later) or can't store the secret, the entry it had already written is taken out again, so nothing is left that can't sign in. The preview is worked out without the secret (it only learns that one was typed); the secret crosses to the daemon once, when you press Add.

Only vendors the plugin knows. Claude Code sends the client secret to whichever sign-in server the MCP server names, so a library entry called "Gmail" at another address could collect a real one. byo-oauth is honoured only for a vendor in the plugin's own BYO_OAUTH_VENDORS list (shared/setup.ts; never read from a library), at that vendor's own MCP host: Google (<name>mcp.googleapis.com), HubSpot (mcp.hubspot.com) and Zoom (mcp.zoom.us). The guide and every link in the steps must be https on that vendor's own sites. Anything else blocks the card, and the sheet names the exact host next to the secret box. A new vendor takes a plugin release. It is user level only: the secret can't go in a project's file. Codex is skipped with the reason: its [mcp_servers.<name>.oauth] table takes client_id, callback_url and callback_port but no secret (developers.openai.com/codex/mcp), and these vendors' token endpoints require one. Kimi and Grok have no such setting. After adding, sign in with Connect as for any OAuth server. To change the secret, remove the server and add it again.

Your own address (per-org). urlTemplate holds one {subdomain} or {org}, as the whole first label of the vendor's host (https://{subdomain}.zendesk.com/api/mcp) or one whole path segment, and must be the server's own address. What you type must be lowercase letters, digits and - (1 to 63, not starting or ending with -, not something that reads like a key); the address it makes is checked again, and a host that isn't the template's is refused. The value is written as typed at both levels (it names your organisation; it is not a secret), and a value that fails is never shown in the preview's address.

Add project servers to agents

Off by default. Turn it on under Settings → Plugins → Paseo MCP → Project servers, or run the Add project servers to agents command. This is the plugin's own setting and is mainly for Codex, which does not read .mcp.json. It is not Paseo's Enable Paseo tools (mcp.injectIntoAgents), which adds Paseo's own mcp__paseo__* tools; that one is on the Paseo tools card.

Setting Default Meaning
Add project servers to agents off Add the workspace's .mcp.json servers to every new agent
Providers Codex Codex, Claude Code, or both
Skip inline-credential servers on Leave out entries carrying tokens in env, headers, args, or the URL

When an agent is created for a chosen provider, the plugin reads .mcp.json from the agent's working directory (or its git root) and adds each server to the agent's MCP configuration. Servers the agent already defines are kept as-is. Nothing is written to any config file, and a failure to read leaves the agent unchanged. The daemon log shows injected N servers into <provider> agent.

Each agent also gets an MCP tab (and the MCP for this agent command) listing the workspace's project servers, with a line showing the agent's provider and whether project servers are added for it.

Settings live on the host at $PASEO_HOME/plugin-settings/paseo-mcp/injection.json (~/.paseo by default; the file and its keys kept their 0.10 names). The hook reads that file directly; if it is missing or invalid, the setting stays off.

The Connectors page

Four tabs: Overview, Connectors, Projects and Help (0.19.0). The words are for people who have never heard of MCP: plain English, "connector" for what used to be called a server, and any term explained where it appears.

0.19.0 made it calmer at the user's request ("it just needs to work for dummies"):

  • No intro block under the tab bar: each tab starts with its own content.
  • Connectors: Add connector, search, filters, the cards (a whole card opens it), then one card of fold-outs: Back up your connectors, Built-in tools, Totals and last check.
  • Add a connector: one heading, the search, "Not in the gallery? Add with a link · Paste setup instructions" right under it, then the cards. Adding with a link (the old Import & Export tab) opens from there and from Help. The install sheet folds the exact files it will write behind "See exactly what will be saved".
  • A connector's page: its status, kind and apps, the one or two things to do, its sign-in when it has one, then fold-outs: What it can do, Your AI apps, Rename, Technical details (its address, Show keys, Save a copy, Copy for a team list, and what each app has saved) and Remove.
  • Help: common questions, each folded (Needs sign-in, a sign-in page that won't load, Not working, missing from an app, project connectors, backups), then the guide.
  • Fold-outs are Accordion / AccordionItem in client/ui.tsx.

Since 0.17.0 it looks and reads like AI Router, the shared design standard of our Paseo plugins: one type scale (15 px sentences, nothing below 13; TYPE in client/ui.tsx), a header with the plug icon, "MCP servers" and the state with a coloured dot, underline tabs with icons (icons only, plus the active label, when the labels don't fit), and on every tab its title, one or two plain sentences and "What you can do here" (behind Learn more on a phone). The words are in shared/guide.ts, tested for plain English. On Paseo 0.11 the page is a full screen with the app's own sidebar row; on 0.10 and later links open in the system browser. Older apps keep the surface and sidebar item they had, decided at runtime (shared/host-features.ts). requirements.paseo is >=0.9.0 since 0.19.2: Paseo accepts the manifest's description only from 0.9.0. Copies go through the app's own clipboard (copyText, clipboardCopier in shared/host-features.ts; 0.20.0); React Native's deprecated Clipboard serves only an app without it, and a refused copy says "Couldn't copy". Toasts come from one hook (useToast in client/ui.tsx): the app's, or inline lines on an app without one. Everything a person sees that may quote a provider or a CLI (toasts, inline errors, failure lists, previews) goes through one redactor, shared/redact.ts; CLI failures are a fixed sentence plus the CLI's last line, redacted (cliFailure). A Copy button never offers a key: the saved config has none while keys are shown, and previews are masked by the host, including TOML lines kept as written (an oauth table's client_secret), for display only. The manifest has no name, icon or media yet: Paseo 0.11.0-beta.5's manifest schema is strict and rejects them, so they wait until every host runs 0.11.1.

Overview is calm (0.18.0, AI Router 0.15.0's rules): a status card with the state in words ("Fix the server that isn't working", "All set: your servers are working"), three rows (health, AI apps, sign-in), when health was last checked, and two buttons (the next step, then Browse servers or Add a server). Which servers need a look, and why, folds behind a quiet link in the card. Below it, one link, New to MCP servers? How it works, opens a single card with what MCP servers are, how it works (four steps), how to use it and the words you'll see; it is open until the first server is added. AI Router is one quiet line. Every other tab opens with its title and one or two sentences, with "What you can do here" behind a small link. Spacing comes from one scale (SPACE in client/ui.tsx).

Copy to all my AI apps (0.15.0; it replaces Sync accounts) copies every user-level server into every AI app and account on this host that doesn't have it: Claude → Codex and Kimi, Codex → Claude, and between two accounts of one app. It opens a preview first: one row per server with where it will go, which copy is used, "includes its saved key" when it carries one, and a tick box to leave it out; then Copy N servers. It writes through the same writers as a server's Add to missing, one write and one backup per file, in each file's own format, and reads every server back. It never removes or overwrites: who has a server is decided by name, the plan is worked out again when you press Copy, and the write refuses a name the file has by then ("it already has a different server called jam; left as it is"). Between apps only the settings every app shares are copied; a switched-off server, an app-only setting or a ${VAR} the other app wouldn't fill in is skipped with the reason. The result is per server and per app, so a file that failed shows. Paseo's own server and project .mcp.json servers are left out, and no sign-in is copied. Project trust isn't touched: Sync accounts copies that, and only where the other account doesn't have it yet. It is on Overview, in the next step when servers are missing from some apps, and on the Connectors tab's Missing from some apps filter.

Connectors is a gallery of the connectors you have, in the same style as the Add gallery (0.15.0). Search reads each server's name and description; the pills are All, Needs attention (health), Needs sign-in and Missing from some apps, each with its count. A card shows:

  • the name and one plain line: the catalogue's description when the address or package is a known one, otherwise "Your server at mcp.example.com" or "Runs on this computer";
  • a health dot and word (Working, Warning, Not working, Not installed, Needs sign-in, Not checked yet);
  • which apps have it and which don't: "In Claude and Codex · missing in 1 Claude account and Kimi";
  • its sign-in state ("Signed in", "2 accounts need to sign in", "Uses a saved key", "No sign-in needed");
  • a settings button (the app's gear icon; a text button in an app without icons) that opens the server's own page, which has its tool list, sign-in rows, Add to missing, rename, export, Copy as catalogue entry and Remove.

Above the gallery, Paseo's built-in tools ("Paseo's built-in tools · on · 61 tools") and the totals ("6 servers · 1 needs attention · 2 need sign-in") are two closed rows, so the gallery starts near the top. One Refresh re-reads the settings, health, sign-in and every server's tool list. The logic is in shared/servers.ts (serverGallery).

Compare-before-replace (0.20.0)

Settings files other programs write too (~/.claude.json, a project's .mcp.json, Codex's and Grok's config.toml) are changed only through readStamped + replaceGuarded (server/safe-write.ts): the inode, size, mtime and a SHA-256 of the text are recorded at the read; the new text goes to a temporary file in the same folder; the file is stamped again right before the rename, and any difference deletes the temporary file and throws a retryable ConcurrentChangeError ("Claude Code changed its settings file while saving. Nothing was changed; try again."). The rename keeps the file's mode; the read-back must equal the bytes written, and if it doesn't nothing more is written (no rollback) and the user is told where the backup is. Temporary files are created 0600 and removed on any failure; the stamp hashes the file's bytes, and a file that isn't valid UTF-8 is refused. Reveal to edit carries the connector's version (entryVersionFromText); Save is refused if it changed since. The plugin's own files (settings copies, caches, saved keys) and exports use the plain atomic writer.

Where each connector is set up (0.20.0)

Every surface uses the same plain labels (shared/scope.ts): Everywhere for a user-level config (~/.claude.json's top level, ~/.codex/config.toml, the other apps' own files), This project · for a project's .mcp.json (and what the injection hook adds from it), and This project, just for you for Claude Code's local scope (projects[<dir>].mcpServers in ~/.claude.json), shown read only as "Claude Code · just for you": the plugin never writes those entries, and Remove gives the claude mcp remove <name> -s local command instead. The workspace and agent panels group by scope, project first. When a name is set up at several levels, loadFor keeps the winner (local, then project, then user, Claude Code's documented order) and records what it hides (shadows), and the row says which copy is used. Rows name sources without paths; Technical details lists home-relative paths with Copy. Add starts on the scope being browsed (startingScope) and names the file it writes. Codex's project .codex/config.toml is not read yet.

Removing a server

Each server's page offers three scopes, each a two-step confirm that names the files. Since 0.20.0 the second step is Paseo's own dialog (Modal), with Cancel, rather than a box further down the page (an app without Modal keeps it in the page). Each opening is a fresh session (shared/remove-dialog.ts): Cancel forgets the app picked, and Confirm is single-use:

Scope What is written
this app One AI app's config (pick which when the server is in several)
all apps Every AI app config that defines it
everywhere All apps plus every registered project's .mcp.json that defines it

Only everywhere keeps the server gone: Claude Code reads a project's .mcp.json straight back, so the other two scopes say which projects still define it. Project files are backed up first, rewritten atomically, read back and must parse, and an emptied file is left as {"mcpServers":{}} rather than deleted. The confirm warns that those files are usually version-controlled (the change shows in git status) and that inline credentials in a deleted definition are lost; export first if in doubt. The result is reported per target, so a partial failure is visible.

Per-workspace switches

The workspace's Connectors panel and each agent's Connectors panel list the servers an agent there loads, each tagged with its origin (user-level, this project's .mcp.json, local) and with a switch where the editor has one. A switch takes effect when a new agent session starts; a running agent keeps the servers it started with. State is read from the config on every refresh, so a /mcp disable done in a terminal shows up here.

Provider Server origin Lever
Claude Code user-level, local projects["<workspace dir>"].disabledMcpServers in ~/.claude.json — off for this workspace only, the definition is untouched. Same list /mcp disable writes.
Claude Code this project's .mcp.json enabledMcpjsonServers / disabledMcpjsonServers for that directory (the approval lists). "Asks at launch" means neither list names it yet.
Codex this project's .mcp.json (added by Add project servers to agents) The plugin's own $PASEO_HOME/plugin-settings/paseo-mcp/workspace-disabled.json; the agent.create hook leaves the server out for that directory.
Codex user-level No switch. Codex layers config.toml on top of what Paseo passes it, and enabled = false there is global. The row shows its state and says so.
Kimi, Grok any No switch.

The project entry is keyed by the workspace's own directory, so a worktree workspace gets its own entry rather than sharing the project root's. Writing ~/.claude.json is guarded: the file must parse, the new document may differ only inside that one project entry (checked before the write), the write is a temp file plus rename, a backup is taken first, and the result is read back and must hold the state just written. Any failure refuses the write and reports why.

Health checks

On by default. The host probes every MCP server on a timer: HTTP endpoints get the same JSON-RPC initialize POST an MCP client opens a session with (configured headers and URL sent intact, so a token in the query string counts), stdio commands are looked up on the login shell's PATH. The most recent result is cached on the host, and every panel, pill, and the MCP surface reads that cached verdict, so opening ten agents never probes your servers ten times. Refresh and Check now still run a fresh probe and wait for it. Configure it under Settings → Plugins → Paseo MCP → Health checks, or run the Configure MCP health checks command.

Setting Default Meaning
Check servers in the background on Probe on a timer, not only when Refresh is pressed
Interval 10 minutes Time between background checks (1 to 1440 minutes)
Composer chip on Show a Connectors chip on a chat when one of its connectors is failing or needs sign-in
Chat notices: sign-in problems on Add one card to a chat when an MCP tool call there fails for lack of a sign-in (see In-chat sign-in card)

Settings live on the host at $PASEO_HOME/plugin-settings/paseo-mcp/health.json; an unreadable or invalid file means the defaults apply. The daemon log shows health check: N servers, M need attention, K OAuth after each pass.

Background passes only run while a Paseo app is connected to the daemon (any RPC from the app counts; the composer chip reads the cached verdict once a minute). With no app connected for 15 minutes the log says health check: no app connected, pausing until one is and nothing is probed; the first read after that answers with the last verdict and refreshes it in the background. A pass that fails is retried after 1, 2, 4 … minutes, never more often than the interval. Probes run eight at a time. The tool lists follow the same rules on six times the interval.

Opening a panel never waits on a probe. The cached read answers at once: with the last verdict when there is one, or with "checking" on a host that has none yet. When there is no verdict, only a saved one, or one older than the interval (after a pause), a pass starts in the background and the panel reads again every second until the verdict is in. If a read fails meanwhile, the panel backs off (2, 4, 8 … minutes, counted from the last successful read) rather than asking every second.

The last verdicts and tool lists are also kept on disk, so a plugin reload, an update or a daemon restart does not start from nothing. After a restart the first read shows the saved verdict and tool lists marked "as of HH:MM (saved before the plugin restarted)" and checks again straight away. The file is $PASEO_HOME/plugin-data/paseo-mcp/cache.json (the same plugin-data/<id> place Shared Browser uses), written atomically once per completed pass, 0600, and only while an app is connected. It holds no URLs, query strings, header values, env values or command arguments: servers are keyed by name and a hash of their URL, headers and command, and notes are the same redacted ones the panels show. It does hold what the panels show next to each server: account labels (with the account's email), config file paths and the stdio command name. Treat it like ~/.claude.json, which holds all of that and more: don't share it. A tool list last read more than 7 days ago is not restored, and a server that stops answering keeps its old list for at most 7 days. A corrupt file, or one from another version, is ignored. Deleting it is safe.

Two calls back into the Paseo daemon sit on the panel's path: its project list and its provider settings. A busy daemon takes seconds to answer them (paseo-mcp.auth took 10.0 s in a daemon log). Since 0.15.0 both go through one cache (server/daemon-cache.ts): a read answers from the last copy at once and refreshes it in the background when it is older than 15 s; one call is in flight at a time; a failed call keeps the last good copy and isn't retried from a read for 15 s. Only the very first read, or one that needs a project registered since (adding a server to a project Paseo just learned about), waits. Refresh in the Add gallery or on sign-in re-reads both in the background, and changing Paseo's tool settings from the Connectors tab marks the provider settings out of date.

Status Meaning Needs attention
ok The endpoint answered initialize, or is alive and rejected the anonymous request the way the protocol says to (400, 405, 406, a redirect) no
auth-required 401 or 403: an OAuth server. This is how a working server answers a probe with no grant; the editor holds the sign-in. Shown as informational no
warn 404 (wrong path, or an inactive n8n workflow) or a 5xx from the server yes
down Connection refused or reset, DNS failure, TLS rejected, or no answer in 5 seconds yes
binary-missing A stdio command not found on PATH yes
unknown No readable definition no

Health notes never contain a URL, query string, token, or header value; the verdict is redacted before it is cached, shown, or logged. Missing sign-ins are reported per account on each server's page (and counted on its card), which reads each editor's own grant list.

Composer chip

A chat's composer gets a Connectors chip only when a connector that chat loads needs you (0.19.1; trimmed in 0.19.2): 2 broken or 1 needs sign-in, short enough not to be cut off. A connector switched off for the chat's project (or everywhere), a tool that comes with the Codex app, or a sign-in for a connector no settings file lists doesn't count, and only a running chat or one active in the last hour gets a chip. Press it, or type /connectors in the composer, to open that chat's Connectors panel: what needs a look (broken first), the connectors it loads with their switches, and folded below them what it hasn't used, the project's own connectors and the technical details. Paseo shows the chat's context use itself, so the panel doesn't estimate it. Turn the chip off with the Composer chip setting.

Since 0.19.2 the plugin no longer registers /mcp (Paseo can't hide a slash command, and two entries for one panel were noise), so the provider's own /mcp is untouched. Use /connectors.

The chip never makes a network call or starts a process: its estimate uses cached tool counts only.

Tools each server exposes

Each server's card on the Servers section (the Tools disclosure) and the Tools card on its page show what a server would hand an agent: every tool's name, title, description and the arguments it takes (required ones starred), plus the server's own name and version from the handshake. The host asks each HTTP server with the same two requests a client sends, initialize then tools/list, a few servers at a time, and caches the answer; the section reads the cache, and Refresh asks again.

Nothing is guessed. A server that cannot be asked says why:

Shown as Meaning
N tools The server answered tools/list; expand Tools on the card to read them
sign in to list An OAuth server: it answers an anonymous request with 401 and lists its tools only to a signed-in editor
runs on demand A stdio (command) server: its tools are only knowable while an agent has it running, and the plugin does not start processes
not listed The endpoint answered but not with MCP (a web page at the URL, a JSON-RPC error, a timeout), with the redacted reason

A server that listed its tools before and does not answer on a later pass (a timeout, a restart, an error page) keeps its earlier list, marked "as of HH:MM" with the reason the new ask failed, so a blip never shrinks the tool count on the chip. An edited URL starts over.

Tool descriptions are text the server controls; they are flattened to one capped line before they are stored or shown. The data carries each tool's name and argument list so a per-tool policy control can be added alongside it later.

Paseo tools

Every Paseo daemon runs its own MCP server, paseo, and with daemon.mcp.injectIntoAgents on it adds that server to every agent it starts. It hands each agent 39 tools for agents, terminals, schedules, heartbeats and workspaces, plus 22 browser_* tools when browser tools are on: 61 in all (Paseo 0.9.1). The Built-in tools fold-out at the bottom of Connectors shows and changes what agents get:

Control Daemon setting Default
Add to every agent on this host mcp.injectIntoAgents, the app's Settings → Orchestration → Enable Paseo tools off when unset in config.json
Per provider providers.<id>.paseoTools.enabled on (absent means on)
Per tool providers.<id>.paseoTools.disabledTools, bare names such as list_agents none
Turn off browser tools browserTools.enabled off

The host-wide switch changes every agent on the host, so it asks twice. The tool switches write to every provider at once under All providers, or to one provider when one is picked. A tool that is off for only some providers says which. mcp.enabled (the --no-mcp flag) turns the server off altogether; the config API cannot change it, so the card only says so when it is off.

Every change goes through the daemon's config API (paseo.config.patch), never by editing config.json. Only the fields that change are sent. disabledTools starts from the current list, so names the plugin does not know are kept, and a provider patch carries only paseoTools, so extends, env and every other field stay as they are. The config is read back after the write and the card shows what was saved. The daemon copies the policy into an agent when it starts, so a change applies to agents started after it; a running agent keeps the tools it started with.

Where the tool list comes from:

  • Live, when the daemon has no password. /mcp/agents is then open, so the host asks it for tools/list at the address the daemon bound, which Paseo writes to listen in $PASEO_HOME/paseo.pid. When that file is missing or has no address, it uses PASEO_LISTEN, daemon.listen in config.json, or 127.0.0.1:$PORT (default 6767). The card says "Tool list live from this host". The host asks once at most every ten minutes, in the background, or now when you press Refresh. A failed ask is not retried before then, and a good list is kept until a newer one replaces it.
  • The catalogue in shared/paseo-tools.ts, copied from Paseo 0.9.1, otherwise. With a password (PASEO_PASSWORD or daemon.auth.password), /mcp/agents wants a per-run token that only agents get. The plugin never sends or guesses one. The card says "as of Paseo 0.9.1". When the host runs another Paseo version, the card and the Overview line add: "This list is from Paseo 0.9.1; this host runs X, so new tools may be missing and removed ones may still show." The version is read from the @getpaseo/server package the daemon started the plugin from.

The card also points to Add project servers to agents for servers from a project's .mcp.json.

The same count feeds everything else: the Overview has a Paseo tools line, the workspace and agent panels list Paseo tools (built in) among what an agent loads, and the composer chip counts it as one more server with its tools, for the agent's own provider only.

Project level vs user level

Each health result carries the configs that define the server: an editor's global config (~/.claude.json, ~/.codex/config.toml, …) is user level and affects every workspace; a .mcp.json in a registered project is project level. The workspace and agent panels list issues under three headings — this project, user config, other project — and tag each project server row with its health. A server defined at both levels is probed once, using the editor's copy, since that is what the editor actually runs.

Workspace context

The Workspace connectors tab and each agent's Connectors tab lead with what an agent started in that workspace actually loads, counted from the same files the CLI reads:

  • the workspace's .mcp.json (read natively by Claude Code, or added by Add project servers to agents for the chosen providers),
  • Claude Code's per-directory ("local") entries for that workspace,
  • the editor's user-level config (~/.claude.json, ~/.codex/config.toml, …), which loads in every workspace.

A name defined at two levels is counted once. The workspace tab shows the heaviest wired editor; the agent tab shows the agent's own provider.

The agent tab also lists servers the agent was actually started with that no editor config or Paseo tools explain, tagged Added when created: other plugins add these from their own agent.create hooks (Shared Browser's shared-browser, for one), and whoever creates an agent can pass its own. Each shows its name and transport; an HTTP one shows its tool count from the same probe the Tools view uses (asked in the background, at most every ten minutes), a stdio one says "runs on demand". They count towards that agent's budget, at their real tool count where the probe has one and five otherwise. They come from the record the daemon writes after every hook ran ($PASEO_HOME/agents/<project dir>/<agent id>.json, read-only); Paseo's agent API does not return an agent's servers, and a hook only sees what plugins earlier in id order added. The workspace tab has no agent, so it cannot know these and does not count them. Under the count: how many are stdio (a child process per agent session) versus http (no local process), how many need attention here versus elsewhere, and a Running now section listing the MCP server processes currently running for this workspace with their resident memory. That section reads the daemon host's process table (ps, /proc on Linux, lsof on macOS), attributing a server to the workspace when its agent, or the server process itself, works in the workspace directory. When the table cannot be read the panel says so rather than showing a zero.

Context meter

The agent tab's Context section estimates what that agent's MCP tool definitions cost, the same number the chip shows:

Server Counted as
Listed HTTP server Measured: each tool's name, description and input schema as JSON, ÷ 4, from the raw tools/list answer (before descriptions are cut for display)
Added when created, HTTP Measured the same way from its own probe
Paseo tools (built in) 135 tokens per tool, measured on Paseo 0.9.1's own tools/list (61 tools, about 8,250 tokens)
Anything not listed (stdio, OAuth before sign-in, unreachable) 2,500: five tools at 500, an assumption between Paseo's measured 135 and the roughly 1,500 per tool Anthropic reported for large servers

A server switched off for the workspace is not counted. Every number is an estimate and says "≈" or "~". When the agent has reported its context use, the section adds "This chat: 360k of 1M context; MCP definitions ≈38k of that." With tool search on, the definitions load on demand, so the chip says "deferred" and the panel says so instead of claiming a share. Heaviest servers lists each server, biggest first, with how it was estimated.

Below that, from the agent's own timeline: Used in this chat ("supabase ×4, linear ×1") and Loaded but unused with their names. The host reads the timeline through Paseo's plugin API, the newest 2,000 items at most, in the background, and keeps the answer a minute; the panel says when older items were not read. Claude agents' tool calls are named mcp__<server>__<tool> and Codex agents' <server>.<tool>; both are mapped to the servers the agent loads. A name two loaded servers could own counts for both.

Turn off the unused ones for this workspace uses the same per-workspace switches as the rows below it. It asks first, names exactly the servers that change, and applies to new sessions only. It only offers servers that have a switch for this provider and are on now; unused servers without one are named and left alone. It is never offered when the chat is longer than the 2,000 items read or the last read failed; the panel says which. On confirm the host reads the whole chat again and switches off only if its own list matches the one you confirmed; otherwise it switches nothing and asks you to review again. A call to a server outside the loaded list counts as a use of any loaded server it could belong to.

In-chat sign-in card

With Chat notices: sign-in problems on (the default), when an MCP tool call in a chat fails with an auth-shaped error (401 or 403, "unauthorized", "invalid_token", "needs authentication"), the host adds one small card to that chat: "linear needs sign-in", with Connect. Connect opens the agent's MCP panel with that server's sign-in rows first, the same OAuth flow as its row. The card carries the server and provider names only, never the error text.

There is at most one card per server per chat, even across plugin restarts: the first time a chat is seen, its timeline is read back for an earlier card, and if that read fails nothing is added. The host checks each finished turn only while a Paseo app is connected; the panel's chat read catches failures from before that. Paseo's own server never gets a card.

Context-budget warning

Without tool search, every server's tool definitions are sent to the agent with its first prompt. Cursor stops at 40 tools and warns that some models ignore more. The panel warns at 8 servers ("getting heavy", about 40 tools at five per server) and flags 16 or more as "over budget" (about 80 tools, well over 100K tokens before any work, the range where Paseo-launched agents fail with "Prompt is too long"). The warning lists the user-level servers the agent loads, since those are the ones that can be moved into one project's .mcp.json or removed from the editor config, with a button to MCP management. The thresholds live in shared/budget.ts.

The server lines assume five tools per server. Paseo's built-in server is counted by its real number of tools (61, or 39 without browser tools), so a second check counts tools: about 40 is "getting heavy" and 80 "over budget", the same lines as 8 and 16 servers at five each. The worse of the two checks wins. Without Paseo tools both checks always agree, so the numbers are unchanged.

Tool search

Claude Code's MCP tool search is on by default: it loads tool names at the start and definitions only when needed, so a long tool list costs little context. The host judges it per provider (shared/tool-search.ts) and the panel says which applies:

Verdict When Budget
on A Claude-based provider with none of the settings below, or ENABLE_TOOL_SEARCH set to true, auto, or auto:N with N up to 10 (auto's own threshold) The tool check does not raise the tier; the server check still does (stdio processes and connections are real). One line: "Claude Code's tool search is on: tool definitions load up front only while they fit in 10% of the context window, and on demand past that."
off A custom ANTHROPIC_BASE_URL (any host but api.anthropic.com), ENABLE_TOOL_SEARCH=false, or CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS, which Claude Code says keeps tool search off even with ENABLE_TOOL_SEARCH set, unless managed settings set it (source 5). Also every non-Claude CLI except Codex. As before, and the warning names the reason: "AI Router re-routes this provider through OmniRoute (custom ANTHROPIC_BASE_URL) whenever its endpoint is up, so Claude Code's tool search is off and all 76 tool definitions load with the first prompt."
unknown Codex (it defers MCP tools only when the model supports tool search), Claude on Google Cloud's Agent Platform or Microsoft Foundry, ENABLE_TOOL_SEARCH=auto:N with N above 10 (tools load up front until they fill N% of the context window) or outside 0-100, or a provider whose CLI is not known As before, with the reason

Sources, later ones winning:

  1. The daemon's own environment.

  2. The provider entry's env in the daemon config. A provider with extends: "claude" (or another built-in) starts with that built-in's env and puts its own on top, as Paseo does.

  3. AI Router. It routes the built-in claude provider while its routeAgents setting is on (read-only from $PASEO_HOME/plugin-settings/ai-router/routing.json; a missing or unreadable file, or a version other than 1, means not routed, as AI Router reads it). It always routes its own ai-router provider. A routed session gets both ANTHROPIC_BASE_URL and CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1.

  4. The env in Claude Code's own settings files: the user settings.json in the provider's CLAUDE_CONFIG_DIR (or ~/.claude), then, on the workspace and agent panels, the workspace's .claude/settings.json and .claude/settings.local.json. Claude Code's docs say a value there "overwrites the same variable exported in your shell, and when more than one settings file sets a variable, the highest-precedence one applies" (settings reference, env); local beats project beats user (settings precedence). An empty value cancels one set lower down. A missing or broken file counts as not there.

  5. Claude Code's managed settings, above everything: managed-settings.json and then every *.json in managed-settings.d/ (alphabetical, hidden files skipped), in /Library/Application Support/ClaudeCode/ on macOS, /etc/claude-code/ on Linux and WSL (managed settings). Read for Claude-based providers only. The value that keeps tool search on under CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS and through a gateway (AI Router's routing) is a managed ENABLE_TOOL_SEARCH="force". The docs only say "on Claude Code v2.1.227 or later, managed settings can keep tool search on" (env vars); Claude Code 2.1.280 checks for exactly force (isToolSearchForceOverride, first-party connections only, and a custom ANTHROPIC_BASE_URL still counts as one). Tested on a fleet on 2026-09-25: with {"env":{"ENABLE_TOOL_SEARCH":"force"}} in /etc/claude-code/managed-settings.json, routed chats sent 13 tools up front instead of 70; a managed "true" changed nothing. So the panel says on for force ("…set ENABLE_TOOL_SEARCH=force, which keeps tool search on through a gateway and under CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS (Claude Code v2.1.227 or later)"), and lets a managed true/auto count only where the betas flag is not set. On a cloud provider (Bedrock, Mantle, Claude Platform on AWS, Agent Platform, Foundry) force has no effect. A missing or broken file counts as not there. The macOS configuration profile, the Windows registry and server-managed settings are not files and are not read.

Health issues under the count are split the same way: servers this workspace loads first, each with an Open button that lands on that server in MCP management, and problems elsewhere folded away.

Sign-in state and Codex

Claude Code records which servers each account still has to authorize in its own config dir, so its sign-in state is a file read. Codex keeps MCP OAuth grants in the OS keyring, or, where there is none (a Linux container, or mcp_oauth_credentials_store = "file"), in $CODEX_HOME/.credentials.json. The plugin reads that file directly: a usable grant there shows as connected at once. Nothing from it leaves the host except that yes/no; no token is read into a response or a log.

For everything the file cannot answer (keyring grants, whether a server wants OAuth at all), Codex is asked with codex mcp list --json, which checks each server over the network and can take seconds. So it never runs while a panel waits:

  • Panel reads answer at once from the file and Codex's last answer. Only the Servers section and a Codex agent's own panel start a check, in the background, and only when the last answer is missing, older than 30 minutes, or that account's config.toml or grant file changed. Refresh and a finished sign-in ask again.
  • One Codex process at a time, 20 second limit each. A failed check keeps the last good answer, shown with its time and the reason, and is retried after 1, 2, 4 … minutes (up to 30).
  • The workspace panel never starts Codex; it shows Claude sign-in rows only.

"failed to clean up stale arg0 temp dirs"

Codex prints this on every start when it cannot tidy $CODEX_HOME/tmp/arg0: something in there belongs to another user, usually root, left by running codex through docker exec without --user. It is harmless. The plugin captures Codex's output instead of passing it to its log, and explains the warning once per account with the exact directory. To silence it, fix the ownership once, as root on the daemon host (in the paseo-dev-stack container: docker compose exec --user root paseo chown -R paseo:paseo /home/paseo/.codex/tmp).

AgentLink integration

AgentLink is optional. Standard ~/.claude, ~/.codex, ~/.kimi-code, and ~/.grok setups work on their own.

When AgentLink account directories exist under ~/.agent-link or ~/.agent-auth, Paseo MCP discovers them automatically. It also reads hand-managed ~/.claude-accounts and ~/.codex-accounts directories. OAuth remains per account and is never copied.

Development

npm install
npm run typecheck
npm test
paseo plugin add /absolute/path/to/paseo-mcp --link

npm run bench runs the load harness (tests/perf/harness.ts): the plugin in its own process against a sandbox HOME with a fake codex, playing daemon and app, and prints processes started per minute, event-loop stalls, and RPC timings with the panel closed and open. npm run bench -- --quick takes about a minute. --daemon-delay 8 makes the fake daemon take 8 s to answer its project list and provider settings, the way a busy daemon does (0.15.0).

Paseo's plugin host supplies the runtime. The npm dependencies are development types only.

Scanned 15 Oct 2026, 12:32 UTC from itsjustanks/paseo-mcp.