NYC
skills/smithery/ai/mcp-standards

mcp-standards

SKILL.md

MCP Standards Skill

1. Overview

What is MCP in Claude Code?

Model Context Protocol (MCP) is the standard way to extend Claude Code with custom tools and integrations. MCP servers provide:

  • Tool Integration: Connect to external APIs, databases, and services
  • Context Providers: Supply relevant information to Claude during conversations
  • Action Handlers: Execute operations in external systems
  • Data Sources: Access project-specific or organization-specific data

Why Standardization Matters

Standardized MCP servers ensure:

  1. Predictable Behavior: Developers know what to expect from MCP tools
  2. Easier Debugging: Consistent patterns make issues easier to identify
  3. Better Discoverability: Standard naming helps Claude and users find tools
  4. Maintainability: Common patterns reduce maintenance burden
  5. Team Consistency: Multiple developers follow same conventions

MCP in the Plugin Ecosystem

MCP servers are plugin components alongside agents, commands, and skills:

plugin/
├── agents/          # Specialized Claude instances
├── commands/        # CLI commands
├── skills/          # Knowledge documents
└── mcp-servers/     # MCP tool providers ← We're here

Key Difference: While agents use built-in tools, MCP servers provide NEW tools that extend Claude's capabilities.


2. MCP Server Structure

Standard Directory Layout

mcp-servers/
├── server-name/
│   ├── index.ts                  # Server entry point
│   ├── package.json              # Dependencies and metadata
│   ├── tsconfig.json             # TypeScript configuration
│   ├── README.md                 # Server documentation
│   ├── tools/                    # Tool implementations
│   │   ├── read-tool.ts
│   │   ├── write-tool.ts
│   │   └── index.ts              # Tool exports
│   ├── lib/                      # Shared utilities
│   │   ├── client.ts             # API client
│   │   ├── validation.ts         # Input validation
│   │   └── errors.ts             # Error handling
│   └── tests/                    # Test files
│       ├── read-tool.test.ts
│       └── write-tool.test.ts

Entry Point Pattern (index.ts)

#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";

// Import tools
import { fetchTool, createTool, updateTool } from "./tools/index.js";

const server = new Server(
  {
    name: "mcp-plugin-server",
    version: "1.0.0",
  },
  {
    capabilities: {
      tools: {},
    },
  }
);

// Register tools
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [fetchTool.definition, createTool.definition, updateTool.definition],
}));

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  switch (name) {
    case fetchTool.name:
      return fetchTool.handler(args);
    case createTool.name:
      return createTool.handler(args);
    case updateTool.name:
      return updateTool.handler(args);
    default:
      throw new Error(`Unknown tool: ${name}`);
  }
});

// Start server
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
}

main().catch(console.error);

Tool Module Pattern

// tools/fetch-tool.ts
import { z } from "zod";

const inputSchema = z.object({
  id: z.string().describe("The resource ID to fetch"),
  includeMetadata: z.boolean().optional().describe("Include metadata in response"),
});

export const fetchTool = {
  name: "mcp__plugin__fetch_resource",
  definition: {
    name: "mcp__plugin__fetch_resource",
    description: "Fetch a resource by ID from the external service",
    inputSchema: {
      type: "object",
      properties: {
        id: {
          type: "string",
          description: "The resource ID to fetch",
        },
        includeMetadata: {
          type: "boolean",
          description: "Include metadata in response",
        },
      },
      required: ["id"],
    },
  },
  handler: async (args: unknown) => {
    const validated = inputSchema.parse(args);

    try {
      const result = await fetchResourceById(validated.id);
      return {
        content: [
          {
            type: "text",
            text: JSON.stringify(result, null, 2),
          },
        ],
      };
    } catch (error) {
      throw new Error(`Failed to fetch resource: ${error.message}`);
    }
  },
};

3. Tool Naming Conventions

Standard Pattern

mcp__<plugin-name>__<tool-name>

