Skip to main content
Glama
README.md
# cml2-mcp

Thin MCP server for Cisco CML2.

It deliberately avoids mirroring the CML API as typed (Pydantic) tools — every
prior generation that did so broke when the controller shipped a schema change.
Instead this server exposes only:

- `cml_openapi(refresh=False)` — fetch CML's live `openapi.json` (cached 24 h).
- `cml_api(method, path, body=None)` — generic authenticated REST call after
  `/api/v0`. Re-authenticates and retries once on HTTP 401.
- Resource `cml://openapi.json` — same content as `cml_openapi()`.

The model is expected to read the OpenAPI spec first and then craft calls.

## Configuration

Required environment:

| Var             | Example                          |
| --------------- | -------------------------------- |
| `CML_URL`       | `https://cml.example.net/`       |
| `CML_USERNAME`  | `admin`                          |
| `CML_PASSWORD`  | `…`                              |

Optional:

- `CML_VERIFY_SSL=true` — verify TLS (default: off; CML often uses self-signed certs).
- `CML_CACHE_DIR` — token / openapi cache directory (default: `~/.cache/cml/`).

The token is written to `$CML_CACHE_DIR/token` with mode `0600`.

## Running

With [uv](https://docs.astral.sh/uv/) directly from the source tree:

```sh
uv run cml2-mcp
```

After publishing to PyPI (or via `uv tool install .`):

```sh
uvx cml2-mcp
```

## Claude Desktop / Claude Code registration

Wrap the command so the password is fetched from a secure store rather than
appearing in plain text. Example with macOS Keychain:

```json
{
  "mcpServers": {
    "cml2": {
      "command": "sh",
      "args": [
        "-c",
        "CML_PASSWORD=$(security find-generic-password -a <account> -s <service> -w) exec uvx cml2-mcp"
      ],
      "env": {
        "CML_URL": "https://cml.example.net/",
        "CML_USERNAME": "admin"
      }
    }
  }
}
```

For Claude Code:

```sh
claude mcp add cml2 -- sh -c 'CML_PASSWORD=$(security find-generic-password -a <account> -s <service> -w) exec uvx cml2-mcp'
```

(Set `CML_URL` / `CML_USERNAME` in the same env or via `claude mcp add ... -e KEY=VAL`.)

TDQS

A3.9/5.0

Scored across 2 tools

Disambiguation5/5

The two tools have clearly distinct purposes: cml_api makes arbitrary REST calls to the CML2 API, while cml_openapi fetches the OpenAPI specification. There is no overlap in functionality.

Naming Consistency5/5

Both tools use the consistent prefix 'cml_' followed by a descriptive snake_case name ('api' and 'openapi'), following a clear pattern.

Tool Count2/5

With only two tools, the server is extremely minimal for what appears to be a full API wrapper. Most use cases would require the user to manually parse the OpenAPI spec and craft REST calls, making the tool set feel incomplete.

Completeness2/5

The server provides only a low-level API call tool and a spec fetcher, lacking any higher-level operations (e.g., CRUD for labs, devices, etc.). Users must implement all logic themselves, which is a significant gap for a CML2 integration.

Maintenance

ActivityInactive
ResponsivenessNo issues