# MCP Pricing Server - Implementation Guide

## Overview
A JSON-RPC 2.0 compliant MCP (Model Context Protocol) server for providing pricing information via HTTP endpoint.

## Components Created

### 1. JsonRpc2Validator (`src/components/JsonRpc2Validator.php`)
- Validates JSON-RPC 2.0 requests and responses using JSON Schema
- Error code constants for standard JSON-RPC errors
- Helper methods for creating valid responses

### 2. PricingService (`src/components/PricingService.php`)
- Handles pricing lookups with user-specific price bands
- Supports single item pricing, bulk pricing, and price search
- Integrates with User model for account-based pricing

### 3. McpController (`src/controllers/McpController.php`)
- HTTP endpoint for JSON-RPC 2.0 requests
- Implements MCP protocol methods (initialize, tools/list, tools/call, resources)
- CORS enabled for external access

## API Endpoint

**URL**: `https://yourdomain.com/NGauge/mcp/rpc`

**Method**: `POST`

**Content-Type**: `application/json`

## Available Tools

### 1. get_price
Get pricing for a single item with optional user context.

**Request**:
```json
{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "get_price",
    "arguments": {
      "item_id": "12345",
      "user_id": "user-abc"
    }
  },
  "id": 1
}
```

**Response**:
```json
{
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"item_id\": \"12345\", \"price\": 99.99, \"currency\": \"GBP\", \"price_band\": \"price2\"}"
      }
    ]
  },
  "id": 1
}
```

### 2. get_prices_bulk
Get pricing for multiple items at once.

**Request**:
```json
{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "get_prices_bulk",
    "arguments": {
      "item_ids": ["item-1", "item-2", "item-3"],
      "user_id": "user-123"
    }
  },
  "id": 2
}
```

### 3. search_prices
Search for products with price filters.

**Request**:
```json
{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "search_prices",
    "arguments": {
      "category": "electronics",
      "min_price": 50,
      "max_price": 500,
      "user_id": "user-123",
      "limit": 20
    }
  },
  "id": 3
}
```

### 4. get_account_info
Get account and price band information for a user.

**Request**:
```json
{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "get_account_info",
    "arguments": {
      "user_id": "user-123"
    }
  },
  "id": 4
}
```

### 5. create_mcp_token
Create an MCP/OAuth access token from username/email and password.

**Request**:
```json
{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "create_mcp_token",
    "arguments": {
      "username": "user@example.com",
      "password": "your-password"
    }
  },
  "id": 5
}
```

### 6. composer_update
Run `composer update <package>` through `ngauge-packagemanager`.

**Request**:
```json
{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "composer_update",
    "arguments": {
      "package": "portalpeople/ngauge-mcp",
      "dry_run": true,
      "access_token": "<oauth-access-token>"
    }
  },
  "id": 6
}
```

### 7. composer_update_all
Run `composer update` for all packages through `ngauge-packagemanager`.

**Request**:
```json
{
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "name": "composer_update_all",
    "arguments": {
      "dry_run": true,
      "access_token": "<oauth-access-token>"
    }
  },
  "id": 7
}
```

**Notes**:
- Use `create_mcp_token` with username/email + password to mint a bearer token.
- `composer_update` and `composer_update_all` require OAuth authentication via `access_token` argument or `Authorization: Bearer <token>` header.
- Package must be in `vendor/package` format.

## Testing

### Test with cURL

```bash
# Initialize
curl -X POST https://yourdomain.com/NGauge/mcp/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "initialize",
    "params": {
      "protocolVersion": "2024-11-05",
      "clientInfo": {"name": "test-client", "version": "1.0.0"}
    },
    "id": 1
  }'

# List available tools
curl -X POST https://yourdomain.com/NGauge/mcp/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tools/list",
    "id": 2
  }'

# Get price for an item
curl -X POST https://yourdomain.com/NGauge/mcp/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tools/call",
    "params": {
      "name": "get_price",
      "arguments": {"item_id": "12345"}
    },
    "id": 3
  }'

# Create MCP bearer token
curl -X POST https://yourdomain.com/NGauge/mcp/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tools/call",
    "params": {
      "name": "create_mcp_token",
      "arguments": {
        "username": "user@example.com",
        "password": "your-password"
      }
    },
    "id": 4
  }'

# Update a package with bearer token
curl -X POST https://yourdomain.com/NGauge/mcp/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tools/call",
    "params": {
      "name": "composer_update",
      "arguments": {
        "package": "portalpeople/ngauge-mcp",
        "dry_run": true,
        "access_token": "<oauth-access-token>"
      }
    },
    "id": 5
  }'
```