Components:

  • mcp__ - Universal prefix indicating MCP tool
  • <plugin-name> - Plugin identifier (matches plugin.json id)
  • <tool-name> - Descriptive snake_case tool name

Real-World Examples

// Frontend Plugin
"mcp__frontend__figma_fetch"           // Fetch Figma designs
"mcp__frontend__figma_export_assets"   // Export Figma assets
"mcp__frontend__lighthouse_audit"      // Run Lighthouse audit

// Code Analysis Plugin
"mcp__code-analysis__claudemem_search"       // Search codebase
"mcp__code-analysis__claudemem_enrich"       // Enrich file context

// Bun Backend Plugin
"mcp__bun__apidog_sync"                // Sync with Apidog
"mcp__bun__apidog_validate"            // Validate API spec

// SEO Plugin
"mcp__seo__analyze_page"               // Analyze page SEO
"mcp__seo__check_schema"               // Validate schema markup

Tool Name Guidelines

DO:

  • Use snake_case for tool names
  • Use action verbs (fetch, create, update, analyze)
  • Be specific about what the tool does
  • Keep names under 50 characters

DON'T:

  • Use camelCase or PascalCase
  • Use generic names like "do_thing"
  • Include version numbers in names
  • Use abbreviations unless widely known

Verb Conventions

Verb Use Case Example
fetch Retrieve single resource fetch_user
list Retrieve multiple resources list_projects
search Query with filters search_files
create Create new resource create_issue
update Modify existing resource update_config
delete Remove resource delete_cache
validate Check data validity validate_schema
analyze Perform analysis analyze_performance
sync Synchronize data sync_database
export Export data export_report

4. Transport Configuration

stdio Transport (Most Common)

Standard for local development and command-line usage:

{
  "mcpServers": {
    "frontend-tools": {
      "command": "node",
      "args": ["${CLAUDE_PLUGIN_ROOT}/mcp-servers/frontend-tools/index.js"],
      "transport": "stdio"
    }
  }
}

When to Use:

  • Local plugin development
  • Command-line integrations
  • Single-user scenarios
  • No network requirements

Advantages:

  • Simple setup
  • No port conflicts
  • Secure (local only)
  • Low latency

HTTP Transport

For remote services or multi-user scenarios:

{
  "mcpServers": {
    "shared-service": {
      "url": "http://localhost:3000/mcp",
      "transport": "http",
      "headers": {
        "Authorization": "Bearer ${API_TOKEN}"
      }
    }
  }
}

When to Use:

  • Remote API services
  • Shared team resources
  • Cloud-hosted tools
  • Microservice architecture

Advantages:

  • Network accessible
  • Scalable
  • Can use load balancing
  • Standard HTTP tooling

WebSocket Transport

For real-time bidirectional communication:

{
  "mcpServers": {
    "realtime-service": {
      "url": "ws://localhost:8080/mcp",
      "transport": "websocket"
    }
  }
}

When to Use:

  • Real-time updates
  • Streaming responses
  • Bidirectional communication
  • Live collaboration tools

Environment Variable Interpolation

All transports support environment variable substitution:

{
  "mcpServers": {
    "apidog-sync": {
      "command": "node",
      "args": ["${CLAUDE_PLUGIN_ROOT}/mcp-servers/apidog/index.js"],
      "env": {
        "APIDOG_API_TOKEN": "${APIDOG_API_TOKEN}",
        "APIDOG_PROJECT_ID": "${APIDOG_PROJECT_ID}"
      }
    }
  }
}

Pattern: ${VARIABLE_NAME} is replaced at runtime.


5. Tool Categories

Read-Only Tools

Purpose: Retrieve information without side effects.

Characteristics:

  • Safe to call multiple times
  • No state changes
  • Fast response times
  • Cacheable results

Examples:

// Fetch single resource
mcp__plugin__fetch_config
mcp__plugin__get_status

// List multiple resources
mcp__plugin__list_projects
mcp__plugin__list_files

// Search/query
mcp__plugin__search_code
mcp__plugin__query_database

Write Tools

Purpose: Create, update, or delete resources.

