# MCP Server Tool Design and Naming Best Practices: A Complete Guide

> Master MCP server tool design with snake_case names, Pydantic validation, and dual JSON/Markdown responses. Follow best practices for efficient development at anthropics/skills.

- Repository: [Anthropic/skills](https://github.com/anthropics/skills)
- Tags: best-practices
- Published: 2026-02-16

---

**Use snake_case tool names prefixed with the service name, implement strict Pydantic or Zod validation, and provide dual JSON/Markdown response formats with proper pagination metadata.**

The **Anthropic Skills** repository provides authoritative guidelines for building Model Context Protocol (MCP) servers that are discoverable, composable, and safe for AI agents. Following the conventions established in [`skills/mcp-builder/reference/mcp_best_practices.md`](https://github.com/anthropics/skills/blob/main/skills/mcp-builder/reference/mcp_best_practices.md) ensures your tools integrate seamlessly with Claude and other MCP clients.

## Server Naming Conventions

Consistent server naming prevents collisions and clarifies the service domain.

### Python Servers

Use the pattern `{service}_mcp` (lowercase with underscores) as defined in [`skills/mcp-builder/reference/python_mcp_server.md`](https://github.com/anthropics/skills/blob/main/skills/mcp-builder/reference/python_mcp_server.md).

- **Examples**: `slack_mcp`, `github_mcp`, `jira_mcp`

```python
from mcp.server.fastmcp import FastMCP

# Correct naming pattern

mcp = FastMCP("slack_mcp")

```

### Node.js/TypeScript Servers

Use the pattern `{service}-mcp-server` (lowercase with hyphens) as specified in [`skills/mcp-builder/reference/node_mcp_server.md`](https://github.com/anthropics/skills/blob/main/skills/mcp-builder/reference/node_mcp_server.md).

- **Examples**: `slack-mcp-server`, `github-mcp-server`

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

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

```

## Tool Naming Best Practices

Clear tool names reduce ambiguity for AI agents deciding which function to invoke.

### Use Snake Case with Service Prefixes

All tool names must use **snake_case** (`search_users`, `create_project`) and include the service prefix to avoid namespace collisions.

- **Correct**: `slack_send_message`, `github_create_issue`
- **Incorrect**: `send_message`, `create_issue`

### Start with Action Verbs

Begin tool names with standard action verbs that indicate the operation type:

- **get** / **list** – Retrieve single or multiple items
- **create** – Instantiate new resources
- **update** – Modify existing resources
- **delete** – Remove resources

## Tool Design Fundamentals

Robust tool design requires strict validation, comprehensive metadata, and safety annotations.

### Strict Input Validation

Use **Pydantic** (Python) or **Zod** (TypeScript) with `.strict()` configuration to prevent extra fields and validate types automatically.

```python
from pydantic import BaseModel, Field, ConfigDict

class SearchInput(BaseModel):
    model_config = ConfigDict(str_strip_whitespace=True, extra="forbid")
    
    query: str = Field(..., min_length=2, max_length=200)
    limit: int = Field(default=20, ge=1, le=100)

```

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

const IssueSearchSchema = z.object({
  query: z.string().min(2).max(200),
  limit: z.number().int().min(1).max(100).default(20),
  offset: z.number().int().min(0).default(0),
  response_format: z.enum(["markdown", "json"]).default("markdown"),
}).strict();

```

### Tool Annotations

Provide **annotations** in [`skills/mcp-builder/reference/mcp_best_practices.md`](https://github.com/anthropics/skills/blob/main/skills/mcp-builder/reference/mcp_best_practices.md) to guide agent behavior:

- **readOnlyHint**: `true` if the tool only reads data
- **destructiveHint**: `true` if the tool deletes or irreversibly modifies data
- **idempotentHint**: `true` if repeated calls with the same inputs produce the same results
- **openWorldHint**: `true` if the tool interacts with external systems beyond the local environment

```python
@mcp.tool(
    name="slack_search_messages",
    annotations={
        "readOnlyHint": True,
        "destructiveHint": False,
        "idempotentHint": True,
        "openWorldHint": True,
    },
)
async def slack_search_messages(params: SearchInput) -> str:
    """Search Slack messages matching query. Returns JSON or Markdown."""
    ...

```

## Response Handling and Pagination

Dual-format responses and consistent pagination metadata improve usability across different client types.

### Dual Response Formats

Support both **JSON** (machine-readable) and **Markdown** (human-readable) outputs. Default to Markdown for general agent usage unless the client specifically requests JSON.

```python
if params.response_format == SearchInput.ResponseFormat.JSON:
    return json.dumps({
        "total": total,
        "count": len(messages),
        "offset": params.offset,
        "messages": messages,
        "has_more": total > (params.offset + len(messages)),
    }, indent=2)

# Markdown formatting for human readability

lines = [f"# Search Results", ""]

for msg in messages:
    lines.append(f"**{msg['user']}**: {msg['text']}")
return "\n".join(lines)

```

### Pagination Metadata

For list operations, return standard pagination fields as defined in [`skills/mcp-builder/reference/mcp_best_practices.md`](https://github.com/anthropics/skills/blob/main/skills/mcp-builder/reference/mcp_best_practices.md):

- **has_more**: Boolean indicating additional results available
- **next_offset**: Integer for the next page starting position
- **total_count**: Total number of matching items
- **Default limit**: 20-50 items per request

## Transport and Error Handling

Proper transport selection and sanitized error messages ensure reliable operation.

### Transport Selection

Choose transport based on deployment context:

- **stdio**: Use for local CLI tools and single-user environments
- **streamable HTTP**: Use for remote services requiring multiple concurrent clients
- **Avoid SSE**: Server-Sent Events transport is deprecated in favor of streamable HTTP

```typescript
// stdio transport for local usage
const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
await server.connect(new StdioServerTransport());

```

### Error Handling Best Practices

Map API errors to user-friendly messages without exposing internal stack traces or sensitive tokens:

```typescript
function handleApiError(error: unknown): string {
  if (error instanceof AxiosError) {
    if (error.response) {
      switch (error.response.status) {
        case 404: return "Error: Resource not found.";
        case 403: return "Error: Permission denied.";
        case 429: return "Error: Rate limit exceeded.";
        default: return `Error: API request failed with status ${error.response.status}`;
      }
    }
  }
  return `Error: ${error instanceof Error ? error.message : String(error)}`;
}

```

## Code Quality and Testing

Maintainable MCP servers require modular architecture and comprehensive validation.

### Code Reusability

Extract common functionality into shared modules:

- **API clients**: Reusable HTTP clients with authentication
- **Pagination helpers**: Standardized offset/limit handling
- **Formatters**: Shared JSON and Markdown rendering utilities
- **Error handlers**: Centralized exception mapping

### Testing Requirements

As specified in [`skills/mcp-builder/reference/mcp_best_practices.md`](https://github.com/anthropics/skills/blob/main/skills/mcp-builder/reference/mcp_best_practices.md), validate your server with:

- **Functional tests**: Verify individual tool logic
- **Integration tests**: Test against live or mocked APIs
- **Security tests**: Ensure no token leakage in errors
- **Build verification**: Confirm `npm run build` (Node) or `python -m <module>` (Python) executes without errors

## Summary

- **Name servers** using `{service}_mcp` (Python) or `{service}-mcp-server` (Node) patterns
- **Name tools** with snake_case, service prefixes, and action verbs (get, list, create, update, delete)
- **Validate inputs** strictly using Pydantic (Python) or Zod (Node) with `.strict()` configuration
- **Annotate tools** with `readOnlyHint`, `destructiveHint`, `idempotentHint`, and `openWorldHint`
- **Support dual formats** (JSON and Markdown) with standard pagination metadata (`has_more`, `next_offset`, `total_count`)
- **Choose transport** based on context: stdio for local tools, streamable HTTP for remote services
- **Handle errors** gracefully without exposing stack traces or sensitive credentials

## Frequently Asked Questions

### What is the correct naming convention for MCP server packages?

Python servers should use the pattern `{service}_mcp` with lowercase and underscores (e.g., `slack_mcp`, `github_mcp`), while Node.js/TypeScript servers should use `{service}-mcp-server` with lowercase and hyphens (e.g., `slack-mcp-server`). These patterns are defined in [`skills/mcp-builder/reference/mcp_best_practices.md`](https://github.com/anthropics/skills/blob/main/skills/mcp-builder/reference/mcp_best_practices.md) and prevent naming collisions across the MCP ecosystem.

### How should I handle input validation in MCP tools?

Use strict schema validation with **Pydantic** for Python or **Zod** for TypeScript, ensuring you configure `.strict()` or `extra="forbid"` to reject unexpected fields. Define clear constraints using `Field()` or `.min()`/`.max()` validators, and include descriptive metadata so the LLM understands parameter purposes. This approach is documented in [`skills/mcp-builder/reference/python_mcp_server.md`](https://github.com/anthropics/skills/blob/main/skills/mcp-builder/reference/python_mcp_server.md) and [`skills/mcp-builder/reference/node_mcp_server.md`](https://github.com/anthropics/skills/blob/main/skills/mcp-builder/reference/node_mcp_server.md).

### What transport protocol should I choose for my MCP server?

Select **stdio** transport for local CLI tools and single-user environments where the server runs as a subprocess, or **streamable HTTP** for remote services that must support multiple concurrent clients over the network. Avoid using Server-Sent Events (SSE) as it is deprecated in favor of streamable HTTP. The transport selection guidance appears in [`skills/mcp-builder/reference/mcp_best_practices.md`](https://github.com/anthropics/skills/blob/main/skills/mcp-builder/reference/mcp_best_practices.md).

### Why should MCP tools support both JSON and Markdown response formats?

Supporting dual formats accommodates different consumption patterns: **JSON** provides structured data for programmatic processing and agent tool chaining, while **Markdown** offers human-readable output for direct user display. Default to Markdown unless the client explicitly requests JSON via a parameter. This pattern is implemented in the reference examples within [`skills/mcp-builder/reference/python_mcp_server.md`](https://github.com/anthropics/skills/blob/main/skills/mcp-builder/reference/python_mcp_server.md) and ensures maximum compatibility across diverse MCP clients.