# Platform-2Step MCP

MCP server that enables AI agents (Claude, GPT, etc.) to interact with AgendaPro's Platform API safely through a human-in-the-loop confirmation system.

> **Note**: This project is part of the agendapro-mcp-megarepo. For shared documentation (architecture, security, OpenAPI specs), see the root `CLAUDE.md` and `docs/shared/`.

## Architecture Overview

```
User → Claude Desktop → MCP Server (Python) → Platform-2Steps (Rails) → Platform API
                              ↓                       ↓
                        Auth (device flow)    PostgreSQL (pending ops)
                              ↓                       ↑
                   Platform-2Steps BFF        Frontend (confirm/reject)
```

**Key principle**: Mutations require human confirmation. The MCP cannot confirm operations - only create pending ones.

## Authentication

Uses OAuth 2.0 Device Authorization Grant (RFC 8628) for secure authentication through the Platform-2Steps BFF. The BFF returns Cognito tokens for API access.

**Important**: The MCP server runs non-interactively (stdio transport), so authentication must be done beforehand using the `platform-mcp-auth` CLI.

```bash
# 1. Set environment variable
export PLATFORM_2STEPS_BFF_URL=https://ap-api.agendaprodev.com/platform-2steps-bff

# 2. Authenticate (one-time, interactive)
platform-mcp-auth login

# 3. Start Claude Desktop - tokens are cached automatically
```

**CLI Commands:**
| Command | Description |
|---------|-------------|
| `platform-mcp-auth login` | Authenticate with device flow (interactive) |
| `platform-mcp-auth login --force` | Re-authenticate even if valid tokens exist |
| `platform-mcp-auth status` | Check token status |
| `platform-mcp-auth logout` | Clear cached tokens |

Tokens are stored securely at `~/.platform-mcp/tokens.json` with 0o600 permissions.

See `SECURITY_ARCHITECTURE.md` in the megarepo's `docs/shared/` directory for detailed authentication documentation.

## Operation Flow

| HTTP Method | Behavior |
|-------------|----------|
| GET | Pass-through to Platform API |
| POST (batches only) | Creates `PendingBatch` with operations, returns `batch_id` |

All mutations go through batch API (1-50 operations per batch). User must confirm via AgendaPro frontend before execution.

## API Endpoints

### Reading (pass-through)
- `GET /api/v1/categories` - List categories
- `GET /api/v1/categories/:id` - Get category
- `GET /api/v1/categories/services` - List services grouped by category
- `GET /api/v1/services` - List services
- `GET /api/v1/services/:id` - Get service
- `GET /api/v1/bookings` - List bookings
- `GET /api/v1/bookings/:id` - Get booking
- `GET /api/v1/locations` - List locations
- `GET /api/v1/locations/:id` - Get location
- `GET /api/v1/memberships` - List memberships
- `GET /api/v1/memberships/:id` - Get membership
- `GET /api/v1/providers` - List providers
- `GET /api/v1/providers/:id` - Get provider
- `GET /api/v1/users/:id` - Get user

### Batches (only mutation path)
- `POST /api/v1/batches` - Create batch (max 50 ops, 30min TTL)
- `GET /api/v1/batches/:id` - Get batch status
- `POST /api/v1/batches/:id/confirm` - Execute batch (frontend only)
- `POST /api/v1/batches/:id/reject` - Reject batch (frontend only)

## MCP Tools

The backend uses resource-based authorization. `company_id` is only required for:
- **LIST operations**: Passed as query parameter
- **Batch CREATE operations**: Passed as query parameter on the batch endpoint
- **get_user**: Passed as query parameter (for JWT validation)

SHOW operations infer the company from the resource ID.

### Read tools (LIST - require company_id as query param)
- `list_categories(company_id, page?, per_page?)`
- `list_services_by_category(company_id, active?)` - Services grouped by category
- `list_services(company_id, service_category_id?, active?, page?, per_page?)`
- `list_bookings(company_id, start_date, end_date, location_id?, status_id?)`
- `list_locations(company_id, page?, per_page?)`
- `list_memberships(company_id, page?, per_page?)`
- `list_providers(company_id, location_ids?, active?, public_name?, service_ids?, page?, per_page?)`

### Read tools (SHOW - no company_id needed)
- `get_category(category_id)`
- `get_service(service_id)`
- `get_booking(booking_id)`
- `get_location(location_id)`
- `get_membership(membership_id, date?)`
- `get_provider(provider_id)`

### Read tools (special case)
- `get_user(user_id, company_id)` - company_id as query param for JWT validation