### Test with PHP

```php
<?php
$client = new \yii\httpclient\Client();

$response = $client->createRequest()
    ->setMethod('POST')
    ->setUrl('https://yourdomain.com/NGauge/mcp/rpc')
    ->setFormat(\yii\httpclient\Client::FORMAT_JSON)
    ->setData([
        'jsonrpc' => '2.0',
        'method' => 'tools/call',
        'params' => [
            'name' => 'get_price',
            'arguments' => ['item_id' => '12345']
        ],
        'id' => 1
    ])
    ->send();

$result = $response->data;
print_r($result);
```

## Configuration Notes

### Product Model Adaptation
The `PricingService` tries to find products using multiple model names:
- `\backend\models\Products`
- `\backend\models\Productsbe`
- `\backend\models\Items`
- `\backend\models\Itemsbe`

**Update the `findProduct()` and `buildProductQuery()` methods in PricingService to match your actual product model.**

### Price Band Fields
The system looks for price columns named `price1`, `price2`, etc. based on the user's account.

If your database uses different column names, update the `extractPrice()` method in `PricingService`.

## Security Considerations

1. **Authentication**:
  - Composer tools now use OAuth2 bearer token validation.
  - Use HTTPS only when calling `create_mcp_token` because it carries credentials.
  - Consider enforcing auth for pricing tools as a future hardening step.

2. **Rate Limiting**: Add rate limiting to prevent abuse:
```php
// In behaviors() method of McpController
'rateLimiter' => [
    'class' => \yii\filters\RateLimiter::class,
],
```

3. **CORS**: Currently allows all origins. Restrict in production:
```php
'Origin' => ['https://trusted-domain.com'],
```

## Logging

All requests and responses are logged at the INFO level:
- Check `backend/runtime/logs/app.log` for MCP request/response logs
- Errors are logged with full stack traces when YII_DEBUG is enabled

## Error Handling

Standard JSON-RPC 2.0 error codes:
- `-32700`: Parse error
- `-32600`: Invalid Request
- `-32601`: Method not found
- `-32602`: Invalid params
- `-32603`: Internal error

All errors return properly formatted JSON-RPC 2.0 error responses.

## Next Steps

1. **Customize Product Lookup**: Update `PricingService` methods to match your database schema
2. **Expand Auth Scope**: Apply OAuth checks to pricing tools if required by your environment
3. **Add Rate Limiting**: Protect the endpoint from abuse
4. **Monitor Performance**: Add metrics and monitoring
5. **Documentation**: Create API documentation for consumers

## Auto setup page

Use the built-in setup page to generate endpoint-aware config snippets using `Yii::$app->request->hostInfo` (and app base URL):

- `https://yourdomain.com/mcp/setup`
- `https://yourdomain.com/mcp/config/setup`

Machine-readable output:

- `https://yourdomain.com/mcp/setup?format=json`

## Integration with Claude Desktop

This module now includes a local stdio bridge at `bin/mcp-http-bridge.php` so Claude Desktop can use this HTTP MCP endpoint.

1. Create a token using `create_mcp_token`.
2. Copy `examples/claude/claude_desktop_config.json` into your Claude Desktop config file.
3. Update:
  - script path (`/absolute/path/to/NGauge_modules/ngauge-mcp/bin/mcp-http-bridge.php`)
  - endpoint (`--endpoint=https://yourdomain.com/NGauge/mcp/rpc`)
  - token (`MCP_ACCESS_TOKEN`)
4. Restart Claude Desktop.

Claude config location:
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`

## Integration with VS Code

VS Code can use the same bridge script through `mcp.json`.

1. Create a token using `create_mcp_token`.
2. Copy `examples/vscode/mcp.json` to `.vscode/mcp.json` (workspace) or to your user MCP config.
3. Update endpoint and token values.
4. Start/restart the server from VS Code MCP controls.

## Bridge CLI options

Run with:
```bash
php bin/mcp-http-bridge.php --endpoint=https://yourdomain.com/NGauge/mcp/rpc
```

Optional flags:
- `--access-token=<token>` (or set `MCP_ACCESS_TOKEN`)
- `--timeout=<seconds>`
- `--header="Name: Value"` (repeatable)
- `--insecure` (development only)
