Skill Router — connect your agent

Skill Router is this organization's library of skills: written instructions for doing a particular kind of work the way the organization does it. It is an MCP server. Once connected, an agent can list, search and pull skills, report how they were used, and propose edits.

https://THIS-HOST stands for the scheme and host you fetched this page from. Substitute it everywhere below, or fetch /llms.txt from the same host: it is this document with the real URLs filled in.

Installing it is two things, and it is not installed until both are done:

  1. Connect the server — Install, below.
  2. Wire up the stubs — Then make the skills trigger, further down: something in the repository that makes every session sync one SKILL.md stub per skill — a plugin in Claude Code/OpenCode V2, an extension in Copilot CLI, a session-start hook in Codex. Without it the server is connected and no skill ever triggers. A hook does not wait for the sign-in: write it in the same go.

Install

Install it for the project you are working in — not globally. The first thing under each client below is its per-project form: a file in the repository, or a command run at its root. Use that one. The stubs and the hook further down live in the repository too, and a server registered for the whole machine turns up in every unrelated project the person opens.

Each client's global (user-wide) form comes after it and is marked. Use it only if the person asked for the server on every project. "Install this" does not ask for that, and a global command being the shorter one is not a reason either. If you are not in a project, ask which one it is for.

Where a step is a file that already exists, add the entry to it and keep everything else.

Give the client the endpoint URL and nothing else. It discovers the sign-in by itself. Find the client you are running in below.

VS Code

.vscode/mcp.json in the repository. Safe to commit.

{
  "servers": {
    "skill-router": {
      "type": "http",
      "url": "https://THIS-HOST/mcp"
    }
  }
}

Global — only if the person asked for it. From a terminal, for your user profile: every workspace you open.

code --add-mcp '{"name":"skill-router","type":"http","url":"https://THIS-HOST/mcp"}'

Copilot CLI

.mcp.json at the root of the repository. Safe to commit. Copilot CLI reads it once the folder is trusted.

{
  "mcpServers": {
    "skill-router": {
      "type": "http",
      "url": "https://THIS-HOST/mcp"
    }
  }
}

Global — only if the person asked for it. For every project on this machine. It is saved to ~/.copilot/mcp-config.json.

copilot mcp add --transport http skill-router https://THIS-HOST/mcp

Claude Code

Run once, at the root of the repository. It writes .mcp.json there. Safe to commit.

claude mcp add --transport http --scope project skill-router https://THIS-HOST/mcp

Global — only if the person asked for it. For every project on this machine.

claude mcp add --transport http --scope user skill-router https://THIS-HOST/mcp

Then run /mcp inside Claude Code and choose skill-router to sign in. A server from .mcp.json is approved once before it is used.

OpenCode V2

opencode.json at the root of the repository. Safe to commit.

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "servers": {
      "skill-router": {
        "type": "remote",
        "url": "https://THIS-HOST/mcp"
      }
    }
  }
}

Run /mcps inside OpenCode and choose skill-router to sign in. Only if it is wanted on every project: the same entry goes in ~/.config/opencode/opencode.json instead.

Codex CLI

.codex/config.toml in the repository. Safe to commit. Codex reads it once the project is trusted.

[mcp_servers.skill-router]
url = "https://THIS-HOST/mcp"

Then sign in, from the repository.

codex mcp login skill-router

Global — only if the person asked for it. For every project on this machine: it is saved to ~/.codex/config.toml.

codex mcp add skill-router --url https://THIS-HOST/mcp
codex mcp login skill-router

Anything else

Register a remote / Streamable HTTP MCP server at https://THIS-HOST/mcp, in the client's per-project configuration if it has one. A client that implements MCP authorization needs no further configuration.

What happens next — tell the person

The first connection opens a browser on https://THIS-HOST. A person has to finish it: they sign in with the GitHub account that belongs to the organization, check the name of the client that is asking, and press Approve. You cannot do this step for them. If you ran the install command, say that a browser window needs their approval rather than waiting silently.

The connection then appears under Settings → API tokens on https://THIS-HOST, where it can be revoked.

Then make the skills trigger: stubs, synced at session start

This is the second half of the install. Connecting the server is not enough on its own. An MCP tool is only called when you think to call it, whereas a native skill's description is in front of you on every turn. So a repository that uses the library keeps one stub SKILL.md per skill in .claude/skills/ — which Claude Code, GitHub Copilot and OpenCode all read: the skill's name and description as the trigger, over a body that says to call get_skill. A stub holds none of the skill's content, so it never goes stale and every real use is still recorded.

