Chapter 49

Plugin vs Skill: Core Differences, Capability Boundaries and Ten Plugin-Exclusive Capabilities

Chapter 49: Plugin Architecture Deep Dive: The Four-Layer Model of MCP Server, Hooks, LSP, and Monitoring

49.1 Plugin vs. Skill: A Critical Distinction

Before diving into architecture, we need to settle a confusion that trips up almost every new developer entering the Claude ecosystem: the difference between a Plugin and a Skill.

A Skill is the smallest unit of reusable behavior. It is a single Markdown file. The file's YAML frontmatter declares metadata — name, description, parameters — and the file body uses natural language to describe the task scenario, input/output expectations, and behavioral constraints. A Skill contains no executable code. It is a behavioral specification document: Claude reads it and knows how to perform a certain class of task.

A Plugin is a full software package. A Plugin can contain:

Plugin is the container; Skill is the content. A Plugin can declare which Skills it provides, or it can provide no Skills at all and purely extend Claude's tool or perception capabilities.

Plugin (software package)
├── plugin.json              ← manifest file
├── mcp/                     ← MCP Server implementation
├── hooks/                   ← lifecycle hook implementations
├── lsp/                     ← Language Server Protocol
├── monitor/                 ← observability collectors
└── skills/                  ← optional bundled Skill files

Skill (single .md file)
└── my-skill.md              ← exists independently, no Plugin needed

The philosophical root of this design is separation of concerns: Skills serve "tell Claude what to do," Plugins serve "give Claude the ability to do it."

49.2 The Four-Layer Model Overview

Claude's Plugin architecture is organized into four distinct layers, each with a clear boundary of responsibility.

┌─────────────────────────────────────────────┐
│  Layer 4: Monitor Layer                      │
│  Decision tracing, cost tracking, audit logs │
├─────────────────────────────────────────────┤
│  Layer 3: LSP Layer                          │
│  Domain language understanding, completion   │
├─────────────────────────────────────────────┤
│  Layer 2: Hooks Layer                        │
│  Lifecycle injection, pre/post processing    │
├─────────────────────────────────────────────┤
│  Layer 1: MCP Layer                          │
│  Tool definitions, resource exposure         │
└─────────────────────────────────────────────┘

Every layer is optional. A minimal Plugin might only have the MCP layer. A security audit Plugin might only have the Hooks and Monitor layers. A language support Plugin might only have the LSP layer. The four-layer model is a conceptual framework, not a mandatory checklist.

49.3 Layer 1: The MCP Server Layer

MCP Protocol Fundamentals

The Model Context Protocol (MCP) is an open protocol defined by Anthropic that governs communication between Claude and external tools. An MCP Server is the server-side implementation of this protocol, exposing three resource types to Claude:

Tools: Functions that Claude can actively invoke. Tools have explicit JSON Schema definitions for their input parameters and return format. Claude decides during inference whether to call a tool and what arguments to pass.

Resources: Data sources that Claude can read. Unlike tools, resources don't require explicit invocation — Claude can request the content of a resource URI at any point in a conversation. Resources are well-suited for documents, configuration files, database views, and other "passively read" content.

Prompts: Pre-defined prompt templates that users can reference in conversation to trigger specific workflows.

MCP Transport Modes

MCP Servers support two transport modes:

STDIO mode (local plugins):
Claude Code process → stdin/stdout → MCP Server process
Use cases: local tools, CLI integrations, no-network processing

HTTP/SSE mode (remote plugins):
Claude Code process → HTTP/SSE → remote MCP Server
Use cases: cloud services, cross-machine calls, persistent connections

Declaring an MCP Server in plugin.json

{
  "name": "my-database-plugin",
  "version": "1.0.0",
  "mcp": {
    "servers": [
      {
        "id": "db-server",
        "transport": "stdio",
        "command": "node",
        "args": ["./mcp/server.js"],
        "env": {
          "DB_URL": "${config.databaseUrl}"
        }
      }
    ]
  }
}

