# ExMCP User Guide

A comprehensive guide to using ExMCP, the Elixir implementation of the Model Context Protocol.

## Table of Contents

1. [Introduction](#introduction)
2. [Installation](#installation)
3. [Quick Start](#quick-start)
4. [Core Concepts](#core-concepts)
5. [Building MCP Servers](#building-mcp-servers) (DSL)
   - [Tools, Resources, Prompts](#implementing-tools)
   - [Content Helpers](#content-helpers)
   - [Low-Level Handler API](#low-level-handler-api)
6. [Using the Native Service Dispatcher](#using-the-native-service-dispatcher)
7. [Building MCP Clients](#building-mcp-clients)
   - [Batch Requests](#batch-requests)
   - [Bi-directional Communication](#bi-directional-communication)
   - [Human-in-the-Loop Approval](#human-in-the-loop-approval)
8. [Transport Layers](#transport-layers)
9. [Advanced Features](#advanced-features)
10. [Best Practices](#best-practices)
11. [Troubleshooting](#troubleshooting)

## Related Documentation

- **[Configuration Guide](../CONFIGURATION.md)** - Complete configuration reference
- **[Transport Guide](../TRANSPORT_GUIDE.md)** - Detailed transport selection and optimization
- **[Security Guide](../SECURITY.md)** - Security best practices and authentication
- **[Development Guide](../DEVELOPMENT.md)** - Setup, testing, and contributing
- **[Phoenix Integration Guide](PHOENIX_GUIDE.md)** - Phoenix/Plug integration details

## Introduction

ExMCP is a complete Elixir implementation of the Model Context Protocol (MCP), enabling AI models to securely interact with local and remote resources through a standardized protocol. It supports all major MCP features including tools, resources, prompts, sampling, and the latest roots and subscription capabilities.

### Key Features

- **Full Protocol Support**: Implements MCP specification version 2025-03-26
- **Multiple Transports**: stdio, Streamable HTTP (with Server-Sent Events)
- **Both Client and Server**: Build MCP servers or connect to existing ones
- **OTP Integration**: Built on Elixir's OTP principles for reliability
- **Type Safety**: Comprehensive type specifications throughout
- **Extensible**: Easy to add custom tools, resources, and prompts

## Installation

Add `ex_mcp` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:ex_mcp, "~> 1.0.0-rc.0"}
  ]
end
```

Then run:

```bash
mix deps.get
```

## Quick Start

### Creating a Simple MCP Server

```elixir
defmodule MyServer do
  use ExMCP.Server.Handler
  use ExMCP.Server.DSL, name: "my-server", version: "1.0.0"

  tool "hello", "Says hello to someone" do
    param :name, :string, required: true, description: "Name to greet"

    run fn %{name: name}, state ->
      {:ok, "Hello, #{name}!", state}
    end
  end
end

# Start the server
{:ok, server} = MyServer.start_link(transport: :stdio)
```

### Creating a Simple MCP Client

```elixir
# Connect to a server
{:ok, client} = ExMCP.Client.start_link(
  transport: :stdio,
  command: ["node", "some-mcp-server.js"]
)

# List available tools
{:ok, tools} = ExMCP.Client.list_tools(client)

# Call a tool
{:ok, result} = ExMCP.Client.call_tool(client, "hello", %{name: "World"})
```

## Core Concepts

### The Model Context Protocol

MCP enables AI models to interact with external systems through:

- **Tools**: Functions that can be called with parameters
- **Resources**: Data sources that can be read
- **Prompts**: Templates for generating model interactions
- **Sampling**: Direct LLM integration for response generation
- **Roots**: URI-based boundaries for organizing resources
- **Subscriptions**: Monitor resources for changes

### Client-Server Architecture

MCP follows a client-server model:

- **Clients** (typically AI assistants) connect to servers to access functionality
- **Servers** expose tools, resources, and prompts to clients
- Communication happens over various transport layers

### Request-Response Pattern

All MCP interactions follow JSON-RPC 2.0:

```json
// Request
{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {"name": "hello", "arguments": {"name": "World"}},
  "id": 1
}

// Response
{
  "jsonrpc": "2.0",
  "result": {"content": [{"type": "text", "text": "Hello, World!"}]},
  "id": 1
}
```

## Building MCP Servers

ExMCP provides a declarative DSL for building servers. Combine `use ExMCP.Server.Handler` with `use ExMCP.Server.DSL` to declare capabilities next to their handlers.

### Implementing Tools

Tools allow clients to execute functions. Declare them with `tool` and a colocated `run` handler:

```elixir
defmodule FileSearchServer do
  use ExMCP.Server.Handler
  use ExMCP.Server.DSL

  tool "file_search", "Search for files by pattern" do
    param :pattern, :string, required: true, description: "Glob pattern"
    param :path, :string, default: ".", description: "Search path"
    annotations readOnlyHint: true, destructiveHint: false

    run fn %{pattern: pattern, path: path}, state ->
      files = Path.wildcard(Path.join(path, pattern))
      {:ok, "Found #{length(files)} files:\n#{Enum.join(files, "\n")}", state}
    end
  end
end
```

### Implementing Resources

Resources provide data that clients can read. Declare with `resource` and a colocated `read` handler:

```elixir
defmodule ConfigServer do
  use ExMCP.Server.Handler
  use ExMCP.Server.DSL

  resource "file:///config.json", "Application configuration" do
    title "Configuration"
    mime_type "application/json"

    read fn _params, state ->
      {:ok, %{text: Jason.encode!(Jason.decode!(File.read!("config.json")))}, state}
    end
  end

  resource "file:///status", "Current server status" do
    title "Server Status"
    mime_type "text/plain"

    read fn _params, state ->
      {:ok, "Server is running", state}
    end
  end
end
```

### Implementing Prompts

Prompts are templates for model interactions. Declare with `prompt` and a colocated `render` handler:

```elixir
defmodule CodeReviewServer do
  use ExMCP.Server.Handler
  use ExMCP.Server.DSL

  prompt "code_review", "Review code for best practices" do
    title "Code Review"
    arg :language, required: true, description: "Programming language"
    arg :focus, description: "Area to focus on"

    render fn %{language: lang} = args, state ->
      focus = Map.get(args, :focus, "general best practices")

      {:ok,
       %{
         messages: [
           %{role: "system", content: %{type: "text", text: "You are an expert #{lang} code reviewer focusing on #{focus}."}},
           %{role: "user", content: %{type: "text", text: "Please review the following #{lang} code:"}}
         ]
       }, state}
    end
  end
end
```

### Combining Tools, Resources, and Prompts

A single server can declare any combination:

```elixir
defmodule MyFullServer do
  use ExMCP.Server.Handler
  use ExMCP.Server.DSL

  @impl true
  def init(_args), do: {:ok, %{request_count: 0}}

  tool "greet", "Greets a person" do
    param :name, :string, required: true

    run fn %{name: name}, state ->
      new_state = %{state | request_count: state.request_count + 1}
      {:ok, "Hello, #{name}!", new_state}
    end
  end

  resource "app://stats", "Request statistics" do
    title "Server Stats"
    mime_type "application/json"

    read fn _params, state ->
      {:ok, %{text: Jason.encode!(%{requests: state.request_count})}, state}
    end
  end

  prompt "summarize", "Summarize text content" do
    title "Summarize"
    arg :style, description: "Summary style"

    render fn args, state ->
      style = Map.get(args, :style, "brief")
      {:ok, "Provide a #{style} summary of the content the user shares.", state}
    end
  end
end

# Start with any transport
{:ok, _} = MyFullServer.start_link(transport: :stdio)
```

### Content Responses

Tool handlers can return strings, `%{text: text}`, or full MCP result maps.
Resource handlers can return strings, `%{text: text}`, `%{blob: blob}`, or full
resource content maps. Prompt handlers can return strings, message lists, or
full `%{messages: messages}` maps.

### Resource Subscriptions

Allow clients to monitor resource changes:

```elixir
@impl true
def handle_resource_subscribe(uri, state) do
  subscriptions = MapSet.put(state.subscriptions, uri)
  {:ok, %{state | subscriptions: subscriptions}}
end

# When a resource changes, notify subscribers
ExMCP.Server.notify_resource_update(server, "file:///config.json")
```

### Progress Notifications

For long-running operations:

```elixir
@impl true
def handle_call_tool("long_task", %{"_progressToken" => token}, state) do
  Task.start(fn ->
    for i <- 1..100 do
      ExMCP.Server.notify_progress(self(), token, i, 100)
      Process.sleep(100)
    end
  end)

  {:ok, %{content: [%{type: "text", text: "Task started"}]}, state}
end
```

### Server-to-Client Requests

Servers can make requests back to clients:

```elixir
# Ping the client
{:ok, _} = MyServer.ping(server)

# Request client's roots
{:ok, roots} = MyServer.list_roots(server)

# Ask client to sample an LLM
{:ok, response} = MyServer.create_message(server, %{
  "messages" => [
    %{"role" => "user", "content" => "What is the weather?"}
  ],
  "modelPreferences" => %{
    "hints" => ["gpt-4", "claude-3"],
    "temperature" => 0.7
  }
})
```

### Low-Level Handler API

For cases where you need full control without the DSL, implement the `ExMCP.Server.Handler` behaviour directly. This gives you manual control over capability negotiation and tool/resource listing:

```elixir
defmodule MyLowLevelServer do
  use ExMCP.Server.Handler

  @impl true
  def init(_args), do: {:ok, %{}}

  @impl true
  def handle_initialize(_params, state) do
    {:ok, %{
      name: "my-server",
      version: "1.0.0",
      capabilities: %{tools: %{}}
    }, state}
  end

  @impl true
  def handle_list_tools(_cursor, state) do
    tools = [
      %{
        name: "echo",
        description: "Echoes input back",
        input_schema: %{
          type: "object",
          properties: %{
            message: %{type: "string"}
          },
          required: ["message"]
        }
      }
    ]
    {:ok, tools, nil, state}
  end

  @impl true
  def handle_call_tool("echo", %{"message" => msg}, state) do
    {:ok, [%{type: "text", text: msg}], state}
  end
end

{:ok, _} =
  ExMCP.Server.HandlerServer.start_link(
    handler: MyLowLevelServer,
    transport: :test
  )
```

> The DSL approach (`use ExMCP.Server.Handler` plus `use ExMCP.Server.DSL`) is recommended for most use cases. Use hand-written handler callbacks when you need dynamic capability negotiation or non-standard request handling.

## Using the Native Service Dispatcher

The Native Service Dispatcher provides ultra-fast communication for trusted Elixir services within the same cluster using the `ExMCP.Service` macro.

### Service Implementation

Services are created using the `ExMCP.Service` macro:

```elixir
defmodule MyToolService do
  use ExMCP.Service, name: :my_tools

  @impl true
  def init(_args) do
    {:ok, %{cache: %{}, stats: %{calls: 0}}}
  end

  @impl true
  def handle_mcp_request("list_tools", _params, state) do
    tools = [
      %{
        "name" => "calculator",
        "description" => "Perform mathematical calculations",
        "inputSchema" => %{
          "type" => "object",
          "properties" => %{
            "expression" => %{"type" => "string", "description" => "Math expression"}
          },
          "required" => ["expression"]
        }
      }
    ]
    {:ok, %{"tools" => tools}, state}
  end

  @impl true  
  def handle_mcp_request("tools/call", %{"name" => "calculator", "arguments" => args}, state) do
    expression = args["expression"]
    
    try do
      # Safe expression evaluation (implement your own)
      result = safe_eval(expression)
      content = [%{"type" => "text", "text" => "Result: #{result}"}]
      
      new_state = update_in(state.stats.calls, &(&1 + 1))
      {:ok, %{"content" => content}, new_state}
    rescue
      error ->
        {:error, %{"code" => -32603, "message" => "Calculation error: #{inspect(error)}"}, state}
    end
  end

  def handle_mcp_request(method, _params, state) do
    {:error, %{"code" => -32601, "message" => "Method not found: #{method}"}, state}
  end

  # Optional: Handle notifications (fire-and-forget)
  @impl true
  def handle_mcp_notification("clear_cache", _params, state) do
    {:noreply, %{state | cache: %{}}}
  end

  # Optional: Custom lifecycle management
  @impl true
  def terminate(_reason, _state) do
    # Cleanup resources
    :ok
  end
end
```

### Service Registration and Discovery

Services automatically register when started and can be discovered across the cluster:

```elixir
# Start your service (automatically registers with ExMCP.Native)
{:ok, pid} = MyToolService.start_link()

# Check if service is available
ExMCP.Native.service_available?(:my_tools)
#=> true

# List all services across the cluster
services = ExMCP.Native.list_services()
#=> [
#=>   {:my_tools, #PID<0.123.0>, %{registered_at: ~U[2024-01-01 10:00:00Z]}},
#=>   {:data_processor, #PID<0.124.0>, %{registered_at: ~U[2024-01-01 10:01:00Z]}}
#=> ]
```

### Direct Service Communication

Call services directly without MCP client overhead:

```elixir
# Simple call
{:ok, tools} = ExMCP.Native.call(:my_tools, "list_tools", %{})

# Call with metadata and progress tracking
{:ok, result} = ExMCP.Native.call(
  :my_tools,
  "tools/call",
  %{"name" => "calculator", "arguments" => %{"expression" => "2 + 2"}},
  timeout: 10_000,
  meta: %{"user_id" => "user123", "trace_id" => "abc"},
  progress_token: "calc-001"
)

# Fire-and-forget notifications
:ok = ExMCP.Native.notify(:my_tools, "clear_cache", %{})
```

### Cross-Node Communication

Services work seamlessly across BEAM cluster nodes:

```elixir
# Connect nodes (if not already connected)
Node.connect(:"worker@cluster.local")

# Call service on specific node
{:ok, result} = ExMCP.Native.call(
  {:data_processor, :"worker@cluster.local"},
  "tools/call",
  %{"name" => "process_dataset", "arguments" => %{"dataset_id" => "abc123"}}
)

# The pluggable registry discovers services across nodes (requires Horde adapter)
available? = ExMCP.Native.service_available?(:data_processor)
#=> true (even if on remote node)
```

### Resilience Patterns

Add optional resilience for unreliable services:

```elixir
# Retry with exponential backoff
{:ok, result} = ExMCP.Resilience.call_with_retry(
  :flaky_service,
  "process_data",
  %{"input" => "data"},
  max_attempts: 3,
  backoff: :exponential,
  initial_delay: 100
)

# Fallback for unavailable services
result = ExMCP.Resilience.call_with_fallback(
  :primary_service,
  "get_data",
  %{},
  fallback: fn -> 
    {:ok, %{"data" => "cached_value", "source" => "cache"}} 
  end
)

# Circuit breaker for failing services
{:ok, result} = ExMCP.Resilience.call_with_circuit_breaker(
  :unstable_service,
  "risky_operation",
  %{},
  failure_threshold: 5,
  timeout: 60_000
)
```

### Performance Optimization

BEAM-local services are optimized for performance:

```elixir
# Measure performance
:timer.tc(fn -> 
  ExMCP.Native.call(:my_service, "fast_operation", %{}) 
end)
#=> {15, {:ok, result}}  # ~15 microseconds!

# Batch operations for efficiency
results = Enum.map(1..1000, fn i ->
  ExMCP.Native.call(:calculator, "tools/call", %{
    "name" => "add",
    "arguments" => %{"a" => i, "b" => i * 2}
  })
end)
# Completes in milliseconds due to zero serialization overhead
```

### Advanced Service Patterns

#### Resource-like Services

Services can expose resource-like interfaces:

```elixir
defmodule DatabaseService do
  use ExMCP.Service, name: :database

  @impl true
  def handle_mcp_request("list_resources", _params, state) do
    resources = [
      %{
        "uri" => "db://users/table",
        "name" => "Users Table",
        "description" => "User data",
        "mimeType" => "application/json"
      }
    ]
    {:ok, %{"resources" => resources}, state}
  end

  @impl true
  def handle_mcp_request("resources/read", %{"uri" => "db://users/table"}, state) do
    users = Database.all_users()
    content = %{
      "uri" => "db://users/table",
      "mimeType" => "application/json",
      "text" => Jason.encode!(users)
    }
    {:ok, %{"contents" => [content]}, state}
  end
end
```

#### Event-Driven Services

Services can publish and subscribe to events:

```elixir
defmodule EventService do
  use ExMCP.Service, name: :events

  @impl true
  def handle_mcp_request("resources/subscribe", %{"uri" => uri}, state) do
    # Track subscription
    subscriptions = MapSet.put(state.subscriptions, uri)
    {:ok, %{}, %{state | subscriptions: subscriptions}}
  end

  # Publish events to subscribers
  def publish_event(uri, event_data) do
    ExMCP.Native.notify(:events, "resource_updated", %{
      "uri" => uri,
      "data" => event_data
    })
  end
end
```

## Building MCP Clients

### Connecting to Servers

```elixir
# Connect to a stdio server
{:ok, client} = ExMCP.Client.start_link(
  transport: :stdio,
  command: ["python", "mcp_server.py"],
  args: ["--config", "prod.json"]
)

# Connect to a Streamable HTTP server
{:ok, client} = ExMCP.Client.start_link(
  transport: :http,
  url: "http://localhost:8080",
  endpoint: "/mcp/v1"  # Optional: specify custom endpoint (defaults to "/mcp/v1")
)
```

### Using Tools

```elixir
# List available tools
{:ok, tools} = ExMCP.Client.list_tools(client)

Enum.each(tools, fn tool ->
  IO.puts("Tool: #{tool.name} - #{tool.description}")
end)

# Call a tool
{:ok, result} = ExMCP.Client.call_tool(
  client,
  "search",
  %{query: "elixir metaprogramming"},
  # Optional progress callback
  _progress_token = "search-123"
)

# Handle the result
Enum.each(result, fn
  %{type: "text", text: text} ->
    IO.puts(text)
  %{type: "image", data: data, mimeType: mime} ->
    # Handle image content
end)
```

### Working with Resources

```elixir
# List resources
{:ok, resources} = ExMCP.Client.list_resources(client)

# Read a resource
{:ok, content} = ExMCP.Client.read_resource(client, "file:///data.json")

case content do
  %{text: text} -> 
    IO.puts("Text content: #{text}")
  %{blob: blob} ->
    IO.puts("Binary content: #{byte_size(blob)} bytes")
end
```

### Using Prompts

```elixir
# List prompts
{:ok, prompts} = ExMCP.Client.list_prompts(client)

# Get a prompt
{:ok, messages} = ExMCP.Client.get_prompt(
  client,
  "analyze_code",
  %{language: "elixir", complexity: "high"}
)

# Use with your LLM
response = MyLLM.chat(messages)
```

### Working with Roots

```elixir
# List available roots
{:ok, roots} = ExMCP.Client.list_roots(client)

Enum.each(roots, fn root ->
  IO.puts("Root: #{root.name} at #{root.uri}")
end)
```

### Resource Subscriptions

> **Note:** The MCP specification only defines `resources/subscribe`. The `unsubscribe_resource` function is an ExMCP extension.

```elixir
# Subscribe to resource changes (MCP standard)
{:ok, _} = ExMCP.Client.subscribe_resource(client, "file:///config.json")

# Handle notifications in your client process
def handle_info({:mcp_notification, "resource/updated", %{"uri" => uri}}, state) do
  IO.puts("Resource updated: #{uri}")
  # Re-read the resource
  {:ok, content} = ExMCP.Client.read_resource(state.client, uri)
  # Process updated content...
  {:noreply, state}
end

# Unsubscribe when done (ExMCP extension - not in MCP spec)
{:ok, _} = ExMCP.Client.unsubscribe_resource(client, "file:///config.json")
```

### Using Sampling

```elixir
# For servers that support LLM sampling
{:ok, response} = ExMCP.Client.create_message(
  client,
  %{
    messages: [
      %{role: "user", content: %{type: "text", text: "Explain quantum computing"}}
    ],
    max_tokens: 500,
    temperature: 0.7
  }
)

IO.puts("Assistant: #{response.content.text}")
```

### Batch Requests

Send multiple requests in a single call for better performance:

```elixir
# Define multiple requests
requests = [
  {:list_tools, []},
  {:list_resources, []},
  {:read_resource, ["file:///config.json"]},
  {:call_tool, ["get_status", %{}]}
]

# Send as batch
{:ok, [tools, resources, config, status]} = ExMCP.Client.batch_request(client, requests)

# Process results
IO.puts("Found #{length(tools)} tools")
IO.puts("Found #{length(resources)} resources")
```

### Bi-directional Communication

Enable servers to make requests back to clients:

```elixir
defmodule MyClientHandler do
  @behaviour ExMCP.Client.Handler
  
  @impl true
  def init(args) do
    {:ok, %{model: args[:model] || "gpt-4", roots: args[:roots]}}
  end
  
  @impl true
  def handle_ping(state) do
    {:ok, %{}, state}
  end
  
  @impl true
  def handle_list_roots(state) do
    {:ok, state.roots, state}
  end
  
  @impl true
  def handle_create_message(params, state) do
    # Server is asking client to sample an LLM
    messages = params["messages"]
    
    # Call your LLM integration
    response = MyLLM.chat(messages, model: state.model)
    
    result = %{
      "role" => "assistant",
      "content" => %{
        "type" => "text",
        "text" => response.content
      },
      "model" => state.model
    }
    
    {:ok, result, state}
  end
end

# Start client with handler
{:ok, client} = ExMCP.Client.start_link(
  transport: :stdio,
  command: ["mcp-server"],
  handler: MyClientHandler,
  handler_state: %{
    model: "claude-3",
    roots: [%{uri: "file:///home", name: "Home"}]
  }
)
```

### Human-in-the-Loop Approval

Implement approval flows for sensitive operations:

```elixir
# Option 1: Use the built-in console approval handler
{:ok, client} = ExMCP.Client.start_link(
  transport: :stdio,
  command: ["mcp-server"],
  handler: {ExMCP.Client.DefaultHandler, [
    approval_handler: ExMCP.Approval.Console,
    roots: [%{uri: "file:///data", name: "Data"}]
  ]}
)

# Option 2: Implement a custom approval handler
defmodule MyApprovalHandler do
  @behaviour ExMCP.Approval
  
  @impl true
  def request_approval(type, data, opts) do
    case type do
      :sampling ->
        # Show approval UI for LLM sampling
        if show_sampling_dialog(data) == :approved do
          {:approved, data}
        else
          {:denied, "User cancelled"}
        end
        
      :response ->
        # Review LLM response before sending
        reviewed_response = show_response_review(data)
        if reviewed_response do
          {:modified, reviewed_response}
        else
          {:denied, "Response rejected"}
        end
        
      :tool_call ->
        # Approve dangerous tool calls
        tool_name = data["name"]
        if dangerous_tool?(tool_name) do
          case show_tool_approval(tool_name, data["arguments"]) do
            :approve -> {:approved, data}
            :deny -> {:denied, "Tool call blocked"}
            {:modify, new_args} -> {:modified, %{data | "arguments" => new_args}}
          end
        else
          {:approved, data}
        end
    end
  end
  
  defp dangerous_tool?(name) do
    name in ["delete_file", "execute_command", "send_email"]
  end
end

# Use custom approval handler with default client handler
{:ok, client} = ExMCP.Client.start_link(
  transport: :stdio,
  command: ["mcp-server"],
  handler: {ExMCP.Client.DefaultHandler, [
    approval_handler: MyApprovalHandler
  ]}
)
```

## Transport Layers

ExMCP supports multiple transport layers. For detailed configuration and optimization information, see the **[Transport Guide](../TRANSPORT_GUIDE.md)**.

### stdio Transport

Best for local process communication:

```elixir
# Server
{:ok, server} = MyServer.start_link(transport: :stdio)

# Client
{:ok, client} = ExMCP.Client.start_link(
  transport: :stdio,
  command: ["./my_mcp_server"]
)
```

### Streamable HTTP Transport

For HTTP-based communication with optional Server-Sent Events (SSE) streaming.

> **Note:** Use `transport: :http` for the HTTP transport with Server-Sent Events support. The `:sse` transport name has been removed.

```elixir
# Server
{:ok, server} = MyServer.start_link(
  transport: :http,
  port: 8080,
  path: "/mcp"
)

# Client
{:ok, client} = ExMCP.Client.start_link(
  transport: :http,
  url: "http://localhost:8080",
  endpoint: "/mcp/v1",  # Optional: defaults to "/mcp/v1"
  headers: [{"Authorization", "Bearer token"}]
)

# Client with custom endpoint
{:ok, client} = ExMCP.Client.start_link(
  transport: :http,
  url: "https://api.example.com",
  endpoint: "/ai/mcp",  # Custom endpoint path
  headers: [{"Authorization", "Bearer token"}]
)
```

### Custom Transports

You can implement custom transports by implementing the `ExMCP.Transport` behaviour. See the API Reference for details.

## Advanced Features

### Auto-Reconnection

ExMCP clients automatically handle reconnection on failure:

```elixir
# Client automatically reconnects on failure
{:ok, client} = ExMCP.Client.start_link(
  transport: :stdio,
  command: ["flaky-server"],
  # Reconnection options
  reconnect: true,
  reconnect_interval: 1000,
  max_reconnect_attempts: 10
)

# Handle errors gracefully
case ExMCP.Client.call_tool(client, "risky_tool", %{}) do
  {:ok, result} ->
    process_result(result)
  {:error, %{"code" => -32603, "message" => msg}} ->
    Logger.error("Internal error: #{msg}")
  {:error, reason} ->
    Logger.error("Tool failed: #{inspect(reason)}")
end
```

### Custom Transports

Implement the `ExMCP.Transport` behaviour:

```elixir
defmodule MyTransport do
  @behaviour ExMCP.Transport

  @impl true
  def connect(opts) do
    # Establish connection
    {:ok, state}
  end

  @impl true
  def send_message(message, state) do
    # Send message
    {:ok, new_state}
  end

  @impl true
  def receive_message(state) do
    # Receive message (blocking)
    {:ok, message, new_state}
  end

  @impl true
  def close(state) do
    # Clean up
    :ok
  end
end

# Use custom transport
{:ok, client} = ExMCP.Client.start_link(
  transport: MyTransport,
  custom_option: "value"
)
```

## Best Practices

### 1. State Management

Keep server state immutable and use proper OTP patterns:

```elixir
defmodule MyServer do
  use ExMCP.Server.Handler

  defstruct [:db_conn, :cache, subscriptions: MapSet.new()]

  @impl true
  def init(args) do
    {:ok, db_conn} = Database.connect(args[:db_url])
    {:ok, %__MODULE__{db_conn: db_conn, cache: %{}}}
  end
end
```

### 2. Error Handling

Always return proper error tuples:

```elixir
@impl true
def handle_call_tool("database_query", %{"sql" => sql}, state) do
  case Database.query(state.db_conn, sql) do
    {:ok, results} ->
      {:ok, %{structuredContent: %{results: results}, content: []}, state}

    {:error, :invalid_sql} ->
      {:error, "Invalid SQL syntax", state}

    {:error, reason} ->
      {:error, "Database error: #{inspect(reason)}", state}
  end
end
```

### 3. Tool Design

Make tools focused and composable:

```elixir
# Good: Focused tools
tool "list_files", "List files in directory" do
  param :path, :string
  run fn %{path: path}, state -> {:ok, list_files(path), state} end
end

tool "read_file", "Read file contents" do
  param :path, :string, required: true
  run fn %{path: path}, state -> {:ok, File.read!(path), state} end
end

# Bad: One monolithic tool that does everything
```

### 4. Resource URIs

Use consistent URI schemes:

```elixir
# Good: Clear URI structure
resources = [
  %{uri: "file:///data/users.json"},
  %{uri: "db://postgres/users/table"},
  %{uri: "api://v1/users"}
]

# Bad: Inconsistent URIs
resources = [
  %{uri: "/data/users.json"},
  %{uri: "postgres:users"},
  %{uri: "users-api"}
]
```

### 5. Capability Declaration

With the DSL, capabilities are auto-detected from your declarations. If you define `tool` blocks, the server automatically advertises tool support. If you define `resource` blocks, resource support is advertised. No manual capability wiring needed.

```elixir
defmodule MyServer do
  use ExMCP.Server.Handler
  use ExMCP.Server.DSL

  # Capabilities auto-detected: tools + resources
  tool "search", "Search for items" do
    param :query, :string
    run fn %{query: query}, state -> {:ok, search(query), state} end
  end

  resource "app://data", "Application data" do
    title "Data"
    mime_type "application/json"
    read fn _params, state -> {:ok, load_data(), state} end
  end
end
```

## Troubleshooting

### Common Issues

**Connection Failures**

```elixir
# Check server is running
{:error, :enoent} = ExMCP.Client.start_link(
  transport: :stdio,
  command: ["non-existent-server"]
)

# Solution: Verify command path
{:ok, client} = ExMCP.Client.start_link(
  transport: :stdio,
  command: ["/usr/local/bin/mcp-server"]
)
```

**Protocol Errors**

```elixir
# Wrong protocol version
{:error, %{"code" => -32600}} = ExMCP.Client.list_tools(client)

# Solution: ExMCP uses protocol version 2025-03-26
# Ensure your server supports this version
```

**Transport Issues**

```elixir
# stdio: Process dies immediately
# Check server stderr
{:ok, client} = ExMCP.Client.start_link(
  transport: :stdio,
  command: ["server", "--verbose"],
  env: [{"DEBUG", "true"}]
)

# HTTP streaming: Connection refused
# Ensure server is listening
{:ok, client} = ExMCP.Client.start_link(
  transport: :http,
  url: "http://localhost:8080",
  endpoint: "/mcp/v1"  # Ensure endpoint matches server configuration
)

# Native Service Dispatcher: Service not found
# Check if service is registered
ExMCP.Native.service_available?(:my_service)

# List all services
ExMCP.Native.list_services()

# Cross-node communication
# Ensure nodes are connected
Node.connect(:"worker@cluster.local")

# If using Horde adapter, check cluster members
# Horde.Cluster.members(ExMCP.ServiceRegistry.Horde.Registry)
```

### Debugging

Enable debug logging:

```elixir
# In config/config.exs
config :logger, level: :debug

# Or at runtime
Logger.configure(level: :debug)
```

Trace protocol messages:

```elixir
# Start a debugging proxy
defmodule DebugTransport do
  def send_message(message, state) do
    IO.puts(">>> #{message}")
    ActualTransport.send_message(message, state)
  end
  
  def receive_message(state) do
    case ActualTransport.receive_message(state) do
      {:ok, message, new_state} ->
        IO.puts("<<< #{message}")
        {:ok, message, new_state}
      other ->
        other
    end
  end
end
```

### Performance Tuning

```elixir
# Increase timeout for slow operations
{:ok, result} = ExMCP.Client.call_tool(
  client,
  "expensive_operation",
  %{size: "large"},
  60_000  # 60 second timeout
)

# Use connection pooling for multiple clients
defmodule MCPPool do
  use GenServer
  
  def start_link(server_config, pool_size: size) do
    # Create pool of clients
  end
  
  def checkout(pool) do
    # Get available client
  end
end
```

## Next Steps

- Explore the [examples](https://github.com/azmaveth/ex_mcp/tree/master/examples) directory for complete working examples
- Read the [API documentation](https://hexdocs.pm/ex_mcp)
- Check out the [MCP specification](https://modelcontextprotocol.io)
- Join the community and contribute on [GitHub](https://github.com/azmaveth/ex_mcp)

## Support

For questions and issues:

- GitHub Issues: Report bugs and feature requests
- Discussions: Ask questions and share ideas
- Stack Overflow: Tag questions with `ex-mcp` and `elixir`

Remember to include:
- ExMCP version
- Elixir/OTP versions
- Transport being used
- Minimal reproduction code
- Error messages and stack traces
