Build an AI Plugin: Skills, Tools, and MCP
A useful plugin packages one clear capability that an assistant can discover and use. A skill tells the assistant how to carry out a repeatable workflow. An MCP server exposes live data or an action through a typed tool. The plugin is the installable package that brings these pieces together. This lesson uses the portable Agent Plugins layout documented by OpenAI, checked October 12, 2026. Product schemas and distribution rules can change; check the linked documentation when publishing.
For the broader agent/host permission boundary, read AI Agents, Plugins, and Subagents. For a private machine setup, read Build a Private Local AI Assistant.
Choose the smallest extension
| Need | Start with | Why |
|---|---|---|
| Repeatable analysis over files the host already permits | Skill | Instructions and examples may be enough. |
| Current Jira records, a design selection, or an external API | MCP read tool | The server can authenticate, validate, and return a narrow result. |
| A write such as updating an issue | Separate MCP write tool | A distinct capability makes authorization and auditing clear. |
| Several related workflows and tools to distribute | Plugin | One versioned package can hold the skill and server configuration. |
🟨 The assistant's description of a tool is a hint for tool selection. The server still has to enforce authentication, authorization, input limits, and data scoping. OpenAI's MCP server guide requires a handler that authorizes the request.
Write the contract before the code
Example goal: “Summarize open documentation tasks for the selected project.” Define the input (projectId, maximum result count), output (task IDs, titles, status), allowed principal (current signed-in user), freshness, timeout, and failure response. List fields that must never leave the server, such as access tokens, private comments, or unrelated project data. Make the read operation separate from any later “post summary” action.
The host should pass an authenticated identity to the server through its connection, not trust a user ID supplied by the model. A server response should return only what the next step needs. A plugin manifest is configuration, not a place to put credentials. OpenAI's packaging guidance documents the connection settings and secret handling.
Make a skill-only package first
This is a minimal illustrative portable package structure. The skill works over already-authorized inputs and does not require an MCP server.
review-helper/
├── plugin.json
└── skills/
└── change-summary/
└── SKILL.mdplugin.json:
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
"name": "review-helper",
"version": "0.1.0",
"description": "Summarize an authorized code change for human review.",
"author": { "name": "Example Team" }
}skills/change-summary/SKILL.md:
---
name: change-summary
description: Summarize an authorized code diff with evidence for a human reviewer.
---
Use this skill when the user asks for a concise summary of a code change.
1. Read only the diff and supporting files already available in the task.
2. Describe behavior changes and affected interfaces, citing file paths.
3. Separate observed test results from tests you recommend running.
4. If the diff or test output is missing, say what evidence is missing.
5. Do not include secrets, personal data, or unrelated file contents.
Return: summary, evidence, test status, and unresolved questions.The name and description front matter and a task-specific procedure follow OpenAI's skill authoring guide. Install and test this package with a synthetic repository before using real work. A skill can guide behavior, but cannot enforce file or network permissions.
Add a read-only MCP tool when live data is needed
The next example shows the tool registration part of a TypeScript MCP server. It uses an in-memory catalog so it can be inspected without credentials. It omits the HTTP transport and real identity provider; those must be wired according to the target host's current SDK and authentication documentation. The official MCP server guide documents McpServer, registerTool, schemas, and tool results.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
const server = new McpServer({ name: "review-helper", version: "0.1.0" });
const catalog = [
{ id: "DOC-1", projectId: "docs", title: "Clarify retry policy" },
{ id: "DOC-2", projectId: "docs", title: "Add error example" }
];
server.registerTool(
"list_doc_tasks",
{
title: "List documentation tasks",
description: "List tasks in the selected project for a change summary.",
inputSchema: {
projectId: z.string().min(1).max(40),
limit: z.number().int().min(1).max(20).default(10)
},
outputSchema: {
tasks: z.array(z.object({ id: z.string(), title: z.string() }))
},
annotations: {
readOnlyHint: true,
destructiveHint: false,
openWorldHint: false
}
},
async ({ projectId, limit }) => {
// In production, authorize the authenticated principal for projectId here.
const tasks = catalog
.filter((item) => item.projectId === projectId)
.slice(0, limit)
.map(({ id, title }) => ({ id, title }));
return {
structuredContent: { tasks },
content: [{ type: "text", text: `Found ${tasks.length} tasks.` }]
};
}
);🟥 The comment marks a required production boundary. The sample has no authentication and must not be connected to a private source as written. In production, derive the principal from the verified connection, check their right to projectId, enforce a deadline and rate limit, and return a bounded error without leaking internals. Validate output as well as input. Do not place tokens or personal details in tool results; hidden result metadata is not a secure vault.
For a bundled remote server, a portable mcp.json can declare a named streamable-http connection. Its URL and authentication method are deployment choices. Do not put a client secret in this file. OpenAI's portable MCP configuration has the current schema and examples.
Test the plugin as a product
- Validate the manifest against its declared schema and load the package in a test host.
- Test the skill with a matching request, a request that should not invoke it, missing evidence, and malicious instructions inside a diff.
- Test every tool with authorized and unauthorized identities, invalid IDs, large limits, empty results, timeouts, and upstream failures.
- Inspect the model-visible tool result and logs for tokens, private fields, and unnecessary payloads.
- Version the package, document permissions and data flow, and retest after SDK or host upgrades.
The OpenAI connection and testing guide covers local testing for its product surface. Other hosts may use different install formats; check their current documentation before claiming compatibility.
Practice: split a capability
Prompt: Your plugin can read Jira issues and transition them to Done. A user asks for a sprint summary. What two tools should you expose, and what should the summary skill call?
Answer
Expose a narrow read tool for the selected sprint and a separate transition tool with a distinct permission check. The summary skill should call only the read tool and return a draft summary with issue references. Transitioning issues requires a separately authorized workflow and must not be implied by the summary request.
Quick Recap
- A skill captures a repeatable procedure; an MCP server supplies live, checked operations.
- Start with one bounded use case, a typed contract, and a small result.
- Package and version the skill and server together only when distribution helps.
- Test authorization, negative cases, privacy, and failure behavior before using real data.
Key Takeaways
Good plugins make their data and action boundaries easy to understand. The server enforces access; the skill explains the workflow; the host controls which capabilities the assistant may use.
