Home AI How to Build a Claude Code Plugin: Add Custom Commands and Hooks

How to Build a Claude Code Plugin: Add Custom Commands and Hooks

Published: May 27, 2026

How to Build a Claude Code Plugin: Add Custom Commands and Hooks

You’ve mastered Claude Code skills, but you crave more: custom slash commands that trigger specific actions, hooks that automate responses to events, and deep integration with your development workflow. That’s where plugins come in. A Claude Code plugin is a piece of code that extends the platform’s capabilities, giving you precise control and new features. This tutorial walks you through building a plugin from scratch, explaining every step, so you can turn your ideas into powerful extensions.

If you’re just getting started with Claude Code’s extension ecosystem, read our comprehensive guide Claude Code Skills and Plugins for a broad overview. It covers the differences between skills, plugins, and MCP servers, helping you choose the right tool for the job. Once you know you need a plugin, you’re in the right place.

What Are Claude Code Plugins?

Plugins are executable modules written in JavaScript or TypeScript that run inside Claude Code’s process. Unlike skills, which are declarative Markdown instructions, plugins are imperative: they can define new slash commands, register event hooks, and directly interact with the editor and file system. Think of plugins as the power tools for users who need fine-grained automation.

There are two main pieces to a plugin:

  • The plugin.json manifest, which declares commands, hooks, and permissions.
  • One or more script files that implement the command handlers and hook callbacks.

When Claude Code starts, it scans the ~/.claude/plugins/ and .claude/plugins/ directories, loads each plugin, and makes its commands available. The user must explicitly enable a plugin via /plugin enable.

Anatomy of a Plugin Manifest

The plugin.json file is the heart of your plugin. It tells Claude Code what the plugin does and what it’s allowed to access. Here’s a breakdown of the key fields:

FieldTypeDescription
namestringUnique identifier, used in commands and enable/disable.
versionstringSemantic version, e.g., “1.0.0”.
descriptionstringBrief summary for the plugin listing.
commandsarrayList of command objects (name, description, handler).
hooksobjectEvent names mapped to handler functions.
permissionsarrayAllowed actions like “read”, “write”, “network”.

Permissions are crucial: Claude Code will prompt the user to approve any permission that might affect their system. Keep the permission set minimal to build trust.

Example Manifest

{
  "name": "json-pretty-printer",
  "version": "0.2.0",
  "description": "Pretty-prints JSON with configurable indentation",
  "commands": [
    {
      "name": "pretty-json",
      "description": "Format selected JSON with 2-space indent",
      "handler": "formatJson"
    }
  ],
  "hooks": {},
  "permissions": ["read", "write"]
}

Writing the Plugin Logic

The actual code lives in a JavaScript or TypeScript file. The handler functions receive a context object that provides access to Claude Code’s API. For a simple command, you might read the current selection, transform it, and write it back.

Let’s implement the formatJson handler from the example:

function formatJson(args) {
  const editor = claudeCode.editor;
  const selection = editor.selectionText;
  if (!selection) {
    claudeCode.showMessage('No JSON selected');
    return;
  }

  try {
    const parsed = JSON.parse(selection);
    const pretty = JSON.stringify(parsed, null, 2);
    editor.replaceSelection(pretty);
    claudeCode.showMessage('JSON formatted');
  } catch (e) {
    claudeCode.showMessage('Invalid JSON: ' + e.message);
  }
}

The claudeCode global provides methods like showMessage and access to the editor. Consult the official plugin API docs for the full surface area.

Registering Hooks

Hooks let your plugin react to events such as file saves, command executions, or chat interactions. Define them in the manifest’s hooks object and implement the corresponding functions.

For example, to run code after every file save:

// In plugin.json
"hooks": {
  "onFileSaved": "onFileSaved"
}

// In plugin.js
function onFileSaved(event) {
  const filepath = event.filepath;
  claudeCode.log(`File saved: ${filepath}`);
  // Maybe trigger a linter or send a notification
}

Common hooks include onFileOpened, onChatMessage, onCommandExecuted. They are powerful for creating responsive workflows.

