Skip to main content
Glama
label-printer-mcp

label-printer-mcp

label-printer-mcp

Print a 4x6 shipping label from a PDF — as a CLI, a Python library, or an MCP server.

A return label turns up as a 4x6 page, or as a US Letter sheet with the label in one corner and a packing slip below it, or sideways. This finds the label, crops to it losslessly, checks it really is 4x6, and prints it at exactly 100% on a 4x6 thermal printer.

Why it crops instead of scaling

A carrier's barcode encodes the tracking number in the width of its bars. Scale the label — or rasterise and resample it — and you can get a parcel whose barcode a scanner won't read, with nothing wrong on screen to warn you. It gets rejected at the counter.

So the crop here is a CropBox change: the page keeps every object it had and simply shows a smaller window onto them. Vector artwork stays vector and prints at whatever resolution the printer has. Nothing is resampled, ever, and printing passes print-scaling=none so CUPS doesn't helpfully fit the label to the media.

A test pins this: it counts the barcode's vector drawings after the crop. A rasterising crop would leave one image and zero drawings, and would pass a size check while producing a label a scanner may refuse.

Related MCP server: Open Xprinter MCP

Why it refuses

Finding the label on a letter sheet is a heuristic. Candidates are scored on aspect ratio alone — "biggest block" and "most ink" both pick the packing slip on a busy sheet, while a 4x6 label is the only thing on it with a 1.5 ratio and a short side over three inches.

When nothing scores well it refuses and says what it saw, with measurements, rather than cropping to the wrong thing:

no region looks like a 4x6 label. Closest: 3.41x8.01in (aspect 2.35), 8.5x11.0in (aspect 1.29).
A 4x6 has aspect 1.50 and a short side of 4.0in; refusing rather than cropping to the wrong thing.
that region is 3.2x4.8in, not 4.0x6.0in (aspect is right, size is not). Scaling it would corrupt the
barcode, so nothing was written. Print it as-is only if you mean to.

Paper is cheap; a mislabelled return is not.

Install

python3 -m venv .venv
.venv/bin/pip install -e .          # or: pip install pymupdf mcp

CLI

python -m labelprint inspect   label.pdf              # what it is, and where the label looks to be
python -m labelprint normalise label.pdf out.4x6.pdf  # crop to the label; --page N for a later page
python -m labelprint preview   label.pdf out.png      # look before you print
python -m labelprint queues                           # the print queues this machine has
python -m labelprint print     out.4x6.pdf MyPrinter  # DRY RUN: prints the command, sends nothing
python -m labelprint print     out.4x6.pdf MyPrinter 1 --live

print is a dry run unless you pass --live. Everything returns JSON.

MCP server

{
  "mcpServers": {
    "label": {
      "command": "/path/to/label-printer-mcp/.venv/bin/python",
      "args": ["/path/to/label-printer-mcp/server.py"]
    }
  }
}

Tools: inspect, prepare, preview, printers, send. send is a dry run unless called with live=True, so an assistant has to say explicitly that it means to move paper — and should show you the preview first.

Library

from pathlib import Path
import labelprint

r = labelprint.normalise(Path("return.pdf"), Path("out.4x6.pdf"))
if r.ok:
    print(r.size_in)                                    # [4.0, 6.0]
    labelprint.print_label(Path(r.out), "MyPrinter", dry_run=False)
else:
    print(r.reason)                                     # why it refused, with measurements

Printer setup

Add the printer in System Settings → Printers & Scanners, then:

lpstat -p                    # the queue name
lpoptions -p <queue> -l      # what options it actually supports

That second command matters. A CUPS queue silently ignores options it doesn't advertise, which looks exactly like them working — so check that your printer really takes media=Custom.4x6in and print-scaling before trusting a label that came out looking fine.

Tests

.venv/bin/python test_labelprint.py

15 tests on synthetic fixtures shaped like the real thing: a 4x6 page, a label on a letter sheet, a label above a packing slip (the case the finder exists for), a landscape label, an undersized label, a packing slip alone, a blank page. The fixtures are synthetic on purpose — a real return label carries somebody's address and tracking number.

Requirements

macOS or Linux with CUPS (lp, lpstat), Python 3.10+, and PyMuPDF. The MCP server additionally needs the MCP SDK (v2+).

Licence

MIT.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      C
      maintenance
      Enables printing PNG labels to a nearby thermal label printer over local serial or Bluetooth, with tools for rendering previews, checking printer status, and printing with confirmation.
      5
      1
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables AI clients to prepare and preview labels, then safely submit physical print jobs to an Xprinter XP-330B through local stdio or authenticated remote access, with status, cancellation, and retry receipts.
      MIT
    • A
      license
      A
      quality
      B
      maintenance
      Enables printing labels whose layout adapts to the roll reported by a CUPS/IPP label printer, as well as PDF, Markdown, and HTML documents, with preview, copies, duplex, page ranges, and print-job management.
      7
      MIT
    • A
      license
      A
      quality
      B
      maintenance
      Enables AI agents to print HTML, web pages, PDFs, and images to local printers by bridging to an existing client over WebSocket, including previews, printer discovery, batch jobs, and customizable PDF/print settings.
      16
      184 npm
      MIT