Tutorial

AI Agent Skills vs MCP vs Plugins: which should developers use in 2026? From discovery to JSON Schema

Do not ask which one to pick. Ask whether you are shipping a brief, a hand, or a box that must travel together.

Yesterday’s Google Agent Plugins 2026 showed what the box looks like. Today answers the review question people ask wrong: “Skills, MCP, or Plugins — which do we use?” Wrong, because they are not three products. A skill is the brief the model reads; format: Agent Skills. MCP is the hand the runtime uses; protocol: What is MCP. A plugin is the August 2026 package format; spec: Agent Plugins 1.0.0. Google said it in public: a single skill, a single MCP server, or a single client is not a reason to box. This article walks discovery and JSON Schema to a decision, then hands off to MCP and JSON Schema. The Cloud sample is still google-cloud-developer.

Wrong question: they are not rivals on one layer

The usual review failure is three cards on the table and one vote. A skill does not expose tools/list and does not take tools/call. It is a directory plus SKILL.md: at startup only name and description enter context (about a hundred tokens); the body and scripts/, references/ load on demand. No JSON-RPC, no handshake, no “skill input schema” on the wire. MCP is the opposite: a process or HTTP endpoint, arguments that must validate as JSON. A plugin does neither. It only fixes a directory and two closed manifests. Treating a plugin as “stronger MCP” is treating a carton as an engine.

Discovery is not one path either. A skill is found by a description string: write “what it does + when to use it” or the model never selects it. MCP is found by tools/list — names plus inputSchema. A plugin is found by root plugin.json, then the fixed skills/ and mcp.json locations. The three can stack: the client sees the box, then skill metadata, then an MCP tool list. Stacking is not substitution. If you lack a hand and write a longer skill instead, you are betting the model will fake an API with a shell. That is hallucination, not integration.

Lateral delegation is not on these three cards. How another agent is found and given a Task is an A2A Agent Card; see A2A vs MCP. Today you only decide: does this repo need a brief, a hand, and must those two lock into one portable directory. Layer first, then choose.

What you are choosing What it solves Discovery surface
SkillReusable flow, format, guardrails; optional local scriptsname / description on SKILL.md
MCP serverDeterministic calls into live systems (DB, API, cloud)tools/list + inputSchema
PluginKeep a skill and an MCP from forking when the client changesplugin.json, then the fixed directories

Four questions walk the decision tree

Collapse the multiple choice into four questions. Answer in order; do not skip. First: must the model touch a live system outside the repo — a database, official-docs search, a billing API, gcloud? If yes, you need MCP (or an existing native tool such as local gh). If no, do not start an empty server to look serious. Second: do you need to freeze a flow such as “check the project, then billing, never commit keys,” and keep it across sessions? If yes, write a skill. A pasted system prompt disappears when the window is squeezed.

Third: must the answers to the first two ship together? An invoice MCP without the weekly-summary skill will be abused; the skill without the MCP can only demo on fake data. Only then consider a plugin. Fourth: are you sending this to more than one client — Cursor, Claude Code, Antigravity, Codex? One IDE that already has native MCP / Skills install is shorter as native config. Google nailed this on the Developers Blog: a plugin earns its keep when the components share a goal and must travel together.

Below is a decision record you can commit. It is not a spec field. It is a JSON fixture from the review: the four answers, the choice, and the component names you intend to pack. Make it parse, then assert in CI that choice does not fight the four flags — mustTravelTogether false and choice plugin is boxing too early.

{
  "task": "weekly-invoice-summary",
  "needRuntimeTools": true,
  "needReusableBrief": true,
  "mustTravelTogether": true,
  "clients": ["cursor", "claude-code", "antigravity"],
  "choice": "plugin",
  "components": [
    "skill:write-weekly-summary",
    "mcp:invoice-tools"
  ]
}

Skill only: discovery is the description, there is no tools/call

A skill wins on three things: no handshake, progressive load, humans can diff it. At startup the client injects metadata only; the model reads the body after the description matches. So the description must say both what and when, third person, with keywords, and it has limits (name 64, description 1024). An internal codename or a first-person slogan is a zero discovery surface. Agent Skills owns the format; Plugins only says it lives at skills/<name>/SKILL.md.

A skill may ship scripts/. Those are not new MCP tools. They mean “run this with the shell you already have.” That fits local CLIs: gh, gcloud, your lint.sh. The script keeps deterministic work off the prompt and writes a summary back. There is still no transport: no OAuth discovery, no inputSchema; argv checking is the script’s job. If you need stable JSON arguments or remote auth, do not pretend a script is MCP.

Skill-only fits output formats (PR body, incident report), local CLI workflows, and domain guardrails (“read this checklist first”). Anti-pattern: a “query production” skill that asks the model to invent SQL and pipe it through a generic shell. That is a brief pretending to be a hand. You also cannot validate argument shape at discovery — there is no schema.

MCP only: discovery is tools/list, the contract is inputSchema

MCP wins on three things a skill cannot give: a live connection, structured input, and a failure boundary. The client connects, tools/list returns names and inputSchema, the model fills arguments, the runtime calls tools/call. Whether arguments pass is JSON Schema, not “the model sounded sure.” Auth, quotas, and transport versions stay on the MCP spec; the 2026-07-28 stateless revision can sit behind a normal HTTP load balancer. A skill does none of that.

