# Migration Guide: Version 0.3.0

Version 0.3.0 introduces significant improvements to the observability system and streamlines the transport layer with a unified HTTP transport implementation.

## What's Changed

## Observability System Refactoring

The observability system has been completely refactored to use a **metadata-driven architecture** with consistent event naming and status tags.

### Breaking Changes

#### 1. Unified Event Names with Status Tags (MAJOR CHANGE)

All events now use consistent base names with a `status` tag instead of separate event names for success/failure.

**Before (v0.2.x):**
```php
// Different event names for success and failure
$handler->record_event('mcp.request.success', ['method' => 'tools/call']);
$handler->record_event('mcp.request.error', ['method' => 'tools/call']);

$handler->record_event('mcp.tool.execution_success', ['tool_name' => 'my-tool']);
$handler->record_event('mcp.tool.execution_failed', ['tool_name' => 'my-tool']);

$handler->record_event('mcp.component.registered', ['component_type' => 'tool']);
$handler->record_event('mcp.component.registration_failed', ['component_type' => 'tool']);
```

**After (v0.3.0):**
```php
// Single event name with status tag
$handler->record_event('mcp.request', ['status' => 'success', 'method' => 'tools/call']);
$handler->record_event('mcp.request', ['status' => 'error', 'method' => 'tools/call']);

// Tool events are consolidated into request events with metadata
$handler->record_event('mcp.request', [
    'status' => 'success',
    'method' => 'tools/call',
    'component_type' => 'tool',
    'tool_name' => 'my-tool',
    'ability_name' => 'my_ability'
]);

$handler->record_event('mcp.component.registration', ['status' => 'success', 'component_type' => 'tool']);
$handler->record_event('mcp.component.registration', ['status' => 'failed', 'component_type' => 'tool']);
```

**Benefits:**
- Easier filtering: Query all requests with one event name, filter by status
- Better grouping: Calculate success rates easily
- Consistent API: Same pattern everywhere
- Richer context: Tool/prompt/resource metadata automatically included

**Migration for Monitoring Systems:**

If you're using external monitoring systems that query event names:

```sql
-- Before: Query success events
SELECT * FROM events WHERE event_name = 'mcp.request.success'

-- After: Query with status filter
SELECT * FROM events WHERE event_name = 'mcp.request' AND tags->>'status' = 'success'
```

#### 2. Metadata-Driven Observability

Observability events are now recorded centrally at the transport layer (RequestRouter) instead of in individual handlers. Handlers attach `_metadata` to responses, which flows up to the transport layer.

**Impact:** If you've created custom MCP handlers (Tools, Prompts, Resources), **no migration needed** - the system is backward compatible. However, if you were manually calling `observability_handler->record_event()` in custom code, update to return `_metadata` instead.

**Before (v0.2.x):**
```php
class CustomToolsHandler {
    public function call_tool(array $params): array {
        // Manual observability call
        $this->observability_handler->record_event(
            'mcp.tool.execution_success',
            ['tool_name' => $params['name']]
        );
        
        return ['result' => 'success'];
    }
}
```

**After (v0.3.0):**
```php
class CustomToolsHandler {
    public function call_tool(array $params): array {
        // Return metadata instead
        return [
            'result' => 'success',
            '_metadata' => [
                'component_type' => 'tool',
                'tool_name' => $params['name'],
            ]
        ];
    }
}
```

The RequestRouter automatically:
- Extracts `_metadata` from responses
- Merges with request context (method, transport, server_id)
- Records event with duration timing
- Strips `_metadata` before returning to client

#### 3. Removed Helper Method

**Removed:** `McpObservabilityHelperTrait::record_error_event()`

This helper method appended `_failed` suffix to event names, which conflicts with the new status tag pattern.

**Before (v0.2.x):**
```php
$this->record_error_event('mcp.tool.execution', $exception, ['tool_name' => 'my-tool']);
// Created event: mcp.tool.execution_failed
```

**After (v0.3.0):**
```php
// Use standard record_event with status and error categorization
$this->record_event('mcp.request', [
    'status' => 'error',
    'tool_name' => 'my-tool',
    'error_type' => get_class($exception),
    'error_category' => self::categorize_error($exception),
]);
```

**Note:** `categorize_error()` method is still available in the helper trait.

#### 4. Enhanced Event Tags

All events now include richer context automatically:

