magi-tool 0.0.6

provide tools for Magi AI agents
Documentation
# PegBoard Implementation - Key Changes

## Latest Update: Added `select_tools` Method

**New method for batch tool retrieval**

Added `select_tools(&[&str]) -> Option<Vec<Tool>>` method to efficiently retrieve multiple tools in one call.

### What's New:
- **All-or-nothing semantics**: Returns `Some(Vec<Tool>)` only if ALL requested tools exist
- **Returns `None` if ANY tool is missing** - makes it easy to check for required tool sets
- **Use case**: When you need a specific subset of tools for a specialized task
- **Example**: Select only the tools needed for a particular operation instead of getting all tools

```rust
// Get specific tools for a task
let tools = pegboard.select_tools(&["web-search", "fs-read", "db-write"]);
if let Some(tools) = tools {
    // All 3 tools found, can use them safely
    send_to_llm(&tools).await?;
} else {
    // One or more tools missing
    println!("Missing required tools");
}
```

### API Addition:
- Location: `magi-tool/src/pegboard.rs:274`
- Documentation: `magi-tool/docs/PEGBOARD_DESIGN.md` (Tool Retrieval section)

---

## Previous Update: Automatic Tool Discovery

✅ **`list_tools()` is now fully implemented!**

The PegBoard now automatically calls `list_tools()` on each MCP service during registration to discover all available tools. This is a required method in the rmcp protocol.

### What Changed:
- Removed TODO comment - `list_tools()` is always available in rmcp
- Automatically calls `service.list_tools(None)` to get all tools (no pagination)
- Converts rmcp tool format to PegBoard's Tool format
- Tools are discovered and registered in one seamless operation

## Summary

Updated PegBoard implementation to match the actual use case: **automatic tool name prefixing to avoid conflicts when sending to LLM**.

## The Problem You Described

- Multiple MCP servers may have tools with the same name (e.g., `search()`)
- When sending tools to LLM, names must be unique
- Need to route LLM's tool calls back to the correct MCP service

## The Solution

### Automatic Name Prefixing

When you register an MCP service with a namespace (e.g., "web_search"), the PegBoard:

1. **Prefixes all tool names**: `search``web_search-search`
2. **Modifies the Tool struct**: Changes `tool.name` to the prefixed version
3. **Maintains routing info**: Stores mapping of `"web_search-search"``(service_index, "search")`
4. **Returns prefixed tools to LLM**: When you call `get_all_tools()`, tools have unique prefixed names

### Example Flow

```rust
// Register service "web_search" that has tool "search"
pegboard.add_service("web_search".to_string(), service).await?;

// Get tools for LLM - name is already "web_search-search"
let tools = pegboard.get_all_tools();
// tools[0].name == "web_search-search" ✅

// LLM calls "web_search-search"
let (service_idx, original_name) = pegboard.get_tool_route("web_search-search").unwrap();
// service_idx = 0, original_name = "search"

// Call the service with original name
services[service_idx].call_tool("search", params).await?;
```

## Key Changes from Initial Implementation

### Before (Wrong)
- Used `namespace::tool_name` as internal key only
- Tool's `name` field was unchanged
- Would have sent duplicate names to LLM ❌

### After (Correct)
- Uses `namespace-tool_name` format (with dash)
- Tool's `name` field is **modified** to use prefixed name
- LLM receives unique tool names ✅
- Routing maintains original name for service calls ✅

## API Changes

### Main Methods

1. **`add_service(namespace, service)`** - Automatic discovery + prefixing
   ```rust
   // Service has "search" → becomes "web-search"
   pegboard.add_service("web".to_string(), service).await?;
   ```

2. **`get_tool(prefixed_name)`** - Get by prefixed name
   ```rust
   let tool = pegboard.get_tool("web-search").unwrap();
   assert_eq!(tool.name, "web-search"); // Name is prefixed!
   ```

3. **`get_tool_route(prefixed_name)`** - NEW! Critical for routing
   ```rust
   let (service_idx, original_name) = pegboard.get_tool_route("web-search").unwrap();
   // Returns: (0, "search")
   ```

4. **`get_all_tools()`** - Tools ready for LLM
   ```rust
   let tools = pegboard.get_all_tools();
   // All tools have unique prefixed names
   ```

## Data Structures

### PegBoard

```rust
pub struct PegBoard {
    // Tools with prefixed names (e.g., "web-search")
    tools: DashMap<String, Tool>,

    // Services with namespaces
    services: Vec<(String, RunningService<...>)>,

    // Routing: "web-search" → (service_idx, "search")
    tool_routing: DashMap<String, ToolRoute>,

    // Namespace → prefixed tool names
    namespace_tools: DashMap<String, Vec<String>>,
}
```

### ToolRoute (NEW)

```rust
struct ToolRoute {
    service_index: usize,      // Which service
    original_name: String,      // Original tool name
}
```

## Complete Workflow

```
1. Register Service
      Service has: ["search", "fetch"]
      PegBoard stores: ["web-search", "web-fetch"]
   2. Get Tools for LLM
      tools = pegboard.get_all_tools()
      tools[0].name == "web-search" (prefixed!)
   3. Send to LLM
      LLM sees unique names
   4. LLM Calls "web-search"
      (service_idx, original_name) = pegboard.get_tool_route("web-search")
      Returns: (0, "search")
   5. Execute
      services[0].call_tool("search", params)
```

## Why `DashMap<String, Tool>` is Better

✅ **Direct O(1) lookup** by prefixed name (what LLM uses)
✅ **Single source of truth** - tools already have prefixed names
✅ **No synchronization issues** - one data structure
✅ **Thread-safe** - DashMap handles concurrency
✅ **Easy to add/remove** - no reindexing needed

❌ Vec approach would require:
- Maintaining Vec and DashMap in sync
- Extra indirection (name → index → tool)
- Complexity when removing tools

## Testing

All 12 tests pass:
- Name prefixing logic
- Tool registration with namespaces
- Routing information retrieval
- Namespace operations
- Error handling

Run: `cargo test -p magi-tool`

## Documentation

See `PEGBOARD_DESIGN.md` for:
- Complete API reference
- Usage examples
- Architecture decisions
- Future enhancements