add-mcp-tool

Add a new tool to an existing FastMCP server with guided configuration

9 stars

Best use case

add-mcp-tool is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Add a new tool to an existing FastMCP server with guided configuration

Teams using add-mcp-tool should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/add-mcp-tool/SKILL.md --create-dirs "https://raw.githubusercontent.com/jpoutrin/product-forge/main/plugins/typescript-experts/skills/add-mcp-tool/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/add-mcp-tool/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How add-mcp-tool Compares

Feature / Agentadd-mcp-toolStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Add a new tool to an existing FastMCP server with guided configuration

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Add MCP Tool

Add a new tool to an existing FastMCP server with interactive configuration.

## Usage

```bash
/add-mcp-tool [tool-name]
```

## Arguments

- `[tool-name]`: Optional - Name for the new tool (will prompt if not provided)

## Execution Instructions for Claude Code

When this command is run:

1. **Locate the FastMCP server** in the current project
2. **Gather tool requirements** through interactive questions
3. **Generate tool code** based on configuration
4. **Add tool to server** by modifying existing files or creating new ones

## Interactive Session Flow

### 1. Locate Server

First, find the FastMCP server in the project:

```
Looking for FastMCP server...

Found: src/server.ts

Is this the correct server file? (yes/no):
```

If not found:
```
I couldn't find a FastMCP server. Please provide the path to your server file:
```

### 2. Tool Basics

```
Let's add a new tool to your MCP server.

Tool name (lowercase, use hyphens):
Example: "fetch-data", "create-file", "query-database"

Tool name:
```

```
Tool description (shown to LLM clients):
Example: "Fetches data from the specified API endpoint"

Description:
```

### 3. Parameters

```
Does this tool require parameters?

1. No parameters
2. Simple parameters (strings, numbers, booleans)
3. Complex parameters (objects, arrays, enums)

Select (1-3):
```

If parameters needed:
```
Let's define the parameters one by one.

Parameter 1:
  Name:
  Type (string/number/boolean/array/object):
  Required? (yes/no):
  Description:

Add another parameter? (yes/no):
```

For enum types:
```
Enum values (comma-separated):
Example: "json,xml,csv"

Values:
```

### 4. Return Type

```
What does this tool return?

1. Simple text response
2. Multiple content items
3. Image content
4. Audio content
5. Resource reference
6. Custom structure

Select (1-6):
```

### 5. Tool Features

```
Which features do you need?

[ ] Logging (log.info, log.error, etc.)
[ ] Progress reporting (for long operations)
[ ] Streaming output (for text generation)
[ ] Authorization check (canAccess)
[ ] Error handling with UserError

Select features (comma-separated numbers, or 'none'):
```

### 6. Tool Annotations

```
Optional annotations for LLM clients:

Read-only hint (tool doesn't modify state)? (yes/no) [default: no]:

Open-world hint (tool accesses external systems)? (yes/no) [default: no]:

Streaming hint (tool produces streaming output)? (yes/no) [default: no]:
```

## Generated Code Examples

### Simple Tool (no parameters)

```typescript
server.addTool({
  name: "{{tool-name}}",
  description: "{{description}}",
  execute: async () => {
    return "Result here";
  },
});
```

### Tool with Parameters

```typescript
server.addTool({
  name: "{{tool-name}}",
  description: "{{description}}",
  parameters: z.object({
    {{#each parameters}}
    {{name}}: z.{{type}}(){{#if description}}.describe("{{description}}"){{/if}}{{#unless required}}.optional(){{/unless}},
    {{/each}}
  }),
  execute: async (args) => {
    const { {{parameterNames}} } = args;

    // TODO: Implement tool logic
    return "Processed: " + JSON.stringify(args);
  },
});
```

### Tool with Logging

```typescript
server.addTool({
  name: "{{tool-name}}",
  description: "{{description}}",
  parameters: z.object({
    url: z.string().describe("URL to fetch"),
  }),
  execute: async (args, { log }) => {
    log.info("Starting operation", { url: args.url });

    try {
      // TODO: Implement logic
      const result = await fetchData(args.url);
      log.info("Operation completed successfully");
      return result;
    } catch (error) {
      log.error("Operation failed", { error: String(error) });
      throw error;
    }
  },
});
```

### Tool with Progress Reporting

```typescript
server.addTool({
  name: "{{tool-name}}",
  description: "{{description}}",
  parameters: z.object({
    items: z.array(z.string()).describe("Items to process"),
  }),
  execute: async (args, { reportProgress }) => {
    const total = args.items.length;
    const results: string[] = [];

    for (let i = 0; i < total; i++) {
      await reportProgress({ progress: i, total });

      // TODO: Process each item
      results.push("Processed: " + args.items[i]);
    }

    await reportProgress({ progress: total, total });
    return results.join("\n");
  },
});
```

### Tool with Streaming Output

```typescript
server.addTool({
  name: "{{tool-name}}",
  description: "{{description}}",
  parameters: z.object({
    prompt: z.string().describe("Input prompt"),
  }),
  annotations: {
    streamingHint: true,
  },
  execute: async (args, { streamContent }) => {
    // Stream content progressively
    await streamContent({ type: "text", text: "Processing...\n" });

    // TODO: Generate content
    const chunks = ["First ", "part, ", "second ", "part."];
    for (const chunk of chunks) {
      await streamContent({ type: "text", text: chunk });
      await new Promise(r => setTimeout(r, 100));
    }

    return; // Return undefined when using streaming
  },
});
```