One client, one server: prefer that client’s native MCP config, not a plugin first. mcp.json is the portable Agent Plugins shape; its fields need not match Cursor or Gemini CLI. The client maps them. One IDE makes the mapping layer surplus. When a second client appears, fold the same connection into root mcp.json and add plugin.json — even with no skills/. A missing skills directory is not an error; the spec says skip an absent location.

The discovery chain is short: connect → tools/list → fill from inputSchema. Do not dump a whole OpenAPI file on an MCP client as a tool list, and do not copy plugin-manifest fields into inputSchema. The package contract answers “is the box there.” The tool contract answers “are this hop’s arguments legal.” Both are JSON Schema; they sit one hop apart. Below is a portable MCP fragment you can write before you box — it drops unchanged into a plugin root later.

{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
  "mcpServers": {
    "invoice-tools": {
      "type": "stdio",
      "command": "./bin/invoice-mcp",
      "args": ["--data", "${PLUGIN_DATA}/invoices"],
      "cwd": "${PLUGIN_ROOT}"
    }
  }
}

When to pack: Plugin pays off for many components and many clients

A plugin is worth it when questions three and four both light up. Typical pair: an MCP that queries numbers plus a skill that writes a human weekly summary; or Google’s pair — gcloud guardrail skills plus Developer Knowledge MCP. Each is misused without the other: a hand alone invents the report; a brief alone has no grounded doc search. After you box, Antigravity, Claude Code, and Codex share a layout. plugin.json stays closed, mcp.json stays separate, secrets stay in environment variables, not headers.

Boxing early costs a manifest to maintain and a review hallucination: “we have a plugin now.” The box does not turn a skill into a tool or give MCP a brief. Independent components fail independently: one server in mcp.json can refuse to start while skills still load; one bad SKILL.md frontmatter does not take the rest down. That is the spec. If your CI rejects the whole package for one unknown top-level field, you are stricter than the client — know it.

Draw the A2A line again. A plugin answers how this agent gains a set of skills and tools. How another team’s invoice agent is discovered is an Agent Card, not stuffing them into your mcp.json as one tool. Multi-turn clarification and async callbacks burst a function-call shape. One sentence for order: hand first, brief second, box last. A plugin with no hand is ordering cartons before you know what you sell.

{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
  "name": "reports-plugin",
  "version": "1.0.0",
  "description": "Invoice MCP plus the weekly-summary skill, shipped together"
}
Situation Default choice Do not
PR body format, incident-report templateSkill onlyStand up an empty MCP server for it
Invoice query API for one IDEThat IDE’s native MCP configBuild a plugin and map it back to one client
Query MCP + weekly-summary skill, three clientsPlugin (plugin.json + skills/ + mcp.json)Stuff a whole remote agent into one tool

Three schemas, three discoveries; check them in JSONVue

Spread the contracts by hop. First: plugin.schema.json — can the box be found; pin $schema to https://agent-plugins.org/schemas/1.0.0/plugin.schema.json. Second: mcp.schema.json — how to connect; type must be explicit. Third: each tool’s inputSchema — do arguments pass. Skill frontmatter is YAML, not any of these three; do not “JSON Schema” a whole SKILL.md. Discovery checks the first two; invocation checks the third.

Keep at least five fixtures: the decision record above, a legal plugin.json, a manifest with one extra top-level field, a portable mcp.json, and one real tools/call arguments object. The record catches a fight between the four questions and choice. Manifests catch the package contract. Arguments catch the tool contract. Developer Knowledge uses an API key at client runtime; the mcp.json you validate in git should hold no secrets.

You can do this in the browser:JSON formatto see whether the decision record and both manifests parse;JSON Schema validatorto check $schema, name, mcpServers, and inputSchema;JSON Diffto compare a portable mcp.json with a client-native export. Data stays on this machine. Further reading:MCP and JSON Schema, the Plugins overview, and how A2A and MCP split the work.

Related: Google Agent Plugins 2026, MCP and JSON Schema, What is MCP, A2A vs MCP.

FAQ

Can scripts inside a skill replace MCP?

Yes when a local CLI already exists, arguments are argv, and you do not need remote auth discovery. No when you need stable JSON input, OAuth, or HTTP tools across machines. A script is an attachment on a skill, not a first-class tool on tools/list.

MCP only, no skill — should I still make a plugin?

One client: no, use native config. Two or more clients and you want one connection description: a plugin that only has mcp.json is valid; skills/ may be absent. Do not add an empty skill to make the tree look finished.

Will plugins replace MCP or Skills?

No. v1 admits only those two component types and defines no install, permissions, or sandbox. What it replaces is “every client invents its own wrapper.” Execution stays MCP and Agent Skills.

Can the three JSON Schemas become one file?

Business fields can live in one canonical schema and generate MCP inputSchema. Do not put plugin.json fields and tool arguments in one “generic validate” file. Discovery failure and call failure are handled differently.

Summary and next steps

Choosing among Skills, MCP, and Plugins in 2026 collapses to one sentence: ask whether you need a hand, a brief, whether those two must travel together, and whether more than one client will install them. Not a three-way vote. Layers that stack.

Ship in this order: write the four answers as a decision-record JSON; when you need a hand, write inputSchema first; when you need a brief, write description first; when both light up and you cross clients, add plugin.json. Check the three contracts in JSONVue. What the box looks like is the Plugins overview; the wire protocol is the MCP articles; cross-agent work is the A2A article.