luckjingle-mcp
This server lets Claude (or any MCP client) control one or more Bluetooth thermal label printers: scanning, configuring, and printing text/images/labels to them.
Scan for nearby BLE printers and probe their GATT services to verify compatibility
Add, list, select, remove, and manage multiple named printer profiles
Set the active printer address or tune per-printer options (paper width, density, font)
Print wrapped text with alignment/font-size control
Print image files (PNG/JPG/etc), optionally dithered, resized to paper width
Compose framed labels with reusable named borders, and save/reprint labels by name
Save and manage reusable label templates and border images
Check daemon availability and get instructions for starting the required background daemon
Provides tools for discovering, probing, adding, selecting, and printing to Bluetooth LE thermal label/sticker printers, including text and image printing with configurable width, density, and font options.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@luckjingle-mcpscan for my Bluetooth label printer and print a test label"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
luckjingle-mcp
An MCP server that lets Claude (or any MCP client) design and print things to cheap Bluetooth thermal label/sticker printers, and manage more than one of them - print to whichever one you select, or name one explicitly per job.
Printer support is pluggable: each printer family is a small driver (see
"Adding support for a new printer"
below). Right now there's one driver, luckprinter, covering "LuckPrinter
SDK" printers - the ones sold under names like NHOWIN, NDYIN,
C&Co 3128, DP-L1S, etc, that all run on the "LuckPrinter SDK" behind
the "Luck Jingle" phone app (159+ rebrand models share this SDK).
These printers don't publish an official protocol or SDK. luckprinter
talks to them using a protocol reverse engineered by the community by
decompiling the Android app (see NOTICE.md for credits/
sources): a standard ESC/POS GS v 0 raster image command plus a handful of
vendor-specific control commands. If your printer's BLE service exposes
characteristics ff01/ff02 under service ff00 (check with
probe_device, see below) it's likely compatible as-is - but see the
"Adding support for a new printer" section if a test print doesn't work,
since more than one incompatible protocol shares that same GATT layout.
Other servers in this repo
This repo also has netprint-mcp - a separate,
much simpler MCP server for printing documents (PDFs, images, etc) to a
regular network printer already set up on this machine, via CUPS. No BLE,
no daemon - see its own README for setup.
Related MCP server: Memobird MCP Server
Architecture
This is split into two processes, which is unfortunately necessary on macOS:
luckjingle-daemon- owns the actual Bluetooth connection. Must run as its own independent app (see "macOS Bluetooth permission" below), not as a subprocess of Claude.luckjingle-mcp- the MCP server Claude talks to. It's a thin client that forwards tool calls to the daemon overhttp://127.0.0.1:8765.
Requirements
Python 3.10+
macOS, Linux, or Windows with Bluetooth LE support (uses bleak)
Install
cd /path/to/iocane # wherever you cloned this repo
python3 -m venv .venv
.venv/bin/pip install -e .macOS Bluetooth permission (important)
On macOS, any process descending from an app that hasn't declared Bluetooth
usage in its Info.plist will hard-crash (not prompt) the instant it
touches CoreBluetooth. Since this MCP server runs as a subprocess of
whatever launched Claude, that includes it. macos_app/LuckJingleDaemon.app
works around this: it's a minimal app bundle that declares
NSBluetoothAlwaysUsageDescription, so macOS can show a normal permission
prompt for it instead.
You must start the daemon by opening this app directly - not from inside Claude/a terminal spawned by Claude:
open /path/to/iocane/macos_app/LuckJingleDaemon.app(The bundle's launcher script locates its own venv relative to itself, so
this works from whatever path you cloned the repo to. If the daemon doesn't
start and nothing obvious shows up, check
~/Library/Logs/luckjingle-daemon.log.)
The first time, macOS will show a Bluetooth permission prompt attributed to
python3.14 (or similar) - accept it. It's a background app (no dock icon,
no window) that just listens on 127.0.0.1:8765; leave it running whenever
you want to print. If you want it running automatically, add it to
System Settings → General → Login Items.
Known glitch: the permission dialog can sometimes get visually stuck (looks like clicking "Allow" does nothing). If that happens, check whether it actually took effect before assuming otherwise - macOS's own dialog rendering can lag behind the real permission state; killing and reopening the daemon app is usually enough to get a clean prompt.
On Linux/Windows this split isn't necessary, but the daemon/client
architecture works the same way regardless - just run luckjingle-daemon
however you'd normally run a background service.
Find and verify your printer
Power on the printer and make sure your phone isn't already connected to it via its own app (force-quit the app, or turn the phone's Bluetooth off temporarily) - a printer already connected elsewhere won't advertise for a new connection.
Ask Claude to run
scan_printers(or start the daemon andcurl -X POST http://127.0.0.1:8765/scan -d '{"timeout": 10}'). Results are sorted by signal strength - your printer is usually the strongest signal since it's right next to you, but cheap BLE modules often advertise a generic factory name (ours showed up asPPS1_6306_BLE, not any recognizable brand name) rather than the retail brand printed on the box.Before trusting a name-based guess, confirm it with
probe_device (address)- a compatible printer's GATT services should include:service 0000ff00-... with characteristics: 0000ff01-... (notify) 0000ff02-... (write)This UUID pattern is a common generic BLE-serial scheme also used by unrelated devices, so a name match alone isn't proof - but the service layout combined with a signal-strength match is a strong signal.
Save it: ask Claude to run
add_printer("<name>", "<address>")- e.g.add_printer("kitchen", "AA:BB:CC:DD:EE:FF")- orcurl -X POST http://127.0.0.1:8765/add_printer -d '{"name": "kitchen", "address": "..."}'. The first printer you add becomes the active one automatically. (If you only ever have one printer,set_printer_address(address)is a shorter equivalent that skips naming it.)
Printer profiles are saved to ~/.config/luckjingle-mcp/config.json and
reused for future print jobs, so you only need to do this once per printer.
Managing multiple printers
list_printers()- see every configured printer and which one is active.select_printer("<name>")- switch which printerprint_text/print_imageuse by default.print_text(..., printer="<name>")/print_image(..., printer="<name>")print to a specific printer for one job, without switching the active one.
set_printer_options(..., name="<name>")- tune width/density/font for a specific printer instead of the active one.remove_printer("<name>")- drop a configured printer. If it was active, another remaining one (if any) becomes active.
Register the MCP server with Claude Code
claude mcp add luckjingle -- /path/to/iocane/.venv/bin/luckjingle-mcpOr add directly to your MCP config (e.g. ~/.claude.json or .mcp.json):
{
"mcpServers": {
"luckjingle": {
"command": "/path/to/iocane/.venv/bin/luckjingle-mcp"
}
}
}This just needs to be done once; the daemon app still needs to be started separately each time you want to print (see above).
Tools
scan_printers(timeout=6.0)- list nearby BLE devices (name, address, signal strength)probe_device(address, timeout=15)- connect and list GATT services/characteristics, to verify a candidate device before adding itadd_printer(name, address, driver="luckprinter")- add or update a named printer profile; the first one added becomes activelist_printers()- list every configured printer and which is activeselect_printer(name)- switch the active printerremove_printer(name)- remove a configured printerset_printer_address(address)- shortcut for the single-printer case: updates the active printer's address (creating one named"default"if none exists yet)get_printer_config()- show the active printer's saved settingsset_printer_options(width, density, font_path, name=None)- tune print quality:widthin dots (384 is standard),density0-2 (light/normal/ dark, leave unset for the printer's own default);nametargets a specific printer instead of the active oneprint_text(text, font_size=24, align="left", printer=None)- print wrapped text;printertargets a specific printer for this job onlyprint_image(image_path, dither=True, printer=None)- print an image file, resized to the paper width and converted to black & white;printertargets a specific printer for this job onlyprint_label(text, font_size=24, align="center", border=None, dither=None, save_as=None, printer=None)compose and print a text label, optionally framed by a saved border (e.g. "Flux Capacitor" with the "dancing kittens" border);
ditheroverrides the automatic choice (dithered only when a border is present); passsave_asto also save it for reprinting later
save_label(name, text, font_size=24, align="center", border=None, dither=None)save a label without printing it, for
print_saved_labellater
list_labels()- list every saved labelremove_label(name)- delete a saved labelprint_saved_label(name, printer=None)- reprint a label saved withsave_labelorprint_label(..., save_as=...)save_border(name, image_path)- save an image as a reusable named border thatprint_label/save_labelcan frame text withlist_borders()- list every saved border's nameremove_border(name)- delete a saved borderget_daemon_info()- checks whether the daemon is reachable and explains how to start it if not
To print a custom design (a label with a logo, a QR code, a styled layout,
etc), have Claude generate a PNG with whatever tool/script fits, save it to
disk, then call print_image with that path.
Saved labels and borders
Labels are a saved (text, font size, alignment, border) combo you can reprint by name later without repeating everything - either save one up front with
save_label, or print now and save in the same call withprint_label(..., save_as="...").Borders are a saved image (e.g. a strip of clipart) that frames a label's text. Save one with
save_border("dancing kittens", "/path/to/ kittens.png"), then reference it by name:print_label("Flux Capacitor", border="dancing kittens"). The text always sits on a plain white panel in the middle so it stays legible regardless of the border's own colors - the border image itself is tiled to frame that panel, so a short repeating strip works better than one large single graphic.
Saved labels and border images live alongside the printer config, in
~/.config/luckjingle-mcp/ - config.json for the label/border metadata,
borders/ for the saved border image files themselves.
Troubleshooting
Any MCP tool call fails with a connection error: the daemon app isn't running - see "macOS Bluetooth permission" above.
Connects (printer's LED changes) but nothing physically prints: the printer probably isn't in the
luckprinter(LuckPrinter SDK) family this driver targets, or uses a variant that builds the image command differently. Runprobe_deviceto confirm the GATT layout matches, and checkPROTOCOL.mdin thermal-pocket-printer-basic for the full command reference if you need to adaptluckjingle_mcp/drivers/luckprinter.py, or write a new driver (see below).Printer turns itself off unexpectedly: this happened during development and turned out to be a bug in an earlier version of this driver (an "disable auto-shutdown" command that actually set the auto-shutdown timer to 0). If you see it again, check nothing is sending
10 FF 12 00 00.Prints too light/dark: set
density(0-2) viaset_printer_options.
Adding support for a new printer
Support for a printer family is a small "driver" object, not a fork of this
project. If probe_device shows a GATT layout this repo doesn't already
handle, or a compatible-looking printer just doesn't print correctly with
the luckprinter driver (see NOTICE.md - more than one incompatible
protocol shares that same ff01/ff02 layout, so a matching GATT layout
alone doesn't guarantee compatibility), here's how to add one:
Reverse-engineer the protocol. These printers don't publish an SDK, so this means decompiling the vendor's Android/iOS app (e.g. with JADX) and finding the BLE write calls, the same way NOTICE.md's sources did for
luckprinter. You're looking for: the GATT write/notify characteristic UUIDs, how the printer is woken/enabled, how a print darkness/density setting is sent (if supported), how a raster image command is framed, and how a job is fed/ended.Implement
PrinterDriver. Readluckjingle_mcp/drivers/base.pyfor the interface, andluckjingle_mcp/drivers/luckprinter.pyfor a complete worked example - it's under 40 lines. You only supply command bytes and GATT characteristics; image resizing, dithering, bitmap packing, chunked writes, and text rendering are all shared and already handled byPrinterSession.Register it by adding your driver class to the
DRIVERSdict inluckjingle_mcp/drivers/__init__.py, keyed by the name people will pass toadd_printer(..., driver="...").Add protocol tests. See
tests/test_printer_protocol.py- these use a fake transport that just records what was written, so you can pin down your driver's exact command bytes without needing the hardware present for every test run (you'll obviously still want to confirm against real hardware before calling it done).Open a PR. Mention what hardware you tested against - a driver that's only ever run against the one printer its author owns is worth having, but should say so.
Development
.venv/bin/pip install -e ".[test]"
.venv/bin/pytest -vTests don't need real printer hardware - they use fake transports that record what would have been sent over BLE/serial, so they run the same on a laptop with no printer nearby.
Available Tools
12 toolsadd_printerA
Add (or update, if the name already exists) a named printer profile. If this is the first printer configured, it becomes the active one automatically - otherwise use select_printer to switch to it.
driver identifies the printer's protocol family - "luckprinter" (the default) covers the LuckPrinter-SDK rebrand family (NHOWIN, PPS1, C&Co 3128, DP-L1S, etc) and is the only driver available so far.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | ||
| driver | No | luckprinter | |
| address | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are present, so the description carries the transparency burden. It discloses the upsert side effect, the automatic first-printer activation, and that luckprinter is currently the only driver. It does not address permissions, failure modes, return values, or whether updating an existing profile resets its other settings.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded: the core upsert behavior comes first, the active-printer rule second, and only the necessary driver detail last. Every sentence earns its place with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 3-parameter configuration tool with no output schema and no annotations, the description covers the main behavior and the optional driver parameter well. It omits the address format and does not clarify the boundary with set_printer_address, so an agent lacks some situational and invocation detail.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description adds strong semantics for the driver parameter, including its default value, protocol family, and rebrand examples, and it implies name is the identity key via 'if the name already exists'. The required address parameter, however, gets no format or example, so parameter coverage remains partial.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the operation ('Add (or update...)') and resource ('named printer profile'), so an agent knows what the tool does. It explicitly contrasts with select_printer for switching active printers, but it does not clarify the overlap with set_printer_address, which also modifies an existing printer's address.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides useful context: the first configured printer becomes active automatically, and select_printer is the tool for switching afterward. However, it does not state when to use add_printer versus set_printer_address or set_printer_options, especially since the upsert behavior overlaps with those more targeted sibling tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_daemon_infoA
Explain how to start the required LuckJingle print daemon, and whether it is currently reachable.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the disclosure burden. It correctly characterizes the operation as informational and includes a reachability check, but it does not explain whether the check performs network I/O, how long it may take, or how unreachable states are represented. No annotation contradiction exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence with no filler. It front-loads the main action and covers both key concerns in a compact, scannable way.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter status tool with an output schema, the description sufficiently conveys purpose and expected content. It names the daemon and the two dimensions of the returned information, and does not need to describe return values because the output schema already exists.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters and no required arguments, so there are no parameter details for the description to clarify. The mention of daemon startup and reachability usefully previews what the result will cover, which is enough for a parameterless tool.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies the resource (LuckJingle print daemon) and the two things the tool does: explain how to start it and report whether it is reachable. This distinguishes it from sibling printer configuration and printing tools. Minor ambiguity remains in 'explain how to start,' which suggests a human-readable narrative rather than a structured daemon-info response.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no explicit guidance about when to call this tool versus alternatives, no prerequisites, and no exclusions. The phrase 'required LuckJingle print daemon' weakly implies setup or troubleshooting context, but an agent is not directly told when this tool is the right choice.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_printer_configA
Return the active printer's saved configuration (address, driver, paper width, etc). Use list_printers to see every configured printer.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden, and it does convey the read-only nature ('Return') and that it reads 'saved' configuration rather than live device state — a useful distinction from probe_device. However, it does not disclose error behavior when no active printer exists, which is a plausible runtime scenario an agent should anticipate.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences with no wasted words: the primary action is front-loaded, the config contents are exemplified compactly, and the sibling routing is appended efficiently. Every clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter, no-output-schema getter, the description is nearly complete: it states what is returned, scopes it to the active printer, and points to the alternative for the full list. The only gap is the edge case of no active printer being selected, which could have been stated explicitly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters and 100% schema description coverage, so the schema leaves nothing undocumented. Per the baseline for 0-param tools, a 4 is appropriate; there is no parameter semantics gap for the description to fill.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Return') with a precise resource ('the active printer's saved configuration') and cites example fields (address, driver, paper width). The qualifier 'active printer's' sharply distinguishes this from siblings like list_printers, and the final sentence explicitly names the sibling for the 'every printer' case.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives an explicit when-to-use-alternative directive: 'Use list_printers to see every configured printer.' This states the condition under which a sibling should be chosen instead, leaving no inference burden on the agent.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_printersA
List every configured printer (name, driver, address, settings) and which one is currently active - the one print_text/print_image use when no printer is named explicitly.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral burden. It discloses exactly what the response contains (name, driver, address, settings, active printer) and clarifies the active printer's default behavior. It does not explicitly state that listing has no side effects, but the verb 'List' strongly implies a read-only operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single well-structured sentence that front-loads the action and scope, then efficiently lists the output fields and adds the important clarifying detail about the active printer's role. Every word earns its place, with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-parameter list tool with no output schema and no annotations, the description provides the essential details: what is listed, what fields are returned, and how to interpret the active printer. It could additionally contrast itself with scan_printers for discovery, but that is more of an optional enhancement than a critical gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters and schema description coverage is 100%, so there are no parameter semantics for the description to add. The baseline of 4 applies because there is nothing missing for an agent to understand how to invoke the tool.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('List') with a clear resource ('every configured printer') and enumerates the return fields: name, driver, address, settings, and active printer. It also distinguishes the active printer's role in print_text/print_image, making the tool's purpose unambiguous and distinct from siblings like scan_printers or get_printer_config.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear context for when to use this tool: to see all configured printers and determine which one print_text/print_image will use by default. It does not explicitly state when not to use it or name alternatives such as scan_printers, but the usage context is concrete and actionable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
print_imageA
Print an image file (PNG/JPG/etc) on the label printer.
The image is resized to the printer's configured paper width and converted to black & white. Leave dither=True for photos/gradients; set dither=False for already-black-and-white line art or text to keep edges crisp. printer names which configured printer to use for this one print job, overriding the active printer - leave unset to use the active printer.
| Name | Required | Description | Default |
|---|---|---|---|
| dither | No | ||
| printer | No | ||
| image_path | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden and does a solid job: it discloses that the image is resized to the printer's paper width, converted to black & white, and that dithering preserves photo/gradient edges. It also clarifies that printer applies only to the current job, leaving the active printer unchanged.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise, front-loaded with the main purpose, and uses only a few sentences to cover behavior and parameter guidance. A minor typo ('printer names which configured printer') prevents a perfect score, but nothing is redundant or wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has only three parameters and no annotations; the description covers purpose, processing behavior, and the two non-obvious parameters. An output schema is present, so return-value documentation is not required here. The description is complete enough for an agent to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must add meaning beyond the schema. It explains dither's effect and the semantics of printer, including how to fall back to the active printer. image_path is not deeply described, but the phrase 'image file (PNG/JPG/etc)' makes its purpose obvious.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description begins with a clear verb and resource: 'Print an image file (PNG/JPG/etc) on the label printer.' This unambiguously identifies the tool's function and distinguishes it from sibling print_text, which handles text rather than images.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides actionable usage guidance: use dither=True for photos/gradients and dither=False for line art/text, and explains how the printer parameter overrides the active printer. It does not explicitly name alternatives like print_text, but the purpose statement and sibling context make the intended use clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
print_textB
Print a block of text on the label printer, wrapped to the paper width.
align may be "left", "center", or "right". printer names which configured printer to use for this one print job, overriding the active printer - leave unset to use the active printer.
| Name | Required | Description | Default |
|---|---|---|---|
| text | Yes | ||
| align | No | left | |
| printer | No | ||
| font_size | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the transparency burden. It discloses two non-obvious behaviors: text wraps to paper width and the printer parameter only overrides the active printer for this one job. However, it does not explain what happens on errors, whether the job is queued or immediate, or any permission/dependency requirements, so transparency is only partial.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is relatively short and front-loaded with the main action, followed by parameter-specific details. All sentences earn their place, though there are minor grammar issues such as 'printer names which configured printer' that slightly reduce clarity. It avoids unnecessary fluff and is appropriately compact.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple four-parameter tool with an output schema, the description covers the main printing behavior and two key parameters. It does not explain font_size semantics, mention how to discover valid printer names (e.g., via list_printers or scan_printers), or provide guidance around error cases. This is adequate but not fully complete for a tool with no annotations.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate for undocumented parameters. It does provide useful semantics for align (allowed values) and printer (override scope and default behavior), which add real meaning beyond the schema. However, font_size is not described at all, and text is left entirely to the schema's 'Text' title, so the compensation is incomplete.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the core action: printing a block of text on a label printer, and adds a useful detail about wrapping to paper width. This distinguishes it reasonably from sibling print_image, though it does not explicitly call out that sibling as the image alternative. Overall it is a clear, specific purpose statement.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The intended use is implied by the description: use this tool when you need to print text. However, there is no explicit guidance about when to choose this over print_image or any other sibling, nor any exclusion or alternative naming. It gives some operating context around the printer parameter but not tool-selection guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
probe_deviceA
Connect to a BLE device and list its GATT services/characteristics.
Useful for confirming a candidate address (found via scan_printers) is really a supported printer before adding it - a compatible printer should expose a custom service containing characteristics ending in ff01, ff02, and ff03. Note that this GATT layout alone doesn't tell you which driver to use - more than one incompatible protocol variant shares it - so still confirm with a real test print after adding it.
| Name | Required | Description | Default |
|---|---|---|---|
| address | Yes | ||
| timeout | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description takes the full burden of behavior. It discloses the operation (connect, list), the expected pattern (characteristics ending in ff01/ff02/ff03), and a limitation (shared layout across incompatible protocols). It does not cover failure modes or permissions, but the core behavioral trait—that probing is not driver identification—is clearly flagged.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The main purpose is the first sentence, and the second paragraph is dense but relevant usage and caveat context. No filler; every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers purpose, usage context, expected output (GATT services/characteristics), and a caution about the tool's limitations. Missing details, such as timeout semantics and failure handling, are minor for this simple tool and there is no output schema demanding more.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema gives only field titles—'Address' and 'Timeout'—so the description must compensate. It clarifies address as a candidate BLE address from scan_printers, which helps, but it never explains what timeout controls or what format address should take; timeout is entirely unaddressed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Connect to a BLE device and list its GATT services/characteristics.' It also clearly situates the tool in a workflow (confirming a candidate address from scan_printers before adding a printer), which differentiates it from siblings like scan_printers and add_printer.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly says when to use it: to confirm a candidate address found via scan_printers is a supported printer before adding it. It also provides an exclusion—the GATT layout alone does not determine the driver, so a test print is still needed—steering agents away from over-reliance. This is precise, alternative-aware guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
remove_printerA
Remove a configured printer. If it was the active one, another remaining configured printer (if any) becomes active.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the transparency burden. It discloses a non-obvious side effect: if the active printer is removed, another configured printer becomes active. This is meaningful behavioral context for a destructive operation, though irreversibility and failure cases are not covered.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two short sentences with the main action front-loaded and the conditional consequence placed second. Every clause earns its place, and there is no filler or redundant wording.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter mutation tool with no output schema, this description gives enough context to form an accurate mental model: the target resource, the operation, and the active-printer fallback. It omits edge cases like missing printer names or permanence, but those are minor for this low-complexity tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, and the description never explicitly mentions the required `name` parameter. The phrase 'a configured printer' weakly implies that `name` identifies which printer to remove, but the description does not compensate for the bare schema by clarifying the parameter's meaning or expected value.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Remove a configured printer.' This cleanly distinguishes the tool from siblings like add_printer and list_printers. It further clarifies the role by stating what happens when the removed printer was the active one.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The intended use is implied rather than explicit: call this when you want to remove a configured printer. The description provides useful context about the active-printer fallback, but it does not explicitly compare with alternatives or state when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
scan_printersA
Scan for nearby Bluetooth LE devices to find the printer's address.
Put the printer in pairing mode (usually: hold its power button, or open its companion app once) before scanning, then look for a device name that resembles the printer (e.g. containing "D1", "GB", "Q3", "Print", or a string of letters/numbers) in the results.
Requires the LuckJingle print daemon to be running (see get_daemon_info).
| Name | Required | Description | Default |
|---|---|---|---|
| timeout | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden of behavioral disclosure. It explains the prerequisite daemon, the need for pairing mode, and what to look for in results (device name patterns). It doesn't detail side effects or error behavior, but for a read-only scan the provided context is valuable and goes beyond a bare operation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured: purpose in the first sentence, usage guidance in the second paragraph, and prerequisite in the third. It is informative without being bloated, though the device-name-pattern list is slightly long.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple optional-parameter scan tool, the description covers the main points: what it does, how to prepare, and the runtime dependency. The missing timeout semantics is the main gap, but it is a minor one given the default value and the overall simplicity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The only parameter, timeout, is entirely undocumented in both the schema and the description. The schema provides only title/type/default with zero description, and the tool description never mentions timeout, its unit, or its effect. Since schema coverage is effectively 0%, the description needed to compensate but did not.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Scan for nearby Bluetooth LE devices to find the printer's address.' This clearly distinguishes the tool as a BLE discovery/scan operation, not a management or configuration action, and it pairs well with the related get_daemon_info pointer.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives actionable context: put the printer in pairing mode before scanning, and requires the LuckJingle print daemon to be running (referencing get_daemon_info). It stops short of explicit 'when not to use' or alternatives, but the preconditions and steps are clear enough for an agent to decide when to call it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
select_printerA
Set which configured printer print_text/print_image use by default when no printer is named explicitly on the call.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden of explaining side effects. It discloses the core mutation and the condition under which it applies, but it does not mention whether the name must already be configured, whether the selection persists, or what happens when an unknown printer name is supplied.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with no filler. It states the action, the affected functions, and the scope condition in a compact and readable way.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter setter, the core invocation is clear, but with no output schema and no annotations, there is no information about return values, error behavior, or persistence. An agent can call it correctly but cannot confidently predict success confirmation or failure mode.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% and the description never formally maps the 'name' parameter to a configured printer identifier. However, phrases like 'which configured printer' and 'no printer is named' make it reasonably clear that the parameter is the printer name, though it could be more explicit about whether it expects a display name or exact configured label.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a concrete verb ('Set') and a specific resource (the default configured printer used by print_text/print_image). By tying the setting to calls that omit a printer name, it clearly scopes the operation and distinguishes it from sibling printer-management tools such as add_printer, remove_printer, or set_printer_options.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It identifies when the setting takes effect ('when no printer is named explicitly on the call'), but it does not explicitly say when to choose this tool over alternatives or state prerequisites like 'the printer must already be configured.' The usage guidance is implied rather than explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_printer_addressA
Save the Bluetooth address of the printer for future print jobs.
This is a shortcut for the common single-printer case - it updates whichever printer is currently active (creating one named "default" if none exists yet). To configure more than one printer, use add_printer instead, which lets you name each one.
| Name | Required | Description | Default |
|---|---|---|---|
| address | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full disclosure burden. It reveals that this mutates the currently active printer and creates a default printer if none exists—important side effects. It could go further on permissions or reversibility, but for a simple setter this is solid.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core purpose is stated in the first sentence, followed by a brief but valuable distinction from the alternative. Every sentence earns its place; no filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter tool with no annotations, this description covers the essential behaviors: what it does, what it affects, fallback creation, and when to choose a sibling instead. The presence of an output schema helps, but nothing needed for correct invocation is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must compensate. It clarifies that 'address' means the printer's Bluetooth address, adding semantic meaning the schema lacks. It does not specify format (e.g., MAC-style), but the core meaning is clear.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Save') and identifies the exact resource: the Bluetooth address of the printer, for future print jobs. It also explicitly distinguishes itself from the sibling add_printer by stating when each should be used.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It clearly states this is a shortcut for the common single-printer case, explains what happens when no printer exists (creates 'default'), and points to add_printer for configuring multiple printers. This gives an agent unambiguous selection criteria.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_printer_optionsA
Tune print quality for a printer. width is the paper width in dots (384 is standard for common 48mm/58mm thermal labels - only change it if prints come out cropped or skewed). density (0=light, 1=normal, 2=dark) adjusts print darkness if prints look too light/dark - leave unset to use the printer's own default. font_path points to a .ttf/.otf file to use for print_text instead of the built-in fallback. name picks which configured printer to change settings for - leave unset to use the active printer.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | ||
| width | No | ||
| density | No | ||
| font_path | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description takes on the behavioral burden and does more than restate the operation: it explains defaults, effects of changing width/density, and the font_path's relationship to print_text. It could additionally disclose persistence, permissions, or side effects, but the behavior described is substantially transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The purpose is front-loaded in one short sentence, and every following clause explains one parameter without redundancy. The conditional phrasing keeps guidance tight rather than expanding into prose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a four-optional-parameter setter with no required fields and no output schema, this description covers the operation, all params, and default behavior. It does not cover error cases or explicit return behavior, but those are minor for this simple configuration tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, and it does completely: each of the four parameters gets semantics, units, valid values (density 0/1/2), file-type expectations, and default behavior. This is exactly the value the schema lacks.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource ('Tune print quality for a printer') and walks through each configurable option, making the tool's purpose clear. It does not explicitly contrast itself with sibling tools like set_printer_address or get_printer_config, so it stops short of full sibling differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives strong parameter-level usage conditions ('only change it if prints come out cropped or skewed', 'if prints look too light/dark', 'leave unset to use the printer's own default'). However, it does not say when to prefer this tool over alternatives or when not to use it, leaving tool-selection guidance implicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
12 tool updates
v0.1.0- First observed
add_printer - First observed
get_daemon_info - First observed
get_printer_config - First observed
list_printers - First observed
print_image - First observed
print_text - First observed
probe_device - First observed
remove_printer - First observed
scan_printers - First observed
select_printer - First observed
set_printer_address - First observed
set_printer_options
TDQS
Each tool targets a distinct action: scanning, probing, configuring, selecting, removing, printing, and daemon status are all clearly separated. The only potentially overlapping tools (set_printer_address vs add_printer, get_printer_config vs list_printers) are explicitly differentiated in their descriptions.
Every tool follows a consistent snake_case verb_noun pattern, such as remove_printer, scan_printers, add_printer, print_text, and get_daemon_info. There are no mixed naming conventions or vague generic verbs.
12 tools is well-scoped for this server's purpose: device discovery, printer profile management, configuration, and printing are all covered without unnecessary duplication. Each tool earns its place in the workflow.
The tool surface covers the full lifecycle: scanning and probing to find a device, adding/updating/removing printers, selecting an active printer, configuring options, checking daemon status, and printing text or images. There are no obvious dead ends or missing core operations for the stated label-printer domain.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Automate 1,000+ services from any MCP-compatible AI agent: build Applets, run actions and queries.
Generate PDFs from templates via AI chat. Works with Claude, ChatGPT, Cursor, and any MCP client.
Manage your IoT device fleet directly from Claude. Create device templates with datastreams and events, provision new devices, read live sensor data, and control outputs. The Blynk connector integrates with the Blynk IoT platform, enabling direct configuration and monitoring of connected devices and infrastructure.
- TaprootOAuthcom.taproothq
Persistent memory layer for AI tools. Save and recall notes across Claude and other MCP clients.
Related MCP Servers
- AlicenseAqualityBmaintenanceEnables comprehensive control and monitoring of Bambu Lab 3D printers through Claude using local MQTT, FTPS, and X.509 authentication. Users can manage print jobs, monitor real-time status, handle filament through AMS, and adjust hardware settings like temperature and lighting.241919MIT
- AlicenseNot gradedqualityDmaintenanceEnables interaction with Memobird thermal printers to print text, HTML, web pages, and images directly from MCP-enabled clients. It includes tools for device binding, image conversion, and monitoring print job status.131ISC
- AlicenseAqualityFmaintenanceConnect AI agents to physical printers. Print receipts, shipping labels, and packing slips to your existing BizPrint-connected printers from Claude and other MCP clients.71MIT
- AlicenseNot gradedqualityBmaintenanceLet Claude print documents on any USB or network printer.1MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/davidray/iocane'
If you have feedback or need assistance with the MCP directory API, please join our Discord server