visualizer-mcp
Sends optional push notifications for watchman alerts such as changed batch without grind change, beans past their prime, unrated shots, and shots missing their target weight, with caps on message frequency and volume.
Click on "Deploy 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., "@visualizer-mcpcompare my last 3 espresso shots"
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.
decentespresso-mcp
Your espresso archive, and someone to talk to about it.
Every shot your Decent DE1 pulls is already measured in fine detail — pressure, flow, weight and temperature about four times a second, next to the targets the profile asked for. Decaid — Decent's new control app, the one replacing the ten-year-old de1app — records all of that and, the part that matters here, serves it over a local REST API. This server reads that API, keeps everything on a machine you own, and hands it to Claude so you can just ask.
Decaid is required. This project speaks to Decaid and to nothing else. If you still run the original de1app, which is what most DE1 owners use today, there is nothing here for you yet — see Which app do you run? below.
Independent community project. Despite the name, this is not a Decent Espresso product and has nothing to do with Decent Espresso International. They make the machine and they make Decaid; this server is an outside client of Decaid's public API, written by someone who owns one of their machines. Nobody there endorses, reviews or supports it.
You: Why was my shot this morning sour and bitter at the same time?
Claude: Sour and bitter together is uneven extraction, so I looked at the puck
rather than the recipe.
The machine did exactly what it was told: it tracked its pressure
target to within 0.03 bar for the whole pour. The puck did not. Its
resistance fell steadily — 0.62 per second across the 22 seconds of
pour, the steepest band the diagnostics have. Water found a path and
widened it, so part of the bed over-extracted while the rest barely got
wet. That is your sour and bitter at once.
The other channeling signs were quiet: no pressure dip after the peak,
flow steady to 0.015 ml/s. Two more need the scale and could not be
computed for this shot.
One thing to correct, though — you noted it probably ran too hot. It
ran 1.6 °C *below* target on average and 2.1 °C under at the worst
point. Temperature was not your problem here.That exchange is real, from shot a0b7eb21. None of the numbers were typed by
hand.
What you get
A complete archive, independent of the tablet. Every shot Decaid holds, with
the full measurement series and the exact profile version it ran on, pulled over
/api/v1 and kept in SQLite. Decaid is the source; this is the memory. The sync
is incremental and notices shots you edited after the fact, the tablet may be
off whenever it likes, and nothing in that chain leaves your network.
Metrics from the machine's own phase markers. The end of preinfusion is read off what the DE1 reports, not guessed from a pressure threshold. Where a shot does not report it, the fallback says so — you always know whether a number was measured or inferred.
Puck diagnostics. Resistance modelled as pressure over flow squared, and five independent channeling indicators: a pressure dip after the peak, flow that will not settle, the pump out-delivering the scale, drops arriving too early, and a bed losing resistance while the pressure is held. Each keeps its own raw value and threshold, so you can disagree with the verdict and still use the evidence. An indicator that could not be measured says so rather than passing quietly.
Target versus actual, per data point. Decaid records what the machine was asked for alongside what it did. Profile compliance here is therefore measured, not estimated — and a shot that followed its profile perfectly and still tasted wrong tells you to look at the coffee instead of the machine.
Three levels of detail. summary to triage, per_phase to find a cause,
detailed for the raw curve. Each step costs roughly twice the one before, so
deep questions stay cheap until you actually ask one.
Profile versioning that understands cosmetics. Two hashes: one identifies a version exactly, the other groups versions that brew identically. Reformat a profile and nothing moves; change the pressure by 0.3 bar and it does.
A watchman. Four rules run after every sync — changed the batch but not the grind, beans past their prime (frozen time subtracted), a shot you never rated, weights that miss their target. With optional ntfy push, capped so the messages stay worth reading: at most one per shot, only the last 48 hours, at most five per run.
Statistics over a period. What you pulled, from which beans, on which profiles, how the grind moved, how it tasted — and the same figures for the period before.
You: How was the last month?
Claude: 117 shots in 30 days, just under four a day, with 13 flushes and aborts
left out. You rated 18 of them, averaging 53. Typical shot: 17.9 g in,
38.4 g out, 1:2.1 in 29 seconds.
The incahuasi accounts for 57 of those and you moved the grinder eleven
times on it — 4.3 down to 2.5, then settling around 2.7. It averages 47
across nine ratings, while the Arabica Honey Process sits at 67 across
four. That is the one to keep.
Busiest day was 28 August: 34 pulls, 27 of them coffee.Writing back, by asking. "Give that one a 90 and note it was the best this week." Behind a switch that is off by default, with a whitelist of fields verified against the real API, and every change read back before it is reported to you.
Related MCP server: meticulous-mcp
Which app do you run?
Decaid is new. Most DE1 owners are still on de1app, the Tcl application that has run the machine for a decade, and for them this server has nothing to offer: de1app keeps no local API to read. There is no workaround planned — the shape of this project follows from what Decaid exposes, and half of what it does would be impossible without it.
de1app | Decaid | |
A local API to archive from | — | REST + WebSocket |
Per-point | — | yes, and profile compliance is built on it |
Machine phase markers ( | — | yes, so |
Profile as structured JSON | Tcl, parsed by hand | yes, with a version hash per shot |
Beans, batches, the next shot's setting | — | yes, and writable |
Your old history is not lost. Decaid imports de1app shots, and this server
recognises them (id de1app-<unix time>, timestamps already in UTC, the
phantom enjoyment: 0.0 of the import normalised back to "not rated"). They
just carry less: no machine phase markers, so pi_end falls back to the profile
step boundary or, failing that, to a pressure heuristic that says so in
warnings. In this archive, 84 of 88 imported shots land on the step boundary
and 3 on the heuristic, while 77 of 81 shots Decaid recorded itself are read
straight off the machine.
If you are moving over, import into Decaid first and point this at Decaid — not the other way round.
How it fits together
Decent DE1 ──BLE──▶ Decaid (tablet, or wherever ← the source
│ you run it)
│ /api/v1, local network only
▼
decentespresso-mcp (Docker) ← the memory
│ internal Docker network
cloudflared
│ https://<host>/<secret>/mcp
▼
Claude ← the interfaceThe tunnel exists so Claude can reach the server. Decaid is never reached through it — that connection is local network only, and the server refuses to start if you point it anywhere else.
Decaid does not need to be running. It is up while you are making coffee; the rest of the time the server waits and catches up afterwards, because the sync is incremental and Decaid keeps the history. A tablet that is off is a normal state, not an error, and nothing will nag you about it.
Requirements
A Decent DE1 running Decaid, reachable on your network. Everything here is verified against Decaid 0.8.5+2624 — twenty-seven findings about what its API really does are tabulated in the specification, and
status()says so when the version it meets differs from the one this was checked against.Docker on a machine on the same network as the tablet.
A Cloudflare tunnel or equivalent, if you want to reach it from claude.ai. Optional — everything works locally without one.
Or just Python 3.12, if you would rather run it without Docker.
Quickstart
git clone https://github.com/The-Walker443/decentespresso-mcp
cd decentespresso-mcp
cp .env.example .envTwo things have to be filled in:
DECAID_URL=http://10.100.100.171:8080 # your tablet, private IP only
MCP_PATH_SECRET=<openssl rand -hex 24> # stands in for authenticationThen:
mkdir -p data && sudo chown -R 10001:10001 data # the container runs as UID 10001
docker compose up -d
docker compose logs -fThe first start backfills everything Decaid has. Point your tunnel at the container:
ingress:
- hostname: coffee.example.com
service: http://decentespresso-mcp:8000
- service: http_status:404Fetch the connector URL — it carries the secret, so it is never logged:
docker compose exec decentespresso-mcp decentespresso-mcp --print-connector-urlPaste it into claude.ai under Settings → Connectors → Add custom connector, leaving the OAuth fields empty. Then ask it something.
Prefer Portainer and a prebuilt image from ghcr.io? compose.portainer.yaml
and the specification cover that
route, including the stack variables.
Security, in three sentences
The connection to your tablet is local network only — DECAID_URL accepts
private IP literals and nothing else, and that traffic never touches the tunnel.
The MCP endpoint hides behind a secret path instead of a login, every other path
returns an empty 404, and access logs are off so the path never lands in a log
file. The container runs non-root on a read-only filesystem, publishes no host
port, and the log formatter redacts the only two secrets it holds — including
inside tracebacks from libraries that know nothing about it.
The tools
| what is in the archive |
| one shot, at the detail you ask for |
| two to four side by side, with deltas |
| profile versions and their targets |
| what the next shot would run on, live from the tablet |
| which batch, roasted when, how much is gone |
| what the watchman found |
| a period, with a comparison |
| housekeeping |
| writing, behind the switch |
Fourteen tools cost 9.9 kB of definitions, eighteen with write mode on cost 12.6 kB — about 700 B each, because the shared vocabulary lives in the server prompt rather than being repeated in every docstring. That budget is pinned by tests; it is meant to stay that way.
Configuration
Everything is an environment variable. The server validates all of it at startup and lists every problem at once, rather than one per restart.
Variable | Default | |
| — | your tablet, private IP literal only |
| — | ≥32 characters, stands in for authentication |
|
|
|
|
| unlocks the four write tools |
| all | comma-separated rule names, or |
| — | guard notifications; empty means none |
|
| guard thresholds |
| — | only for printing the connector URL |
|
| storage is always UTC |
|
| bind address inside the container |
Syncing by hand, if you ever need to:
docker compose exec decentespresso-mcp decentespresso-mcp --backfill # everything
docker compose exec decentespresso-mcp decentespresso-mcp --sync-once # new and changedBoth print a JSON summary. waiting_for_tablet means the tablet was off — not
an error, just nothing to fetch.
Honest limits
It cannot taste. The diagnostics describe what the machine and the puck did. The connection to sour, bitter or thin is inference, and a shot that looks clean can still taste wrong.
Channeling indicators rarely agree. Across 171 real shots no two of them ever fired together, which is why they are reported individually rather than collapsed into one score you would have to trust blindly.
Thresholds are calibrated against one archive — one machine, one grinder, one operator. They are percentile-based and documented one by one in the specification, so they can be re-derived rather than believed.
Decaid only, and Decaid is young. No de1app, no visualizer.coffee, no CSV import. The API this depends on is new enough that a Decaid update could move something; when the version stops matching the one it was verified against,
status()says so rather than pretending.Imported shots know less. Anything de1app recorded lacks the machine's phase markers, so its preinfusion boundary is inferred instead of read off. The metric says which, per shot, and refuses comparisons it cannot support.
It does not write profiles. Reading and versioning them, yes; changing them belongs at the machine.
Nothing is ever deleted. The API can do it. This project does not build it.
Development
python -m venv .venv
.venv/Scripts/pip install -e ".[dev]" # Linux/macOS: .venv/bin/pip
.venv/Scripts/python -m pytest
.venv/Scripts/python -m ruff check src testsFixtures are anonymised real responses, never invented ones. Numbers pinned in tests come from measurement, and the comment says where. The conventions this project runs on — verify before you build, whitelist discipline, what a test has to say — are in CLAUDE.md; the system itself is described in the specification.
data/shots.db in a checkout is a development copy, not the archive. Numbers
from it are never a statement about the real one — for that, ask status().
Project status
Personal project, built spec-driven with Claude Code; provided as-is, no support promised, issues welcome.
Puck-diagnostic physics and threshold rationales are informed by gaggimate-mcp (MIT) — the resistance model, the idea of independent channeling signatures, the temperature bands and the three-level detail system. The thresholds themselves are measured against this archive rather than adopted: the sampling rates differ, and this data carries per-point targets that theirs does not.
Not affiliated with Decent Espresso International. The name says what the project is for, not who made it. Decent builds the DE1 and Decaid; this is an independent community project by an owner of one of their machines, connected to them only by reading Decaid's public API. It is not endorsed, reviewed or supported by Decent Espresso, and any problem you have with it belongs in this issue tracker rather than in theirs.
This server cannot be deployed
Maintenance
Related MCP Connectors
MCP server for Withings health data — sleep, activity, heart, and body metrics.
MCP server for Riveter's enrichment, scraping, and monitoring API
UN FAOSTAT global food & agriculture statistics over a local SQLite mirror, via MCP.
Related MCP Servers
- AlicenseAqualityDmaintenanceMCP server for SQLite — query databases, inspect schemas, explain queries, and export data from your IDE.578 npm3MIT
- AlicenseAqualityDmaintenanceMCP server for controlling Meticulous espresso machines via Claude and other AI clients.22109 npmMIT
- AlicenseAqualityDmaintenanceAn MCP server for Gaggiuino-modified espresso machines, enabling monitoring, shot analysis, and profile management.45 npm1MIT
- AlicenseNot gradedqualityAmaintenanceA remote MCP server for integrating Gaggiuino espresso machines with AI tools. It enables checking machine status, analyzing shot data, managing profiles, and receiving dial-in guidance.1MIT