PyerP MCP Server
by vvbv
README.md
# PyerP MCP Server
An [MCP](https://modelcontextprotocol.io/) (Model Context Protocol) server that exposes a [PyerP](https://github.com/your-org/PyerP) ERP system to LLM agents. The server translates MCP tool calls into HTTP requests against the PyerP REST API, giving any MCP-compatible client (Claude Desktop, OpenCode, Cursor, etc.) the ability to search, read, create and update ERP records through natural language.
## Architecture
```
┌──────────────┐ MCP (stdio/SSE/HTTP) ┌────────────────┐
│ LLM Client │ ◄──────────────────────────────► │ PyerP MCP │
│ (Claude, │ │ Server │
│ Cursor…) │ │ (server.py) │
└──────────────┘ └───────┬────────┘
│ httpx
▼
┌────────────────┐
│ PyerP REST │
│ API (Flask) │
└────────────────┘
```
The server itself is stateless. It authenticates to PyerP via `api_key` query parameter, exactly as the native web client does.
## Requirements
- Python 3.10+
- A running PyerP instance with API access
- A valid PyerP API key
## Installation
```bash
# Clone the repository
git clone <repo-url> PyerP-MCP
cd PyerP-MCP
# Install dependencies (pick one)
pip install -e . # pip
# or
uv pip install -e . # uv
```
## Configuration
Copy the example environment file and fill in your values:
```bash
cp .env.example .env
```
| Variable | Default | Description |
|----------|---------|-------------|
| `PYERP_BASE_URL` | `http://localhost:5000` | Base URL of your PyerP instance (no trailing slash) |
| `PYERP_API_KEY` | *(empty)* | API key for authentication. **Required.** |
| `PYERP_DEFAULT_MODULE` | `admin` | Default module used when `module` is omitted in tool calls |
| `PYERP_REQUEST_TIMEOUT` | `30` | HTTP request timeout in seconds |
## Running the Server
### stdio (default, for Claude Desktop / OpenCode / Cursor)
```bash
python server.py
```
### SSE transport
```bash
python server.py --sse
```
### Streamable HTTP transport
```bash
python server.py --streamable-http
```
### With uv (no install needed)
```bash
uv run --with "mcp[cli]" --with httpx --with python-dotenv server.py
```
## Client Configuration Examples
### Claude Desktop
Add to `claude_desktop_config.json`:
```json
{
"mcpServers": {
"pyerp": {
"command": "python",
"args": ["/absolute/path/to/PyerP-MCP/server.py"],
"env": {
"PYERP_BASE_URL": "http://localhost:5000",
"PYERP_API_KEY": "your_api_key_here"
}
}
}
}
```
### OpenCode
Add to your OpenCode MCP configuration:
```json
{
"mcpServers": {
"pyerp": {
"command": "python",
"args": ["/absolute/path/to/PyerP-MCP/server.py"],
"env": {
"PYERP_BASE_URL": "http://localhost:5000",
"PYERP_API_KEY": "your_api_key_here"
}
}
}
}
```
## Available Tools
### Search
| Tool | Description |
|------|-------------|
| `search` | Search records with **AND** logic. Multiple param/value pairs must all match. |
| `search_or` | Search records with **OR** logic. Any param/value pair can match. |
Both tools accept: `search_param`, `search_value`, `module`, `model`, `search_order_by`, `search_order`, `solve_references`, `page`, `page_size`.
**Search parameter behavior:**
- Multiple params with matching values (e.g. `"name,email"` + `"John,john@test.com"`) applies AND/OR between each pair.
- One param with multiple values (e.g. `"status"` + `"active,pending"`) matches any value for that column (OR/IN).
- Use `"null"` as a value to match NULL fields.
- The `g_` prefix on column names is added automatically; you do **not** need to include it.
### Retrieve
| Tool | Description |
|------|-------------|
| `get_record` | Get a single record by `entity_id` or `entity_uuid`. |
| `get_record_with_references` | Same as above but with foreign key references resolved to human-readable values. |
| `get_all_records` | Paginated list of all records (newest first). Max 1000 per page. |
| `get_all_records_with_references` | Paginated list with FK references resolved. Max 10 per page. |
| `get_detail` | Full detail view: inline fields with labels, reference dropdowns, and related sub-lists. |
### Create & Update
| Tool | Description |
|------|-------------|
| `create_record` | Create a new record. Pass field values as a `data` dictionary. |
| `update_record` | Update an existing record by `entity_uuid`. Only include changed fields. |
### Metadata
| Tool | Description |
|------|-------------|
| `check_api_status` | Health check on a PyerP API module. |
## Available Resources
The server exposes two static MCP resources that LLM clients can read for context:
| URI | Description |
|-----|-------------|
| `pyerp://info/field-conventions` | Column prefix conventions (`g_`, `ref_`, `enc_`, etc.) and system column documentation. |
| `pyerp://info/api-guide` | Quick reference with current configuration, available modules/models, and workflow examples. |
## PyerP Concepts
### Modules
A PyerP instance is organized into modules. Each module exposes its own API under `/api_{module}/`. The main module is `admin`, which manages most data models.
| Module | Description |
|--------|-------------|
| `admin` | Users, roles, permissions, clients, products, providers, warehouse, inventory, etc. |
| `contacts` | Contact management |
| `media` | File/media management |
| `templates` | Template management |
| `messenger` | Email & SMS messaging |
| `communications` | Communications management |
| `reports` | Reporting |
Additional modules may be dynamically loaded depending on the PyerP instance configuration.
### Models (Tables)
Within the `admin` module, common models include:
`users`, `roles`, `permissions`, `clients`, `products`, `providers`, `warehouse`, `inventory`, `inventory_movements`, `remissions`, `price_rate`, `patients`, `diagnoses`, `health_providers`, `media`
### Field Naming Conventions
| Prefix | Meaning | Example |
|--------|---------|---------|
| `g_` | General/regular field | `g_name`, `g_email`, `g_status` |
| `ref_` | Foreign key reference | `ref_roles` (references `roles` table) |
| `uref_` | Unique foreign key (1-to-1) | `uref_profile` |
| `enc_` | Encrypted field | `enc_password` |
| `bol_` | Boolean | `bol_active` |
| `int_` | Integer | `int_quantity` |
| `dec_` | Decimal | `dec_price` |
| `json_` | JSON data | `json_metadata` |
| `dt_` | Date/time | `dt_scheduled` |
System columns (`entity_id`, `entity_uuid`, `created_at`, `updated_at`, `deleted`, `deleted_at`) are auto-managed. Do not include them in create/update calls.
### Soft Delete
PyerP uses soft deletes. All records have a `deleted` column (0 = active, 1 = deleted). All queries automatically filter `WHERE deleted = 0`.
## Usage Examples
```
"Show me the first 20 users"
→ get_all_records(module="admin", model="users", page_size=20)
"Search for a client named ACME"
→ search(search_param="name", search_value="ACME", module="admin", model="clients")
"Find products that are active or pending"
→ search_or(search_param="status", search_value="active,pending", module="admin", model="products")
"Show me the full details of user with entity_id 5"
→ get_detail(entity_id=5, module="admin", model="users")
"Create a new provider"
→ create_record(data={"g_name": "New Provider", "g_email": "info@provider.com"}, module="admin", model="providers")
"Update client 789456123 phone number"
→ update_record(entity_uuid="789456123", data={"g_phone": "5559876543"}, module="admin", model="clients")
```
## Project Structure
```
PyerP-MCP/
├── server.py # MCP server (tools, resources, helpers)
├── pyproject.toml # Project metadata and dependencies
├── .env.example # Configuration template
├── README.md # This file
└── agents.md # LLM agent prompt guide
```
## License
See the PyerP main repository for license information.