Extending AI Coding Assistants: Claude Code vs. GitHub Copilot vs. Cursor
If you have used Claude Code, VS Code GitHub Copilot, and Cursor in the same week, you have probably noticed they share a vocabulary — agents, skills, commands, instructions, hooks, plugins, connectors — and that almost none of those words mean the same thing in any two products. A "skill" in Claude Code is a folder. A "skill" in Copilot is also a folder, but discovered from a different path with stricter naming rules. A "connector" in Anthropic-land is a remote OAuth-authenticated MCP server with a UI listing. A "connector" in GitHub- land is... not a thing GitHub uses that word for at all.
I spent the last few months packaging an MCP server (applypass-mcp) and a
plugin marketplace (applypass-plugins) across all three ecosystems, plus
OpenAI's Codex CLI, and walked away with a much clearer picture of where these
primitives actually overlap, where they diverge, and what the right strategy is
if you want to author once and ship everywhere.
This is the write-up.
The seven primitives, defined once
Before any cross-tool comparison, you need a stable definition for each word, because the vendors use them inconsistently. Here is the dictionary I'll use for the rest of the post:
| Term | What it actually is |
|---|---|
| Instructions | Plain-text Markdown files the harness reads from disk and injects into the model's context — project memory. |
| Skills | Discoverable, model-invoked procedural knowledge bundles (a folder containing SKILL.md plus optional scripts/references). The model decides when to load them. |
| Sub-agents | Specialized personas the harness can spin up with their own tool whitelist and system prompt. Different from the top-level agent. |
| Slash commands / prompts | User-invoked Markdown files. The user explicitly types /foo to fire them. |
| Hooks | Shell commands (or HTTP calls, or MCP calls) the runtime triggers at lifecycle events: PreToolUse, SessionStart, PostToolUse, etc. Deterministic, local. |
| Plugins | Packaged directories bundling some combination of the above plus MCP server refs, installed from a marketplace. |
| Connectors | Anthropic-specific noun for remote OAuth-authenticated MCP servers with a UI listing in Claude Desktop / claude.ai. Nobody else uses this word. |
The crucial insight that's easy to miss: every tool here does plain text injection in the harness. The model itself doesn't understand any of these file formats. The CLI or IDE extension reads the files off disk, parses frontmatter, decides what's in scope, and assembles the prompt. What you're comparing is which directories each harness walks, what schema it parses, and where in the API request the content lands.
That framing makes the differences a lot less mystical.
The cheat sheet
Here is the whole comparison condensed into one table. The rest of the post is me explaining the entries that are surprising or load-bearing.
| Primitive | Claude Code | GitHub Copilot (VS Code) | Cursor |
|---|---|---|---|
| Project instructions | CLAUDE.md, .claude/CLAUDE.md, subdir CLAUDE.md (on-demand) | .github/copilot-instructions.md, AGENTS.md, CLAUDE.md (all merged), *.instructions.md with applyTo: glob | .cursor/rules/*.mdc (Always / Auto-attached / Agent-requested / Manual), AGENTS.md, .cursorrules (legacy) |
@import recursion in instructions | ✅ recursive, 5 hops | ❌ literal text | ❌ literal text |
Reads AGENTS.md? | ❌ (open since 2025, issue #6235) | ✅ since late 2025 | ✅ native |
Skills (Anthropic SKILL.md spec) | ✅ .claude/skills/<name>/SKILL.md | ✅ auto-discovers .claude/skills/, .github/skills/, .agents/skills/, ~/.copilot/skills/ | ✅ as "agent-decided rules" |
| Sub-agents | .claude/agents/<name>.md | .github/agents/<name>.agent.md (different schema, mcp-servers block, handoffs) | None — uses rule-based scoping instead |
| Slash commands | .claude/commands/*.md + skills /skill-name | .github/prompts/<name>.prompt.md + /prompt-name | @ruleName for manual rules; native custom commands |
| Hooks | ✅ first-class, ~32 events, settings.json | ❌ no standalone hooks; only inside the plugin format | ✅ added in a late-2025 release, ~23 events |
| Plugins | ✅ .claude-plugin/plugin.json + git marketplaces (GA Oct 2025) | ✅ preview: auto-detects 4 manifest paths including Claude's | ❌ no native bundle format — VS Code extensions + MCP servers |
| "Connector" concept | Yes — remote OAuth MCP, synced across Claude apps | No — uses "MCP server" + GitHub App Extensions | No — uses "MCP server" |
| MCP support | First-class client, @server://resource refs | First-class, JSON config or per-agent mcp-servers: block | First-class, .cursor/mcp.json |
Instructions: how each tool reads project memory
Every modern coding assistant reads a "magic Markdown file" at the repo root. They all look the same from a distance and they all behave differently up close.
Claude Code
Claude Code reads CLAUDE.md and (with subdirectory CLAUDE.md files) lazily
loads them when work touches that subtree. The single most interesting feature
is @import: lines like @README.md or @docs/architecture.md recursively
pull in those files, up to 5 hops deep, resolved relative to the importing
file. Nobody else does this.
A subtle and undocumented mechanism: CLAUDE.md is not placed in the
system prompt. The harness wraps it in a <system-reminder> block and
re-injects it into a user message on every turn. The trace looks roughly like:
<system-reminder>
# claudeMd
Contents of /path/to/CLAUDE.md (project instructions):
…your CLAUDE.md content…
</system-reminder>
That has two consequences. First, your CLAUDE.md survives long sessions and
compaction events because it's re-attached every turn. Second, you pay for it
every turn in tokens — Anthropic's own guidance is to keep it under ~80–300
lines, because the frontier models reliably follow roughly 150–200 instructions
and the base Claude Code system prompt already consumes ~50 of those.
Claude Code does not natively read AGENTS.md. The open issues
(#6235,
#34235) have been
ignored for long enough that the community workaround — a one-line CLAUDE.md
containing @AGENTS.md, or a symlink — has become the de facto recommendation.
VS Code Copilot
Copilot's discovery pipeline is the most permissive of the three. At the start of every turn it gathers, merges, and injects:
.github/copilot-instructions.mdAGENTS.mdCLAUDE.md(yes — Copilot reads CLAUDE.md too, since late 2025)- Any
*.instructions.mdwhoseapplyTo:glob frontmatter matches the current file scope - User-level instructions from VS Code settings
No documented ordering between them; "personal > repository" is the only explicit precedence rule for conflicts. The diagnostics view (right-click in Chat → Diagnostics) is the most useful debugging tool in this entire space, because it shows every loaded file and any parse errors.
*.instructions.md with applyTo: is the killer feature here — Copilot's
equivalent of Claude's hierarchical CLAUDE.md, except declarative rather than
implicit:
---
applyTo: '**/*.tsx,**/*.ts'
---
TypeScript-only conventions go here.
Cursor
Cursor's .cursor/rules/*.mdc files are the richest instruction format of the
three, with four attachment modes selected by frontmatter:
---
description: 'Frontend component patterns for React'
globs: ['src/components/**/*.tsx']
alwaysApply: false
---
| Type | alwaysApply | globs | description |
|---|---|---|---|
| Always | true | — | optional |
| Auto-attached | false | set | optional |
| Agent-requested | false | — | required — agent decides |
Manual (@ruleName) | false | — | optional |
Cursor also reads AGENTS.md (and explicitly endorses it as a migration target
from the legacy .cursorrules). Subdirectory .cursor/rules/ folders work
like Claude's per-folder CLAUDE.mds.
Two annoying Cursor quirks worth knowing: the agent will strip YAML frontmatter
when generating .mdc files via chat, leaving the rule invalid; and the
documented folder-based RULE.md format doesn't actually load in practice —
only flat .mdc files do, despite what the docs say.
Honest comparison
For pure power, Cursor's rule taxonomy is the most expressive — four attachment
modes with declarative scoping. For ergonomics, Claude's @import recursion is
unmatched. For breadth, Copilot reads everyone's format (including yours,
literally).
If you only want to maintain one file: AGENTS.md is the closest thing to a
portable lingua franca. Cursor reads it natively. Copilot reads it natively.
Codex CLI treats it as canonical. Claude Code does not read it natively, but a
one-line CLAUDE.md that says @AGENTS.md makes it work everywhere.
Skills: Anthropic seeded a standard
Skills are model-invoked procedural knowledge. The format Anthropic shipped is a
folder containing SKILL.md plus optional scripts/, references/, and
assets/ subdirectories. The frontmatter looks like this:
---
name: code-reviewer
description: Review staged git changes for security, logic, and style issues. Use when…
allowed-tools: [Bash, Read, Grep]
disable-model-invocation: false
---
Body: the actual procedure, keep under ~2000 words.
Then progressive disclosure: scripts/, references/, assets/ are only loaded
on demand via the Read tool.
The interesting cross-tool fact: GitHub Copilot adopted Anthropic's SKILL.md
format almost verbatim, and it auto-discovers four directory paths including
.claude/skills/ — meaning a Claude Code skill drops into VS Code Copilot for
free, with two caveats:
- Don't put a slash or colon in the
namefield. Copilot silently fails to load namespaced names likemyorg/skill. - Copilot ignores
argument-hintandmodelin the frontmatter.
Cursor recognizes the same format as "agent-decided rules" — the description is shown to the model, which then decides whether to pull in the body. This is actually exactly how Anthropic's skill discovery works under the hood, which is not a coincidence.
The single most important field, across all three, is description. The model
matches on it. Anthropic's own internal guidance is that descriptions should be
"pushy" — explicit trigger phrases, examples of when to fire — because models
undertrigger skills by default. If your skill never activates, your
description is too vague.
Sub-agents: Claude's first-class concept, Copilot's parallel format, Cursor's omission
Claude Code's sub-agents live in .claude/agents/<name>.md and behave as
additional tool entries in the API request. They have their own tool
whitelist (allowed-tools), permission mode, and system prompt.
VS Code Copilot has a parallel concept — .github/agents/<name>.agent.md —
with a different schema:
---
description: Reviews PRs against our team's coding standards
tools: ['read', 'edit', 'search', 'custom-mcp/tool-1']
model: claude-sonnet-4-5
mcp-servers:
custom-mcp:
type: local
command: some-command
env:
ENV_VAR: ${{ secrets.COPILOT_MCP_ENV_VAR }}
handoffs:
- label: Start Implementation
agent: implementation
prompt: 'Now implement the plan above.'
---
The two formats are not interchangeable. Copilot's tools field uses
Copilot tool names; the mcp-servers: block is unique to Copilot; the
handoffs: block — one-click transitions to a sibling agent — has no
Claude equivalent. Copying a Claude Code agent file into .github/agents/ will
silently fail to materialize as a Copilot custom agent.
Cursor has no sub-agent concept at all — it leans on rule-based scoping instead.
Commands: who can call them, who picks them
Slash commands are user-invoked Markdown files. The user types /foo and the
file body becomes a prompt.
- Claude Code —
.claude/commands/*.md, plus every skill is exposed as/<skill-name>if invoked manually. - Copilot —
.github/prompts/<name>.prompt.mdwith optional frontmatter selecting agent/model/tools. - Cursor — manual rules invoked via
@ruleName, plus native custom commands defined in the UI.
These are roughly equivalent in capability. The differentiator is whether the file format also carries metadata that controls which agent runs or which tools are exposed during the command — Copilot's prompt files do, Claude's commands don't (they're just prompts), Cursor's are UI-managed.
Hooks: the biggest gap between the three
Hooks are where the three tools diverge most sharply. A hook is a shell
command the runtime triggers at a lifecycle event — PreToolUse,
SessionStart, PostToolUse, and so on.
Claude Code
Claude Code has by far the most developed hook system. The event catalog is ~32 events spanning session lifecycle, every tool call, sub-agent lifecycle, state changes (FileChanged, CwdChanged, InstructionsLoaded), and MCP interactions. The schema:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash|Edit",
"hooks": [
{
"type": "command",
"command": "./scripts/audit.sh",
"timeout": 30,
"statusMessage": "Auditing command"
}
]
}
]
}
}
The five handler types are command, http, mcp_tool, prompt, and agent
— the last two are particularly interesting because they let you gate an
action on an LLM-evaluated yes/no decision or on a verifier sub-agent's
output.
Exit-code semantics: 0 = success, 2 = blocking error (the action is
prevented and stderr is fed back to the model as the reason), anything else =
non-blocking error. For PreToolUse, a JSON object on stdout can rewrite the
tool input before it runs:
{
"hookSpecificOutput": {
"permissionDecision": "allow",
"updatedInput": { "command": "rewritten command" },
"additionalContext": "shown to Claude"
}
}
Cursor
Cursor added hooks in a late-2025 release with a schema that's almost a copy of Claude Code's, with three notable differences:
- Event names are
camelCase(preToolUse) instead ofPascalCase. - There are dedicated events for shell execution (
beforeShellExecution,afterShellExecution) and MCP execution (beforeMCPExecution,afterMCPExecution), separate from genericpreToolUse. - There's a
failClosed: trueopt-in. If the hook crashes or times out, Claude Code and Codex CLI fail open (the action proceeds). Cursor fails open by default too — but you can flip individual hooks to fail closed when they're security-critical. This is a meaningful improvement.
Cursor's hooks also have an agentic-loop primitive: a stop or subagentStop
hook can return a followup_message that auto-submits as the next prompt,
with a loop_limit (default 5). Neither Claude Code nor Copilot has this.
Copilot
Copilot has no standalone hook system. Hooks exist only as components inside
the agent-plugins preview format (more on that next). There is no
~/.copilot/hooks.json for personal-machine event interception.
The closest thing for the Copilot cloud agent is GitHub Actions — workflows
triggered on issue_comment or pull_request events. Functionally
equivalent for cloud workflows, but it isn't a hook system in the local-
developer sense.
Hooks portability
If you want hooks that run in Claude Code and Cursor and Copilot's plugin format, the realistic playbook:
- Author against the Claude Code event vocabulary (
PreToolUse,PostToolUse,SessionStart,UserPromptSubmit,Stop) — this is the intersection set. - Use only
type: "command"handlers (the only handler all three implement). - Read JSON from stdin, write JSON to stdout for decisions, use exit 2 + stderr for blocks.
- Reference
${CLAUDE_PLUGIN_ROOT}— Codex aliases it, Copilot's plugin loader honors it. - Keep a parallel
~/.cursor/hooks.jsonin camelCase if Cursor support matters. There's no automatic translator.
Plugins: the surprise winner is the Claude format
Claude Code plugins went GA in October 2025. A plugin is a self-contained directory bundling skills, commands, agents, hooks, MCP server refs, LSP servers, output styles, themes, and settings, distributed via a Git-hosted marketplace manifest. The plugin manifest:
{
"name": "applypass-devkit",
"version": "1.2.0",
"description": "ApplyPass developer toolkit",
"skills": "./skills/",
"commands": ["./commands/deploy.md"],
"agents": ["./agents/reviewer.md"],
"hooks": "./hooks/hooks.json",
"mcpServers": "./.mcp.json"
}
Only name is required. The marketplace manifest at
.claude-plugin/marketplace.json lists plugins with version, category, and
source. Users install via /plugin marketplace add owner/repo then
/plugin install <name>@<marketplace>.
Then comes the surprise: VS Code Copilot's preview agent-plugins format
auto-detects four manifest paths in order, and .claude-plugin/plugin.json
is one of them. Copilot honors ${CLAUDE_PLUGIN_ROOT}. It picks up the same
skills directory layout. It runs Claude-format hooks (subset of events).
This means a well-authored Claude Code plugin — .claude-plugin/plugin.json at
the root, skills/ and commands/ and hooks.json and .mcp.json in
standard locations — installs into VS Code Copilot unchanged. You build
once, you ship to both.
Cursor is the holdout. Cursor has no plugin bundle format as of May 2026.
"Cursor plugin" colloquially means either a VS Code extension or an MCP
server entry in .cursor/mcp.json, both of which are pre-existing
mechanisms. The Anthropic knowledge-work-plugins repo migration to Cursor
that I worked through earlier this year confirmed this in practice — you
end up shipping the MCP server and abandoning the bundled skills/commands/hooks
story, or telling users to install components individually.
(Codex CLI also has plugins, with a .codex-plugin/plugin.json manifest
heavily mirrored on Claude's. Codex even goes out of its way to alias
CLAUDE_PLUGIN_ROOT and CLAUDE_PLUGIN_DATA env vars inside its plugin
hooks for cross-compat. A separate manifest is needed, but it's a 30-minute
copy-and-rename job.)
Connectors: the word is Anthropic-specific
This is the term with the most cross-vendor confusion, so let's be precise.
A Connector, in Anthropic's vocabulary, is a remote MCP server packaged for
end-user consumption — typically OAuth-authenticated, hosted by a third party,
and listed in the directory at claude.ai/connectors. The Help Center is
explicit: "A Connector is a remote MCP server… that Claude calls on your
behalf, after you authorize it via OAuth."
Three flavors:
- Pre-built connectors — Anthropic-curated (Google Drive, Gmail, GitHub, Notion, Slack, Atlassian, etc.). One-click OAuth.
- Custom remote connectors — any HTTPS MCP server URL the user pastes into Customize → Connectors.
- Messages API
mcp_serversblock — gated behind theanthropic-beta: mcp-client-2025-11-20header.
The defining property versus a local MCP server is that the connection origin is Anthropic's servers, not the user's machine. The implication is that installing a connector on Claude Desktop Mac instantly makes it available on Windows desktop and the web — because the integration lives in your Anthropic account.
GitHub Copilot does not use the word "connector." The discovery surface is the
GitHub MCP Registry at github.com/mcp; the integration mechanism is plain
MCP servers configured at .vscode/mcp.json (repo) or ~/.copilot/mcp.json
(user) or repo-level for the cloud agent. There's also the unrelated GitHub-
App-based Copilot Extensions architecture — an HTTPS webhook a GitHub App
exposes for @app-name chat mentions — but that's a separate protocol, not
MCP.
Cursor doesn't use the word either. Cursor's MCP system covers stdio, SSE,
and Streamable HTTP transports including OAuth. Configured in .cursor/mcp.json.
One-click install URLs exist via Cursor Marketplace and cursor.directory. The
one notable Cursor limit: a hard ceiling of roughly 40 active tools across
all MCP servers; exceed it and the agent silently loses access to some.
The bottom line on the noun: when Anthropic says "Connector," they mean a hosted remote OAuth MCP server with a UI listing. When anyone else says "connector" in this space, they usually mean "MCP server." The underlying protocol is the same.
What's actually portable
The architecturally important conclusion from all of this: MCP is the cross-vendor standard that quietly won. Every major coding-assistant vendor in 2026 — Anthropic, GitHub, OpenAI, Cursor, Windsurf — consumes MCP. The plugin formats and connector listings are convenience wrappers on top.
If you build a single thing today, build a remote MCP server with OAuth + Dynamic Client Registration (DCR) per RFC 7591. That single artifact reaches:
| Surface | How it gets in |
|---|---|
| Claude Desktop / claude.ai | Custom remote connector — paste the URL |
| Claude Code | .mcp.json entry or bundled in a plugin |
| Claude API | mcp_servers array on the Messages API |
| Cursor | .cursor/mcp.json entry (one-click install URL works) |
| VS Code Copilot | .vscode/mcp.json entry or user MCP config |
| Copilot CLI | ~/.copilot/mcp.json |
| Copilot cloud agent | Repo-level MCP registry config |
| Codex CLI | .mcp.json user config or plugin |
Same backend. Eight surface integrations. The OAuth + DCR combination is what makes this work cleanly — every harness can self-register as a client without you handing out client IDs.
For the convenience layer above MCP, the practical advice depends on who you want to reach:
- Solo dev / Claude Code only — just write skills, commands, agents
directly in your
.claude/directory. - Want to ship to other Claude Code users — package as a plugin in a git-hosted marketplace.
- Want to also reach VS Code Copilot — same plugin format. The
.claude-plugin/plugin.jsonis auto-detected. Free. - Want to also reach Cursor — ship the MCP server, accept that bundled skills/commands/hooks don't transport. Document them as installable rules.
- Want to also reach Codex CLI — copy your plugin manifest to
.codex-plugin/plugin.jsonwith minor edits. Or just ship the MCP server and let Codex users wire it up themselves. - Want to reach Claude Desktop / claude.ai users — host the MCP server remotely with OAuth-DCR and submit it to the Anthropic Connectors Directory. This is a productization step (review + listing), not engineering.
The case for also building a GitHub Copilot Extension (the GitHub App
architecture) is weak unless you specifically want @your-app mentions on
github.com PRs and issues. The Extension format doesn't carry skills or
commands — it's a chat-handler webhook. Skip it unless that exact surface is
the goal.
tl;dr
- Instructions — every tool reads a magic Markdown file at the repo
root, and they almost all read each other's.
AGENTS.mdis the portable lingua franca. Claude Code is the lone holdout; bridge it with a one-lineCLAUDE.mdcontaining@AGENTS.md. - Skills — Anthropic's
SKILL.mdformat is the de facto standard. Copilot auto-discovers.claude/skills/. Cursor reads them as agent-decided rules. Write one set, run everywhere. - Sub-agents — Claude and Copilot have parallel-but-incompatible schemas. Cursor has no equivalent. Not portable.
- Slash commands — roughly equivalent across the three, separate file layouts. Not portable, but trivially translatable.
- Hooks — Claude Code has the richest system (~32 events). Cursor has
~23 with a
failClosedopt-in nobody else has. Copilot has none outside the plugin format. Author against the intersection set if you need portability. - Plugins — Claude Code's format is the winner: VS Code Copilot auto-detects it, Codex CLI mirrors it. Cursor has no plugin bundle format at all. The Claude plugin you build is the most portable plugin you can build.
- Connectors — the word means "remote OAuth MCP server with a UI listing" in Anthropic-land and nothing in particular in Copilot- or Cursor-land. The underlying transport is MCP, which is universal.
- What to build — a single remote MCP server with OAuth + DCR reaches every coding-assistant ecosystem in 2026. Bundle a Claude-format plugin on top of it if you want skills and commands too. Everything else is a convenience wrapper on those two artifacts.
If you only remember one thing: MCP is the standard. Plugins and connectors are distribution sugar. Build the MCP server first; everything else is a 30-minute manifest file.