lenses-mcp
by srivtx
README.md
# lenses-mcp
> MCP server for the Lens suite.
**by svx** · MIT Licensed
[](https://github.com/srivtx/lenses-mcp/actions/workflows/ci.yml)
[](LICENSE)
A [Model Context Protocol](https://modelcontextprotocol.io) server that exposes the five Lens-suite CLI tools to any MCP client (Claude Desktop, Cursor, etc.).
The Lens CLIs audit files **entirely offline** and return stable JSON. This server spawns the CLI, appends `--json`, captures stdout/stderr, and returns the parsed JSON as MCP text content. It never throws raw: non-zero exits come back as `isError: true` with the CLI's stderr and, when present, the parsed JSON.
## Tools
| Tool | Inputs | CLI |
| --- | --- | --- |
| `booklens_audit` | `path` (string), `fail_on?` (`error`\|`warning`\|`info`\|`none`) | `booklens audit <file> --json` |
| `booklens_fix` | `path` (string), `out?` (string), `dry_run?` (boolean) | `booklens fix <file> --json [-o <out>] [--dry-run]` |
| `officelens_audit` | `path`, `fail_on?` | `officelens <file> --json` |
| `odflens_audit` | `path`, `fail_on?` | `odflens <file> --json` |
| `iconlens_audit` | `path`, `fail_on?` | `iconlens <file> --json` |
| `waxseal_seal` | `archive`, `key`, `out?`, `created_at?` | `waxseal seal <archive> --key <pem> --json [--out <seal>] [--created-at <iso>]` |
| `waxseal_verify` | `archive`, `seal`, `public_key?`, `root?` | `waxseal verify <archive> -s <seal> --json [--public-key <pem>] [--root <hex>]` |
| `waxseal_inspect` | `archive` | `waxseal inspect <archive> --json` |
Exit codes from the CLIs: `0` ok, `1` findings, `2` bad input, `3` I/O. Any non-zero code is surfaced as an MCP error result.
> `iconlens` can read SVG from stdin in a terminal (`iconlens -`), but stdin is reserved by the MCP stdio transport, so over MCP always pass a file path.
## Install
Nothing to install — run it straight from GitHub with [Bun](https://bun.sh):
```sh
bunx github:srivtx/lenses-mcp#main
```
Or clone it and run from source:
```sh
git clone https://github.com/srivtx/lenses-mcp
cd lenses-mcp
bun install
```
## Use with an MCP client
### Claude Desktop
Add to `claude_desktop_config.json` (macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`):
```json
{
"mcpServers": {
"lenses": {
"command": "bunx",
"args": ["github:srivtx/lenses-mcp#main"]
}
}
}
```
### Cursor
Add to `~/.cursor/mcp.json` (or `.cursor/mcp.json` in a project):
```json
{
"mcpServers": {
"lenses": {
"command": "bunx",
"args": ["github:srivtx/lenses-mcp#main"]
}
}
}
```
To run from a local clone instead, use `"command": "bun"` with `"args": ["run", "/absolute/path/to/lenses-mcp/src/index.ts"]`.
### Any MCP client
Run `bunx github:srivtx/lenses-mcp#main` as a **stdio** server. It speaks newline-delimited JSON-RPC on stdin/stdout; all logs go to stderr.
## Command resolution and env overrides
By default each tool runs:
```
bunx github:srivtx/<tool>#main
```
The `#main` ref is **required** — a bare `github:` spec resolves whatever commit bunx has cached, which goes stale. The first call fetches the package over the network.
Set a `LENSES_*_BIN` variable to point at a binary you installed yourself (e.g. with the tool's `install.sh`) and skip the network entirely:
| Variable | Tool |
| --- | --- |
| `LENSES_BOOKLENS_BIN` | `booklens` |
| `LENSES_OFFICELENS_BIN` | `officelens` |
| `LENSES_ODFLENS_BIN` | `odflens` |
| `LENSES_ICONLENS_BIN` | `iconlens` |
| `LENSES_WAXSEAL_BIN` | `waxseal` |
When set, the override replaces the whole `bunx …` invocation:
```json
{
"mcpServers": {
"lenses": {
"command": "lenses-mcp",
"args": [],
"env": {
"LENSES_BOOKLENS_BIN": "/usr/local/bin/booklens",
"LENSES_WAXSEAL_BIN": "/usr/local/bin/waxseal"
}
}
}
}
```
An override that is an executable script (e.g. a local `.ts` CLI with a `#!/usr/bin/env bun` shebang) works too. A blank/whitespace value falls back to the `bunx` default.
## Development
```sh
bun test # unit tests for the command builders + validation
bun run typecheck # tsc --noEmit
bun run start # run the stdio server
```
Smoke-test the MCP handshake and one tool call by piping newline-delimited JSON-RPC into the server. Set the override to a local checkout so the call does not hit GitHub:
```sh
LENSES_ICONLENS_BIN=../svg-a11y/src/cli.ts bun run src/index.ts <<'EOF'
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"smoke","version":"0.0.0"}}}
{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"iconlens_audit","arguments":{"path":"/absolute/path/to/icon.svg","fail_on":"none"}}}
EOF
```
The server prints one JSON-RPC response per request line (the `initialize` result reports `serverInfo.name: "lenses-mcp"`, and the `tools/call` result carries the CLI's JSON). Calling a missing file returns `"isError": true` with the CLI's stderr. All diagnostics go to stderr, never stdout.
## Layout
```
src/tools.ts tool catalog, input validation, pure command builders
src/index.ts stdio MCP server (Server + StdioServerTransport)
tests/ bun test unit tests
```
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues