It reads `$metadata` and hands the model whatever the service declares — the entity sets, the function imports, and which operations each entity set admits to supporting — so an OData v2 or v4 endpoint becomes usable without anyone writing a tool for it. The choice worth making before you start is how many tools that produces: one user's pair of services came to 485 tools and the model reported no API available, which is the case `--universal` exists for, trading the self-documenting per-entity names for a single call that names its target in a parameter.
A single Go binary that takes one OData service URL, reads that service's `$metadata`, and generates MCP tools from what it finds there. It handles OData v2 and v4, and it understands SAP's dialect — the `sap:creatable`-style annotations that mark which operations an entity set allows, and the CSRF tokens SAP expects on writes. One process fronts one service; run several processes if you have several services. The default transport is stdio, which is what Claude Desktop uses, and `--transport http` or `--transport streamable-http` serve HTTP clients instead. It is a Go port of an earlier Python bridge, distributed as a compiled binary with no runtime to install, under the MIT License.
- Per-entity-set tools generated from metadata — `filter_{EntitySet}`, `count_{EntitySet}`, `search_{EntitySet}`, `get_{EntitySet}`, `create_{EntitySet}`, `update_{EntitySet}`, `delete_{EntitySet}`; each one appears only if the entity set declares it supports that operation
- Function imports as tools — each import in the service becomes an individual tool carrying the function's own name
- `odata_service_info` — metadata and capabilities of the service; when a hints entry matches the service URL it also returns `implementation_hints` with the known issues, workarounds and field formats recorded for that service
- OData query options on the read tools — `$filter`, `$select`, `$expand`, `$orderby`, `$top`, `$skip`, `$count`
- One-tool mode for services too large to fit — `--universal` replaces the per-entity tools with a single tool taking `action`, `target` and `params`, where action is one of list, get, create, update, delete, count and call. Measured 2026-01-31: SAP GWSAMPLE_BASIC went from 68 tools / ~16,287 tokens to 1 tool / ~765 tokens, Northwind v2 from 157 tools / ~37,260 tokens to 1 tool / ~912 tokens
- Write operations you can switch off at generation time — `--read-only` (`-ro`) hides create, update, delete and function imports; `--read-only-but-functions` (`-robf`) hides create, update and delete but keeps functions; `--enable` and `--disable` take operation letters (C create, S search, F filter, G get, U update, D delete, A actions, R read)
- Surface control — `--entities "Products,Categories,Order*"` and `--functions "Get*,Create*"` accept wildcards, and `--tool-shrink`, `--tool-prefix`, `--tool-postfix` and `--no-postfix` decide what the generated names look like
- `--claude-code-friendly` (`-c`) — strips the `$` from OData parameter names (`$filter` becomes `filter`, `$select` becomes `select`) so Claude Code CLI's stricter property-name validation accepts the schema; the server maps them back before it calls the service
- Response shaping — `--max-items` (default `100`, validated up to `10,000`), `--max-response-size` (default `5MB`), `--pagination-hints`, `--response-metadata`, `--convert-dates-from-sap`
- Inspection before you connect a client — `--trace` prints the generated tool list and exits, `--trace-mcp` writes a protocol log to the temp directory, `--verbose-errors` returns detailed error context
- A hints file — `hints.json` beside the binary, or `--hints-file`, or `--hint` on the command line; entries match service URLs by wildcard pattern and merge by priority, and ship with defaults for SAP OData services, SAP PO tracking and the public Northwind demo
One binary and one service URL. Pre-built binaries cover Linux (amd64), Windows (amd64) and macOS on both Intel and Apple Silicon; from source it is `go build -o odata-mcp cmd/odata-mcp/main.go`, and there is a Makefile and a Dockerfile (`docker build -t odata-mcp .`). The service URL goes in as a positional argument, as `--service`, or as `ODATA_SERVICE_URL` / `ODATA_URL`. Credentials depend on what the service wants: basic auth through `--user` and `--password` or through `ODATA_USERNAME` / `ODATA_PASSWORD`, cookies through `--cookie-file` (Netscape format) or `--cookie-string`, and anonymous services need neither; a `.env` file in the working directory is read as well. Under Claude Desktop the credentials belong in the config's `env` block — Claude Desktop does not read your system environment, and MCP servers there run locally with the same permissions Claude Desktop itself has. The HTTP transports require an authentication token in every case: `--mcp-token` (or `--mcp-token-file`) on localhost, that token plus `--tls`, `--tls-cert` and `--tls-key` anywhere else, and binding to `0.0.0.0` or `::` additionally requires `--allow-all-interfaces`. Stdio, the default, requires none of this.
Build from source — clone the repository and build it, then point your client at the binary
