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

3.1 KiB

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

cd ~/bin/mcp-pos-print
python3 -m venv .venv
.venv/bin/pip install -e .

Register in ~/.claude.json (global scope):

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

.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.