Characteristics:

  • Modify state
  • Require validation
  • Need error handling
  • Should be idempotent when possible

Examples:

// Create
mcp__plugin__create_file
mcp__plugin__create_issue

// Update
mcp__plugin__update_config
mcp__plugin__update_document

// Delete
mcp__plugin__delete_cache
mcp__plugin__remove_entry

Analysis Tools

Purpose: Process data and provide insights.

Characteristics:

  • Compute-intensive
  • Return structured results
  • May have longer timeouts
  • Often cacheable

Examples:

mcp__plugin__analyze_performance
mcp__plugin__audit_security
mcp__plugin__validate_schema
mcp__plugin__check_quality

Integration Tools

Purpose: Connect to external services.

Characteristics:

  • Bridge systems
  • Handle authentication
  • Manage rate limits
  • Deal with network errors

Examples:

mcp__plugin__sync_database
mcp__plugin__import_data
mcp__plugin__export_report
mcp__plugin__webhook_notify

6. Performance Standards

Response Time Targets

Tool Type Target Max Acceptable
Simple fetch <50ms 200ms
List operation <100ms 500ms
Search <200ms 1000ms
Analysis <500ms 3000ms
Write operation <300ms 2000ms
Sync operation <1000ms 5000ms

Timeout Configuration

// Set timeouts for different operations
const TIMEOUT_CONFIG = {
  fetch: 5000,      // 5 seconds
  search: 10000,    // 10 seconds
  analyze: 30000,   // 30 seconds
  sync: 60000,      // 1 minute
};

async function callWithTimeout<T>(
  operation: Promise<T>,
  timeoutMs: number
): Promise<T> {
  const timeout = new Promise<never>((_, reject) =>
    setTimeout(() => reject(new Error("Operation timed out")), timeoutMs)
  );
  return Promise.race([operation, timeout]);
}

Rate Limiting

class RateLimiter {
  private requests: number = 0;
  private resetTime: number = Date.now() + 60000;

  async checkLimit(limit: number = 100) {
    if (Date.now() > this.resetTime) {
      this.requests = 0;
      this.resetTime = Date.now() + 60000;
    }

    if (this.requests >= limit) {
      throw new Error("Rate limit exceeded. Try again later.");
    }

    this.requests++;
  }
}

Error Handling Patterns

class MCPError extends Error {
  constructor(
    message: string,
    public code: string,
    public details?: unknown
  ) {
    super(message);
    this.name = "MCPError";
  }
}

// Usage in tools
try {
  const result = await externalAPI.fetch(id);
  return { content: [{ type: "text", text: JSON.stringify(result) }] };
} catch (error) {
  if (error.code === "NOT_FOUND") {
    throw new MCPError("Resource not found", "NOT_FOUND", { id });
  }
  if (error.code === "RATE_LIMITED") {
    throw new MCPError("Rate limit exceeded", "RATE_LIMITED");
  }
  throw new MCPError("Unexpected error", "INTERNAL_ERROR", { error });
}

7. Security Patterns

Input Validation

Always validate inputs using a schema validation library:

import { z } from "zod";

// Define strict schemas
const fetchInputSchema = z.object({
  id: z.string().uuid("Invalid UUID format"),
  includePrivate: z.boolean().default(false),
});

// Validate in handler
export const handler = async (args: unknown) => {
  const validated = fetchInputSchema.parse(args);
  // Now 'validated' is type-safe
};

Output Sanitization

Never return raw data that might contain sensitive information:

function sanitizeOutput(data: any): any {
  // Remove sensitive fields
  const { password, apiKey, secret, ...safe } = data;

  // Sanitize nested objects
  if (typeof safe === "object" && safe !== null) {
    for (const key in safe) {
      if (key.toLowerCase().includes("token") ||
          key.toLowerCase().includes("key") ||
          key.toLowerCase().includes("password")) {
        safe[key] = "[REDACTED]";
      }
    }
  }

  return safe;
}

Permission Checks

Implement permission checks before executing operations:

