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).
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
"""Self-test for mcp-pos-print.
|
||||
|
||||
Runs the same code path as the MCP `print` tool (build_receipt + send_bytes)
|
||||
without spinning up an MCP server. Prints one self-documenting page covering
|
||||
every supported markdown feature plus a QR code.
|
||||
|
||||
Usage: .venv/bin/python test_server.py
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
from server import build_receipt, send_bytes, PRINTER_IP, PRINTER_PORT
|
||||
|
||||
|
||||
SELF_TEST_MD = """\
|
||||
# mcp-pos-print
|
||||
|
||||
## Self test
|
||||
|
||||
This page exercises every supported markdown feature. If anything here looks
|
||||
broken on the printout, the parser has a bug.
|
||||
|
||||
### Inline formatting
|
||||
|
||||
Plain text. **Bold text**. `inline code`. A [link to example](https://example.com)
|
||||
that gets rendered as text + url in parens. Mixed: this **bold spans `inline
|
||||
code` and continues** after the code.
|
||||
|
||||
### Lists
|
||||
|
||||
- First bullet, short.
|
||||
- Second bullet that runs long enough to wrap onto a continuation line so we
|
||||
can verify the hanging indent works as expected at 57 columns.
|
||||
- Third bullet with **bold** and `code`.
|
||||
|
||||
1. Numbered one.
|
||||
2. Numbered two with a longer body that exercises hanging-indent for the
|
||||
numbered variant which has a different prefix width.
|
||||
3. Numbered three.
|
||||
|
||||
### Blockquote
|
||||
|
||||
> Single-line quote.
|
||||
|
||||
> Long quote that needs to wrap. The pipe prefix should appear on the first
|
||||
> line; continuation lines indent under the text, not under the pipe.
|
||||
|
||||
### Horizontal rule
|
||||
|
||||
---
|
||||
|
||||
### Code block
|
||||
|
||||
```
|
||||
def hello(name):
|
||||
print(f"hi, {name}")
|
||||
return 42
|
||||
```
|
||||
|
||||
### Long line
|
||||
|
||||
This is a single very long paragraph line that should soft-wrap at the 57
|
||||
column boundary determined by Font B on 80mm thermal paper, demonstrating
|
||||
that the wrap algorithm prefers word boundaries over hard breaks.
|
||||
|
||||
### End
|
||||
|
||||
If all of the above rendered correctly you are good to go.
|
||||
"""
|
||||
|
||||
|
||||
def main() -> int:
|
||||
raw = build_receipt(
|
||||
markdown=SELF_TEST_MD,
|
||||
title="mcp-pos-print",
|
||||
timestamp=True,
|
||||
qr_urls=["https://git.sethpc.xyz/Seth/mcp-pos-print"],
|
||||
cut=True,
|
||||
)
|
||||
print(f"Built {len(raw)} bytes. Sending to {PRINTER_IP}:{PRINTER_PORT}...")
|
||||
try:
|
||||
send_bytes(raw)
|
||||
except Exception as e:
|
||||
print(f"FAIL: {type(e).__name__}: {e}", file=sys.stderr)
|
||||
return 1
|
||||
print("OK — check the printer.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user