Skip to main content
Glama
avisangle

Method CRM MCP Server

by avisangle
README.md
# Method CRM MCP Server

A production-ready Model Context Protocol (MCP) server for Method CRM API integration. This server enables LLMs to interact with Method CRM data through well-designed tools for tables, files, users, events, and API key management.

## Features

- **20 Comprehensive Tools** covering all Method CRM operations
- **API Key Authentication** (fully implemented) with OAuth2 placeholders
- **Dual Transport Support**: stdio (local) and streamable HTTP (remote)
- **Rate Limiting & Retry Logic**: Automatic handling of API limits
- **Type-Safe**: Full Pydantic validation for all inputs
- **Actionable Errors**: Clear error messages with suggestions
- **Pagination Support**: Efficient handling of large datasets
- **Multiple Response Formats**: JSON and Markdown outputs

## Installation

### Prerequisites

- Python 3.10 or higher
- Method CRM account with API access
- API key (obtain from Method CRM dashboard)

### Install Dependencies

```bash
# Clone the repository
git clone https://github.com/avisangle/method-crm-mcp.git
cd method-crm-mcp

# Install dependencies
pip install -e .

# Or install with dev dependencies
pip install -e ".[dev]"
```

## Configuration

### 1. Environment Setup

Copy the example environment file:

```bash
cp .env.example .env
```

### 2. Configure Authentication

Edit `.env` and set your API key:

```bash
# Required: Method CRM API Key
METHOD_API_KEY=your_api_key_here

# Optional: Customize API base URL
METHOD_API_BASE_URL=https://rest.method.me/api/v1/

# Optional: Transport configuration
METHOD_TRANSPORT=stdio        # or 'http'
METHOD_HTTP_PORT=8000         # only used if TRANSPORT=http
```

### 3. Get Your API Key