### Tool with Authorization

```typescript
server.addTool({
  name: "{{tool-name}}",
  description: "{{description}}",
  canAccess: (auth) => {
    // Return true if user is authorized
    return auth?.role === "admin";
  },
  execute: async (args, { session }) => {
    // Tool is only executed if canAccess returns true
    return "Welcome, " + session.id;
  },
});
```

### Tool with UserError

```typescript
import { UserError } from "fastmcp";

server.addTool({
  name: "{{tool-name}}",
  description: "{{description}}",
  parameters: z.object({
    id: z.string().describe("Resource ID"),
  }),
  execute: async (args) => {
    // Validate input
    if (!args.id.match(/^[a-z0-9-]+$/)) {
      throw new UserError("Invalid ID format. Use lowercase letters, numbers, and hyphens only.");
    }

    // Check resource exists
    const resource = await findResource(args.id);
    if (!resource) {
      throw new UserError(`Resource not found: ${args.id}`);
    }

    return JSON.stringify(resource);
  },
});
```

### Tool with Image Content

```typescript
import { imageContent } from "fastmcp";

server.addTool({
  name: "{{tool-name}}",
  description: "{{description}}",
  parameters: z.object({
    imageId: z.string().describe("Image identifier"),
  }),
  execute: async (args) => {
    // Return image from URL
    return imageContent({
      url: `https://example.com/images/${args.imageId}.png`,
    });

    // Or from file path:
    // return imageContent({ path: `/path/to/${args.imageId}.png` });

    // Or from buffer:
    // return imageContent({ buffer: imageBuffer });
  },
});
```

### Tool with Multiple Content Types

```typescript
server.addTool({
  name: "{{tool-name}}",
  description: "{{description}}",
  execute: async () => {
    return {
      content: [
        { type: "text", text: "Here's the analysis:" },
        { type: "text", text: "Line 1: Found 5 issues" },
        { type: "text", text: "Line 2: 3 warnings" },
      ],
    };
  },
});
```

## File Placement Options

```
Where should the tool code be added?

1. Inline in server.ts (simple projects)
2. New file in tools/ directory (recommended for organization)
3. Existing tools file (specify which)

Select (1-3):
```

## Implementation Notes

1. **Find existing server**: Use Glob/Grep to locate FastMCP server files
2. **Parse existing structure**: Understand current tool organization
3. **Generate imports**: Add necessary imports (z, UserError, imageContent, etc.)
4. **Insert code**: Add tool at appropriate location
5. **Preserve formatting**: Match existing code style
6. **Summary**: Show the generated tool and file location

Related Skills

agent-tools

9
from jpoutrin/product-forge

Reference for configuring tool permissions when launching Claude Code agents. Use when setting up --allowedTools flags, restricting file access, or configuring agent permissions.

zod

9
from jpoutrin/product-forge

Zod schema validation patterns and type inference. Auto-loads when validating schemas, parsing data, validating forms, checking types at runtime, or using z.object/z.string/z.infer in TypeScript.

typescript-import-style

9
from jpoutrin/product-forge

Merge-friendly import formatting (one-per-line, alphabetical). Auto-loads when writing TypeScript/JavaScript imports to minimize merge conflicts in parallel development. Enforces consistent grouping and sorting.

setup-mcp-auth

9
from jpoutrin/product-forge

Configure authentication for an existing FastMCP server

fastmcp

9
from jpoutrin/product-forge

FastMCP TypeScript framework patterns for MCP servers. Auto-loads when building MCP servers, creating tools/resources/prompts, implementing authentication, configuring transports, or working with FastMCP in TypeScript.

add-mcp-resource

9
from jpoutrin/product-forge

Add a new resource or resource template to an existing FastMCP server

plan-with-team

9
from jpoutrin/product-forge

Validate plan file ownership

privacy-compliance

9
from jpoutrin/product-forge

GDPR, CCPA, and privacy compliance guidance for data protection. Use when handling personal data, implementing consent management, or ensuring regulatory compliance across jurisdictions.

oauth

9
from jpoutrin/product-forge

OAuth 2.0 and OpenID Connect implementation patterns. Use when implementing authentication, authorization flows, or integrating with OAuth providers like Google, GitHub, or custom identity providers.

mcp-security

9
from jpoutrin/product-forge

Use when securing MCP servers, preventing prompt injection, implementing authorization, validating user input, or building secure multi-agent pipelines. Provides 5-layer defense architecture patterns.

rag-cag-security

9
from jpoutrin/product-forge

Security patterns for RAG and CAG systems with multi-tenant isolation. Use when building retrieval-augmented or cache-augmented generation systems that require tenant isolation, access control, and secure data handling.

chunking-strategies

9
from jpoutrin/product-forge

Document chunking strategies for RAG systems. Use when implementing document processing pipelines to determine optimal chunking approaches based on document type and retrieval requirements.