async function checkPermission(userId: string, action: string): Promise<void> {
  const permissions = await getPermissions(userId);

  if (!permissions.includes(action)) {
    throw new MCPError(
      `User ${userId} lacks permission: ${action}`,
      "PERMISSION_DENIED"
    );
  }
}

// Use in tool handler
export const handler = async (args: unknown) => {
  await checkPermission(args.userId, "resource:write");
  // Proceed with operation
};

Sensitive Data Handling

Environment variables for secrets:

// NEVER hardcode secrets
const API_KEY = process.env.EXTERNAL_API_KEY;

if (!API_KEY) {
  throw new Error("EXTERNAL_API_KEY environment variable is required");
}

// Use in requests
const response = await fetch(url, {
  headers: {
    Authorization: `Bearer ${API_KEY}`,
  },
});

Audit logging for sensitive operations:

async function auditLog(
  action: string,
  userId: string,
  details: object
): Promise<void> {
  const timestamp = new Date().toISOString();
  console.log(JSON.stringify({ timestamp, action, userId, details }));
}

// Log before sensitive operations
await auditLog("delete_resource", userId, { resourceId });
await deleteResource(resourceId);

8. Configuration Patterns

Plugin Manifest (plugin.json)

{
  "id": "example-plugin",
  "name": "Example Plugin",
  "version": "1.0.0",
  "mcpServers": {
    "example-tools": {
      "command": "node",
      "args": ["${CLAUDE_PLUGIN_ROOT}/mcp-servers/example-tools/index.js"],
      "env": {
        "EXAMPLE_API_TOKEN": "${EXAMPLE_API_TOKEN}",
        "EXAMPLE_BASE_URL": "${EXAMPLE_BASE_URL}"
      }
    }
  }
}

Project Settings (.claude/settings.json)

Override plugin defaults per project:

{
  "enabledPlugins": {
    "example-plugin@marketplace": true
  },
  "mcpServers": {
    "example-tools": {
      "env": {
        "EXAMPLE_BASE_URL": "https://custom-api.example.com"
      }
    }
  }
}

Environment Variables

Project .env file:

# API credentials
EXAMPLE_API_TOKEN=your-token-here
APIDOG_API_TOKEN=your-apidog-token

# Configuration
EXAMPLE_BASE_URL=https://api.example.com
CHROME_EXECUTABLE_PATH=/usr/bin/chromium

Access in MCP server:

const config = {
  apiToken: process.env.EXAMPLE_API_TOKEN,
  baseUrl: process.env.EXAMPLE_BASE_URL || "https://api.example.com",
};

9. Best Practices

DO

  1. Use Standard Naming: Follow mcp__plugin__action_resource pattern
  2. Validate All Inputs: Use Zod or similar schema validation
  3. Handle Errors Gracefully: Return clear error messages
  4. Document Tools: Provide detailed descriptions and examples
  5. Set Appropriate Timeouts: Don't let operations hang indefinitely
  6. Test Thoroughly: Unit tests for all tools
  7. Log Operations: Help with debugging and auditing
  8. Use Environment Variables: Never hardcode secrets
  9. Implement Rate Limiting: Protect external APIs
  10. Version Your Servers: Track breaking changes

DON'T

  1. Don't Return Sensitive Data: Sanitize outputs
  2. Don't Ignore Errors: Handle and report all failures
  3. Don't Use Hardcoded Credentials: Always use environment variables
  4. Don't Skip Validation: Trust no input
  5. Don't Create Side Effects in Read Tools: Keep reads idempotent
  6. Don't Use Blocking Operations: Use async/await properly
  7. Don't Expose Internal Implementation: Abstract away complexity
  8. Don't Forget Error Context: Include relevant details
  9. Don't Mix Concerns: One tool, one purpose
  10. Don't Skip Documentation: Future you will thank you

Testing MCP Servers

Unit test example:

import { describe, it, expect, beforeEach } from "bun:test";
import { fetchTool } from "./fetch-tool.js";

