Caddis
MCP server for the LCM2M Caddis VM2M API
Ask AI about Caddis
Powered by Claude · Grounded in docs
I know everything about Caddis. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
lcm2m-caddis-mcp
An MCP server that exposes the LCM2M Caddis VM2M API to LLM tools like Claude Desktop, Claude Code, and Cursor. Read-only wrappers over equipment, runs, cycles, telemetry, alarms, and more — served as CSV/YAML so LLM context stays cheap.
Requirements
- An LCM2M account (username and password)
- Node.js 25+ or Docker
Use with an MCP client
The server speaks MCP over stdio and works with any MCP-compatible client —
Claude Code, Claude Desktop, Cursor, Windsurf, VS Code (Copilot Chat), Zed,
Continue, Cline, Goose, and others. Configure your client to launch
npx -y @lcm2m/caddis-mcp with CADDIS_USERNAME and CADDIS_PASSWORD in its
env. Concrete examples for the two most common config styles follow.
Claude Code (CLI)
claude mcp add caddis \
--env CADDIS_USERNAME=you@example.com \
--env CADDIS_PASSWORD='your-password' \
-- npx -y @lcm2m/caddis-mcp
Claude Desktop, Cursor, and similar (JSON config)
Most clients accept an mcpServers block in a JSON config file — for example
claude_desktop_config.json (Claude Desktop) or .cursor/mcp.json (Cursor):
{
"mcpServers": {
"caddis": {
"command": "npx",
"args": ["-y", "@lcm2m/caddis-mcp"],
"env": {
"CADDIS_USERNAME": "you@example.com",
"CADDIS_PASSWORD": "your-password"
}
}
}
}
Other clients (Windsurf, VS Code, Zed, Continue, Cline, etc.) use similar formats — consult your client's MCP docs for the exact config path and schema. Restart the client after editing config.
Configuration
| Variable | Default | Description |
|---|---|---|
CADDIS_USERNAME | (required) | LCM2M account username/email |
CADDIS_PASSWORD | (required) | LCM2M account password |
CADDIS_COMPANY_ID | (auto) | Required if your user belongs to multiple companies |
CADDIS_MAX_RETRIES | 3 | Max 429 retries per request |
CADDIS_MAX_RETRY_WAIT_MS | 30000 | Max total wait budget per request |
Available tools
All tools are read-only and prefixed with caddis_. Each maps 1:1 to a VM2M
route; responses are CSV (uniform rows) or YAML (nested).
- Company:
get_company - Devices:
list_devices,get_device - Equipment:
list_equipment,get_equipment,get_equipment_utilization,get_equipment_schedule,get_equipment_cycles,get_equipment_statuslogs,get_equipment_telemetry,get_equipment_shift_history,list_equipment_excessive_downtimes,get_equipment_excessive_downtime - Org units / tree:
get_org_unit,get_org_unit_schedule,list_org_unit_excessive_downtimes,get_tree - Alarms:
list_alarms - Tags:
list_tags,get_tag,list_tag_groups,get_tag_group - Runs:
list_runs,get_run,get_run_cycles - Status reasons:
list_status_reasons
Troubleshooting
Missing credentials—CADDIS_USERNAME/CADDIS_PASSWORDaren't reaching the child process. Withdocker run -e VAR,VARmust also be set in the parent shell.This user belongs to multiple companies…— setCADDIS_COMPANY_IDto one of the numeric IDs listed in the error.401 Unauthorized— bad creds. Test with:curl -X POST https://api.lcm2m.com/vm2m/sessions \ -H 'Content-Type: application/json' \ -d '{"username":"you@example.com","password":"...","company_id":1}'- Persistent
429— raiseCADDIS_MAX_RETRY_WAIT_MSor back off the client's call rate. - Blank Inspector page in Firefox — use Chrome/Brave/Edge, or
npx @modelcontextprotocol/inspector --cli ....
Alternative install
The MCP client examples above use npx. To run from Docker or a local clone
instead, build using one of the methods below and swap the command/args in
your client config.
Docker
git clone https://github.com/LCM2M/lcm2m-caddis-mcp.git
cd lcm2m-caddis-mcp
docker build --target runtime -t lcm2m-caddis-mcp .
Command string:
docker run -i --rm -e CADDIS_USERNAME -e CADDIS_PASSWORD lcm2m-caddis-mcp
Local Node
git clone https://github.com/LCM2M/lcm2m-caddis-mcp.git
cd lcm2m-caddis-mcp
npm install
npm run build
Command string: node /absolute/path/to/lcm2m-caddis-mcp/dist/index.js
How it works
- Auth: first call hits
POST /vm2m/sessions→ JWT, cached and proactively refreshed 30s before expiry. 401 triggers a single re-login + retry. TheAuthorizationheader carries the raw JWT (noBearerprefix). - Rate limiting: backend enforces 20 req/10s per endpoint and 60 req/10s per
user. On 429, the client parses
Retry-After, applies ±20% jitter, and retries up toCADDIS_MAX_RETRIES(capped byCADDIS_MAX_RETRY_WAIT_MS). - Response format: CSV when rows are uniform, YAML otherwise. The raw body is passed through — ~3× cheaper in tokens than equivalent JSON.
- Errors: 4xx responses surface as
isError: truetool results so the LLM can see the backend error body and recover; 5xx rethrow.
Development
# Create .env.local with CADDIS_USERNAME and CADDIS_PASSWORD (gitignored).
npm install
npm run dev # MCP Inspector web UI + tsx
npm test # node --test via tsx
npm run build # tsc -> dist/
npm run typecheck
npm run lint # biome check
npm run dev opens the MCP Inspector in a Chromium browser (Firefox has
rendering issues). Edit source, click Restart, re-run the tool.
Project layout
src/
index.ts # MCP server entry (stdio)
config.ts # zod env config
client.ts # CaddisApiClient: login, retry, rate-limit
tools/
schemas.ts # shared zod helpers + runTool error wrapper
index.ts # tool registration
wrappers/ # 1:1 VM2M route wrappers
composite/ # higher-level multi-call tools
