Skip to content
Back to skills

Mcp Tool Design

ASecurity

Best practices for designing MCP tools including naming conventions, input schemas, error responses, pagination, idempotency, and descriptions. Use when the user is designing tools for an MCP server, writing tool schemas, handling tool errors, or needs guidance on tool naming and input validation patterns.

  • 4 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 27, 2026
data-aitypescriptgogit

Works with

  • cursor
  • mcp

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add VersoXBT/claude-initial-setup --skill mcp-tool-design --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Mcp Tool Design?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Mcp Tool Design
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/versoxbt-mcp-tool-design/badge)](https://www.skillsdirectory.com/skills/versoxbt-mcp-tool-design)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: mcp-tool-design
description: >
  Best practices for designing MCP tools including naming conventions, input schemas,
  error responses, pagination, idempotency, and descriptions. Use when the user is
  designing tools for an MCP server, writing tool schemas, handling tool errors,
  or needs guidance on tool naming and input validation patterns.
---

# MCP Tool Design

Best practices for designing well-structured, reliable MCP tools. Covers naming,
schemas, error handling, pagination, idempotency, and effective descriptions.

## When to Use
- User is designing new tools for an MCP server
- User is writing JSON Schema for tool inputs
- User needs pagination or idempotency patterns
- User wants guidance on tool naming or descriptions
- User is handling errors in MCP tool responses

## Core Patterns

### Naming Conventions

Use snake_case, verb-first names. Group related tools with a common prefix.

```typescript
// Good: verb_noun pattern, clear scope
server.tool("search_issues", ...);
server.tool("create_issue", ...);
server.tool("update_issue", ...);
server.tool("list_repositories", ...);
server.tool("get_file_contents", ...);

// Bad: inconsistent, vague, or noun-first
server.tool("issues", ...);          // What action?
server.tool("doSearch", ...);        // camelCase
server.tool("repository-list", ...); // kebab-case, noun-first
```

### Writing Effective Descriptions

Descriptions tell Claude WHEN and WHY to use the tool. Be specific and directive.

```typescript
// Good: explains purpose, trigger conditions, and constraints
server.tool(
  "search_codebase",
  "Search for code patterns across the repository using regex. " +
  "Use this when the user asks to find functions, classes, imports, " +
  "or specific code patterns. Supports file type filtering. " +
  "Returns up to 50 matches per call -- use pagination for more.",
  schema,
  handler
);

// Bad: vague, tells Claude nothing useful
server.tool(
  "search",
  "Searches for things.",
  schema,
  handler
);
```

### Input Schema Design

Use JSON Schema with descriptive property names, descriptions, and constraints.

```typescript
import { z } from "zod";

server.tool(
  "search_issues",
  "Search GitHub issues by query, state, and labels.",
  {
    query: z.string()
      .describe("Search query string. Searches title and body text."),
    state: z.enum(["open", "closed", "all"])
      .default("open")
      .describe("Filter by issue state. Default: open."),
    labels: z.array(z.string())
      .optional()
      .describe("Filter by label names. Returns issues matching ALL labels."),
    limit: z.number()
      .int()
      .min(1)
      .max(100)
      .default(20)
      .describe("Maximum results to return. Range: 1-100. Default: 20."),
    cursor: z.string()
      .optional()
      .describe("Pagination cursor from a previous response.")
  },
  handler
);
```

### Error Response Patterns

Return structured errors with the `isError` flag so Claude can reason and recover.

```typescript
server.tool("get_file", "Read a file by path.", { path: z.string() }, async ({ path }) => {
  // Validate input
  if (path.includes("..")) {
    return {
      isError: true,
      content: [{ type: "text", text: "Path traversal not allowed. Use absolute paths within the project." }]
    };
  }

  try {
    const content = await fs.readFile(path, "utf-8");
    return {
      content: [{ type: "text", text: content }]
    };
  } catch (error) {
    if (error.code === "ENOENT") {
      return {
        isError: true,
        content: [{ type: "text", text: `File not found: ${path}. Check the path and try again.` }]
      };
    }
    return {
      isError: true,
      content: [{ type: "text", text: `Failed to read file: ${error.message}` }]
    };
  }
});
```

### Pagination Pattern

Use cursor-based pagination for tools that return large result sets.

```typescript
server.tool(
  "list_commits",
  "List commits for a repository branch. Returns 20 commits per page with a cursor for pagination.",
  {
    repo: z.string().describe("Repository name in owner/repo format."),
    branch: z.string().default("main").describe("Branch name. Default: main."),
    cursor: z.string().optional().describe("Pagination cursor from previous response.")
  },
  async ({ repo, branch, cursor }) => {
    const page = cursor ? decodeCursor(cursor) : 1;
    const commits = await fetchCommits(repo, branch, page, 20);
    const hasMore = commits.length === 20;

    const result = {
      commits: commits.map(c => ({
        sha: c.sha,
        message: c.message,
        author: c.author,
        date: c.date
      })),
      next_cursor: hasMore ? encodeCursor(page + 1) : null,
      has_more: hasMore
    };

    return {
      content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
    };
  }
);
```

### Idempotency Pattern

Design tools so repeated calls with the same input produce the same result.

```typescript
server.tool(
  "set_config",
  "Set a configuration value. Idempotent -- calling with the same key and value multiple times has no additional effect.",
  {
    key: z.string().describe("Configuration key."),
    value: z.string().describe("Configuration value.")
  },
  async ({ key, value }) => {
    const current = await getConfig(key);
    if (current === value) {
      return {
        content: [{ type: "text", text: `Config "${key}" already set to "${value}". No change needed.` }]
      };
    }

    await setConfig(key, value);
    return {
      content: [{ type: "text", text: `Config "${key}" set to "${value}".` }]
    };
  }
);
```

## Anti-Patterns
- Tool names longer than 64 characters (may be truncated or rejected)
- Missing `required` array in raw JSON Schema (Claude may skip required fields)
- Descriptions that say "This tool does X" instead of "Use this when..."
- Returning HTML or binary data as text content (use appropriate MIME types)
- Tools with more than 10-15 parameters (split into multiple focused tools)
- Not including pagination for tools that can return unbounded results
- Destructive tools without confirmation (delete, drop, reset)
- Using generic error messages like "Something went wrong" without actionable guidance

## Quick Reference

| Aspect | Guideline |
|--------|-----------|
| Name format | `snake_case`, verb_noun, max 64 chars |
| Description | Explain WHEN to use, not just what it does |
| Parameters | Include type, description, defaults, constraints |
| Required fields | Always specify in schema |
| Pagination | Cursor-based, 20-50 items per page |
| Errors | Use `isError: true`, give actionable messages |
| Idempotency | Same input = same result for write operations |
| Tool count | Prefer many focused tools over few large ones |

Description template:
```
"[Action description]. Use when [trigger conditions]. [Constraints/limits]. [Returns description]."
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.