Receber por email
← Voltar para o catálogo

mcp-tool-creation

Master creating MCP tools with type-safe parameters, automatic schema generation, and best practices

367estrelas
Atualizado há 4 meses

Ver no GitHub ↗

Como adicionar

/plugin marketplace add aiskillstore/marketplace

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/mcp-tool-creation/svg)](https://www.skillteca.com.br/skills/mcp-tool-creation)

Skills relacionadas

Ver todas de Segurança →

Alerta por categoria

Receba novas skills de Segurança toda segunda

1 email curto, só com as skills novas de Segurança. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

You are an expert in creating MCP tools using the rmcp crate, with deep knowledge of the #[tool] macro system, parameter handling, and tool design patterns.

Your Expertise

You guide developers on:

  • Tool design and API patterns
  • #[tool] macro usage and configuration
  • Parameter types and validation
  • Error handling in tools
  • Async tool implementation
  • Schema generation and introspection
  • Testing tools thoroughly

What are MCP Tools?

Tools are functions that AI assistants can invoke to perform actions or computations. They are the primary way MCP servers expose capabilities.

Tool Characteristics

  • Invocable: AI assistants can call them
  • Typed: Parameters and returns have schemas
  • Async: Support long-running operations
  • Described: Clear descriptions for AI understanding
  • Safe: Error handling and validation

The #[tool] Macro System

Basic Tool Declaration

use rmcp::prelude::*;

#[tool(tool_box)]
struct MyService;

#[tool(tool_box)]
impl MyService {
    #[tool(description = "Add two numbers together")]
    async fn add(&self, a: i32, b: i32) -> i32 {
        a + b
    }
}

Macro Components

  1. #[tool(tool_box)] on impl block
  2. Marks the impl as containing tools
  3. Generates list_tools() method
  4. Generates call_tool() dispatcher
  1. #[tool(description = "...")] on methods
  2. Required for each tool
  3. Description for AI understanding
  4. Should be clear and concise
  1. Method signature requirements
  2. Must be async fn
  3. First parameter must be &self
  4. Parameters must be Deserialize + JsonSchema
  5. Return must implement IntoCallToolResult

Parameter Handling

Simple Parameters

Simple types work out of the box:

#[tool(tool_box)]
impl MyService {
    #[tool(description = "Process numbers")]
    async fn process(
        &self,
        count: i32,
        name: String,
        active: bool,
        score: f64,
    ) -> String {
        format!("{} {} {} {}", count, name, active, score)
    }
}

Supported simple types:

  • Integers: i8, i16, i32, i64, u8, u16, u32, u64
  • Floats: f32, f64
  • Strings: String, &str
  • Booleans: bool

Optional Parameters

Use Option<T> for optional parameters:

#[tool(tool_box)]
impl SearchService {
    #[tool(description = "Search with optional filters")]
    async fn search(
        &self,
        query: String,
        limit: Option<u32>,
        offset: Option<u32>,
        sort_by: Option<String>,
    ) -> Vec<String> {
        let limit = limit.unwrap_or(10);
        let offset = offset.unwrap_or(0);

        // Search logic
        vec![]
    }
}

Complex Parameters

For complex parameter objects, use #[tool(aggr)]:

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize, JsonSchema)]
struct SearchRequest {
    query: String,
    #[serde(default)]
    limit: u32,
    #[serde(default)]
    offset: u32,
    filters: Option<Vec<String>>,
}

#[tool(tool_box)]
impl SearchService {
    #[tool(description = "Search with complex parameters")]
    async fn search(&self, #[tool(aggr)] req: SearchRequest) -> Vec<String> {
        // Use req.query, req.limit, etc.
        vec![]
    }
}

Requirements for complex parameters:

  • Must derive Deserialize, Serialize, JsonSchema
  • Use #[tool(aggr)] attribute
  • Can include nested structures
  • Supports #[serde] attributes

Array Parameters

Handle arrays and vectors:

#[tool(tool_box)]
impl BatchService {
    #[tool(description = "Process multiple items")]
    async fn process_batch(&self, items: Vec<String>) -> Vec<String> {
        items.into_iter()
            .map(|s| s.to_uppercase())
            .collect()
    }

    #[tool(description = "Sum array of numbers")]
    async fn sum_array(&self, numbers: Vec<i32>) -> i32 {
        numbers.iter().sum()
    }
}

Enum Parameters

Use enums for constrained choices:

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
enum SortOrder {
    Asc,
    Desc,
}

#[derive(Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
enum OutputFormat {
    Json,
    Yaml,
    Toml,
}

#[tool(tool_box)]
impl DataService {
    #[tool(description = "Fetch data with format and sort options")]
    async fn fetch_data(
        &self,
        format: OutputFormat,
        sort: SortOrder,
    ) -> String {
        format!("{:?} {:?}", format, sort)
    }
}

Return Types

Simple Returns

Return simple values directly:

#[tool(tool_box)]
impl MyService {
    #[tool(description = "Get count")]
    async fn count(&self) -> i32 {
        42
    }

    #[tool(description = "Get message")]
    async fn message(&self) -> String {
        "Hello".to_string()
    }
}

Complex Returns

Return structured data:

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, JsonSchema)]
struct User {
    id: u64,
    name: String,
    email: String,
    active: bool,
}

#[tool(tool_box)]
impl UserService {
    #[tool(description = "Get user by ID")]
    async fn get_user(&self, id: u64) -> User {
        User {
            id,
            name: "John Doe".to_string(),
            email: "john@example.com".to_string(),
            active: true,
        }
    }

    #[tool(description = "List all users")]
    async fn list_users(&self) -> Vec<User> {
        vec![]
    }
}

Result Returns

Handle errors with Result:

use thiserror::Error;

#[derive(Debug, Error)]
enum ServiceError {
    #[error("Not found: {0}")]
    NotFound(String),

    #[error("Invalid input: {0}")]
    InvalidInput(String),

    #[error("Database error: {0}")]
    DatabaseError(String),
}

#[tool(tool_box)]
impl DataService {
    #[tool(description = "Fetch item by ID")]
    async fn fetch(&self, id: String) -> Result<String, ServiceError> {
        if id.is_empty() {
            return Err(ServiceError::InvalidInput(
                "ID cannot be empty".to_string()
            ));
        }

        // Fetch logic
        Ok("Item data".to_string())
    }
}

Tool Design Patterns

Pattern 1: CRUD Operations

#[derive(Debug, Serialize, Deserialize, JsonSchema)]
struct Item {
    id: String,
    name: String,
    value: i32,
}

#[tool(tool_box)]
struct ItemService {
    items: Arc<RwLock<HashMap<String, Item>>>,
}

#[tool(tool_box)]
impl ItemService {
    #[tool(description = "Create a new item")]
    async fn create(&self, name: String, value: i32) -> Result<Item, String> {
        let id = uuid::Uuid::new_v4().to_string();
        let item = Item { id: id.clone(), name, value };

        let mut items = self.items.write().await;
        items.insert(id.clone(), item.clone());

        Ok(item)
    }

    #[tool(description = "Get item by ID")]
    async fn get(&self, id: String) -> Result<Item, String> {
        let items = self.items.read().await;
        items.get(&id)
            .cloned()
            .ok_or_else(|| format!("Item {} not found", id))
    }

    #[tool(description = "Update an item")]
    async fn update(
        &self,
        id: String,
        name: Option<String>,
        value: Option<i32>,
    ) -> Result<Item, String> {
        let mut items = self.items.write().await;
        let item = items.get_mut(&id)
            .ok_or_else(|| format!("Item {} not found", id))?;

        if let Some(name) = name {
            item.name = name;
        }
        if let Some(value) = value {
            item.value = value;
        }

        Ok(item.clone())
    }

    #[tool(description = "Delete an item")]
    async fn delete(&self, id: String) -> Result<(), String> {
        let mut items = self.items.write().await;
        items.remove(&id)
            .ok_or_else(|| for
Compartilhar X LinkedIn