| name | mcp-fleet |
| description | This skill should be used when the user explicitly invokes `/oracle:mcp-fleet` to set up, extend, or wire multi-workspace MCP server access from Claude Code. Drives a Chrome-profile-isolated OAuth/token discovery flow per workspace (Slack, Linear, Notion, GitHub orgs, Atlassian sites), persists credentials to a mode-600 store under ~/.claude/oracle/mcp-fleet/, builds a Claude-Code-native multi-server .mcp.json, and prints copy-pasteable wiring instructions. Sidesteps the OAuth-token-collision bugs in anthropics/claude-code#39952, microsoft/vscode#293533, and atlassian/atlassian-mcp-server#23 by giving every workspace its own Chromium --user-data-dir and per-instance API tokens. Triggers on the literal slash-command form. Subcommands: list, add <service> [label], remove <service> <label>, build, publish. Use when this capability is needed. |
/oracle:mcp-fleet
Multi-workspace MCP plumbing for Claude Code. Discovers per-workspace
credentials in isolated Chrome profiles (so OAuth providers can't
collide them), generates a multi-server .mcp.json, and prints how to
wire it.
Why this exists
Vendor remote MCP servers and Claude Code's OAuth handling both share a
documented limitation: OAuth tokens are keyed by server-name or by
URL-origin and collapse across workspaces -- authenticating into
the second Slack workspace overwrites the first. Filed bugs:
anthropics/claude-code#39952, microsoft/vscode#293533,
atlassian/atlassian-mcp-server#23. The official Atlassian workaround
is "use separate browser profiles per site".
This skill operationalises that workaround. Every workspace gets its
own Chromium --user-data-dir for the OAuth flow, and we extract
per-instance API tokens (xoxc/xoxd, lin_api_*, secret_*,
github_pat_*, Atlassian API token) so the resulting .mcp.json
stores credentials in env rather than relying on Claude Code's
collision-prone OAuth keyring.
Inputs
$ARGUMENTS selects the subcommand. Default is list.
What this skill does
Run the steps below in order. State what is happening at each step so
the user can interrupt if something looks wrong.
Step 1 -- Resolve the plugin script root
SCRIPT_ROOT=""
if [ -n "${CLAUDE_PLUGIN_ROOT:-}" ] && [ -d "$CLAUDE_PLUGIN_ROOT/scripts/mcp-fleet" ]; then
SCRIPT_ROOT="$CLAUDE_PLUGIN_ROOT/scripts/mcp-fleet"
fi
if [ -z "$SCRIPT_ROOT" ]; then
PLUGIN_PATH=$(find "$HOME/.claude/plugins/cache" -maxdepth 3 -type d -path "*/oracle/*" 2>/dev/null | sort -V | tail -1)
if [ -n "$PLUGIN_PATH" ] && [ -d "$PLUGIN_PATH/scripts/mcp-fleet" ]; then
SCRIPT_ROOT="$PLUGIN_PATH/scripts/mcp-fleet"
fi
fi
if [ -z "$SCRIPT_ROOT" ]; then
echo "Could not resolve oracle plugin script root." >&2
exit 1
fi
State the resolved path. Confirm Node is on PATH (node --version,
require >= 18).
Step 2 -- Dispatch on the subcommand
Parse $ARGUMENTS. Recognised forms:
| Subcommand | Underlying call |
|---|
list (or empty) | node "$SCRIPT_ROOT/detect.mjs" --list |
add <service> [label] | node "$SCRIPT_ROOT/detect.mjs" <service> [label] |
remove <service> <label> | node "$SCRIPT_ROOT/detect.mjs" --remove <service> <label> |
build | node "$SCRIPT_ROOT/build-matrix.mjs" |
publish | node "$SCRIPT_ROOT/publish.mjs" |
<service> must be one of: slack, linear, notion, github,
atlassian. <label> is a user-chosen identifier used as the MCP
server-name suffix (e.g. acme-prod produces slack__acme-prod).
For add, the script will launch a headed Chrome window with an
isolated profile dir under
~/.claude/oracle/mcp-fleet/chrome-profiles/<service>/<label>/. The
user signs in to the one workspace they want to bind to that
label, then either pastes the resulting token at the prompt or, for
Slack, lets the script auto-extract the cookie. Tokens land in
~/.claude/oracle/mcp-fleet/workspaces.json (mode 600).
After every add and remove, build-matrix.mjs re-runs
automatically and rewrites ~/.claude/oracle/mcp-fleet/mcp-fleet.json.
Step 3 -- Run the requested subcommand and stream output
Run the resolved command. Do not buffer -- the discovery flow is
interactive and the user must see prompts in real time.
node "$SCRIPT_ROOT/<resolved-script>" <args>
If the command exits non-zero, surface the stderr message verbatim and
stop -- do not retry without user direction.
Step 4 -- After a successful add or build, recommend publish
Tell the user:
Discovery complete. Run /oracle:mcp-fleet publish to see the three
ways to wire mcp-fleet.json into Claude Code.
For publish itself, just print the script's output -- it is the
wiring instructions.
Per-service notes
Slack. Uses cookie extraction (the d cookie is the xoxd token,
and the xoxc token is read from localStorage.localConfig_v2.teams.<TEAM_ID>.token)
because Slack restricts MCP-eligible apps to the Marketplace and
internal apps. Compatible with slack-mcp-server@1.2.3 (korotovsky).
No app registration needed.
Linear. Personal API key per workspace. The Linear UI shows the
key value once at creation -- the script cannot re-extract a forgotten
key. Compatible with mcp-server-linear@1.6.0 (dvcrn -- note the
package is unscoped on npm, not @dvcrn/...), with TOOL_PREFIX
auto-set to linear_<label>_ so concurrent workspaces don't collide
on tool names.
Notion. Internal integration token per workspace, prefix ntn_
(older secret_ prefix is also accepted). The user must share at
least one page or database with the integration after creating it, or
the MCP server will see an empty workspace. Compatible with
@notionhq/notion-mcp-server@2.2.1.
GitHub. One fine-grained PAT per org, scoped to that org's
resource owner. Multiple orgs = multiple tokens = multiple MCP server
instances. Recommended scopes: Contents R, Issues RW, Pull requests RW,
Metadata R. The matrix uses the official public Docker image
ghcr.io/github/github-mcp-server; Docker must be installed and
running for these entries to start. To swap to the Go binary install
path, edit the github-pat spec in build-matrix.mjs.
Atlassian. API token + email + site URL per Cloud site. Uses
mcp-atlassian==0.21.1 (sooperset) via uvx for per-instance
auth, sidestepping atlassian/atlassian-mcp-server#23. Requires
uv on PATH -- install via
curl -LsSf https://astral.sh/uv/install.sh | sh. Note: the npm
package named mcp-atlassian is by a different author and is not
what we use.
Storage layout
~/.claude/oracle/mcp-fleet/
├── workspaces.json # source of truth, mode 600
├── mcp-fleet.json # generated, .mcp.json-compatible
└── chrome-profiles/
├── slack/
│ ├── acme-prod/ # one profile per (service, label)
│ └── personal/
├── linear/
│ └── acme/
...
Failure modes and recovery
If a Chrome profile gets stuck (cookies expired, two-factor changed),
delete its directory under chrome-profiles/<service>/<label>/ and
re-run /oracle:mcp-fleet add <service> <label> -- a fresh profile
will be created.
If workspaces.json becomes corrupt, the store will refuse to load
and detect.mjs will print the path. Fix the JSON or delete and
re-discover.
If mcp-fleet.json is missing keys after build, the matrix builder
emits warnings to stderr -- the most common cause is a partial add
that exited mid-flow. Re-run add for that workspace.
Caveats
- Per-service token-extraction is best-effort. Vendors change DOM and
flash-modal behaviour without notice. The scripts always fall back
to manual paste at a prompt.
- Pinned upstream MCP server versions in
build-matrix.mjs are
verified at the date noted there; bump deliberately.
- v0 wires every workspace as its own stdio child process under Claude
Code. With many workspaces this becomes a footprint cost. v1 will
optionally route through a single MetaMCP container -- the
experimental scaffold lives at
scripts/mcp-fleet/templates/docker-compose.yml.tmpl.
Source: Executioner1939/claude-code-skills — distributed by TomeVault.