odse-mcp
by AsobaCloud
README.md
# <img src="https://raw.githubusercontent.com/AsobaCloud/odse-mcp/master/docs/odse.svg" alt="ODS-E" width="36" height="36" align="bottom" /> ODS-E Conversion
MCP server for [ODS-E](https://github.com/AsobaCloud/odse) energy telemetry conversion. AI coding/data agents call its tools over the [Model Context Protocol](https://modelcontextprotocol.io/) to convert OEM payloads into ODS-E records and validate them via the published [`odse`](https://pypi.org/project/odse/) Python package.
[](https://www.npmjs.com/package/@asobacloud/odse-mcp)
[](https://github.com/AsobaCloud/odse-mcp/actions/workflows/ci.yml)
[](LICENSE)
The installed **`odse`** package is the source of truth for OEM support, transform behavior, and validation semantics.
## Quick start
Set `ODSE_WORKSPACE` to an absolute path used when resolving relative `payload_file` values. If omitted, the server uses its process working directory. The server speaks MCP over **stdio** (no HTTP port).
**Prerequisite:** `python3` on `PATH` must be able to `import odse` (`pip install odse`).
### Cursor
Add to `~/.cursor/mcp.json` or the project `.cursor/mcp.json`:
```json
{
"mcpServers": {
"odse": {
"command": "npx",
"args": ["-y", "@asobacloud/odse-mcp"],
"env": {
"ODSE_WORKSPACE": "/absolute/path/to/your/data"
}
}
}
}
```
Restart Cursor (or reload MCP servers), then ask the agent to convert or validate ODS-E data.
### Claude Code
CLI (user scope):
```bash
claude mcp add --transport stdio --scope user \
--env ODSE_WORKSPACE=/absolute/path/to/your/data \
odse -- npx -y @asobacloud/odse-mcp
```
Or put the same JSON under `mcpServers` in project `.mcp.json` (team-shared) or `~/.claude.json` (user-wide):
```json
{
"mcpServers": {
"odse": {
"command": "npx",
"args": ["-y", "@asobacloud/odse-mcp"],
"env": {
"ODSE_WORKSPACE": "/absolute/path/to/your/data"
}
}
}
}
```
Verify with `claude mcp list`. Project `.mcp.json` servers need approval the first time you open the repo in Claude Code.
### Codex
CLI:
```bash
codex mcp add odse --env ODSE_WORKSPACE=/absolute/path/to/your/data -- npx -y @asobacloud/odse-mcp
```
Or edit `~/.codex/config.toml` (or project `.codex/config.toml` in a trusted project):
```toml
[mcp_servers.odse]
command = "npx"
args = ["-y", "@asobacloud/odse-mcp"]
[mcp_servers.odse.env]
ODSE_WORKSPACE = "/absolute/path/to/your/data"
```
Codex CLI, the IDE extension, and the ChatGPT desktop Codex host share this config.
### Run directly
```bash
pip install odse
npx -y @asobacloud/odse-mcp
```
## Tools
| Tool | Runtime | What it does |
|------|---------|----------------|
| `ListSupportedOEMs` | `python3` | Returns OEM source keys from the installed `odse` transformer registry, plus `odse.__version__`. |
| `ConvertToODSE` | `python3` | Calls `odse.transformer.transform` on raw CSV/JSON text or a file. Optional `source`, `asset_id`, `timezone`, `timeout`. If `source` is omitted, OEM is auto-detected from payload **content** only. |
| `ValidateODSERecord` | `python3` | Calls `odse.validate_batch` on a JSON **array** of records. |
### ConvertToODSE
| Argument | Description |
|----------|-------------|
| `payload` | Raw telemetry text (CSV or JSON) |
| `payload_file` | Path to a telemetry file (relative paths resolve against `ODSE_WORKSPACE`) |
| `source` | OEM key (e.g. `huawei`); omit to auto-detect |
| `asset_id` | Optional asset id forwarded to `odse.transform` |
| `timezone` | Optional timezone forwarded to `odse.transform` (e.g. `+02:00`) |
| `timeout` | Timeout in ms (default `120000`, max `600000`) |
Example (explicit source):
```json
{
"name": "ConvertToODSE",
"arguments": {
"source": "huawei",
"payload": "Time,Active Power(kW),Inverter State\n2024-01-01 12:00:00,12.0,0\n"
}
}
```
Successful responses are JSON on the tool text channel, for example:
```json
{
"records": [
{
"timestamp": "2024-01-01T12:00:00Z",
"kWh": 1.0,
"error_type": "normal",
"error_code": "0"
}
],
"count": 1,
"source": "huawei",
"odse_version": "0.8.2"
}
```
Live-fetch arguments (`url`, `headers`, `body`, …) are rejected: not part of this release.
### ValidateODSERecord
Pass `records` as a **JSON string** of an array of objects. Optional `level` is forwarded to `odse.validate_batch` (default `schema`).
```json
{
"name": "ValidateODSERecord",
"arguments": {
"records": "[{\"timestamp\":\"2024-01-01T12:00:00Z\",\"kWh\":1.0,\"error_type\":\"normal\"}]"
}
}
```
Validation follows **`odse.validate_batch`** package semantics (not a separate JSON Schema checker). Extra properties and numeric bounds behave as the installed `odse` version defines them.
Tool exit code `1` becomes `isError: true` on the MCP result. Unexpected crashes are reported as errors.
## Requirements
| Runtime | Required for |
|---------|----------------|
| Node.js ≥ 22 | MCP server |
| Python 3 + [`odse`](https://pypi.org/project/odse/) | All tools (`pip install odse`) |
## Architecture
```
rules/*.json → tool schemas + execution specs
scripts/*.py → odse wrappers (list / convert / validate)
src/loader.ts → bundled rules
src/runner.ts → spawn, timeouts, exit-code → isError
src/index.ts → MCP stdio server
bin/cli.js → npx / bin entrypoint
```
## Development
```bash
git clone https://github.com/AsobaCloud/odse-mcp.git
cd odse-mcp
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install odse
npm install
npm run build
npm test
```
| Script | Purpose |
|--------|---------|
| `npm run build` | Compile TypeScript → `dist/` |
| `npm test` | E2E via real MCP `Client` + fixture assertions |
| `npm start` | Run the server on stdio |
| `npm run dev` | `node --watch` on `dist/` |
Local MCP config (instead of npx):
```json
{
"mcpServers": {
"odse": {
"command": "node",
"args": ["/absolute/path/to/odse-mcp/dist/index.js"],
"env": {
"ODSE_WORKSPACE": "/absolute/path/to/your/data",
"PATH": "/absolute/path/to/odse-mcp/.venv/bin:/usr/bin:/bin"
}
}
}
}
```
Ensure the `PATH` (or environment) makes `python3` resolve to an interpreter that can `import odse`.
## Releasing
CI runs build + E2E on every push/PR (Node 22 + `pip install odse`). To publish a new version:
1. Bump `version` in `package.json`
2. Commit, push, and create a GitHub Release (`gh release create vX.Y.Z --generate-notes`)
3. `.github/workflows/publish.yml` publishes to npm (Trusted Publisher / OIDC, or `NPM_TOKEN` if configured)
## License
MIT © [Asoba](https://asoba.co)
TDQS
A4/5.0
Scored across 3 tools
Disambiguation5/5
Each tool has a clearly distinct purpose: conversion, listing supported OEMs, and validation. There is no functional overlap between them, making misselection unlikely.
Naming Consistency5/5
All tool names follow the same Verb+Object pattern (ConvertTo, ListSupported, Validate) with consistent PascalCase. The naming convention is uniform and predictable.
Tool Count5/5
With 3 tools, the server is well-scoped for its stated purpose of converting and validating ODS-E records. Each tool is necessary and the count is within the ideal 3-15 range.
Completeness5/5
The tool surface covers the core workflow: convert data, list supported sources, and validate output. There are no obvious missing operations for the declared domain of ODS-E conversion and validation.
Maintenance
ActivitySlowing
ResponsivenessNo issues