Files
Mortdecai 421c3c9660 feat: initial MCP server for printing markdown to Epson TM-m30
Single tool `print(markdown, title?, timestamp?, qr_urls?, cut?)`. Hand-rolled
line-by-line markdown parser translates headings/bold/code/lists/blockquotes/
HR/links to ESC/POS, then sends raw bytes via TCP to 192.168.0.184:9100.

Mirrors mcp-gemma4 layout (stdio server, single server.py, mcp package).
2026-05-25 16:51:17 -04:00

83 lines
3.1 KiB
Markdown

# mcp-pos-print
MCP server that prints markdown to the Epson TM-m30 receipt printer at `192.168.0.184:9100`. Mirrors the [`mcp-gemma4`](../mcp-gemma4/) layout.
See `~/bin/POS_PRINT.md` for the printer reference (Font B columns, native QR, troubleshooting) and `DECISIONS.md` for design rationale.
## Tool
`print(markdown, title?, timestamp?, qr_urls?, cut?)`
- **`markdown`** *(required)* — body to print. Supports `# / ## / ###` headings, `**bold**` and `` `code` `` inline, fenced ```` ``` ```` code blocks (no wrap), `---` / `***` horizontal rules, `-` / `*` / `1.` lists, `> blockquotes`, `[text](url)` links, blank lines. Long lines word-wrap at 57 cols.
- **`title`** — bold centered header above the body.
- **`timestamp`** — bool, default `true`. Prints `Mon, May 25 14:32 ET` under title.
- **`qr_urls`** — list of URLs printed as native hardware QR codes at the end.
- **`cut`** — bool, default `true`. Auto-cut paper.
Returns `Printed N bytes to 192.168.0.184:9100.` on success, or a clear error string if the printer is unreachable.
## Install
```bash
cd ~/bin/mcp-pos-print
python3 -m venv .venv
.venv/bin/pip install -e .
```
Register in `~/.claude.json` (global scope):
```json
{
"mcpServers": {
"pos-print": {
"command": "/home/claude/bin/mcp-pos-print/.venv/bin/python",
"args": ["/home/claude/bin/mcp-pos-print/server.py"]
}
}
}
```
Restart Claude Code or run `/mcp` to reconnect.
## Test
```bash
.venv/bin/python test_server.py
```
Prints a self-test page covering headings, inline formatting, lists, code blocks, blockquotes, horizontal rules, and a QR code. Requires the printer reachable at `192.168.0.184:9100`.
## Config
- `POS_PRINTER_IP` — default `192.168.0.184`
- `POS_PRINTER_PORT` — default `9100`
- `POS_PRINTER_TIMEOUT` — TCP timeout in seconds, default `10`
## Markdown subset
Hand-rolled line-by-line parser, no markdown-it-py dependency. Supported:
| Markdown | Output |
|---|---|
| `# Heading` | bold + 2x size, centered |
| `## Heading` / `### Heading` | bold, left |
| `**bold**` inline | bold run |
| `` `code` `` inline | passed through (already monospace) |
| ```` ```fenced``` ```` | preserved verbatim, no wrap; `»` marks chars truncated past col 57 |
| `---` / `***` / `___` | thin separator across full width |
| `- item` / `* item` | `* item`, hanging-indented continuation |
| `1. item` | `1. item`, hanging-indented continuation |
| `> quote` | `\| ` prefix |
| `[text](url)` | `text (url)` |
| blank line | blank line |
| anything else | word-wrapped at 57 cols |
Not supported (by design): images, tables, nested lists, footnotes, HTML. Use `qr_urls` for QR codes — there's no QR markdown syntax.
## Troubleshooting
- **`cannot reach printer`** — power, network, paper, or ARP issue. `ping 192.168.0.184` first.
- **`timed out`** — same. The TM-m30's WiFi sometimes drops ARP entries; a ping usually re-resolves.
- **Garbled output** — most likely you mixed an external script that uses Font A; this server hard-codes Font B (57 cols). If you're seeing it from `print`, file a bug.
- **No paper feed at end** — make sure `cut` isn't explicitly `false`.