[ odoo-mcp-server ]
Da Planet Security  ·  C99/BCHS MCP server for Odoo 15+ XML-RPC
v1.2.0 C99 nob.h build OPA / Rego gates CycloneDX + SPDX KICS SAST BSD-2-Clause WASM + native
4MCP Tools
2Build Targets
4Rego Gates
0Runtime Deps
1#ifdef

What this is

A Model Context Protocol server written in C99 using the BCHS stack (BSD, C, Hypermedia, SQL). Connects any MCP-capable LLM to your Odoo 15/16/17 instance over its native XML-RPC API.

One source tree produces two artifacts from a single nob.c build driver — a native FreeBSD ELF binary and a wasm32-wasi module for Cloudflare Workers. The only platform-specific code is a single #ifdef __wasm__ in net.h.

Claude Desktop
─ stdio/HTTP ─▶
odoo-mcp-server (ELF)
─ XML-RPC ─▶
dapla.net Odoo
Claude.ai / online LLM
─ HTTPS/MCP ─▶
CF Workers + odoo-mcp.wasm
─ XML-RPC ─▶
dapla.net Odoo

MCP Tools

search_read_records
model, domain?, fields?, limit?, offset?, order?
Search any Odoo model with an optional domain filter and return specified fields. Limit capped at 500. Supports pagination and sort order.
get_model_fields
model, attributes?
Introspect the field schema for any Odoo model via fields_get. Returns field names, types, labels, and required flags. Use before querying an unfamiliar model.
create_record
model, values{}
Create a new record in any Odoo model. Returns the new integer ID. Empty values dict is rejected before the RPC call.
update_record
model, record_ids[], values{}
Write field values to one or more records. Supports bulk update by passing multiple IDs. Both IDs and values are validated before the call.

Build

Prerequisites

FreeBSD/BSD: pkg install kcgi libressl  ·  wasi-sdk for WASM target  ·  cc (clang or gcc)

Native ELF

shell
# Bootstrap the build driver once
cc nob.c -o nob

# Build native FreeBSD binary
./nob

# Run
env ODOO_URL=https://dapla.net \\
    ODOO_DB=your_db \\
    ODOO_USER=you@dapla.net \\
    ODOO_API_KEY=your_api_key \\
    ./odoo-mcp-server

WASM (Cloudflare Workers)

shell
# Build WASM module
cc -Dwasm nob.c -o nob && WASI_SDK=/opt/wasi-sdk ./nob
# → odoo-mcp.wasm

# Deploy via Terraform (wrangler called via local-exec)
cd terraform/
terraform init && terraform apply

The single #ifdef boundary

net.h
/* Only this file knows about the target */
#ifdef __wasm__
  /* CF Worker JS shim exports http_fetch → Workers fetch() */
  __attribute__((import_module("env"), import_name("http_fetch")))
  extern int wasm_http_fetch(...);
  #define net_http_post(...) wasm_http_fetch(...)
#else
  /* BSD sockets + libtls — implemented in net.c */
  int net_http_post(...);
#endif

Deployment

stdio/local works out of the box. For Claude.ai and other online LLMs the WASM module runs on Cloudflare Workers behind your dapla.net WAF rules.
Platform
Notes
Status
Cloudflare Workers
WASM target via wasi-sdk. Terraform cloudflare_worker + local-exec wrangler until provider issue #6852 (wasm_module) is fixed. Lives at WAF layer — rate limiting, Bot Management, Zero Trust all apply.
✓ Primary
FreeBSD jail (dapla.net)
Native ELF behind HAProxy. Same jail pattern as OPDS server. pledge/capsicum sandbox, no WASM runtime needed.
✓ Local
GCP Cloud Run
Native ELF in scratch container. cloudbuild.yaml not included — use the Dockerfile pattern and GCP Secret Manager for credentials.
~ Fallback

Environment variables

ODOO_URL=https://dapla.net   # base URL, no trailing slash
ODOO_DB=your_database_name    # Odoo database name
ODOO_USER=you@dapla.net       # login email
ODOO_API_KEY=your_api_key     # Settings → Technical → API Keys

Claude Desktop config

~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "odoo": {
      "command": "/path/to/odoo-mcp-server",
      "env": {
        "ODOO_URL":     "https://dapla.net",
        "ODOO_DB":      "your_db",
        "ODOO_USER":    "you@dapla.net",
        "ODOO_API_KEY": "your_api_key"
      }
    }
  }
}

Policy gates

All four Rego policies evaluated by OPA before any deploy. Every gate uses --fail-defined violations[_] — any finding blocks CI.

Policy
Input / blocks
c_quality.rego
cppcheck SARIF. Blocks: null-deref, buffer OOB, use-after-free, uninit reads, format-string injection, banned APIs (strcpy/gets/sprintf/rand), style violations.
terraform_sast.rego
KICS SARIF (replaces Trivy/Checkov). Blocks: WAF disabled, HTTPS not enforced, TLS < 1.2, hardcoded credentials, plaintext secrets.
terraform_plan.rego
terraform plan JSON via conftest. Blocks: sensitive fields not marked sensitive, route outside mcp.dapla.net, compat date < 2024, Worker name violation, secret destroy without approval label.
sarif.rego
osv-scanner SARIF (from cdxgen CycloneDX SBOM). Blocks: CVE CVSS ≥ 7.0.

SBOM

cdxgen produces sbom.cdx.json (CycloneDX 1.6) and sbom.spdx.json (SPDX 2.3) on every CI run. osv-scanner ingests the CycloneDX SBOM for CVE scanning.

build-native
build-wasm
cdxgen SBOM
osv-scanner
cppcheck
KICS
conftest
OPA gate
tf apply

GitHub  ·  Releases  ·  CI  ·  Security