1. Log in to your Method CRM account
2. Navigate to **Settings** → **API Settings**
3. Click **Create API Key** (requires Administrator role)
4. Name your key (e.g., "MCP Server")
5. Copy the generated key immediately (it won't be shown again)
6. Paste it into `.env` as `METHOD_API_KEY`

## Usage

### Running the Server

#### stdio Transport (Local Use)

Default mode for use with MCP clients like Claude Desktop:

```bash
python -m method_mcp.server
```

Or using the module directly:

```bash
python src/method_mcp/server.py
```

#### HTTP Transport (Remote Access)

For remote access or multiple clients:

```bash
# Set transport in .env
METHOD_TRANSPORT=http
METHOD_HTTP_PORT=8000

# Start server
python -m method_mcp.server
```

The server will listen on `http://localhost:8000`

### MCP Client Configuration

#### Claude Desktop

Add to your Claude Desktop configuration (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):

```json
{
  "mcpServers": {
    "method-crm": {
      "command": "python",
      "args": [
        "-m",
        "method_mcp.server"
      ],
      "cwd": "/absolute/path/to/method-crm-mcp",
      "env": {
        "METHOD_API_KEY": "your_api_key_here"
      }
    }
  }
}
```

#### MCP Inspector (Testing)

Test tools using MCP Inspector:

```bash
npx @modelcontextprotocol/inspector python src/method_mcp/server.py
```

## Available Tools

### Table Operations (5 tools)

| Tool | Description | Read-Only | Destructive |
|------|-------------|-----------|-------------|
| `method_tables_query` | Query records with filtering, pagination, aggregation | ✅ | ❌ |
| `method_tables_get` | Get specific record by ID with optional expansion | ✅ | ❌ |
| `method_tables_create` | Create new record in any table | ❌ | ❌ |
| `method_tables_update` | Update record fields (batch support: 50 records) | ❌ | ❌ |
| `method_tables_delete` | Delete record permanently | ❌ | ✅ |

**Example**: Query active customers
```json
{
  "table": "Customer",
  "filter": "Status eq 'Active'",
  "top": 50,
  "orderby": "CreatedDate desc",
  "response_format": "json"
}
```

**Example**: Create new inventory item
```json
{
  "table": "ItemInventory",
  "fields": {
    "Name": "New Product",
    "Sku": "SKU-001",
    "SalesDesc": "Product description",
    "SalesPrice": 99.99,
    "PurchaseCost": 80,
    "PurchaseDesc": "Purchase description",
    "IsActive": true,
    "IncomeAccount": "Sales of Product Income",
    "COGSAccount": "Cost of Goods Sold",
    "AssetAccount": "Inventory",
    "PurchaseTaxCode": "Tax on Purchases",
    "SalesTaxCode": "Tax on Sales",
    "IsUsedOnPurchaseTransaction": true,
    "IsUsedOnSalesTransaction": true,
    "IsTrackQtyOnHand": true,
    "TenantID": 1
  },
  "response_format": "json"
}
```
**Note**: Required fields vary by table. For `ItemInventory`:
- **Required**: `Sku` (must be unique), `COGSAccount`, `AssetAccount`, `TenantID`
- **Recommended**: `IncomeAccount`, tax codes, transaction flags
- Account fields must match existing QuickBooks account names exactly

### File Management (6 tools)

| Tool | Description | Read-Only | Destructive |
|------|-------------|-----------|-------------|
| `method_files_upload` | Upload file (max 50MB) with optional record linking | ❌ | ❌ |
| `method_files_list` | List files with filtering and pagination | ✅ | ❌ |
| `method_files_download` | Download file content (base64-encoded) | ✅ | ❌ |
| `method_files_get_url` | Generate temporary download URL (20-min expiry) | ✅ | ❌ |
| `method_files_update_link` | Update file link to different table/record | ❌ | ❌ |
| `method_files_delete` | Delete file permanently | ❌ | ✅ |

**Example**: Upload invoice PDF
```json
{
  "filename": "invoice-2024-01.pdf",
  "content": "<base64-encoded content>",
  "link_table": "Invoice",
  "link_record_id": "INV-12345",
  "description": "January 2024 invoice"
}
```

### User Information (1 tool)

| Tool | Description | Read-Only |
|------|-------------|-----------|
| `method_user_get_info` | Get current authenticated user information | ✅ |

### Event Automation (4 tools)

| Tool | Description | Read-Only | Destructive |
|------|-------------|-----------|-------------|
| `method_events_create_routine` | Create event-driven automation routine | ❌ | ❌ |
| `method_events_list_routines` | List all event routines with pagination | ✅ | ❌ |
| `method_events_get_routine` | Get specific routine details by ID | ✅ | ❌ |
| `method_events_delete_routine` | Delete event routine permanently | ❌ | ✅ |

**Example**: Send email on new customer
```json
{
  "name": "New Customer Welcome",
  "description": "Send welcome email to new customers",
  "trigger_config": {
    "event": "record_created",
    "table": "Customer"
  },
  "actions": [
    {
      "action": "send_email",
      "template": "welcome",
      "to_field": "Email"
    }
  ],
  "enabled": true
}
```

### API Key Management (4 tools)

| Tool | Description | Read-Only | Destructive | Admin Required |
|------|-------------|-----------|-------------|----------------|
| `method_apikeys_create` | Create new API key (returns key once) | ❌ | ❌ | ✅ |
| `method_apikeys_list` | List API keys (keys masked for security) | ✅ | ❌ | ❌ |
| `method_apikeys_update` | Update key metadata (name, permissions, status) | ❌ | ❌ | ✅ |
| `method_apikeys_delete` | Delete/revoke API key permanently | ❌ | ✅ | ✅ |

**Example**: Create production API key
```json
{
  "name": "Production Server",
  "description": "Main API integration key",
  "permissions": ["read:all", "write:all"]
}
```

## API Rate Limits

Method CRM enforces the following limits:

- **Per-minute**: 100 requests per account
- **Daily**: 5,000-25,000 requests (varies by license count)
- **File size**: 50MB per file
- **Storage**: 10GB total per account
- **Batch updates**: 50 related records maximum

The MCP server automatically handles rate limiting with:
- Exponential backoff retry logic
- Clear error messages with retry-after timing
- Actionable suggestions for rate limit errors

## Error Handling

All tools return actionable error messages with suggestions:

**Example error responses**:

```
Error: Rate limit exceeded (100 requests/minute or daily limit reached).
Retry-After: 45 seconds
Suggestion: Wait before making more requests. Method CRM limits: 100 req/min per account, 5,000-25,000 daily depending on licenses.
```

```
Error: Resource not found - Record 'CUST-999' not found in table 'Customer'
Suggestion: Verify that the table name, record ID, or file ID is correct. Use list/query tools to find the correct identifier.
```

```
Error: Permission denied - Your account doesn't have Admin role required for this operation
Suggestion: Your account doesn't have permission to perform this operation. Check that you have the required role (e.g., Admin for API key creation) or that the resource belongs to your account.
```

## Advanced Features

### OData Query Filtering

Use powerful OData-style filters for queries:

```
# Equality
"Status eq 'Active'"

# Comparison
"CreatedDate gt '2024-01-01'"
"Total ge 1000"

# Logical operators
"Status eq 'Active' and Total gt 500"
"Type eq 'Invoice' or Type eq 'Estimate'"

# String functions
"startswith(Name, 'John')"
"contains(Email, '@example.com')"
"endswith(Status, 'ed')"

# Null checks
"Email ne null"
```

### Aggregations

Perform aggregations directly in queries:

```
# Count records
"count()"

# Sum amounts
"sum(Amount)"

# Group by with aggregation
"groupby((Status),aggregate($count as Total))"

# Multiple aggregations
"sum(Amount),average(Total),count()"
```

### Pagination

All list/query tools support pagination:

```json
{
  "table": "Customer",
  "top": 100,
  "skip": 0
}
```

Responses include pagination metadata:

```json
{
  "total": 500,
  "count": 100,
  "offset": 0,
  "has_more": true,
  "next_offset": 100
}
```

### Response Formats

Choose between JSON (structured) and Markdown (human-readable):

```json
{
  "response_format": "json"    // or "markdown"
}
```

## Architecture

```
method-crm-mcp/
├── src/method_mcp/
│   ├── server.py           # FastMCP server initialization
│   ├── client.py           # HTTP client with retry logic
│   ├── auth.py             # Authentication (API Key + OAuth2 placeholders)
│   ├── models.py           # Pydantic models (20 tools)
│   ├── errors.py           # Error handling utilities
│   ├── utils.py            # Shared helpers (pagination, formatting)
│   └── tools/
│       ├── tables.py       # 5 table operation tools (CRUD)
│       ├── files.py        # 6 file management tools
│       ├── user.py         # 1 user info tool
│       ├── events.py       # 4 event automation tools (complete CRUD)
│       └── apikeys.py      # 4 API key management tools (complete CRUD)
├── tests/                  # Unit and integration tests
├── evaluations/            # Evaluation questions for testing
├── pyproject.toml          # Dependencies and project metadata
├── .env.example            # Environment configuration template
└── README.md               # This file
```

## Development

### Running Tests

```bash
# Install dev dependencies
pip install -e ".[dev]"

# Run all tests
pytest

# Run with coverage
pytest --cov=src/method_mcp --cov-report=html
```

### Code Quality

```bash
# Type checking
mypy src/method_mcp

# Linting
ruff check src/method_mcp

# Formatting
black src/method_mcp
```

## Troubleshooting

### Authentication Errors

**Problem**: `Error: Authentication failed. Your API key or access token is invalid or expired.`

**Solutions**:
- Verify `METHOD_API_KEY` is set correctly in `.env`
- Check that the key hasn't been revoked in Method CRM dashboard
- Ensure no extra whitespace in the key value

### Rate Limiting

**Problem**: `Error: Rate limit exceeded (100 requests/minute)`

**Solutions**:
- Wait for the retry-after period (check error message)
- Reduce request frequency in your application
- Use pagination to reduce total requests
- Consider upgrading Method CRM license for higher limits

### File Upload Errors

**Problem**: `Error: File size exceeds 50MB limit`

**Solutions**:
- Compress files before uploading
- Split large files into smaller chunks
- Use external storage (S3, etc.) and store URLs in Method CRM

### Connection Errors

**Problem**: `Error: Unable to connect to Method API`

**Solutions**:
- Check internet connection
- Verify `METHOD_API_BASE_URL` is correct
- Check if Method CRM is experiencing downtime
- Verify firewall/proxy settings

## Security Best Practices

1. **API Key Storage**
   - Store in environment variables, never in code
   - Use `.env` file (never commit to version control)
   - Rotate keys periodically (every 90 days recommended)

2. **Access Control**
   - Use separate API keys for different environments (dev/staging/prod)
   - Revoke unused keys promptly
   - Monitor key usage via `method_apikeys_list`

3. **File Handling**
   - Validate file types before upload
   - Scan uploaded files for malware
   - Enforce file size limits in application logic

4. **Error Messages**
   - Don't expose sensitive information in logs
   - Use debug mode only in development

## OAuth2 Support (Coming Soon)

OAuth2 authentication is planned for a future release:

- **Authorization Code Flow**: User-based authentication
- **Client Credentials Flow**: Machine-to-machine with token refresh
- **Implicit Flow**: Browser-based SPAs

Placeholders are already in the codebase (`auth.py`).

## Contributing

Contributions are welcome! Please:

1. Fork the repository
2. Create a feature branch
3. Add tests for new features
4. Ensure all tests pass
5. Submit a pull request

## Support

- **Issues**: Report bugs and request features on GitHub
- **Documentation**: https://developer.method.me/
- **Method CRM Support**: https://help.method.me/

## License

MIT License - See [LICENSE](LICENSE) file for details.

## Changelog

### Version 1.0.0 (2025-11-22)

**Initial Public Release**
- ✅ 20 fully implemented tools (complete API coverage)
- ✅ API Key authentication
- ✅ Dual transport support (stdio + HTTP)
- ✅ Rate limiting and retry logic
- ✅ Comprehensive error handling
- ✅ Pydantic validation for all inputs
- ✅ Pagination support with cursor-based navigation
- ✅ Multiple response formats (JSON/Markdown)
- ✅ File operations with multipart/form-data support
- ✅ Complete CRUD for tables, events, and API keys
- ⏳ OAuth2 support (placeholders added)

---

**Built with** ❤️ **using** [FastMCP](https://github.com/modelcontextprotocol/python-sdk) **and the** [Model Context Protocol](https://modelcontextprotocol.io)