After the MCP Server starts, it performs the standard MCP handshake and advertises its available tools and resources to Claude. Claude adds these tools to the current session's available tool list.

Tool Definition Example

A complete database query tool in TypeScript:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const server = new McpServer({
  name: "database-plugin",
  version: "1.0.0",
});

server.tool(
  "query_database",
  "Execute a read-only SQL query against the connected database",
  {
    sql: z.string().describe("The SQL query to execute (SELECT only)"),
    limit: z.number().optional().default(100).describe("Max rows to return"),
  },
  async ({ sql, limit }) => {
    if (!sql.trim().toUpperCase().startsWith("SELECT")) {
      throw new Error("Only SELECT queries are allowed");
    }
    const results = await db.query(sql, { limit });
    return {
      content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
    };
  }
);

const transport = new StdioServerTransport();
await server.connect(transport);

49.4 Layer 2: The Hooks Layer

Design Philosophy

Hooks are injection points into Claude's workflow lifecycle. Unlike MCP tools that passively wait to be called, Hooks are proactive — they fire automatically at each step of Claude's operation without Claude needing to invoke them explicitly.

This design makes Hooks ideal for:

Built-in Hook Points

PreToolCall          ← before tool invocation (can modify args or abort)
PostToolCall         ← after tool returns (can modify result)
PreResponse          ← before response generation (can modify system prompt)
PostResponse         ← after response is generated (can modify output text)
SessionStart         ← when a session begins
SessionEnd           ← when a session ends
TokenBudgetWarning   ← when token usage hits the warning threshold

Hook Implementation

// hooks/pre-tool.ts
import type { PreToolCallHook, HookContext } from "@claude/plugin-sdk";

export const preToolCall: PreToolCallHook = async (
  toolName: string,
  toolInput: Record<string, unknown>,
  context: HookContext
) => {
  const sensitiveTools = ["execute_bash", "write_file", "delete_file"];
  
  if (sensitiveTools.includes(toolName)) {
    const userId = context.session.userId;
    const hasPermission = await checkUserPermission(userId, toolName);
    
    if (!hasPermission) {
      return {
        action: "block",
        reason: `User ${userId} lacks permission for ${toolName}`,
      };
    }
  }
  
  return { action: "allow" };
};

Hooks return an action object with one of three values: allow (proceed normally), block (abort the operation), or modify (continue with altered parameters or results).

49.5 Layer 3: The LSP Layer

Language Server Protocol (LSP) was originally designed by Microsoft for VS Code to standardize communication between editors and language intelligence services. Claude Code adopts LSP in its Plugin system, allowing Plugins to provide deep understanding of specific languages or domains.

When a user asks Claude Code to analyze code in a proprietary DSL or specialized framework, Claude's default language understanding may be insufficient. An LSP Plugin can supply:

Registering an LSP Server

{
  "lsp": {
    "languages": ["solidity", "my-dsl"],
    "server": {
      "command": "node",
      "args": ["./lsp/server.js"]
    },
    "documentSelector": [
      { "language": "solidity", "pattern": "**/*.sol" },
      { "language": "my-dsl", "pattern": "**/*.mydsl" }
    ]
  }
}

Claude Code automatically starts the LSP Server when it encounters matching files and injects LSP-provided diagnostics and symbol information into Claude's context window.

49.6 Layer 4: The Monitor Layer

The Monitor layer is the most frequently overlooked in development but the most critical in production. It collects telemetry data throughout Claude's working process, providing the foundation for debugging, optimization, and auditing.

Data Dimensions

Decision chain:  thinking summaries for each reasoning step
Tool calls:      tool name, arguments, return values, latency
Token usage:     prompt tokens, completion tokens, cost estimates
Error records:   tool failures, MCP timeouts, parse errors
Session meta:    user ID, session ID, start/end timestamps

Monitor Collector Implementation

// monitor/collector.ts
import type { MonitorCollector } from "@claude/plugin-sdk";

