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-HOSTstands for the scheme and host you fetched this page from. Substitute it everywhere below, or fetch/llms.txtfrom the same host: it is this document with the real URLs filled in.
- MCP endpoint:
https://THIS-HOST/mcp - Transport: Streamable HTTP
- Auth: OAuth 2.1 sign-in (preferred, nothing to configure), or an API token
Installing it is two things, and it is not installed until both are done:
- Connect the server — Install, below.
- Wire up the stubs — Then make the skills trigger, further down: something in the
repository that makes every session sync one
SKILL.mdstub 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.
- It comes from the server, so it follows the sign-in. If the server's tools are not there yet, say that this step is still to do, and that the person has to run
/mcpand approve the sign-in first. - It takes effect in the next session, or in this one after
/reload-plugins./skill-router-statusshows what the startup sync did. - Claude Code loads it only when started from the repository root, and only once that folder's trust dialog has been accepted.
- Skills are reloaded live, so a stub written during a session is usable in that session.
- Committing
.claude/skills/skill-router-sync/gives the plugin to everybody who opens the repository, and it is code that runs at the start of their sessions. Say so when you propose committing it.
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.
- Targets CLI 1.0.91 / prerelease 1.0.92-4, reviewed 2026-10-04. Extensions and native tool RPCs are experimental; check
copilot --versionand use--experimental. - The awaited first-prompt hook executes the sync, writes
.claude/skills, and explicitly reloads skills before inference. First-prompt skill expansion ordering is not guaranteed by the experimental API./skill-router-statusreports the automatic result. - Remove the old Skill Router echo-only
.github/hooks/skill-router.jsonentry; preserve unrelated hooks. Do not keep an instruction that asks the CLI model to sync. - MCP authentication and refresh stay with Copilot. Normal permissions apply; no blanket approval or OAuth file access. Router IDs stay in extension memory, not stub state. Failures continue without router tools or model-driven sync.
- VS Code and the cloud agent do not run CLI extensions. Their instructions still ask the model to sync. The cloud agent needs an API token in its MCP configuration.
- Committing
.github/extensions/skill-router-sync/shares executable code; ask first.
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.
- Codex discovers skills in
.agents/skills/and reads nothing under.claude/. Its stubs and their state file go there, which is what--outin the hook's command is for. - The sync is code: at session start the hook runs the script, which calls
sync_skill_stubsthrough Codex's own app-server, over the sign-in Codex already has, and writes what comes back. No model turn and no API token. It puts the session'ssession_idinto the context. - It needs Node, and a Codex CLI whose app-server has the experimental
mcpServer/tool/call. - Codex skips a hook until the person has reviewed it: each of them approves it once with
/hooks, and again whenever it changes. Tell the person to run/hooks. Until they have, nothing syncs:AGENTS.mdsays what the hook does and where itssession_idgoes, and does not ask the model to sync in its place. - Codex reads
.codex/hooks.json, like the.codex/config.tomlbeside it, only in a project it trusts. - Codex loads a new MCP server when it starts. In the session that installs it the tools are not there yet: write these files anyway, and the first sync happens in the next session, once the person has run
codex mcp login. - A sign-in that has expired is a warning and not a failure: the hook says to run
codex mcp login skill-router, and the session carries on. Every run leaves sanitized diagnostics in.codex/logs/skill-router-sync/. - Committing
.codex/hooks.jsonand.codex/scripts/gives them to everybody who opens the repository, and they are code that runs at the start of their sessions. Say so when you propose committing them..codex/logs/is not for committing: add it to.gitignore.
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.
- Requires OpenCode V2; the plugin targets API 2.0.22. Check
opencode --version. Do not install this plugin in V1. - This step follows the MCP sign-in. If tools are not available, tell the person to run
/mcpsand sign in, then ask forsetup_session_syncagain. - It syncs
.claude/skillsbefore first-prompt skill resolution, not when an empty session is created. Start a new session after installation;/skill-router-statusreports the automatic result. Do not substitute a model-driven startup sync. - The returned router session ID stays in plugin memory and is attached to context and later router calls. Reloads/service restarts reset it; do not save it to a file.
- Committing
.opencode/plugins/skill-router-sync/shares executable startup code. Ask first, ignore its node_modules directory, and install dependencies per checkout.
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
list_skills— the catalogue, with slugs.search_skills— when you know the problem and not the name.get_skill— the whole document. Pass areason: why you reached for it.report_skill_usage— afterwards, whether it helped or fell short.propose_skill_update— opens a review; a person approves it. Nothing you send changes a skill directly.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:
install_skill_stubs— every stub, now, in the directory your harness reads.setup_session_sync— the session-start files for the agent you are.create_api_token— a token for a script or CI, to be saved in.env.local.
Two more curate the library, also when a person asks:
create_skill— a new skill, live at once andpersonalunless told otherwise.assign_skill_to_projects— put a skill in projects, or take it out, by project slug.
Discovery, for clients
- Protected resource metadata:
https://THIS-HOST/.well-known/oauth-protected-resource - Authorization server metadata:
https://THIS-HOST/.well-known/oauth-authorization-server - These instructions as plain text:
https://THIS-HOST/llms.txt - The stub sync script:
https://THIS-HOST/sync-skills.mjs