Codex does not read .claude/skills/. Its stubs go in .agents/skills/, and a repository people open with Codex and with another agent keeps both sets.

They are kept in step through the MCP server you are already signed in to: by code that runs at session start in Claude Code and Codex, at the first prompt in Copilot CLI/OpenCode V2, by you on other Copilot surfaces. No API token for the CLI integrations.

1. Make it happen at the start of every session. The agents differ, so set up the one in use — or each of them, in a repository people open with more than one. Everything here goes in the repository: every path is relative to its root, not to the home directory.

Codex's setup is only files: on this page, or, for the scripts its hook runs, fetched from this host. Write them whether or not the server is connected yet: they are what gets the stubs written in every session from the next one on. If the server's tools are already there, setup_session_sync hands you the same files. Claude Code's, Copilot CLI's and OpenCode V2's come from the server, and so follow the sign-in.

Claude Code

Call setup_session_sync on the skill-router server and write the files it returns. They are a plugin, in .claude/skills/skill-router-sync/: code that runs at session start, calls sync_skill_stubs over the connection Claude Code is already signed in to, and writes the result. No model turn and no API token.

GitHub Copilot CLI (VS Code/cloud fallback instructions)

For the copilot CLI, call setup_session_sync with agent: copilot. Write the returned extension files in .github/extensions/skill-router-sync/. Start copilot --experimental; the bundled SDK runs the sync through the existing authenticated MCP connection, without a model turn, npm install, or separate router token. This follows MCP sign-in.

.github/copilot-instructions.md — if it exists: Replace only the Skill Router section, or append if absent; preserve unrelated instructions.

## Skill Router

In Copilot CLI only: the project extension automatically syncs at the first prompt. Do not perform a model-driven sync. Use its router session_id on later MCP calls; do not invent or persist an ID. If it did not run or failed, say so once and continue without router tools. Check /extensions manage and /mcp.

In VS Code or the cloud agent only (not Copilot CLI): Skill Router: before anything else in this session, call the sync_skill_stubs tool on the skill-router MCP server. Pass as have the stubs object from .claude/skills/.skill-router.json, or leave have out if that file does not exist. Follow the instructions in the answer, and keep the session_id it gives you: pass it as session_id on every later call to that server in this session. Then carry on with what the user asked. If the skill-router MCP server is not connected, say so once and carry on.

Codex CLI

.codex/hooks.json — if it exists: Merge the SessionStart entry into the existing hooks; keep everything else. An entry there whose command is echo 'Skill Router: …' is the older form of this hook: replace it.

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup",
        "hooks": [
          {
            "type": "command",
            "command": "node .codex/scripts/sync-skills.mjs --codex-login --out .agents/skills",
            "timeout": 120,
            "statusMessage": "Syncing Skill Router stubs",
            "additionalContextLimit": 500
          }
        ]
      }
    ]
  }
}

AGENTS.md — if it exists: Append the section; keep everything else. A ## Skill Router section for Codex that is already there is replaced by this one: an older one tells the model to sync, which the hook now does.

## Skill Router

For Codex only. Other agents are set up elsewhere and should skip this.

The command hook in `.codex/hooks.json` runs `.codex/scripts/sync-skills.mjs --codex-login` when a session starts. It calls `sync_skill_stubs` through Codex's app-server using the existing Skill Router MCP login, then writes the returned changes to `.agents/skills` locally. Fetching and writing are automatic and require no model turn or separate router token. The helper uses an ephemeral thread with hooks disabled to prevent recursion.

The hook adds this conversation's router `session_id` to the session context. Pass that exact value on every later call to the skill-router MCP server. Do not repeat the sync from the model, invent an ID, or save the ID to disk. If the hook did not run or failed, mention that once and continue with the user's task; do not substitute a model-driven sync.

The hook requires Node and a Codex CLI supporting the experimental `mcpServer/tool/call` app-server API. Codex manages authentication and refresh; the scripts do not read or export OAuth secrets. Authentication failures display a warning with `codex mcp login skill-router` as the recovery command. Other failures return a nonzero exit status. Each run writes sanitized diagnostics under `.codex/logs/skill-router-sync/`.

Changed command hooks must be reviewed and trusted through `/hooks` before Codex runs them. SessionStart follows Codex's session timing; this does not guarantee skill discovery before the first prompt is submitted.

Fetch each of these and write it at its path exactly as served, replacing one that is already there. They are code: do not reformat, shorten or correct them.