export const collector: MonitorCollector = {
  onDecision(event) {
    metrics.record("claude.decision", {
      sessionId: event.sessionId,
      model: event.model,
      inputTokens: event.usage.inputTokens,
      outputTokens: event.usage.outputTokens,
      latencyMs: event.latencyMs,
    });
  },
  
  onToolCall(event) {
    metrics.record("claude.tool_call", {
      tool: event.toolName,
      success: event.success,
      latencyMs: event.latencyMs,
    });
  },
};

49.7 The Complete plugin.json Specification

{
  "name": "my-awesome-plugin",
  "version": "1.2.3",
  "description": "One-sentence description of what this Plugin does",
  "author": "Your Name <[email protected]>",
  "license": "MIT",
  "keywords": ["database", "sql", "analytics"],
  
  "engines": {
    "claude-code": ">=1.0.0"
  },
  
  "config": {
    "schema": {
      "databaseUrl": {
        "type": "string",
        "description": "PostgreSQL connection string",
        "required": true,
        "secret": true
      },
      "maxQueryRows": {
        "type": "number",
        "description": "Maximum rows per query",
        "default": 1000
      }
    }
  },
  
  "mcp": {
    "servers": [
      {
        "id": "main",
        "transport": "stdio",
        "command": "node",
        "args": ["./mcp/server.js"]
      }
    ]
  },
  
  "hooks": {
    "preToolCall": "./hooks/pre-tool.js",
    "postResponse": "./hooks/post-response.js"
  },
  
  "lsp": {
    "languages": ["sql"],
    "server": { "command": "node", "args": ["./lsp/server.js"] }
  },
  
  "monitor": {
    "collector": "./monitor/collector.js",
    "sampling": 1.0
  },
  
  "skills": [
    "./skills/query-builder.md",
    "./skills/schema-explorer.md"
  ],
  
  "permissions": ["read:files", "network:outbound"]
}

Key field notes:

49.8 The Plugin Isolation Model

Security is the primary consideration in Plugin system design. Claude Plugins employ multi-layer isolation:

Process isolation: Each MCP Server and LSP Server runs in an independent child process communicating with the main process via STDIO or HTTP. A child process crash does not affect the Claude Code main process.

Permission isolation: The declared permissions field determines which system resources a Plugin can access. Claude Code enforces these limits at the sandbox layer — even if Plugin code attempts to access unauthorized resources, OS-level restrictions block the attempt.

Configuration isolation: Plugin configuration (including secrets) is fully isolated from other Plugins' configuration. Each Plugin can only access its own configuration namespace.

49.9 How the Four Layers Collaborate

Taking a "code security audit Plugin" as an example, here is how the four layers work together:

User request: "Audit this Solidity contract for security vulnerabilities"

1. LSP Layer:
   - LSP Server reads .sol files
   - Provides Solidity parsing and known vulnerability pattern matching
   - Injects diagnostic results into Claude's context

2. MCP Layer:
   - Claude decides to call "fetch_vulnerability_database"
   - MCP Server connects to vulnerability database API
   - Returns latest CVEs and vulnerability patterns

3. Hooks Layer:
   - PreToolCall Hook verifies permission to access external API
   - PostResponse Hook validates output contains no misleading content

4. Monitor Layer:
   - Records complete decision chain for this audit session
   - Collects tool call counts and latency statistics
   - Reports to monitoring system for later analysis

Each layer fulfills its role, collectively providing a reliable, auditable, and extensible Plugin capability.

Summary

Claude's four-layer Plugin architecture — MCP → Hooks → LSP → Monitor — provides a clear separation of concerns: MCP connects the external world, Hooks control workflow, LSP deepens language understanding, and Monitor provides end-to-end observability. The Plugin vs. Skill distinction is fundamental: Plugin is a software package container, Skill is a behavioral specification document. Mastering the plugin.json manifest format is the starting point for developing any Plugin. The next chapter will build a complete Plugin from scratch.

Rate this chapter
4.7  / 5  (3 ratings)

💬 Comments