Skip to content
DMarketer Tayeeb – Digital Marketing Expert in Bangalore | SEO, SEM & SMM Expert
Contact

How to Build an OpenAI Agent Plugin with Skills and MCP

An OpenAI Agent Plugin is a package that can ship reusable Skills, an MCP server connection, both together, and optionally UI. The required entry point is .codex-plugin/plugin.json. Skills describe repeatable workflows; MCP tools connect the agent to code or external systems through schemas and permissions. A plugin is the distribution boundary that gives those components a stable identity across ChatGPT and Codex surfaces that support them.

This tutorial builds a small, reviewable package: a Skill turns approved evidence into a cited marketing brief, while a read-only local MCP server lists and reads evidence records. It includes a manifest check and a test. DMT’s Agent Plugins overview owns the concepts and marketer use cases; this page owns the build, test, security and distribution workflow.

OpenAI plugin architecture showing Skills, an MCP server and optional UI
OpenAI’s official Agent Plugin architecture: Skills, MCP server and optional UI. Source: OpenAI Developers; captured August 11, 2026.

Choose the smallest plugin shape

ShapeUse it whenAvoid it when
Skills onlyThe workflow can operate with existing agent tools and needs repeatable instructions, templates or scriptsYou need a new external API/tool capability
MCP onlyYou need typed tools or data access but no opinionated workflowUsers also need a consistent multi-step operating method
Skills + MCPThe workflow and the tools should travel togetherThe Skill would merely restate each tool description
MCP + UIA visual interactive result materially helps and the MCP integration supports itA table, file or ordinary text answer is enough

OpenAI explicitly recommends starting with the smallest shape. Every added server, hook, permission and UI asset increases security review, maintenance and compatibility work.

The example package

dmt-source-review/
├── .codex-plugin/
│   └── plugin.json          # required manifest
├── skills/
│   └── source-review/
│       └── SKILL.md         # reusable workflow
├── server/
│   └── index.js             # local MCP stdio server
├── scripts/
│   └── check.mjs            # package validation
├── test/
│   └── package.test.js
├── .mcp.json                # bundled MCP server map
└── package.json

Only plugin.json belongs inside .codex-plugin/. OpenAI’s current path rules keep skills/, .mcp.json, .app.json, hooks/ and assets/ at the plugin root. Manifest component paths should start with ./ and remain inside that root.

Step 1: create the plugin manifest

{
  "name": "dmt-source-review",
  "version": "0.1.0",
  "description": "Review an allowlisted evidence package and return a cited brief.",
  "author": {
    "name": "Digital Marketer Tayeeb",
    "url": "https://dmarketertayeeb.com"
  },
  "license": "MIT",
  "skills": "./skills/",
  "mcpServers": "./.mcp.json",
  "interface": {
    "displayName": "DMT Source Review",
    "shortDescription": "Build an evidence-first marketing brief",
    "developerName": "Digital Marketer Tayeeb",
    "category": "Productivity",
    "capabilities": ["Read"],
    "defaultPrompt": [
      "Use DMT Source Review to turn the approved evidence package into a cited brief."
    ]
  }
}

The manifest identifies the package, points to components and controls install-surface metadata. Public plugins usually add a homepage, repository, privacy policy, terms, icons, screenshots and richer descriptions. Do not claim a write capability when the server is intentionally read-only.

Step 2: write a focused Skill

The Skill should explain when it applies, how to use available tools, what output is required and where it must stop. It should not hide credentials or duplicate an API manual.

---
name: source-review
description: Use when an approved evidence package must become a cited marketing brief without adding unsupported claims or publishing it.
---

# Source review

1. Call the evidence MCP server to list approved evidence records.
2. Reject records without a canonical URL, source class, capture date and claim summary.
3. Separate confirmed facts, vendor claims, DMT analysis and unknowns.
4. Write a brief with the reader job, practical impact, risks and open questions.
5. Put the supporting URL next to every material claim.
6. Do not publish, message, upload, delete or change an external system.
7. If evidence is incomplete, return BLOCKED instead of guessing.

Keep large schemas, examples and reference material in files the Skill can load only when needed. This progressive disclosure prevents every invocation from carrying irrelevant context.

Step 3: build the MCP server

The Model Context Protocol defines how hosts and servers exchange tools and structured results. The example uses the official TypeScript SDK and stdio transport. It exposes only two read tools: list JSON records and read one validated record.

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { readFile, readdir } from "node:fs/promises";
import { resolve, sep } from "node:path";

const root = resolve(process.env.DMT_EVIDENCE_ROOT || "./evidence");
const server = new McpServer({ name: "dmt-evidence", version: "0.1.0" });

