Context Course documentation

Building Your Own Plugin

Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Building Your Own Plugin

Unit 2 produced a text-processor MCP server. In this lesson we package it as a plugin. For Claude Code and Codex that means a manifest plus bundled skills and MCP config; for OpenCode it means a plugin module written in TypeScript, with MCP configuration handled separately; for Pi it means a package declared in package.json, optionally paired with pi-mcp-adapter when you want MCP tools inside Pi.

Plugin Project Structure

Create your plugin directory alongside the server you already built:

mkdir text-processor-plugin
cd text-processor-plugin

# Directory structure
mkdir -p skills/{analyze-text,extract-keywords,check-reading-level}

# Create core files
touch README.md
touch skills/analyze-text/SKILL.md
touch skills/extract-keywords/SKILL.md
touch skills/check-reading-level/SKILL.md

Notice what’s different from Unit 2: no server code here. For Claude Code and Codex, the plugin references the MCP server you already built and deployed. OpenCode’s native plugin branch below adds behavior in code and can still point at the same server separately if you want both surfaces. The Pi branch below reuses the same skills/ directory inside a Pi package, and can pair those skills with the pi-mcp-adapter setup from Unit 2 when you want MCP tools available in Pi.

Step 1: Write the Skills

This step applies directly to Claude Code, Codex, and Pi, where the bundle can ship skills/. If you’re following the OpenCode branch, skip ahead to Step 2: OpenCode plugins are code modules, not skill bundles.

Skills teach the agent when and how to use your MCP tools. Each skill wraps one of the tools from your text-processor server.

Skill 1: Analyze Text

Create skills/analyze-text/SKILL.md:

# Analyze Text

Use the `analyze_text` tool from the text-processor MCP server to compute text statistics.

## When to Use

Use this skill when the user asks about text statistics, word counts, character counts, sentence counts, average word length, or readability metrics.

## How to Use

Call the `analyze_text` tool with the full text as input. The tool returns JSON with:
- `total_characters`, `characters_without_spaces`
- `total_words`, `total_sentences`
- `average_word_length`, `average_sentence_length`
- `unique_words`

## Example

User: "How complex is this paragraph?"

1. Call `analyze_text` with the paragraph
2. Interpret the statistics (high unique word ratio = diverse vocabulary, long average sentence length = complex prose)
3. Summarize findings in plain language

Skill 2: Extract Keywords

Create skills/extract-keywords/SKILL.md:

# Extract Keywords

Use the `extract_keywords` tool from the text-processor MCP server to find the most important words in text.

## When to Use

Use this skill when the user asks for keywords, key terms, topic extraction, or content summarization.

## How to Use

Call `extract_keywords` with the text and an optional `count` parameter (default: 5). The tool returns JSON with a `keywords` array, each containing `word` and `frequency`.

## Example

User: "What are the main topics in this article?"

1. Call `extract_keywords` with count=10
2. Group related keywords into themes
3. Present themes with supporting keyword frequencies

Skill 3: Check Reading Level

Create skills/check-reading-level/SKILL.md:

# Check Reading Level

Use the `check_reading_level` tool from the text-processor MCP server to estimate text difficulty.

## When to Use

Use this skill when the user asks about reading level, text difficulty, grade level, or audience appropriateness.

## How to Use

Call `check_reading_level` with the text. The tool returns JSON with:
- `grade_level` (numeric Flesch-Kincaid grade)
- `reading_level` (Elementary School, Middle School, High School, or College/Academic)

## Example

User: "Is this documentation appropriate for beginners?"

1. Call `check_reading_level` with the text
2. Compare the grade level to the target audience
3. Suggest simplifications if the level is too high

Step 2: Create the Plugin Entry Point

Claude Code and Codex use manifests to declare what the plugin provides. OpenCode uses a plugin module instead. Pi uses package.json plus optional extensions/. The entry point differs by platform.

Claude Code
Codex
OpenCode
Pi

Create the Claude Code manifest directory:

mkdir -p .claude-plugin

Create .claude-plugin/plugin.json:

{
  "name": "text-processor-plugin",
  "version": "1.0.0",
  "description": "Text analysis skills powered by the text-processor MCP server",
  "author": {
    "name": "Your Name"
  }
}

Create .mcp.json to point at your server. If you’re using the local server from Unit 2:

{
  "mcpServers": {
    "text-processor": {
      "command": "python",
      "args": ["../text-processor-mcp/server.py"]
    }
  }
}

Or if you deployed to Hugging Face Spaces:

{
  "mcpServers": {
    "text-processor": {
      "url": "https://YOUR-USERNAME-text-processor-mcp.hf.space/gradio_api/mcp/"
    }
  }
}

Step 3: Create the Human-Facing README

For all three platforms, keep human documentation in README.md. Claude Code and Codex use plugin.json for install metadata; OpenCode loads the plugin module directly, so the README is for developers rather than the runtime.

Create README.md at the plugin root:

# Text Processor Plugin

Plugin examples for text-analysis workflows built from the Unit 2 server and the Unit 3 skills.

## Skills

- **Analyze Text** — Word counts, sentence stats, vocabulary diversity
- **Extract Keywords** — Find the most frequent meaningful terms
- **Check Reading Level** — Flesch-Kincaid grade level estimation

## Claude Code and Codex

The manifest-first versions bundle:
- skills in `skills/`
- plugin metadata in `.claude-plugin/plugin.json` or `.codex-plugin/plugin.json`
- optional MCP config in `.mcp.json`

## OpenCode

The OpenCode version is a local or npm-loaded JS/TS plugin module under `.opencode/plugins/` or the `plugin` array in `opencode.json`.

## Related MCP Server

If you also connect the `text-processor` MCP server, it provides:
- `analyze_text` — Compute text statistics
- `extract_keywords` — Extract frequent terms
- `check_reading_level` — Estimate reading difficulty
- `reverse_text` — Reverse a string

## Setup

The plugin can use your local server or the deployed Spaces version:

**Local:** Ensure `text-processor-mcp/server.py` is available and the MCP runtime is installed.

**Remote:** Update the `.mcp.json` or `opencode.json` URL to your deployed Space:
`https://YOUR-USERNAME-text-processor-mcp.hf.space/gradio_api/mcp/`

Step 4: Test Your Plugin

Claude Code
Codex
OpenCode
Pi

To test a local plugin, expose it through a local marketplace file. Create marketplace.json next to your plugin directory:

{
  "name": "local-example-plugins",
  "owner": { "name": "you" },
  "plugins": [
    {
      "name": "text-processor-plugin",
      "source": "./text-processor-plugin",
      "description": "Text analysis skills"
    }
  ]
}

Then inside Claude Code:

/plugin marketplace add /absolute/path/to/marketplace.json
/plugin install text-processor-plugin@local-example-plugins

Once installed, test the skills conversationally:

Analyze the reading level of this text: "The mitochondria is the powerhouse of the cell.
It provides energy through oxidative phosphorylation."

After editing plugin files, use /plugin to disable and re-enable the plugin so Claude Code picks up changes.

Complete Plugin Structure

For Claude Code and Codex (assuming you use both), the final manifest-first plugin directory looks like:

text-processor-plugin/
├── .claude-plugin/
│   └── plugin.json                       # Claude Code manifest
├── .codex-plugin/
│   └── plugin.json                       # Codex manifest
├── .mcp.json                             # Shared MCP config for both
├── README.md                             # Documentation
└── skills/
    ├── analyze-text/SKILL.md
    ├── extract-keywords/SKILL.md
    └── check-reading-level/SKILL.md

For OpenCode, the plugin surface is separate:

.opencode/
├── plugins/
│   └── text-processor-plugin.ts
└── package.json          # optional

Notice there’s no server code in the manifest-first plugin. The MCP server lives separately — either running locally from Unit 2’s text-processor-mcp/ directory or deployed on Hugging Face Spaces. Claude Code and Codex point at that server through .mcp.json. OpenCode can point at it separately in opencode.json, but that MCP config is adjacent to the plugin rather than inside it.

This separation is a key design principle: skills describe how to use tools, MCP servers provide the tools, and plugins package reusable agent behavior in the way each platform expects.

Best Practices

When building plugins, write skill descriptions that explain when to use a tool (not just what it does), reference existing MCP servers instead of duplicating code, keep only plugin.json inside the manifest directory, never hardcode API keys (use environment variables), version semantically (1.0.0, 1.1.0, 2.0.0), and test locally before publishing. For OpenCode, keep the plugin module small and focused, then add npm dependencies only when the hook or tool really needs them.

Key Takeaways

Claude Code and Codex plugins are manifests that bundle skills and optional MCP or app config. OpenCode plugins are JS/TS modules; skills and MCP servers live beside them. In both worlds the MCP server from Unit 2 stays where it is — the plugin just references or complements it.

Next, a quiz, then installing and using published plugins.

Update on GitHub