**Request Events (`mcp.request`):**
- `status`: `success` | `error`
- `method`: MCP method name
- `transport`: Transport type
- `server_id`: Server ID
- `component_type`: Tool/resource/prompt (when applicable)
- `tool_name`, `ability_name`, `prompt_name`, `resource_uri`: Component details
- `failure_reason`: Specific failure reason (not_found, permission_denied, execution_failed, etc.)
- `error_code`, `error_type`, `error_category`: Error details

**Component Registration Events (`mcp.component.registration`):**
- `status`: `success` | `failed`
- `component_type`: Tool/resource/prompt type
- `component_name`: Component name
- `server_id`: Server ID
- `error_type`: Exception type (for failures)

**Session and Request Tracking:**
- `request_id`: JSON-RPC request ID for request correlation
- `session_id`: MCP session ID (null for non-session transports like CLI)
- `new_session_id`: Newly created session ID (only on initialize)
- `params`: Sanitized request parameters (tool names, URIs, argument counts)

**Permission Error Details:**

When WordPress abilities return `WP_Error` from `has_permission()`, the specific error message and code are now automatically extracted and used:

```php
// Example: Ability returns WP_Error with validation details
$wp_error = new WP_Error(
    'ability_invalid_input',
    'Ability "wpcom-mcp/user-notifications" has invalid input. Reason: input[action] is not one of list, get_settings, get_devices, and test_delivery.'
);

// Old behavior: 
//   - Error message: Generic "Access denied for tool: X"
//   - failure_reason: Always "permission_denied"

// New behavior:
//   - Error message: Full WP_Error message with details
//   - failure_reason: WP_Error code ("ability_invalid_input")
```

**Benefits:**
- More specific failure reasons in logs (e.g., `ability_invalid_input` vs generic `permission_denied`)
- Easier to track and alert on specific permission failure types
- Error messages include full validation context from abilities
- Can monitor specific error patterns (rate limits, quota exceeded, etc.)

#### 5. Instance-Based Handlers (No Longer Static)

Observability handlers now use instance methods instead of static methods, matching the error handler pattern.

**Before (v0.2.x):**
```php
// Handlers were passed as class names
$adapter->create_server(
    'my-server',
    'mcp/v1',
    '/mcp',
    'My Server',
    'Description',
    '1.0.0',
    [ HttpTransport::class ],
    ErrorLogMcpErrorHandler::class,
    NullMcpObservabilityHandler::class  // Class name
);

// Static method calls
MyObservabilityHandler::record_event('event.name', ['tag' => 'value']);
MyObservabilityHandler::record_timing('metric.name', 123.45, ['tag' => 'value']);
```

**After (v0.3.0):**
```php
// Handlers are still passed as class names to create_server
// (the server instantiates them internally)
$adapter->create_server(
    'my-server',
    'mcp/v1',
    '/mcp',
    'My Server',
    'Description',
    '1.0.0',
    [ HttpTransport::class ],
    ErrorLogMcpErrorHandler::class,
    NullMcpObservabilityHandler::class  // Still class name, instantiated by server
);

// Instance method calls (when implementing custom handlers)
class MyObservabilityHandler implements McpObservabilityHandlerInterface {
    public function record_event(string $event, array $tags = [], ?float $duration_ms = null): void {
        // Implementation
    }
}
```

#### 2. Unified Event/Timing Interface

The `record_timing()` method has been removed. Use `record_event()` with the optional `$duration_ms` parameter instead.

**Before (v0.2.x):**
```php
// Separate methods for events and timing
$handler::record_event('mcp.request.success', ['method' => 'tools/call']);
$handler::record_timing('mcp.request.duration', 45.23, ['method' => 'tools/call']);

// This created 2 separate log entries
```

**After (v0.3.0):**
```php
// Unified method with optional duration parameter
$handler->record_event('mcp.request.success', ['method' => 'tools/call'], 45.23);

// This creates 1 log entry with timing included
// Output: [MCP Observability] EVENT mcp.request.success 45.23ms [method=tools/call,...]
```

#### 3. Removed Events

The following events have been removed to reduce log volume:

- `mcp.request.count` - No longer emitted (redundant with success/error events)

**Before:** Each request generated 3-4 log entries:
- `mcp.request.count`
- `mcp.request.success` OR `mcp.request.error`
- `mcp.request.duration`

**After:** Each request generates 1 log entry:
- `mcp.request.success` (with duration) OR `mcp.request.error` (with duration)