function safePath(name) {
  const candidate = resolve(root, name);
  if (candidate !== root && !candidate.startsWith(root + sep)) {
    throw new Error("Path escapes the evidence root");
  }
  return candidate;
}

server.tool("list_evidence", "List JSON evidence records", {}, async () => {
  const names = (await readdir(root)).filter((n) => n.endsWith(".json"));
  return { content: [{ type: "text", text: JSON.stringify(names) }] };
});

server.tool(
  "read_evidence",
  "Read one approved JSON evidence record",
  { name: z.string().regex(/^[a-zA-Z0-9._-]+\\.json$/) },
  async ({ name }) => {
    const text = await readFile(safePath(name), "utf8");
    JSON.parse(text);
    return { content: [{ type: "text", text }] };
  }
);

await server.connect(new StdioServerTransport());

The filename regex and resolved-path check are deliberate. Tool schemas improve model behaviour, but a server must still defend itself against path traversal, malformed JSON, oversized responses and untrusted content.

Step 4: wire the server into the package

{
  "evidence": {
    "command": "node",
    "args": ["${PLUGIN_ROOT}/server/index.js"],
    "env": {
      "DMT_EVIDENCE_ROOT": "${PLUGIN_DATA}/evidence"
    }
  }
}

PLUGIN_ROOT points to the installed package. PLUGIN_DATA is the writable data area. Keeping evidence under the data directory avoids modifying installed package files and gives the server one narrow root.

Step 5: validate and test

npm install
npm run check
npm test

The package check parses both JSON files, verifies required manifest fields, enforces root-relative component paths and confirms the expected MCP server. The test verifies that the manifest exposes the Skill, server and read-only capability. Add server tests for path escape, malformed JSON, missing directories and large records before using real evidence.

Test layerMinimum test
ManifestValid JSON; required identity; component paths stay inside package
SkillTrigger is specific; output and stop conditions are explicit
Tool schemaReject unexpected filenames and parameters
ServerRead allowed file; deny path escape; handle missing/malformed input
Agent workflowNormal, no-op, incomplete evidence and prompt-injection tests
PermissionsNo write/network tool appears unless intentionally required

Step 6: install through a local marketplace

OpenAI documents repo-scoped and personal marketplace files. A marketplace is a JSON catalogue; its entries point to plugin folders relative to the marketplace root. You can also add a marketplace source with the Codex CLI:

codex plugin marketplace add owner/repo
codex plugin marketplace add owner/repo --ref main

Use the ChatGPT desktop app’s Plugins Directory to install and test a local plugin. If you are registering a remote MCP server for ChatGPT, enable developer mode, create the connection, copy its technical ID and map it in .app.json. A bundled local server uses .mcp.json instead.

Approval policy and least privilege

Codex lets users enable/disable a plugin-scoped MCP server, restrict enabled tools and set approval modes. A safe default for a new server is prompt/approval, then auto-approve only a tool that is demonstrably read-only and narrowly scoped.

[plugins."dmt-source-review".mcp_servers.evidence]
enabled = true
default_tools_approval_mode = "prompt"
enabled_tools = ["list_evidence", "read_evidence"]

[plugins."dmt-source-review".mcp_servers.evidence.tools.list_evidence]
approval_mode = "approve"

Do not auto-approve a tool merely because its name begins with “read.” Inspect implementation, authentication, returned data and side effects. A search endpoint can leak confidential query text; a “get report” endpoint can create a billable job.

Security and privacy threat model

ThreatControl
Prompt injection in evidence contentTreat records as data; Skill says source text cannot expand permissions; separate policy from content
Path traversalResolve against one root and validate the filename/schema
Secret exposureNever return raw credentials; use host-managed auth and least-privilege scopes
Over-broad toolSplit read/write; use narrow parameters; require confirmation for mutation
Unbounded resultPaginate, cap size and return structured summaries
Dependency compromisePin versions, lock dependencies, audit and rebuild
Misleading install metadataCapabilities, privacy links and descriptions must match actual behaviour

OpenAI’s security and privacy guide and the current MCP specification should be part of code review, not links added at the end of a finished product.

Hooks are optional—and separately trusted

A plugin can include lifecycle hooks under hooks/hooks.json or through the manifest. OpenAI notes that installing/enabling a plugin does not automatically trust bundled hooks; users must review them. Add a hook only when it enforces a lifecycle requirement the Skill or server cannot provide. Hidden install-time or session commands are a trust liability.

Local bundled MCP vs registered remote MCP