describe("fetchTool", () => {
  it("should fetch resource by ID", async () => {
    const result = await fetchTool.handler({ id: "test-123" });
    expect(result.content[0].text).toContain("test-123");
  });

  it("should throw on invalid ID", async () => {
    await expect(
      fetchTool.handler({ id: "invalid" })
    ).rejects.toThrow("Invalid UUID format");
  });
});

Documentation Requirements

Every MCP server needs:

  1. README.md: Overview, installation, usage
  2. Tool descriptions: In tool definitions
  3. Input schemas: With descriptions for each field
  4. Error documentation: Possible error codes and meanings
  5. Examples: Sample requests and responses

10. Examples

Example 1: Simple Read-Only MCP Server

#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";

const server = new Server(
  { name: "simple-reader", version: "1.0.0" },
  { capabilities: { tools: {} } }
);

// Tool definition
const fetchConfigTool = {
  name: "mcp__simple__fetch_config",
  definition: {
    name: "mcp__simple__fetch_config",
    description: "Fetch configuration by key",
    inputSchema: {
      type: "object",
      properties: {
        key: { type: "string", description: "Configuration key" },
      },
      required: ["key"],
    },
  },
  handler: async (args: unknown) => {
    const { key } = z.object({ key: z.string() }).parse(args);

    const configs = {
      apiUrl: "https://api.example.com",
      timeout: "5000",
      retries: "3",
    };

    const value = configs[key];
    if (!value) {
      throw new Error(`Configuration key not found: ${key}`);
    }

    return {
      content: [
        { type: "text", text: `${key}=${value}` },
      ],
    };
  },
};

server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [fetchConfigTool.definition],
}));

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === fetchConfigTool.name) {
    return fetchConfigTool.handler(request.params.arguments);
  }
  throw new Error(`Unknown tool: ${request.params.name}`);
});

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
}

main().catch(console.error);

Example 2: Full CRUD MCP Server

#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// In-memory store (replace with real database)
const store = new Map<string, any>();

// Create tool
const createTool = {
  name: "mcp__crud__create_item",
  definition: {
    name: "mcp__crud__create_item",
    description: "Create a new item",
    inputSchema: {
      type: "object",
      properties: {
        name: { type: "string" },
        value: { type: "string" },
      },
      required: ["name", "value"],
    },
  },
  handler: async (args: unknown) => {
    const { name, value } = z.object({
      name: z.string(),
      value: z.string(),
    }).parse(args);

    if (store.has(name)) {
      throw new Error(`Item already exists: ${name}`);
    }

    const item = { name, value, createdAt: new Date().toISOString() };
    store.set(name, item);

    return {
      content: [{ type: "text", text: JSON.stringify(item) }],
    };
  },
};

// Read tool
const readTool = {
  name: "mcp__crud__read_item",
  definition: {
    name: "mcp__crud__read_item",
    description: "Read an item by name",
    inputSchema: {
      type: "object",
      properties: {
        name: { type: "string" },
      },
      required: ["name"],
    },
  },
  handler: async (args: unknown) => {
    const { name } = z.object({ name: z.string() }).parse(args);
    const item = store.get(name);

    if (!item) {
      throw new Error(`Item not found: ${name}`);
    }

    return {
      content: [{ type: "text", text: JSON.stringify(item) }],
    };
  },
};

// Update tool
const updateTool = {
  name: "mcp__crud__update_item",
  definition: {
    name: "mcp__crud__update_item",
    description: "Update an existing item",
    inputSchema: {
      type: "object",
      properties: {
        name: { type: "string" },
        value: { type: "string" },
      },
      required: ["name", "value"],
    },
  },
  handler: async (args: unknown) => {
    const { name, value } = z.object({
      name: z.string(),
      value: z.string(),
    }).parse(args);

    const existing = store.get(name);
    if (!existing) {
      throw new Error(`Item not found: ${name}`);
    }

    const updated = {
      ...existing,
      value,
      updatedAt: new Date().toISOString(),
    };
    store.set(name, updated);

    return {
      content: [{ type: "text", text: JSON.stringify(updated) }],
    };
  },
};