### Batch tools (ONLY way to make mutations)
- `create_batch(operations[], company_id, description?)` - company_id as query param. Supports 1-50 operations per batch. All create/update/delete mutations go through batches.

### Analytics tools
- `query_analytics_db(company_id, sql_query)` - Execute read-only SQL against analytics data
- `get_analytics_status(company_id)` - Check analytics data sync status
- `refresh_analytics_data(company_id)` - Force immediate data refresh (has cooldown)

### Status tools (no company_id needed)
- `get_batch_status(batch_id)`

## Project Structure

```
platform-2step-mcp/
├── pyproject.toml
├── src/platform_2step_mcp/
│   ├── server.py          # MCP server entry point
│   ├── cli.py             # Authentication CLI (platform-mcp-auth)
│   ├── client.py          # HTTP client for Platform-2Steps
│   ├── config.py          # Environment config
│   ├── auth/              # Authentication module
│   │   ├── __init__.py    # Public API exports
│   │   ├── client.py      # AuthClient (RFC 8628 device flow)
│   │   ├── models.py      # TokenData, DeviceAuthResponse, etc.
│   │   ├── storage.py     # Secure token storage
│   │   └── exceptions.py  # Auth-specific exceptions
│   ├── tools/
│   │   ├── categories.py     # Read-only (list, get)
│   │   ├── services.py       # Read-only (list, get)
│   │   ├── bookings.py       # Read-only (list, get)
│   │   ├── locations.py      # Read-only (list, get)
│   │   ├── memberships.py    # Read-only (list, get)
│   │   ├── providers.py      # Read-only (list, get)
│   │   ├── users.py          # Read-only (get)
│   │   └── batches.py        # Mutations (create_batch, get_batch_status)
│   └── resources/
│       └── api_docs.py
├── tests/
│   ├── conftest.py
│   ├── test_cli.py        # CLI tests
│   └── test_auth/         # Authentication tests
```

## Environment Variables

| Variable | Required | Description |
|----------|----------|-------------|
| `PLATFORM_2STEPS_BFF_URL` | Yes | BFF URL for auth AND API (e.g., `https://ap-api.agendaprodev.com/platform-2steps-bff`) |
| `LOG_LEVEL` | No | Logging level (DEBUG, INFO, WARNING, ERROR) |
| `DEBUG_HTTP` | No | Enable cURL-style HTTP request/response logging (true/false) |

The BFF proxies all `/api/v1/*` requests to the backend, so only one URL is needed.

### Debug HTTP Logging

When `DEBUG_HTTP=true`, all HTTP requests and responses are logged in cURL-style format:

```
>>> HTTP Request:
curl -X GET 'https://api.example.com/api/v1/categories?company_id=123' \
  -H 'Authorization: Bearer eyJhbGci...xxxxx' \
  -H 'Content-Type: application/json'

<<< HTTP Response:
# Response: 200 (45ms)
# Headers:
#   content-type: application/json
# Body:
#   {"categories": [...]}
```

Sensitive data (tokens, passwords, completion codes) is automatically sanitized in logs.

## Dependencies

- `mcp>=1.0.0` - Anthropic MCP SDK
- `httpx>=0.27.0` - Async HTTP client
- `pydantic>=2.0` - Schema validation
- `pydantic-settings` - Environment config

## Security Rules

1. **MCP must NOT have confirm capability** - No `confirm_operation` or `confirm_batch` tools
2. **All operations are logged** with agent_session_id for audit
3. **Token storage is secure** - `~/.platform-mcp/tokens.json` with 0o600 permissions

## Limits

| Resource | Limit |
|----------|-------|
| Operation TTL | 5-15 minutes |
| Batch TTL | 30 minutes |
| Operations per batch | 50 |
| Retry attempts | 3 (exponential backoff) |

## PendingOperation Statuses

- `pending` - Awaiting user confirmation
- `confirmed` - Executed successfully
- `rejected` - User rejected
- `expired` - TTL exceeded
- `failed` - Execution error

## Development

```bash
# Install with uv
uv pip install -e .

# Authenticate first (one-time)
export PLATFORM_2STEPS_BFF_URL=https://ap-api.agendaprodev.com/platform-2steps-bff
platform-mcp-auth login

# Run server
platform-2step-mcp
```

### Claude Desktop Configuration (macOS)

Edit `~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "platform-2step": {
      "command": "uvx",
      "args": ["platform-2step-mcp"],
      "env": {
        "PLATFORM_2STEPS_BFF_URL": "https://ap-api.agendaprodev.com/platform-2steps-bff"
      }
    }
  }
}
```