### Migration Steps for Custom Handlers

If you have custom observability handlers, update them:

**Step 1: Convert from static to instance methods**

```php
// Before
class MyHandler implements McpObservabilityHandlerInterface {
    public static function record_event(string $event, array $tags = []): void {
        // ...
    }
    
    public static function record_timing(string $metric, float $duration_ms, array $tags = []): void {
        // ...
    }
}

// After
class MyHandler implements McpObservabilityHandlerInterface {
    public function record_event(string $event, array $tags = [], ?float $duration_ms = null): void {
        // Handle both events and timing in one method
        // If $duration_ms is not null, include it in your tracking
    }
}
```

**Step 2: Remove `record_timing()` method**

The `record_timing()` method no longer exists in the interface. Consolidate all tracking into `record_event()`.

**Step 3: Update Helper Trait Usage**

If using `McpObservabilityHelperTrait::record_error_event()`, it's now an instance method:

```php
// Before
static::record_error_event('operation', $exception, ['context' => 'value']);

// After
$this->record_error_event('operation', $exception, ['context' => 'value']);
```

## Transport Layer Changes

### Removed Transports

The following transport classes have been removed:

### Unified HTTP Transport

`HttpTransport` is now the sole HTTP transport implementation:

- ✅ Full [MCP 2025-06-18 specification](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports.md) compliance
- ✅ Supports both WordPress REST API and JSON-RPC 2.0 formats
- ✅ Handles streaming responses (SSE) and standard JSON responses
- ✅ Built-in session management and batch request support

## Using HttpTransport

All MCP servers use `HttpTransport` by default:

```php
use WP\MCP\Core\McpAdapter;
use WP\MCP\Transport\HttpTransport;

add_action('mcp_adapter_init', function($adapter) {
    $adapter->create_server(
        'my-server-id',
        'my-namespace',
        'mcp',
        'My MCP Server',
        'Server description',
        '1.0.0',
        [ HttpTransport::class ],  // Use HttpTransport
        ErrorLogMcpErrorHandler::class,
        ['my-plugin/my-ability']
    );
});
```

## Advanced Usage

### Custom Authentication

Use transport permission callbacks for custom authentication:

```php
add_action('mcp_adapter_init', function($adapter) {
    $adapter->create_server(
        'secure-server',
        'secure',
        'mcp',
        'Secure Server',
        'Custom auth example',
        '1.0.0',
        [ HttpTransport::class ],
        ErrorLogMcpErrorHandler::class,
        NullMcpObservabilityHandler::class,
        ['my-plugin/ability'],
        [],  // resources
        [],  // prompts
        function() {
            // Custom permission logic
            $api_key = $_SERVER['HTTP_X_API_KEY'] ?? '';
            return validate_api_key($api_key);
        }
    );
});
```

See the [Transport Permissions Guide](../guides/transport-permissions.md) for more authentication patterns.

### Custom Transport Implementations

For specialized requirements (message queues, custom protocols, etc.), create custom transports:

```php
use WP\MCP\Transport\Contracts\McpRestTransportInterface;
use WP\MCP\Transport\Infrastructure\McpTransportContext;
use WP\MCP\Transport\Infrastructure\McpTransportHelperTrait;

class MyCustomTransport implements McpRestTransportInterface {
    use McpTransportHelperTrait;
    
    private McpTransportContext $context;
    
    public function __construct(McpTransportContext $context) {
        $this->context = $context;
        $this->register_routes();
    }
    
    public function register_routes(): void {
        // Register your custom routes
    }
    
    public function check_permission(\WP_REST_Request $request) {
        return is_user_logged_in();
    }
    
    public function handle_request(\WP_REST_Request $request): \WP_REST_Response {
        $body = $request->get_json_params();
        
        $result = $this->context->request_router->route_request(
            $body['method'],
            $body['params'] ?? [],
            $body['id'] ?? 0,
            $this->get_transport_name()
        );
        
        return rest_ensure_response($result);
    }
}
```

See the [Custom Transports Guide](../guides/custom-transports.md) for detailed implementation instructions.

## Next Steps

- **[Custom Transports](../guides/custom-transports.md)** - Learn about custom transport implementations
- **[Transport Permissions](../guides/transport-permissions.md)** - Implement custom authentication
- **[Error Handling](../guides/error-handling.md)** - Configure error management
- **[Architecture Overview](../architecture/overview.md)** - Understand system design

