OpenCart MCP Server
# OpenCart MCP Server
Query and edit your OpenCart store from Claude Code. Products, orders, customers, Journal3 modules, SEO URLs, CMS pages β 42 tools, all through natural language.
Built for store owners and developers who are tired of SSH + phpMyAdmin + admin panel clicking to get simple answers.
```
"Which products are low on stock?"
"Update the meta description for category 25"
"Show me today's orders over Β£50"
"Find the Journal3 module that contains our FAQ text and fix the typo"
```
It just works. You ask, Claude calls the right tool, you get the answer.
> π» **Prefer the terminal?** Check out [**opencart-cli**](https://github.com/chrisbray85/opencart-cli) β same OpenCart understanding, pretty tables, sparklines, AI in the shell (`opencart ask "..."`), interactive REPL, live order watching. `pip install opencart-cli`.
---
## Safe by default
This matters if you're connecting AI to a live store. Every decision here was made with that in mind.
- **Read-only queries** β `query()` only allows SELECT, SHOW, DESCRIBE, EXPLAIN
- **DDL is blocked** β DROP, ALTER, TRUNCATE, CREATE will never run, even through `run_sql()`
- **SSH tunnel** β database credentials stay inside the encrypted connection, never exposed
- **Path traversal blocked** β `get_file()` and `write_file()` reject `..` in paths
- **Access policy** β optional `OPENCART_MCP_POLICY` (`safe` / `manager` / `developer` / `all`) hides write tools by role; default `all` keeps previous behavior. Levels below `all` also apply best-effort secret guardrails (block `config.php` / admin user tables, redact password-like settings)
- **Write confirmation** β Claude Code prompts you before any write tool executes
- **Nothing runs on your server** β no agents, no daemons, no PHP files uploaded. The server runs on your machine and connects over SSH
You can point this at a production store and not worry about it doing something stupid. For day-to-day agent use on production data, prefer `OPENCART_MCP_POLICY=safe` or `developer` (writes allowed for module work, secrets still filtered) over `all`.
---
## What can you actually do with it?
### Store owners
- "How many orders came in this week?" β instant sales summary with daily breakdown
- "What's running low?" β stock report sorted by quantity, lowest first
- "Update the price of product 47 to 29.99" β done, one confirmation click
- "Show me the About Us page content" β full CMS page, ready to review or edit
### Developers
- "Show me the schema for oc_order" β column definitions without opening phpMyAdmin
- "List all OCMOD modifications and their status" β instant audit
- "What extensions are installed?" β full list, no admin panel needed
- "Run this SELECT against the orders table" β custom SQL with safety rails
### Agencies managing multiple stores
- Run dev and live as separate MCP instances in the same Claude session
- `opencart_dev__get_products` vs `opencart_live__get_products` β no confusion
- Compare stock levels, settings, or module content across environments
### Journal3 users
- List, inspect, and edit J3 modules β FAQ accordions, sliders, banners, product tabs
- Read and update theme settings and skin settings per skin
- **Find/replace inside module JSON** β safely change text without rewriting the entire module
- J3 tools return empty results (not errors) if Journal3 isn't installed, so the server works with any theme
---
## How it compares
| Task | Admin panel | SSH + SQL | This MCP server |
|------|------------|-----------|----------------|
| Check stock levels | Click through pages | Write a query, run it | "What's low on stock?" |
| Update a product price | Find product, edit, save | UPDATE query by hand | "Set product 47 to Β£29.99" |
| Read a J3 module | JSON blob in the database | Copy-paste from phpMyAdmin | "Show me module 505" |
| Edit FAQ text | Find module, decode JSON, edit, re-encode | Pain | "Replace X with Y in module 505" |
| Sales report | Reports page, manually filter | Write aggregation queries | "Sales summary for the last 7 days" |
| Check SEO URLs | Admin > Marketing > SEO URL, paginate | SELECT from oc_seo_url | "Show SEO URLs containing 'headphones'" |
| Manage CMS pages | Admin > Catalog > Information | Direct DB access | "Show me the About Us page" |
---
## Quick start
### 1. Install
```bash
git clone https://github.com/chrisbray85/opencart-mcp.git
cd opencart-mcp
python3 -m venv .venv && source .venv/bin/activate
pip install -e .
```
<details>
<summary><strong>Or install with Nix (flake)</strong></summary>
Run it directly, no clone:
```bash
nix run github:chrisbray85/opencart-mcp
```
Install via a flake input (NixOS / home-manager) β adds the `opencart-mcp`
binary to `PATH`:
```nix
{
inputs.opencart-mcp.url = "github:chrisbray85/opencart-mcp";
# then, in your NixOS configuration (configuration.nix / a module):
environment.systemPackages = [
inputs.opencart-mcp.packages.${pkgs.system}.default
];
# β¦or home-manager:
home.packages = [
inputs.opencart-mcp.packages.${pkgs.system}.default
];
}
```
Dev shell with all deps (skip the venv steps above):
```bash
nix develop
```
</details>
### 2. Configure
```bash
cp .env.example .env
```
Fill in your server details:
```env
OPENCART_SSH_HOST=your-server-ip
OPENCART_SSH_USER=your-ssh-username
OPENCART_SSH_KEY=~/.ssh/id_ed25519
OPENCART_DB_USER=your_db_user
OPENCART_DB_PASS=your_db_password
OPENCART_DB_NAME=your_opencart_database
OPENCART_ROOT=/path/to/opencart
OPENCART_STORAGE=/path/to/storage
```
> **Where to find your paths:**
> - `OPENCART_ROOT` β the directory containing `index.php`, `admin/`, `catalog/`, `system/`
> - `OPENCART_STORAGE` β check your `config.php` for the `DIR_STORAGE` value (often outside the web root on OpenCart 3.0.3.3+)
Optional extras:
```env
OPENCART_SSH_PORT=22 # if SSH runs on a non-standard port
OPENCART_DB_HOST=localhost # if MySQL isn't on the same host (e.g. a tunnel)
OPENCART_DB_PREFIX=oc_ # table prefix override
OPENCART_LANGUAGE_ID=1 # skip auto-detect from config_language
# OPENCART_MCP_POLICY=all # safe | manager | developer | all (default: all)
```
`OPENCART_MCP_POLICY` controls which tools are registered:
| Value | Tools | Secret guardrails |
|-------|--------|-------------------|
| `safe` | Read-only tools | yes |
| `manager` | + catalog/order/settings/coupon writes | yes |
| `developer` | + `write_file`, `run_sql`, cache/OCMOD clear | yes |
| `all` (default) | Everything | no (previous behavior) |
Guardrails (when policy is not `all`) block paths like `config.php` / `.env`, queries against admin user/session tables, and redact password-like keys in `get_settings`. This is best-effort, not a hard security boundary.
Any `OPENCART_DB_*` value you leave unset is read from the install's `config.php` automatically, so on most setups the main block above is all you need.
The storefront language is auto-detected from the store's `config_language` setting, so multi-language and non-English stores work without configuration β set `OPENCART_LANGUAGE_ID` only to force a specific one.
#### No SSH? Direct MySQL mode
If your host allows remote MySQL connections (or you run your own tunnel), leave `OPENCART_SSH_HOST` empty and set the `OPENCART_DB_*` values instead:
```env
OPENCART_SSH_HOST=
OPENCART_DB_HOST=your-mysql-host
OPENCART_DB_PORT=3306
OPENCART_DB_USER=your_db_user
OPENCART_DB_PASS=your_db_password
OPENCART_DB_NAME=your_opencart_database
OPENCART_DB_PREFIX=oc_
```
All SQL-backed tools work identically. File and cache tools (`get_file`, `write_file`, `clear_cache`, `refresh_modifications`) refuse to run in this mode β there's no shell to run them on, and they must never touch a local copy of the store thinking it's production. SSH remains the recommended transport: credentials stay inside the encrypted connection.
#### Using DDEV for local development?
Set `OPENCART_SSH_HOST=ddev` and point `OPENCART_ROOT` at the local project directory β commands will run via `ddev exec` inside your container instead of SSH:
```env
OPENCART_SSH_HOST=ddev
OPENCART_DB_USER=db
OPENCART_DB_PASS=db
OPENCART_DB_NAME=db
OPENCART_ROOT=/Users/you/Sites/your-opencart-project
```
Container paths (`/var/www/html` etc.) are auto-resolved β you only need the local project path. (DDEV support contributed by [@IceDBorn](https://github.com/IceDBorn) β thanks!)
### 3. Test the connection
```bash
source .venv/bin/activate
PYTHONPATH=src python -c "
from opencart_mcp.config import Config
from opencart_mcp.db import OpenCartDB
import json
config = Config.from_env()
db = OpenCartDB(config)
result = db.run_query('SELECT COUNT(*) as product_count FROM oc_product WHERE status = 1')
print(json.dumps(result, indent=2))
db.close()
"
```
You should see something like `[{"product_count": "42"}]`. If not, check [Troubleshooting](#troubleshooting).
### 4. Add to Claude Code
<details>
<summary><strong>VS Code (Claude Code extension)</strong></summary>
Open your VS Code settings JSON (`Cmd+Shift+P` β "Open User Settings (JSON)") and add:
```json
{
"claude.mcpServers": {
"opencart": {
"command": "/absolute/path/to/opencart-mcp/.venv/bin/python",
"args": ["-m", "opencart_mcp.server"],
"cwd": "/absolute/path/to/opencart-mcp",
"env": {
"PYTHONPATH": "/absolute/path/to/opencart-mcp/src",
"OPENCART_SSH_HOST": "your-server-ip",
"OPENCART_SSH_USER": "your-ssh-username",
"OPENCART_SSH_KEY": "~/.ssh/id_ed25519",
"OPENCART_DB_USER": "your_db_user",
"OPENCART_DB_PASS": "your_db_password",
"OPENCART_DB_NAME": "your_opencart_database",
"OPENCART_ROOT": "/path/to/opencart",
"OPENCART_STORAGE": "/path/to/storage"
}
}
}
}
```
Restart VS Code and the tools will appear in the Claude Code panel.
</details>
<details>
<summary><strong>Claude Code CLI</strong></summary>
Add to `~/.claude.json` (global) or `.claude/settings.json` (project-level):
```json
{
"mcpServers": {
"opencart": {
"command": "/absolute/path/to/opencart-mcp/.venv/bin/python",
"args": ["-m", "opencart_mcp.server"],
"cwd": "/absolute/path/to/opencart-mcp",
"env": {
"PYTHONPATH": "/absolute/path/to/opencart-mcp/src",
"OPENCART_SSH_HOST": "your-server-ip",
"OPENCART_SSH_USER": "your-ssh-username",
"OPENCART_SSH_KEY": "~/.ssh/id_ed25519",
"OPENCART_DB_USER": "your_db_user",
"OPENCART_DB_PASS": "your_db_password",
"OPENCART_DB_NAME": "your_opencart_database",
"OPENCART_ROOT": "/path/to/opencart",
"OPENCART_STORAGE": "/path/to/storage"
}
}
}
}
```
Restart Claude Code and the tools load automatically.
</details>
<details>
<summary><strong>JetBrains (Claude Code extension)</strong></summary>
JetBrains uses the same `~/.claude.json` configuration as the CLI. Follow the CLI instructions above and restart your IDE.
</details>
---
## Example prompts
These all work out of the box. Just type them into Claude Code.
**Products & stock**
```
"Show me all products with less than 5 in stock"
"Get full details for product 123 including options and images"
"Search for products with 'wireless' in the name"
"Update the price of product 47 to 34.99"
```
**Orders & customers**
```
"Show me today's orders"
"Get order 5892 with line items and status history"
"Find customer john@example.com β how many orders have they placed?"
"Sales summary for the last 7 days with top sellers"
```
**SEO & content**
```
"List all SEO URLs containing 'sale'"
"Update the SEO URL for product 23 to 'wireless-mouse-pro'"
"Show me the FAQ page content"
"Replace 'old company name' with 'new company name' in the About Us page"
```
**Journal3 theme**
```
"List all Journal3 FAQ modules"
"Show me the full content of module 505"
"Replace 'Free shipping over Β£50' with 'Free shipping over Β£75' in the banner module"
"What skin settings are configured for skin 1?"
```
**Technical**
```
"Show the schema for oc_order_product"
"List all tables matching 'journal3'"
"Run: SELECT order_id, total FROM oc_order WHERE total > 100 ORDER BY date_added DESC LIMIT 10"
"What OCMOD modifications are active?"
```
---
## All 42 tools
### Read (27)
| Tool | What it does |
|------|-------------|
| `get_products` | Search products with stock, prices, SEO data. Filter by category |
| `get_product` | Full product details β images, options, categories, attributes |
| `get_orders` | Recent orders filtered by status and date range |
| `get_order` | Full order with line items, totals, status history |
| `get_customers` | Search by name/email with order count and total spent |
| `get_categories` | Category tree with product counts and SEO URLs |
| `get_stock_report` | All products sorted by stock level (lowest first) |
| `get_settings` | OpenCart core settings by group/key |
| `get_j3_settings` | Journal3 theme settings |
| `get_j3_skin_settings` | Journal3 skin/layout settings per skin |
| `get_modules` | Journal3 modules by type β search content within modules |
| `get_j3_module` | Full module JSON data for any J3 module |
| `get_information_pages` | List CMS pages (About Us, FAQ, T&Cs) with content preview |
| `get_information_page` | Full HTML content of a single CMS/information page |
| `get_order_statuses` | All order status mappings with IDs |
| `get_product_attributes` | Product attributes (weight, storage conditions, etc.) |
| `sales_summary` | Revenue, top sellers, daily stats for any period |
| `get_modifications` | OCMOD modifications with status |
| `get_extensions` | Installed extensions list |
| `get_seo_urls` | SEO URL mappings with filtering |
| `query` | Custom read-only SQL (SELECT/SHOW/DESCRIBE/EXPLAIN only) |
| `get_table_schema` | Column definitions for any table |
| `list_tables` | List tables matching a pattern |
| `get_file` | Read files from the server (`from_end` for tail, optional fixed-string `grep`; path traversal blocked). Large logs: prefer `from_end=True`; with grep, only the last ~50k lines are scanned |
| `get_coupons` | List discount coupons with usage counts |
| `get_vouchers` | List gift vouchers |
| `dashboard` | One-call store overview β revenue, order statuses, stock alerts, latest orders |
### Write (15)
| Tool | What it does |
|------|-------------|
| `update_product` | Update price, stock, name, SEO title, meta description |
| `update_setting` | Change OpenCart core settings |
| `update_j3_setting` | Change Journal3 theme settings |
| `update_j3_skin_setting` | Change Journal3 skin settings |
| `update_j3_module` | Find/replace text within J3 module JSON (banners, FAQ, sliders) |
| `update_information` | Find/replace text within CMS page HTML (About Us, T&Cs, etc.) |
| `update_seo_url` | Create or update SEO URL mappings |
| `update_category` | Update category name, meta, status |
| `write_file` | Write files to server via SFTP |
| `run_sql` | Execute INSERT/UPDATE/DELETE (DDL blocked) |
| `clear_cache` | Flush OpenCart + Journal3 caches |
| `refresh_modifications` | Clear OCMOD modification cache |
| `update_order_status` | Change order status + append order history |
| `create_coupon` | Create a discount coupon (percentage or fixed) |
| `update_coupon` | Enable/disable, extend, or edit a coupon |
---
## How it works
```
Your machine Your server
ββββββββββββββββ ββββββββββββββββ
β Claude Code β β β
β β β SSH tunnel β PHP cli β
β MCP Server β βββββββββββββββββββ β β β
β (Python) β PHP via stdin β MySQL β
β β βββββββββββββββββββ β (JSON) β
ββββββββββββββββ ββββββββββββββββ
```
The server runs **on your machine**. It connects to your OpenCart server via SSH, pipes PHP to the remote interpreter via stdin, and gets JSON back. Nothing is installed on your server. No files uploaded, no cleanup, no ports opened.
- **PHP via stdin** β works with any PHP version, nothing written to disk
- **SSH tunnel** β credentials never leave the encrypted connection
- **Paramiko** β pure Python SSH, no system dependencies beyond Python 3.10+
- **Direct MySQL** β `pymysql` fallback for hosts without SSH (file/cache tools disabled)
---
## Multiple stores
Run dev and live as separate instances in the same Claude session:
```json
{
"mcpServers": {
"opencart_dev": {
"command": "/path/to/opencart-mcp/.venv/bin/python",
"args": ["-m", "opencart_mcp.server"],
"cwd": "/path/to/opencart-mcp",
"env": { "OPENCART_DB_NAME": "my_dev_database", "..." }
},
"opencart_live": {
"command": "/path/to/opencart-mcp/.venv/bin/python",
"args": ["-m", "opencart_mcp.server"],
"cwd": "/path/to/opencart-mcp",
"env": { "OPENCART_DB_NAME": "my_live_database", "..." }
}
}
}
```
Claude prefixes tools automatically β `opencart_dev__get_products` vs `opencart_live__get_products` β so there's no confusion about which store you're querying.
You can also run the same store twice with different policies (e.g. daily `safe` plus an occasional `developer` entry) by duplicating the block and setting `OPENCART_MCP_POLICY` per instance.
---
## Tested with
| Component | Versions |
|-----------|----------|
| OpenCart | 3.0.3.2 β 3.0.5.0 (any 3.x should work) |
| PHP | 5.6+ (server-side) |
| Python | 3.10+ (local machine) |
| Journal3 | 3.x (optional β everything works without it) |
| OpenCart forks | ocStore / LiveStore 3.x (SQL-compatible; for Technics-theme tools see the [livestore-mcp](https://github.com/Penikov/livestore-mcp) fork) |
| Hosting | VPS, dedicated servers, shared hosting with SSH or remote MySQL |
| Clients | Claude Code CLI, VS Code extension, JetBrains extension |
Used daily on production stores with 100+ products, thousands of orders, and Journal3 theme.
---
## Troubleshooting
### SSH connection fails
```bash
# Test SSH works
ssh your-user@your-server "echo ok"
# Test PHP is available
ssh your-user@your-server "echo '<?php echo 1;' | php"
```
If SSH needs a password instead of a key:
```bash
ssh-copy-id -i ~/.ssh/id_ed25519.pub your-user@your-server
```
### Empty results
- Run the [test script](#3-test-the-connection) to check credentials
- `OPENCART_ROOT` should point to the directory containing `index.php`
- `OPENCART_STORAGE` should match `DIR_STORAGE` in your `config.php`
### cPanel / shared hosting
cPanel prints `tput: No value for $TERM` warnings over SSH. The server filters these automatically.
### Slow queries
Default timeout is 30 seconds. If queries are slow, check if SSH goes through a VPN (adds latency) or if the server is under load.
### Journal3 tables not found
Normal if you're not running Journal3. The J3 tools return empty results instead of errors.
### Common path issues
| Hosting | Typical OPENCART_ROOT | Typical OPENCART_STORAGE |
|---------|----------------------|-------------------------|
| cPanel | `/home/user/public_html` | `/home/user/oc_storage` |
| Plesk | `/var/www/vhosts/domain/httpdocs` | Above web root |
| Custom VPS | `/var/www/html` or `/var/www/opencart` | Varies |
Check your `config.php` β both `DIR_APPLICATION` and `DIR_STORAGE` are defined there.
---
## Roadmap
- [ ] OpenCart 4.x support
- [x] Direct MySQL transport + language auto-detection *(v0.7.0)*
- [x] Coupon and voucher management tools *(v0.6.0)*
- [x] Order status update tool *(v0.6.0)*
- [ ] Bulk product import/export
- [ ] Customer group management
- [x] Dashboard summary tool (one prompt, full store overview) *(v0.6.0)*
Got a feature request? [Open an issue](https://github.com/chrisbray85/opencart-mcp/issues).
---
## Changelog
See [releases](https://github.com/chrisbray85/opencart-mcp/releases) for full history.
### 0.7.0
- Direct MySQL transport (pymysql) when `OPENCART_SSH_HOST` is empty β file/cache tools refuse in this mode
- Storefront `language_id` auto-detected from `config_language` (override with `OPENCART_LANGUAGE_ID`) β fixes hardcoded `language_id = 1` on non-English stores
- Revenue queries now also exclude canceled-reversal, chargeback, and voided orders
- First unit tests (`tests/test_config.py`)
---
## Contributing
Issues and PRs welcome. If you're running this on a hosting setup or OpenCart version not listed above, let us know what works and what doesn't.
Thanks to the contributors so far:
- [@IceDBorn](https://github.com/IceDBorn) β DDEV support and the Nix flake
- [@ClayRabbit](https://github.com/ClayRabbit) β configurable SSH port and full `config.php` DB fallback
- [@Penikov](https://github.com/Penikov) β direct MySQL transport and language auto-detection; maintains the [livestore-mcp](https://github.com/Penikov/livestore-mcp) fork for LiveStore + Technics stores
## Support
Built and maintained in evenings. If it saves you time on a store, a coffee helps keep
the tools coming:
[](https://buymeacoffee.com/chrisbray85)
## License
MIT β see [LICENSE](LICENSE) for details.
TDQS
Scored across 42 tools
Most tools target distinct resources (products, orders, coupons, settings) with clear read/write separation. Minor overlaps exist: dashboard vs sales_summary both report revenue, and the three settings tools (OpenCart vs Journal3 theme vs skin) could confuse without Journal3 familiarity.
The set predominantly uses a consistent verb_noun convention (get_*, update_*, list_*, create_*). A few exceptions like query, dashboard, sales_summary, and clear_cache break the pattern but remain readable and unambiguous.
42 tools is heavy for any single server, exceeding the 25-tool threshold where agents may struggle to scan the surface. The broad domain (OpenCart core, Journal3 theme, DB, VPS files) justifies many tools, but the count is still on the high side.
Read coverage is broad and write operations cover updates for products, categories, settings, SEO, and content, with run_sql as a fallback for missing writes. However, no create/delete tools exist for products, categories, customers, or vouchers, which are notable gaps for full lifecycle management.