curl -fsSL https://THIS-HOST/codex-hook/sync-skills.mjs --create-dirs -o .codex/scripts/sync-skills.mjs
curl -fsSL https://THIS-HOST/codex-hook/sync-skills-mcp.mjs --create-dirs -o .codex/scripts/sync-skills-mcp.mjs
curl -fsSL https://THIS-HOST/codex-hook/codex-mcp-client.mjs --create-dirs -o .codex/scripts/codex-mcp-client.mjs
curl -fsSL https://THIS-HOST/codex-hook/sync-log.mjs --create-dirs -o .codex/scripts/sync-log.mjs

Any other way of fetching them will do. In Windows PowerShell, curl.exe takes the same arguments.

OpenCode V2

Call setup_session_sync on the skill-router server with agent: opencode and write the returned files to .opencode/plugins/skill-router-sync/. Follow its dependency-install command. OpenCode auto-discovers that plugin package; do not also add it to the plugins array. It syncs through the authenticated MCP tool registry before the first prompt, with no model turn or separate API token.

2. Sync now, if the server's tools are there. Call sync_skill_stubs on the skill-router server. Pass have: the stubs object from .claude/skills/.skill-router.json — leave it out the first time, when that file does not exist. In Codex, pass directory: .agents/skills as well, and take have from the state file there. The answer is only what changed, with instructions for writing it; follow them. Pass project or tags to narrow it, and server if this MCP server is registered under a name other than skill-router.

In Copilot CLI or OpenCode V2, do not substitute this model-driven sync for the extension/plugin. Start a new session and let the first prompt run the automatic sync. If you want stubs during installation itself, install_skill_stubs is the separate one-time install tool.

If the tools are not there — the sign-in is not finished, or the client only loads a new server when it restarts — step 1 still stands for the files you could write. Say that the first sync will happen at the start of the next session, and what the person has to do before then. In Claude Code and Copilot CLI/OpenCode V2 step 1 is itself still to do: say that too.

The answer also carries a session_id. Pass it as session_id on every other call you make to the server in this conversation: it is what counts the conversation as one session and lets a report_skill_usage attach to its get_skill. Every sync also writes one stub that is not from the catalogue, using-skill-router, which keeps that rule in front of you.

3. Ask whether to commit. A stub directory holds no secret and no skill content, so committing it — stubs, state file, and the hook files above or the Claude/OpenCode plugin — gives the whole team the triggers from their first session. The alternative is ignoring the directory and letting each person's first session fill it. Either works; it is the person's call.

A skill with no description gets a stub that will rarely trigger. The fix is to write the description in Skill Router, not to edit the stub — edits to a stub are overwritten.

Where there is no agent — CI, a setup script — the same stubs come from a standalone script. It runs outside any MCP client, so it does need an API token. A person creates one at https://THIS-HOST/settings/tokens, or asks a connected agent for one: create_api_token mints it and says to save it, with this address, in .env.local.

curl -fsSL https://THIS-HOST/sync-skills.mjs -o sync-skills.mjs
SKILL_ROUTER_URL=https://THIS-HOST SKILL_ROUTER_TOKEN=sr_... node sync-skills.mjs --dry-run

It writes .claude/skills/. Where Codex is used too, name both directories: --out .claude/skills --out .agents/skills.

If the client cannot sign in

Scripts, CI, and clients without OAuth present an API token instead. A person creates one at https://THIS-HOST/settings/tokens — it is shown once — and the client sends it as a header:

claude mcp add --transport http skill-router https://THIS-HOST/mcp \
  --header "Authorization: Bearer $SKILL_ROUTER_TOKEN"

That is Claude Code's default scope: this project only, kept outside the repository. Do not add --scope project to it — the shell has already put the token in the header, and that would write it into .mcp.json.

Never write the token into a file that gets committed. Read it from the environment (SKILL_ROUTER_TOKEN) or the client's own secret storage.

Using it

  1. list_skills — the catalogue, with slugs.
  2. search_skills — when you know the problem and not the name.
  3. get_skill — the whole document. Pass a reason: why you reached for it.
  4. report_skill_usage — afterwards, whether it helped or fell short.
  5. propose_skill_update — opens a review; a person approves it. Nothing you send changes a skill directly.
  6. sync_skill_stubs — at the start of a session, as above. Records no usage.

Three more set a repository up. They are for when a person asks, not for a session to reach for:

Two more curate the library, also when a person asks:

Discovery, for clients