// Delete tool
const deleteTool = {
  name: "mcp__crud__delete_item",
  definition: {
    name: "mcp__crud__delete_item",
    description: "Delete an item by name",
    inputSchema: {
      type: "object",
      properties: {
        name: { type: "string" },
      },
      required: ["name"],
    },
  },
  handler: async (args: unknown) => {
    const { name } = z.object({ name: z.string() }).parse(args);

    if (!store.has(name)) {
      throw new Error(`Item not found: ${name}`);
    }

    store.delete(name);

    return {
      content: [{ type: "text", text: `Deleted: ${name}` }],
    };
  },
};

const server = new Server(
  { name: "crud-server", version: "1.0.0" },
  { capabilities: { tools: {} } }
);

server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    createTool.definition,
    readTool.definition,
    updateTool.definition,
    deleteTool.definition,
  ],
}));

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  const tools = { createTool, readTool, updateTool, deleteTool };
  const tool = Object.values(tools).find((t) => t.name === name);

  if (!tool) {
    throw new Error(`Unknown tool: ${name}`);
  }

  return tool.handler(args);
});

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
}

main().catch(console.error);

Example 3: External API Integration MCP Server

#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// Configuration from environment
const API_TOKEN = process.env.EXTERNAL_API_TOKEN;
const BASE_URL = process.env.EXTERNAL_BASE_URL || "https://api.example.com";

if (!API_TOKEN) {
  throw new Error("EXTERNAL_API_TOKEN environment variable is required");
}

// API client
class ExternalAPIClient {
  async fetch(endpoint: string, options = {}) {
    const response = await fetch(`${BASE_URL}${endpoint}`, {
      ...options,
      headers: {
        Authorization: `Bearer ${API_TOKEN}`,
        "Content-Type": "application/json",
        ...options.headers,
      },
    });

    if (!response.ok) {
      throw new Error(`API error: ${response.status} ${response.statusText}`);
    }

    return response.json();
  }
}

const client = new ExternalAPIClient();

// Tool: Fetch user
const fetchUserTool = {
  name: "mcp__external__fetch_user",
  definition: {
    name: "mcp__external__fetch_user",
    description: "Fetch user data from external API",
    inputSchema: {
      type: "object",
      properties: {
        userId: { type: "string", description: "User ID" },
      },
      required: ["userId"],
    },
  },
  handler: async (args: unknown) => {
    const { userId } = z.object({ userId: z.string() }).parse(args);

    try {
      const user = await client.fetch(`/users/${userId}`);
      return {
        content: [{ type: "text", text: JSON.stringify(user, null, 2) }],
      };
    } catch (error) {
      throw new Error(`Failed to fetch user: ${error.message}`);
    }
  },
};

const server = new Server(
  { name: "external-api-server", version: "1.0.0" },
  { capabilities: { tools: {} } }
);

server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [fetchUserTool.definition],
}));

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === fetchUserTool.name) {
    return fetchUserTool.handler(request.params.arguments);
  }
  throw new Error(`Unknown tool: ${request.params.name}`);
});

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
}

main().catch(console.error);

Summary

This skill provides comprehensive MCP server standards for Claude Code plugins:

  1. Structure: Consistent directory layout and entry point patterns
  2. Naming: mcp__plugin__action_resource convention
  3. Transports: stdio (local), HTTP (remote), WebSocket (realtime)
  4. Categories: Read, Write, Analysis, Integration tools
  5. Performance: Response time targets, timeouts, rate limiting
  6. Security: Input validation, output sanitization, permission checks
  7. Configuration: plugin.json, settings.json, environment variables
  8. Best Practices: Comprehensive DO/DON'T list
  9. Examples: Three complete, production-ready MCP servers

Follow these standards to create maintainable, secure, and performant MCP servers for your Claude Code plugins.

Weekly Installs
2
Repository
smithery/ai
First Seen
3 days ago
Installed on
codex2