DecisionBundled .mcp.jsonRegistered remote server and .app.json
RuntimeCommand/server distributed with the plugin and run where supportedHosted endpoint operated by the publisher
AuthenticationLocal environment/configurationRegistered connection and remote auth flow
Best forLocal/repository tools, offline/private data and developer workflowsShared SaaS data/actions and centrally updated logic
Operational burdenCross-platform dependencies and local process lifecycleHosting, uptime, tenancy, OAuth, privacy and incident response
Distribution mappingmcpServers points to ./.mcp.jsonCompatibility apps points to ./.app.json

Do not bundle a local server merely to avoid operating a proper remote service when the tool needs centralized customer data. Do not build a remote service merely to read a local repository. Choose the trust and deployment boundary that matches the data.

Versioning and compatibility

  • Use semantic versions for package releases and record the tested ChatGPT/Codex surface and date.
  • Make breaking tool-schema or Skill-output changes a major version unless the platform defines a different contract.
  • Keep migrations for data under PLUGIN_DATA; do not silently reinterpret old files.
  • Pin the MCP SDK and generate a lockfile. Re-run tests before updating.
  • Provide a changelog that calls out new permissions, network destinations and data use.
  • Fail with an actionable error when a surface does not support a bundled component; do not pretend the capability succeeded.

Example test receipt

The example package built for this article was checked on Windows with Node.js. npm run check parsed the manifest and MCP map and verified component paths. npm test ran one manifest contract test: one pass, zero failures. npm audit reported zero known vulnerabilities at install time. This is a package-structure receipt, not a security certification; production use still needs server edge-case and integration tests.

Prepare for public submission

  • Use a stable name/version and accurate capability descriptions.
  • Provide working homepage, support, privacy and terms URLs where required.
  • Test ChatGPT and Codex surfaces you claim to support.
  • Use production authentication and least-privilege scopes.
  • Provide test credentials/instructions only through the approved review process.
  • Remove placeholder assets and example domains.
  • Review the current submission documentation and error reference immediately before submission.

Public plugins are published to the universal directory shared by ChatGPT and Codex, but capabilities can remain surface-specific. Do not promise that every hook, UI or local-server behaviour works identically everywhere.

Official examples worth studying

OpenAI’s official plugins repository includes public examples such as Figma, Notion and Build web apps. Use them to inspect current package conventions, but keep your own permissions and reader job smaller than a mature integration unless complexity is justified.

Final build checklist

  • The chosen plugin shape is the smallest that solves the job.
  • .codex-plugin/plugin.json is valid and component paths are root-relative.
  • The Skill has a specific trigger, output contract and stop conditions.
  • MCP tools have narrow schemas, bounded results and truthful side effects.
  • Read/write capabilities and approval policies match implementation.
  • Normal, no-op, malformed, path-escape and prompt-injection tests pass.
  • Dependencies are pinned/locked and secrets remain host-managed.
  • Local marketplace installation works in a clean environment.
  • Public metadata, rights, privacy and support information are complete.
  • The package was tested again against current OpenAI docs before distribution.

FAQ

Does every Agent Plugin need an MCP server?

No. OpenAI supports Skills-only plugins. Add MCP only when the agent needs a typed tool or data capability that existing tools cannot provide.

Is installing a plugin the same as trusting its hooks?

No. OpenAI documents plugin-bundled hooks as separately reviewed/trusted. Inspect commands and paths before enabling them.

How this example was verified

DMT built the package shown in this article and ran its manifest check and Node test on August 11, 2026: one test passed, none failed, and the install-time audit reported zero known vulnerabilities. The package is a minimal educational example, not a production security certification.

Bottom line

A good Agent Plugin is not a folder full of prompts. It has a narrow reader job, a truthful manifest, a reusable Skill, minimal typed tools, explicit approvals, defensive server code and a testable distribution path. Start Skills-only when possible. Add MCP when a real capability is missing. Add UI or hooks only when they materially improve the job and their trust cost is justified.

Connect the package to a broader operating model with DMT’s agent-harness controls, accountable subagent workflow, accepted-result measurement method and GPT-5.6 model-routing guide.

Share this article

Written by

Tayeeb Khan

Tayeeb Khan is a digital marketing strategist, SEO specialist, and the founder of Digital Marketer Tayeeb (DMT). Backed by an engineering degree, certifications in Google and Meta advertising, and over a decade of hands-on experience growing startups, Tayeeb bridges the gap between technical infrastructure and marketing execution. His insights on SEO and AI-driven marketing are strictly practitioner-first—built on real tests, real campaigns, and real results. Connect on LinkedIn or via Email.

Leave a Comment

Your email address will not be published. Required fields are marked *

Stay ahead of the curve

Get actionable digital marketing, SEO, and AI insights delivered to your inbox. No fluff, just value.

No spam. Unsubscribe anytime.