Reported issues for gaggiuino-mcp
Pod holds 24 of 88 problems reported by people outside the maintainer team. Issues filed by the project's own owners, members and collaborators are excluded entirely — a maintainer's release checklist is not a warning to a prospective user.
Back to gaggiuino-mcp.
Most discussed
fix(server): make the consent token stateless so /oauth/authorize cannot be evicted
Found while writing the tests for #108/#109 (PR #118). Not a vulnerability that grants anything — it is an availability defect on the login path, and the fix removes a store rather than adding a guard.
What is wrong
handleGet in apps/server/src/oauth/authorize.ts calls codes.remember(...) to park the validated authorization request before any authentication happens — it has to, because the CSRF token in the form is the key to that parked request. createCodeStore's pending ma
Read the thread · 2026-08-05 · closed · 2 comments
feat(server): zero-click Tailscale identity at /oauth/authorize
/oauth/authorize is the only endpoint a human's browser touches. On a tailnet, Tailscale already knows who that human is — so the owner passphrase (#109) could be skipped entirely, and the consent step would become physically uncompletable from the public internet. tsidp does exactly this: server/authorize.go refuses the route over Funnel with "they are not part of the tailnet that they are trying to be authenticated for."
The mechanism is real. Verified in tailscale/tailscale, `ipn/i
Read the thread · 2026-08-05 · open · 2 comments
chore(server): drop the MCP_AUTH_TOKEN startup tombstone
#111 kept env.MCP_AUTH_TOKEN read solely so a deployment that still sets it fails loudly at startup rather than silently serving an open /mcp. One release after 2.0.0, remove it:
- the startup check and its
ConfigError; - the
.env.exampletombstone line and its explanatory comment —envExample.test.tsenforces this direction too, so the code and the template must move in the same commit; - any remaining mention in README, SECURITY.md, AGENTS.md,
server.json,turbo.json.
No behavi
Read the thread · 2026-08-05 · closed · 2 comments
feat(server)!: remove MCP_AUTH_TOKEN in favour of OAuth
This is the breaking change. It lands only after OAuth works end to end against the owner's real connector on iOS (#113's probe is the gate). Nothing before this point removes anything.
Why the token goes rather than staying alongside
It is a control the owner cannot configure, and one nobody on a personal plan can. Keeping it means two auth mechanisms, two sets of gate ordering to reason about, and a writeToolDisabled that answers a per-request question by reading the process envir
Read the thread · 2026-08-05 · closed · 2 comments
feat(server): delegate to an external issuer with MCP_OAUTH_ISSUER
The built-in AS (#108, #109) is the default because it keeps the one-container promise. It should not be the only path — plenty of self-hosters already run Authentik, Authelia, Keycloak, Zitadel or Kanidm, and a Tailscale user may prefer tsidp. This makes that a one-variable choice against code that already exists.
Anthropic supports it with no extra ceremony: *"A cross-host authorization server doesn't need anything special on its own. The authorization_servers field in your protected reso
Read the thread · 2026-08-05 · closed · 2 comments
feat(server): delete_profile tool, gated on explicit user confirmation
upload_profile (#101) gives the server a way to create a profile and no way to remove one. A model that uploads a malformed or duplicate profile leaves the user to clean it up on the machine's own screen, which is the surface the whole server exists to avoid.
DELETE /api/profile-select/{id} is the endpoint. PR #104 deliberately left it on the not-called list (client.ts, "endpoints this client does not call") on the grounds that it was unverified and differs from the selector by HTTP metho
Read the thread · 2026-08-05 · closed · 2 comments
feat: adopt MCP spec 2026-07-28 via SDK v2 (@modelcontextprotocol/server), dual-era
Why
MCP spec 2026-07-28 shipped on July 28 (announcement). Anthropic says support is "rolling out across Claude products soon" — today's Claude clients still speak the 2025-era protocol, so there is no urgency, but once Claude's client update lands there are two concrete wins for this server:
- **
ttlMs/
Read the thread · 2026-08-12 · closed · 1 comment
epic: derived shot diagnostics
Batch from the 2026-08-08 assessment of mxkissnr/gaggiuino-local-profiler — a Home Assistant espresso profiler covering the same hardware, read in source for ideas that fit a stateless MCP server.
Why these belong together
All four are apps/server/src/analysis.ts, and they are the part of that project that transfers without a database: pure functions over datapoints this server already fetches. Nothing here needs a store, user-en
Read the thread · 2026-08-08 · closed · 1 comment
Most recent
epic: loose-boundary tolerance
Batch 3 of the 2026-08-22 codebase improvement audit.
Why these belong together
Four places where a malformed or hostile input takes down more than it should. The repo already has a posture here, stated in AGENTS.md: upstream payloads are validated at the client boundary with deliberately loose schemas so a firmware revision cannot take the server down, maintenance.ts routes uninterpretable keys to extras rather than failing, and expected failures are results rather than exceptions. The
Read the thread · 2026-08-23 · open · 0 comments
epic: the /mcp gate and the OAuth path
Batch 1 of the 2026-08-22 codebase improvement audit.
Why these belong together
The first two are not adjacent issues, they are one edit. Both restructure the same block of apps/server/src/http.ts (:203-239):
- #176 moves
authenticateto run aftercheckRequest, restoring the orderingmcpAuth.tsdocuments and this file currently defeats. - #185 wraps the gate's rejection in
withCorsso an allowlisted browser origin can read the 401 challenge.
They also interact in a way neith
Read the thread · 2026-08-23 · open · 0 comments
chore(repo): add a CONTRIBUTING pointer to the local verification gates
Why
Dev commands live accurately in README §Development, but nothing onboards a contributor to the actual working loop: check vs check:affected, the coverage-ratchet rule (commit the raised numbers; never hand-edit vitest.config.ts), story screenshots for visual review, fake-machine for machineless development, and the fact that the squash-merged PR title is the only thing release-please reads.
Fix
A thin CONTRIBUTING.md that points at the relevant AGENTS.md sections rather than
Read the thread · 2026-08-22 · open · 0 comments
ci: pin the mcp-publisher binary by release checksum
Why
publish-mcp.yml downloads mcp-publisher from releases/latest unpinned (curl | tar xz), then hands that binary a GitHub OIDC token two steps later. Latest is a documented requirement — the registry rejects stale publisher binaries — but the download is currently unverified, an acknowledged supply-chain exposure directly on the publish path.
Fix
Keep resolving latest at run time, but verify the tarball against the release's published checksum before execution (checksum fetched w
Read the thread · 2026-08-22 · open · 0 comments
refactor(server): read GAGGIUINO_URL lazily so resetClient stays a full seam
Why
MACHINE_URL is read once at module load (client.ts:979-980: process.env.GAGGIUINO_URL?.trim() || DEFAULT_MACHINE_URL) and getClient() builds from that frozen constant (client.ts:984-989). resetClient(config) — the labelled test seam — can override every knob except baseUrl, so a test (or runtime config reload) changing the env after import observes nothing. The seam exists precisely to make config-dependent client behavior exercisable.
Fix
Read the environment inside `ge
Read the thread · 2026-08-22 · open · 0 comments
refactor(server): scope cache keys by HTTP method to remove the as-T cast
Why
perform returns cache hits via hit as T (client.ts:699) against a createCache<unknown>; soundness rests entirely on a docblock invariant that GET paths and write-ack paths never collide on a key. Nothing structural enforces it — a future cached GET on a path another verb touches would silently return the wrong payload type, and zod would catch it several layers away or not at all for loose schemas.
Fix
Key cache entries by method as well as path (${method}:${path}), or sco
Read the thread · 2026-08-22 · open · 0 comments
refactor(server): extract the shared target-series pairing used by events and analysis
Why
events.ts:87-107 (toSamples) and analysis.ts:131-148 (meanDeviationFromTarget) independently encode "pair the measured and target series index-wise, treat commanded ≤ 0 as not-commanded, normalize via SCALE_BY_10" — and already drift subtly: events drops instants where a reading is missing, analysis skips them mid-loop. Any future change to that convention must be made twice and can silently disagree.
Fix
Extract pairedSamples(measured, target, fieldName) into `normalize.t
Read the thread · 2026-08-22 · open · 0 comments
refactor: give the x10 wire scaling one home shared by server and shot-graph
Why
Two ×10-scaling homes exist and have diverged:
apps/server/src/normalize.tsSCALE_BY_10covers pressure, targetPressure, temperature, targetTemperature, pumpFlow, weightFlow, targetPumpFlow, shotWeight, waterPumped, timeInShot;packages/shot-graph/src/normalize.tshand-divides only the seven charted fields plus inline duration/timeInShot handling.
If a firmware-scaled field ever reaches the chart or the CSV export (targetTemperature and waterPumped are live candidates), s
Read the thread · 2026-08-22 · open · 0 comments
fix(server): send CORS headers on early rejections for allowlisted origins
Why
CORS headers decorate only responses that come back through handleMcp. The
401 refusal from the security gate is returned bare at http.ts:233, outside
the withCors(...) wrap at http.ts:236-239, so a browser-based client on an
allowlisted origin gets no Access-Control-Allow-Origin on the challenge and
cannot read WWW-Authenticate from JavaScript. Header reflection itself is
correct (mcpAuth.ts:301-324: origin echoed, never *, Vary: Origin,
expose-headers covers the sessi
Read the thread · 2026-08-22 · open · 0 comments
fix(server): bind refresh-token audience to the granted resource parameter
Why
The authorization-code grant seals the refresh token's audience from the caller-supplied resource parameter via audienceFor() (oauth/token.ts:345-355), but handleRefresh verifies the presented refresh token against deps.config.resource only (oauth/tokens.ts:244-260). A grant issued with a non-default resource yields access tokens that authenticate fine while their refresh token is refused as invalid_grant — such deployments loop through re-consent every access-token lifet
Read the thread · 2026-08-22 · open · 0 comments
fix(server): negative-cache failed CIMD client-document resolutions
Why
resolveClient caches only successful CIMD document fetches (oauth/clients.ts:180-215; the cache fill sits behind the success branch), so failed or rejected resolutions re-fetch on every request. An anonymous flood of GET /oauth/authorize carrying distinct public-https client_ids therefore drives one outbound fetch each (3s timeout, 64KB cap) at arbitrary public hosts — an unmetered outbound-fetch sink on an unauthenticated path. The DNS TOCTOU residue is documented and accepted;
Read the thread · 2026-08-22 · open · 0 comments
fix(server): derive the scope gate fail-closed when readOnlyHint is absent
Why
protectedToolsIn selects tools with readOnlyHint === false (oauth/scopeGate.ts:29-33), so a future write tool whose annotations omit readOnlyHint entirely (undefined) is not scope-gated: the runtime derivation falls open where this repo otherwise fails closed. Currently mitigated — defineTool requires annotations and scopeGate.test.ts pins PROTECTED_TOOLS to exactly the three writes, so an accidental loss fails CI — but the derivation direction is wrong for any future too
Read the thread · 2026-08-22 · open · 0 comments
fix(server): answer unknown tools with JSON-RPC -32602 instead of an isError result
Why
handleToolCall throws Error("Unknown tool: …") for an unrecognized name and callTool's catch-all wraps it as { isError: true, … } over HTTP 200. The MCP spec directs servers to answer tools/call for an unknown name with Invalid params (-32602); today a host cannot distinguish a mistyped tool name from a server bug, and the caller's mistake logs at error level as though it were ours. An output-schema .parse() violation falls into the same generic channel instead of the typed I
Read the thread · 2026-08-22 · open · 0 comments
fix(mcp-app): validate tool-result JSON at readToolJson and stop NaN or raw TypeErrors reaching the UI
Why
readToolJson does JSON.parse(text) as T with no runtime validation (packages/ui/src/host/toolResult.ts), and the shot-graph app trusts the result structurally:
- missing
duration→shot.duration / SCALEis NaN → ShotHeader renders "NaN g in NaN s"; - missing
profileobject →shot.profile.namethrows TypeError inside render, putting a raw JS message in front of the user — contradicting the shell rule that users see server-authored errors, never invented ones; - string-numbe
Read the thread · 2026-08-22 · open · 0 comments
fix(server): tolerate a malformed element in /api/profiles/all instead of failing the whole list
Why
MachineProfilesSchema accepts /api/profiles/all as an array of elements each requiring a string name (client.ts:273-278, element schema ~L218-224). One malformed element — a firmware revision omitting name, or a truncated body — fails the whole array parse: MalformedUpstreamError, list_profiles errors out entirely, and profileCatalog.ts degrades to documentation-only, hiding every real profile because of one bad record.
That sits badly next to this repo's own loose-bound
Read the thread · 2026-08-22 · open · 0 comments
fix(server): substitute guidance placeholders with replacer functions
Why
renderDialInGuidance substitutes placeholders with two-argument String.replace (guidance.ts:24-25):
.replace("{user_context}", prompt.userContext ?? "")
.replace("{profiles_text}", getAllProfilesText())
In JS the replacement string interpolates $&, $` and $' sequences, so a user_context in prompts.local.yaml containing e.g. a bare $& corrupts the rendered guidance; profile text flowing through getAllProfilesText() has the same power. Both surfaces inhe
Read the thread · 2026-08-22 · open · 0 comments
The remaining reports are on the project's issue tracker.