Pro Tip

Don’t overuse hooks. They run on every event, so heavy logic can slow down Claude Code. Keep event handlers lean and debounce where appropriate.

Permissions and Security

When a plugin requests permissions, Claude Code asks the user to approve them. The user can revoke these later. As a developer, you should request only the permissions you absolutely need. "permissions": ["read", "write"] is straightforward. Network access requires "network". Avoid "all" unless absolutely necessary.

If your plugin calls external APIs, consider using an API key that the user provides via a configuration command, rather than bundling your own key. This respects the user’s privacy and billing.

Testing and Debugging

During development, you can load your plugin without reinstalling by using the /plugin load command pointing to the plugin directory. This hot-reloads changes.

Open the Developer Tools (View → Toggle Developer Tools) to see console logs from your plugin. Use console.log liberally. When your plugin throws an uncaught exception, Claude Code will show an error toast and log the stack trace.

Common pitfalls:

  • Forgetting to export the handler function (if using modules).
  • Mismatched handler names between plugin.json and the script.
  • Requesting unnecessary permissions, causing user distrust.
  • Blocking the main thread with synchronous operations; use async functions and await carefully.

Packaging and Distribution

Once your plugin works, you can package it as a .zip or share the source directory. Users install by placing it in their plugins folder and running /plugin enable. There is no official plugin registry yet, so distribution is typically via GitHub or internal file shares.

Consider writing a README with usage instructions and examples. If your plugin works with a specific service, document how to obtain API keys.

Best Practices

  • Use TypeScript: Strong typing reduces runtime errors and improves the developer experience.
  • Keep commands focused: One command should do one thing well. Avoid creating a “kitchen sink” command that tries to do too much.
  • Respect user settings: Don’t override user preferences (e.g., tab size) without consent.
  • Handle errors gracefully: Show friendly error messages, not stack traces.
  • Document your plugin: Include help text in the command description and optionally provide a /help command.

Real-World Plugin Examples

The community has created many useful plugins. Here are a few to inspire you:

  • Git integrates: A plugin that adds /git commit, /git push commands with interactive prompts.
  • Todo remover: Scans the current file for TODO comments and either removes them or moves them to a project-wide list.
  • API tester: Sends HTTP requests directly from the editor and pretty-prints the response.
  • Code formatter: Wraps an external formatter (like Prettier) with custom options.
  • AI cost estimator: Calls an LLM usage API to estimate the cost of the current conversation.

These examples demonstrate the range of possibilities.

Conclusion

Plugins transform Claude Code from a helpful assistant into a tailored development environment. With a few dozen lines of JavaScript, you can automate repetitive tasks, integrate with your favorite services, and share your creations with the team. Start small, iterate based on real usage, and soon you’ll have a personalized toolkit that makes every coding session more productive.

To get the most out of Claude Code, explore the full spectrum of extensibility options in our main guide Claude Code Skills and Plugins. And stay tuned for deep dives into MCP security, advanced patterns, and more.

Frequently Asked Questions

Frequently Asked Questions About Claude Code Plugins


Plugins are primarily for Claude Code CLI. Claude Desktop does not currently support the plugin system, though it does support skills. Check the documentation for the latest compatibility.


Yes. As long as your plugin directory includes a compiled JavaScript file (e.g., plugin.js) that Claude Code can load, you can write in TypeScript and compile as part of your development workflow. Just ensure the plugin.json points to the compiled .js file.


Plugins run with the same permissions as the user running Claude Code. If a plugin has “read” and “write” permissions, it can access any file the user can. Only install plugins from trusted sources, as they can exfiltrate data.


Use /plugin disable plugin-name inside Claude Code. You can re-enable it later with /plugin enable.


There is no official plugin store yet. However, you can open-source your plugin and share it with the community via GitHub. The team may highlight notable plugins in the docs.


A plugin runs inside Claude Code’s process and uses its internal APIs. An MCP server runs as a separate process and communicates via JSON-RPC; it can be written in any language. Use plugins for deep, tight integration; use MCP for external tool exposure and language flexibility.