# anyclaw-sdk-tool — Tool SDK

SDK for building MCP tool extensions. Provides the `Tool` trait for tool logic and `ToolServer` that wraps rmcp's `ServerHandler` to serve tools over MCP stdio.

## Files

| File | Purpose |
|------|---------|
| `trait_def.rs` | `Tool` trait — implement this to build a tool |
| `server.rs` | `ToolServer` — MCP server wrapping rmcp `ServerHandler`, dispatches to `Tool` implementations |
| `error.rs` | `ToolSdkError` enum (thiserror) |
| `lib.rs` | Re-exports `Tool`, `ToolServer`, `ToolSdkError`, `ToolContext` |

## Key Types

```rust
pub trait Tool: Send + Sync + 'static {
    fn name(&self) -> &str;
    fn description(&self) -> &str;
    fn input_schema(&self) -> serde_json::Value;
    async fn on_initialize(&self, context: ToolContext) -> Result<(), ToolSdkError> { Ok(()) }
    async fn execute(&self, input: Value) -> Result<Value, ToolSdkError>;
}

/// Context passed to tools during MCP initialization.
pub struct ToolContext {
    pub options: HashMap<String, String>,  // merged config options + installation env vars
}

pub struct ToolServer {
    tools: HashMap<String, Box<dyn Tool>>,
    server_info: ServerInfo,
}
```

## How to Implement

1. Create a struct implementing `Tool`
2. Return tool metadata: `name()`, `description()`, `input_schema()` (JSON Schema object)
3. Optionally implement `on_initialize()` to receive config options and service-injected env vars (e.g., `MEDIA_ENDPOINT`). Use `OnceLock` or `RwLock` for interior mutability since `&self` is shared.
4. Implement `execute()` — receives input as `Value`, returns output as `Value`
5. Register tools with `ToolServer::new(vec![Box::new(MyTool)])` 
6. Serve with `tool_server.serve_stdio().await`

**Example:**
```rust
struct MyTool;

impl Tool for MyTool {
    fn name(&self) -> &str { "my-tool" }
    fn description(&self) -> &str { "Does something useful" }
    fn input_schema(&self) -> Value { json!({"type": "object"}) }
    async fn execute(&self, input: Value) -> Result<Value, ToolSdkError> {
        Ok(json!({"result": "done"}))
    }
}

#[tokio::main]
async fn main() {
    ToolServer::new(vec![Box::new(MyTool)]).serve_stdio().await.unwrap();
}
```

## ToolServer Internals

- Implements rmcp `ServerHandler` — handles `initialize`, `list_tools` and `call_tool` MCP methods
- `initialize()` extracts `ToolContext` from MCP `experimental` capabilities (`anyclaw_tool_context` key) and calls `on_initialize()` on each registered tool
- `build_tool_list()` converts `Tool` metadata to rmcp `RmcpTool` structs
- `dispatch_tool()` routes by name, converts `Tool::execute()` results to `CallToolResult`
- Error from `execute()` returns `CallToolResult::error` (not MCP protocol error) — tool failures are content, not protocol failures

## Anti-Patterns (this crate)

- **Don't depend on internal crates** — this is external-facing SDK
- **Don't handle MCP framing manually** — `ToolServer` + rmcp handle all protocol details
- **Don't return protocol errors for tool failures** — use `ToolSdkError::ExecutionFailed`, which becomes `CallToolResult::error` (content-level error, not MCP error)
- **Don't read `std::env::var` for config** — use `on_initialize(context)` to receive options and service-injected env vars through the MCP handshake
