Skip to main content
Glama

lenses-mcp

MCP server for the Lens suite.

by svx · MIT Licensed

CI license

A Model Context Protocol 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.

Related MCP server: DvalinCode

Install

Nothing to install — run it straight from GitHub with Bun:

bunx github:srivtx/lenses-mcp#main

Or clone it and run from source:

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):

{
  "mcpServers": {
    "lenses": {
      "command": "bunx",
      "args": ["github:srivtx/lenses-mcp#main"]
    }
  }
}

Cursor

Add to ~/.cursor/mcp.json (or .cursor/mcp.json in a project):

{
  "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:

{
  "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

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:

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

Related MCP Connectors

Related MCP Servers