关于 gaggiuino-mcp
gaggiuino-mcp 将 Gaggiuino 意式咖啡机连接到 AI 工具,用于查看状态、分析萃取数据和管理曲线。它结合机器数据、萃取图表、调参指导、工作流提示词以及曲线控制功能。
主要功能
- 01机器状态与设置
- 02萃取摘要和时间序列数据
- 03交互式萃取图表与比较
- 04曲线查看与管理
- 05调参指导和工作流提示词
适用场景
- 01冲煮前查看机器状态
- 02调参过程中比较不同萃取
- 03选择或切换冲煮曲线
- 04查看维护历史
MCP 相关
该远程 MCP 服务向客户端提供机器状态、萃取数据、设置、曲线和机器控制工具,并提供工作流提示词与曲线资源。更改曲线需要身份验证和写入权限。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
docker run -i --rm -e GAGGIUINO_URL -e PORT -e HOST -e MCP_PUBLIC_URL -e MCP_OAUTH_SECRET -e MCP_OAUTH_PASSPHRASE_HASH -e MCP_OAUTH_ISSUER -e MCP_ALLOWED_ORIGINS -e MCP_ALLOWED_HOSTS -e LOG_LEVEL ghcr.io/ljcl/gaggiuino-mcp:6.0.0GAGGIUINO_URLGaggiuino machine URL (default http://gaggiuino.local)PORTServer port (default 8000)HOSTBind address (default 0.0.0.0)MCP_PUBLIC_URLPublic https origin clients reach this server on, with no path (e.g. https://box.tailnet.ts.net). Set with MCP_OAUTH_SECRET to enable OAuth; it is advertised as the OAuth resource and must match the URL entered in the client exactlyMCP_OAUTH_SECRET密钥Signing key for self-issued OAuth access and refresh tokens, at least 32 characters (openssl rand -hex 32). Keep it stable across restarts so clients stay signed inMCP_OAUTH_PASSPHRASE_HASH密钥scrypt hash of the passphrase you type on the OAuth consent page, in the form scrypt$N$r$p$salt$hash. Required whenever OAuth is enabled. Generate with `bun run hash-passphrase` — never store the passphrase itselfMCP_OAUTH_ISSUER密钥External OAuth issuer URL to delegate token issuing to (e.g. a Keycloak or Authentik realm). Puts the server in resource-server-only mode; requires MCP_PUBLIC_URL and refuses to start alongside MCP_OAUTH_SECRET or MCP_OAUTH_PASSPHRASE_HASHMCP_ALLOWED_ORIGINSComma-separated browser origins allowed to call /mcp, or * to allow any (unsafe). Requests with no Origin header are unaffectedMCP_ALLOWED_HOSTSComma-separated Host header values to accept on /mcp. Empty disables host validationLOG_LEVELLog verbosity: debug, info, warn, error, or silent (default info). Logs are one JSON object per line on stderr本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
ghcr.io/ljcl/gaggiuino-mcp:6.0.0Streamable HTTP工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
Gaggiuino MCP 服务
用于将 Gaggiuino 意式咖啡机与 AI 工具集成的远程 MCP 服务。让你的 AI 助手检查机器状态、分析萃取数据并获取调参指导。
功能
MCP 工具
萃取分析
get_status- 当前机器状态(温度、压力、重量、水位)get_latest_shot_id- 一次调用即可获取最近一次萃取的 ID 和关键数据list_recent_shots- 最近几次萃取的摘要,用于查看整个冲煮过程的趋势get_shot_data- 包含各项指标的结构化萃取摘要get_shot_raw_data- 完整的时间序列数据view_shot_graph- 交互式萃取图表,可在支持 MCP Apps 的宿主中显示(其他宿主仅显示文本摘要)(展示压力、流量、重量随时间的变化,以及目标曲线叠加和可选的萃取对比)
配置文件和设置
list_profiles- 机器上的配置文件,并与此服务的文档合并get_profile_info- 关于某个配置文件的所有已知信息get_machine_settings- 机器报告的锅炉、蒸汽和电子秤配置get_maintenance_status- 机器自行跟踪的除垢和反冲洗记录,以及自每次维护以来的萃取次数get_dial_in_guidance- 分析意式咖啡萃取的专家指导select_profile- 切换当前配置文件(会更改机器;需要经过身份验证的服务)upload_profile- 将新的萃取配置文件保存到机器(会更改机器;需要经过身份验证的服务)。仅创建新配置文件,不会更新现有配置文件;机器每次都会分配新的 ID,因此上传两次会留下两个配置文件delete_profile- 从机器中删除配置文件(具有破坏性且不可撤销;需要经过身份验证的服务)。确认时必须原样输入配置文件名称;不能删除当前选中的配置文件;即使宿主中设置了“始终允许”,也始终会提示请求批准
MCP 提示词 - 宿主以斜杠命令或菜单项形式呈现的工作流模板:
dial_in_new_bag- 首次萃取之前从未使用过的咖啡豆(豆种,以及可选的烘焙程度、粉量和期望的杯中风味)diagnose_last_shot- 根据刚才萃取的风味分析这次萃取(问题所在,以及可选的调整内容)choose_profile- 为某款咖啡选择机器上实际存在的配置文件(烘焙程度,以及可选的饮品和备注)espresso_shot_analyst- 将调参指导作为系统提示词(内容与get_dial_in_guidance相同)
每个工作流提示词都会按顺序列出要调用的工具,让分析以机器自身的数据为依据,而不是凭空猜测。
MCP 资源 - 配置文件数据的 gaggiuino://profiles 和 gaggiuino://profiles/{id}
快速开始
此服务以多架构镜像(linux/amd64、linux/arm64)的形式发布于
ghcr.io/ljcl/gaggiuino-mcp,
无需克隆或构建。它也已列入
MCP Registry,
名称为 io.github.ljcl/gaggiuino-mcp。
1. 下载并配置
mkdir gaggiuino-mcp && cd gaggiuino-mcp
curl -O https://raw.githubusercontent.com/ljcl/gaggiuino-mcp/main/docker-compose.yml
curl -o .env https://raw.githubusercontent.com/ljcl/gaggiuino-mcp/main/.env.example
编辑 .env,填写你的 Gaggiuino 机器地址:
# Use the IP directly (recommended)
GAGGIUINO_URL=http://192.168.1.100
# Or if mDNS works on your network
GAGGIUINO_URL=http://gaggiuino.local
2. 启动服务
docker compose up -d
3. 验证
curl http://localhost:8000/health
服务可通过 http://<your-docker-host>:8000/mcp 访问。
选择版本
compose 文件使用 latest,它始终指向最新版本。若要固定使用某个版本,请在 .env 中设置
GAGGIUINO_MCP_TAG:
GAGGIUINO_MCP_TAG=5.0 # newest 5.0.x patch
GAGGIUINO_MCP_TAG=5.0.0 # exact release
未发布的 main 分支构建版本会以 edge(以及 main-<sha>)标签发布。这些版本可能包含尚未在任何正式版本中公布的破坏性变更,因此仅建议在正式发布前试用修复时使用。
使用以下命令升级:
docker compose pull && docker compose up -d
配置
环境变量
| 变量 | 默认值 | 描述 |
|---|---|---|
GAGGIUINO_URL | http://gaggiuino.local | Gaggiuino 机器的 URL |
PORT | 8000 | MCP 服务的端口 |
HOST | 0.0.0.0 | 绑定的主机 |
MCP_PUBLIC_URL | (未设置) | 客户端访问此服务的公共 https 源,不含路径(例如 https://box.tailnet.ts.net)。与 MCP_OAUTH_SECRET 一同设置即可启用 OAuth。此 URL 会作为 OAuth resource 公告,因此必须与在客户端中输入的 URL 完全一致。 |
MCP_OAUTH_SECRET | (未设置) | 用于签发自有 OAuth 令牌的签名密钥,至少 32 个字符(openssl rand -hex 32)。请在重启之间保持此值不变,以便客户端保持登录状态。只设置这两个变量中的一个会导致启动失败。 |
MCP_OAUTH_PASSPHRASE_HASH | (未设置) | 连接客户端时,你在同意页面输入的密码短语的 scrypt 哈希值。内置授权服务器启用时必须设置此变量——否则任何访问同意页面的人都能获得令牌,因此服务会拒绝启动。使用 cd apps/server && bun run hash-passphrase 生成;切勿存储密码短语本身。 |
MCP_OAUTH_ISSUER | (未设置) | 将令牌签发委托给你已运行的身份提供方(Authentik、Keycloak、Authelia、Zitadel、Kanidm、tsidp)。设置此变量后,此服务不会提供自己的 OAuth 端点,而是根据身份提供方的 JWKS 验证 RS256/ES256 令牌。需要设置 MCP_PUBLIC_URL;若同时设置 MCP_OAUTH_SECRET 或 MCP_OAUTH_PASSPHRASE_HASH,则会拒绝启动。请参阅使用外部身份提供方。 |
MCP_ALLOWED_ORIGINS | (空) | 允许调用 /mcp 的浏览器源,以逗号分隔。* 表示允许任何源(不安全)。 |
MCP_ALLOWED_HOSTS | (空) | 接受的 Host 标头值,以逗号分隔。留空则禁用检查。 |
LOG_LEVEL | info | debug、info、warn、error 或 silent。日志以每行一个 JSON 对象的形式输出到 stderr。 |
健康状态和日志
GET /health 返回 JSON:
{
"status": "ok",
"version": "3.2.0",
"uptimeSec": 3412,
"machine": {
"url": "http://gaggiuino.local",
"state": "unreachable",
"lastCheckedAt": "2026-07-27T21:11:15.274Z",
"lastError": "Unable to connect. Is the computer able to access the url?",
"versions": {
"coreVersion": "a06f97fd",
"frontVersion": "a06f97fd",
"staticVersion": "a06f97fd"
}
}
}
只要进程仍在运行,它就会返回 200,即使机器无法访问——你的咖啡机一天中的大部分时间都处于关闭状态,而容器健康检查会读取状态码。machine.state 的值为 ok、unreachable 或 unknown,根据服务已有请求观察得出,而非通过探测获取,因此 /health 不会给机器增加额外负载。machine.versions 也以同样的方式观察得出——首次读取机器设置(get_machine_settings)时才会记录,/health 本身不会获取此值——因此其中的 null 表示“此服务尚未读取设置”,而非“机器未报告版本”。这里只公开这三个已记录的字段:/health 无需身份验证,因此未来固件在 versions 下新增的任何内容都不会出现在其中,除非有人另行决定。
日志以每行一个 JSON 对象的形式输出,因此你可以筛选所需内容:
docker compose logs -f | jq -c 'select(.event == "tool.call" and .outcome != "ok")'
保护端点
在将此服务暴露到局域网之外之前,请启用 OAuth。 以下每种隧道选项都会将 /mcp 暴露到公共互联网;如果没有身份验证,任何知道该 URL 的人都能在你家中的设备上使用全部工具。未配置任何内容时,服务会在启动时打印警告。
必须同时设置以下三个变量:
# 1. The public origin clients will reach the server on — no path, https only.
# This is advertised as the OAuth `resource`, so it must match the URL you
# type into Claude exactly.
MCP_PUBLIC_URL=https://your-machine.tail-scale.ts.net
# 2. The key your tokens are signed with. Keep it stable across restarts, or
# every restart signs you out of your phone.
openssl rand -hex 32
# 3. The passphrase you will type on the consent page. Store the hash, never
# the passphrase — this prompts and prints the whole line to paste in.
cd apps/server && bun run hash-passphrase >> ../../.env
然后重新创建容器——仅执行普通重启是不够的:
docker compose up -d --force-recreate
Compose 记录的是 env_file 路径的列表,而非文件内容。因此,编辑 .env 后运行 docker compose up -d(或 restart),可能会继续使用现有容器及其创建时的环境变量。新变量不会传递给进程,服务会像你未设置它们一样,在未启用身份验证的情况下启动——也不会报错,因为从服务的角度来看,什么都没有配置。检查实际传入了哪些变量:
docker inspect gaggiuino-mcp --format '{{range .Config.Env}}{{println .}}{{end}}' | grep MCP_
只设置其中一部分会导致启动失败,并指出缺少哪个变量。这是有意为之:静默回退到开放端点,正是有人误以为隧道受到保护而将其暴露出去的原因。**排查问题时还要记住一个推论:**如果服务正在运行且未启用身份验证,那它就没有收到这三个变量中的任何一个——如果你认为自己已经设置了它们,那么过期的是容器,而不是配置有误。
为什么使用 OAuth,而不是共享令牌
此服务只接受 OAuth 凭据,因为**只有 OAuth 凭据能由 Claude 连接器提供。**连接器是在账户级别添加的,因此同一个条目必须适用于 claude.ai、Claude Desktop 和 iOS;而在个人套餐中,“添加自定义连接器”对话框提供的是 OAuth Client ID 和 Secret,没有请求头字段。本地 stdio 桥接也行不通——它无法在 iOS 上运行。共享密钥请求头会留在 .env 中,却没有客户端能够发送它,因此在此项目专为其构建的部署方式中,写入工具将永远无法使用。(如果较早的 2.0 之前安装版本在长期保留的 .env 中仍有 MCP_AUTH_TOKEN,请删除该行:没有任何代码会读取它,而且它不会限制 /mcp。)
连接时,Claude 会发现该端点,并跳转到此服务提供的同意页面,然后由你输入密码短语。无需注册,也无需保存客户端密钥。
各部分的保护机制
/health 和 /.well-known/* 发现文档故意不要求身份验证——容器的健康检查不会提供凭据,而客户端为了进行身份验证而获取的文档本身不能要求身份验证。
select_profile、upload_profile 和 delete_profile——这些会更改设备的工具——需要 espresso:write 作用域。没有该作用域的令牌会收到 403,促使 Claude 向你请求额外权限,而不是悄无声息地失败。如果完全没有配置,它们会拒绝运行并说明原因;这就是为什么对局域网来说,开放服务是一个可以接受的默认设置,而用于控制设备的工具则不是。
除非请求中的 Origin 标头所指的源已列在 MCP_ALLOWED_ORIGINS 中,否则请求会被拒绝。这可以阻止你访问的任意网页向运行在你自己网络上的服务发送 POST 请求——令牌在这里帮不上忙,因为浏览器会替你发送令牌。没有 Origin 的请求(Claude Desktop、curl,以及任何非浏览器请求)不受影响,因此默认的空列表对几乎所有人来说都是正确的设置。
列出某个源后,/mcp 也会响应来自该源的 CORS 预检请求,并回显 Access-Control-Allow-Origin(以及 Access-Control-Expose-Headers: mcp-session-id,否则浏览器客户端虽然能读取握手信息,却无法读取继续通信所需的会话 ID)。允许一个随后会被浏览器拦截的源,就等于配置了一个什么也不允许的允许列表。
scripts/test-auth.sh 会探测正在运行的服务,检查上述所有内容——发现链、401 的格式、跨主机重定向和源验证。**请从局域网外部运行它。**它发现的每个问题,都是 Claude 访问该 URL 时遇到的问题;最常见的问题是 MCP_PUBLIC_URL 与你输入 Claude 的 URL 不一致,而这个问题在 localhost 上无法发现。
BASE_URL=https://your-machine.tail-scale.ts.net ./scripts/test-auth.sh
自定义
服务附带通用配置文件和提示词。启动时,会将两个本地覆盖文件合并到默认配置之上:
-
prompts.local.yaml- 设备专用的调节指导(你的磨豆机型号、滤篮及其他设备详情)。 -
profiles.local.yaml- 你自己的配置文件,或对默认配置的覆盖/移除(将配置文件 ID 设为null即可移除)。
从示例开始:
curl -O https://raw.githubusercontent.com/ljcl/gaggiuino-mcp/main/apps/server/src/data/prompts.example-local.yaml
curl -O https://raw.githubusercontent.com/ljcl/gaggiuino-mcp/main/apps/server/src/data/profiles.example-local.yaml
这些文件包含个人设备配置,因此发布的镜像中不会包含它们。要使用这些文件,请取消注释 docker-compose.yml 中的 volumes: 块:
volumes:
- ./profiles.local.yaml:/app/apps/server/src/data/profiles.local.yaml:ro
- ./prompts.local.yaml:/app/apps/server/src/data/prompts.local.yaml:ro
如果是从仓库检出代码,请将每个 *.example-local.yaml 复制为同目录下的 *.local.yaml,位置在 apps/server/src/data/ 中——这些文件已加入 gitignore,并会自动加载。
缺少覆盖文件没有问题。如果覆盖文件存在但无法读取(例如,容器用户 UID 65534 无权读取某个绑定挂载),或文件不是有效的 YAML,或格式不正确,服务就会在启动时停止。日志行是 config.invalid,其中会指出文件名和错误的键。服务不会忽略损坏的覆盖文件并继续运行。
连接 AI 工具
许多 AI 工具(例如 Claude Desktop)会通过自己的服务路由 MCP 请求,而不是从你的本地计算机发出。这意味着你的 MCP 服务需要可通过公开的 HTTPS URL 访问。
本节中的所有选项都会将
/mcp发布到互联网。请先启用 OAuth——参见保护端点——并将MCP_PUBLIC_URL设置为你即将发布的确切源站地址。
无论选择哪种入口,以下三点决定了它是否能正常工作:
- 所有内容都由同一源站提供。
/mcp、/.well-known/*和/oauth/*都 必须可通过MCP_PUBLIC_URL访问。这样才能只使用这一个 容器。 - 使用稳定的主机名。 重启后会变更的快速隧道主机名会改变所公布的
resource,导致连接器每次都会中断。 - 不要跨主机重定向。 如果已注册的 URL 通过
301/302/307/308重定向到 其他主机,Authorization标头会在重定向过程中被丢弃。这通常是“在 MCP Inspector 或 Claude Code 中可用,但在 claude.ai 中不行”的原因——服务器前方的裸域名到www规范化是最常见的触发因素。scripts/test-auth.sh会对此进行检查。
Claude 会根据 URL 全局缓存发现文档约 5 分钟,因此元数据更改不会立即生效——修复故障部署后,错误的元数据仍可能继续提供几分钟。
Tailscale Funnel(推荐)
Tailscale Funnel 可通过安全的 HTTPS URL 将你的服务公开到互联网:
tailscale funnel --bg 8000
# URL: https://your-machine.tail-scale.ts.net/mcp
# then, in your .env
MCP_PUBLIC_URL=https://your-machine.tail-scale.ts.net
使用 Funnel,而不是 Serve。 Claude 从 Anthropic 自己的基础设施访问你的连接器;如果任何解析出的地址无法通过全球路由,它就会拒绝该主机名,甚至不会发送一个字节——其中明确包括 100.64.0.0/10,这是 tailnet 自己使用的地址范围。tailscale serve 发布的地址正是这些。Funnel 发布的则是指向 Tailscale 中继的公共记录。
连接器也仅支持 IPv4,因此无法访问只发布 AAAA 记录的主机名。以下一项检查即可同时验证这两点:
dig +short your-machine.tail-scale.ts.net # must return a routable IPv4 address
有两种常见做法不应采用:
- 不要将 Anthropic 的出口地址段(
160.79.104.0/21)加入允许列表以进行访问控制。 Funnel 不会转发客户端 IP,而/oauth/authorize是由你自己的浏览器访问的,并非由 Anthropic 访问——因此 IP 允许列表会导致登录失败,却没有任何保护作用。 - 不要信任
Tailscale-User-Login标头。tailscaled确实会剥除伪造的副本,但仅限于它代理的流量。本项目使用network_mode: host和HOST=0.0.0.0,因此监听器可被直接访问,主机上的任何程序都能自行设置该标头。若信任该标头,就会导致一种可进行写操作的认证绕过。
Cloudflare Tunnel
使用 cloudflared 创建一条通往你服务的持久隧道。请使用具有自有主机名的命名隧道,而不是快速隧道——trycloudflare.com 主机名每次重启都会改变,MCP_PUBLIC_URL 也必须随之更改。
ngrok
ngrok http 8000
同样需要注意:免费 ngrok 主机名会变更。请预留一个域名,否则每次都需要重新添加连接器。
位于你自己的反向代理之后
将三个路径——/mcp、/.well-known/* 和 /oauth/*——都代理到同一个上游,并将 MCP_PUBLIC_URL 设置为公开源站地址。代理不得跨主机进行规范化(参见上文的重定向说明),并且必须原样传递 Authorization 标头。
使用外部身份提供方
如果你已在运行 Authentik、Authelia、Keycloak、Zitadel、Kanidm 或 tsidp,
请将 MCP_OAUTH_ISSUER 设置为其颁发者 URL,此服务便不再充当
授权服务器:
MCP_PUBLIC_URL=https://gaggiuino.tail1234.ts.net
MCP_OAUTH_ISSUER=https://idp.example.com/realms/home
/oauth/authorize、/oauth/token 和
/.well-known/oauth-authorization-server 将完全停止提供服务,而受保护资源的
元数据会将 Claude 指向你的颁发者。此处不再签署任何内容,因此
MCP_OAUTH_SECRET 和 MCP_OAUTH_PASSPHRASE_HASH
不再有用——如果同时设置 MCP_OAUTH_ISSUER,服务会在启动时拒绝
此类设置,而不会忽略它们,因为忽略设置会让你误以为会显示实际上永远不会
呈现的同意页面。
MCP_PUBLIC_URL 仍为必填项:令牌的受众会根据它进行校验。
无论使用哪种产品,身份提供方都必须满足以下四项要求:
- 在
code_challenge_methods_supported中声明支持 S256 PKCE。 - Anthropic 的出口网段
160.79.104.0/21能够访问发现端点。 身份提供方前方的 WAF 会破坏此流程,即使此服务本身完全可以正常访问。 - 令牌受众等于
MCP_PUBLIC_URL+/mcp。 Keycloak 需要为此配置 Audience-mapper 客户端范围——它不支持当前规范版本中的 RFC 8707,因此规范中 要求最严格的一项属性需要在此处手动配置。 - 重定向 URI 为
https://claude.ai/api/mcp/auth_callback;如果 使用 Claude Code,还要添加不限定端口的http://localhost/callback和http://127.0.0.1/callback。
不要求动态客户端注册,这一点值得明确说明,因为它改变了兼容性情况: Claude 接受粘贴到连接器对话框中的预注册客户端 ID,因此 Authelia、 Authentik、Zitadel 和 Kanidm——这些产品都不支持 DCR——只需手动创建一个客户端, 就都可以正常使用。
令牌必须使用 RS256 或 ES256 签名。不会验证其他算法,这是一项有意的限制,
而非疏漏:使用从 JWKS 获取的密钥来验证 HS256,会造成经典的算法混淆攻击,
攻击者可以使用颁发者的公钥签署令牌。
发现过程会先遵循 RFC 8414,若失败则回退到
/.well-known/openid-configuration,因此仅发布 OpenID 文档的颁发者也能直接使用。
scripts/test-auth.sh 已会针对 authorization_servers 指向的端点探测此链路,
因此它既能诊断外部颁发者,也能诊断内置颁发者。
关于 tsidp,这里需要说明其成本,而非推荐使用:这意味着要增加第二个容器、
持久化 /data 卷、为 allow_dcr 授予 tailnet ACL 权限、设置
TAILSCALE_USE_WIP_CODE=1,并依赖一个自称处于实验阶段且版本低于 1.0 的上游项目。
如果你已经在深度使用 Tailscale,可以考虑它;否则,内置授权服务器需要维护的组件更少。
仅限本地网络
如果你的 AI 工具直接连接(例如使用本地 MCP 服务配置),请使用直接地址:
http://<docker-host-ip>:8000/mcp
添加到 Claude Desktop
- 前往 设置 > 集成 > 添加更多 > 添加远程 MCP 服务
- 将 URL 设置为你的公共 HTTPS 端点(例如
https://your-machine.tail-scale.ts.net/mcp) - 保存并启用
架构
AI Tool (Claude Desktop, etc.)
|
| HTTPS
v
+-----------------------------+
| HTTPS Tunnel |
| (Tailscale / Cloudflare / |
| ngrok / reverse proxy) |
+-----------------------------+
|
| HTTP (localhost:8000)
v
+-----------------------------+
| Gaggiuino MCP Server |
| (Docker container) |
| Bun + Streamable HTTP |
+-----------------------------+
|
| HTTP (local network)
v
+-----------------------------+
| Gaggiuino |
+-----------------------------+
/mcp 端点仅通过官方 v2 MCP SDK 提供无状态的 2026-07-28 MCP 修订版:
每个请求都会在 _meta 中携带其协议版本和客户端信息;没有 initialize 握手,也没有会话;
server/discover 是可选的能力探测接口。仍使用 2025 修订版(2025-03-26 至
2025-11-25)的客户端会收到 HTTP 400 和 JSON-RPC 错误 -32022,其
data.supported 会列出 2026-07-28。GET 和 DELETE 请求会返回 405。
由于每个请求都会根据其 Mcp-Method 和 Mcp-Name
标头进行校验(不匹配时返回 HTTP 400、-32020),反向代理或隧道无需解析 JSON 正文,
便可按工具名称记录、计量或拦截请求——例如,在边缘拦截
delete_profile。
开发
git clone https://github.com/ljcl/gaggiuino-mcp.git
cd gaggiuino-mcp
bun install # Install all dependencies
bun run build # Build all packages (Turborepo)
bun run test # Run all tests
bun run lint # Lint all packages
bun run check # lint + test + typecheck + build + knip + boundaries + size
# Server
cd apps/server
bun run dev # Watch mode
bun run test # Server tests only
# Shot graph UI (run from the repo root)
bun run storybook # Storybook on port 6006
# Regenerate JSON schemas (after changing Zod schemas in loader.ts)
cd apps/server
bun run generate-schemas
main 分支的 Storybook 已发布到 GitHub Pages:ljcl.github.io/gaggiuino-mcp — 这是一个静态构建,无需在本地运行任何程序即可浏览冲煮曲线图和 UI 组件。
无咖啡机时运行
咖啡机一天中大部分时间都处于关闭状态,而且你可能根本不在它所在的网络中。bun run fake-machine 会提供录制的 /api/* 负载,让服务有内容可供交互:
bun run fake-machine # port 8080; --port N to change it
# in another shell
GAGGIUINO_URL=http://localhost:8080 bun run dev
这样你就拥有了一个可运行的服务:get_status、list_profiles、
get_profile_info、get_maintenance_status,以及两个真实的约 190 个样本的冲煮记录,可用于 get_shot_data 和 view_shot_graph。工具发出请求后,/health 会报告 machine.state: "ok";读取设置后,则会填充 machine.versions。
这些负载是录制的,而非编造的——状态响应是从真实硬件上原样捕获的,设置和配置定义来自随附的参考资料,冲煮记录也是真实捕获的数据。测试会通过相同的路由表驱动真实客户端,因此,如果客户端会拒绝某个负载,该问题就会导致 CI 失败,而不会悄悄让模拟程序使用固件并不支持的通信格式。
它不是什么。 它不能替代硬件验证,而且有些事情从设计上就无法实现:
- 写入操作会返回 501。 它不保存任何状态,因此如果确认了
select_profile,紧接着的list_profiles就会与之矛盾。 - 它无法复现任何真正由固件特性导致的情况:ESP32 在将冲煮记录写入闪存时返回的 503、一次只处理一个请求的串行机制、仅通过 WebSocket 进行的配置更新路径,或尚未有人捕获过的固件版本中的类型不一致问题。
- 冲煮记录 ID 会偏移到
900000000+范围,这样测试样例中的冲煮记录就不会被误认为是你自己的记录。
它永远不会随产品发布:可执行文件位于仓库根目录下的 scripts/ 中,该目录不属于 Docker 构建上下文;它的负载也会与其余测试脚手架一同从镜像中排除。
Docker
docker-compose.yml 会拉取已发布的镜像。若要改为从当前检出版本构建并运行镜像,请在其上叠加构建覆盖配置:
docker compose -f docker-compose.yml -f docker-compose.build.yml build
docker compose -f docker-compose.yml -f docker-compose.build.yml up -d
docker compose logs -f
该覆盖配置会将结果标记为 ghcr.io/ljcl/gaggiuino-mcp:dev,确保本地构建不会被误认为是已发布的版本。
故障排除
无法连接到 gaggiuino.local
mDNS(.local 主机名)可能无法在 Docker 容器中正常工作。请在 GAGGIUINO_URL 中直接使用 IP 地址。
AI 工具无法连接到服务
请确保你的服务可通过 HTTPS 从公网访问。Claude Desktop 等工具会通过自己的服务器转发请求,因此,仅限 Tailnet 的访问方式(例如 tailscale serve)无法使用——你需要设置一个公网隧道(例如 tailscale funnel)。
服务已启动,但无法连接到 Gaggiuino
容器使用 network_mode: host 来共享主机的网络堆栈。如果你的 Gaggiuino 位于不同的网络分段,请调整 Docker 网络配置。
许可证
MIT © Luke Clark
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。