🎃 Add an opt-in Prometheus /metrics endpoint for tool calls
维护者通常 1 天内回复
@rangarajan19 已经在做这个了。
开始于 2026年10月17日。
评估
- 难度
- 3/5
- 预计耗时
- 1-2 天
- 新手友好度
- 56/100
- Issue 类型
- 功能
- 描述清晰度
- 描述清楚
- 活跃度
- 活跃
- 技术栈
- grafana, nodejs, prometheus, typescript
- 领域
- backend, observability
调研方向
Start with logToolCall() in packages/backend/src/mcp-server/dynamic-mcp-tools.ts, where the metrics recording belongs, and with tracing.ts as the env-gated no-op pattern to copy. Then read processGauges.snapshot() in common/process-vitals.ts, app.listen in main.ts and isCloud() in common/deployment.service.ts. Done means the new src/common/metrics.spec.ts passes via npx jest src/common/metrics, /metrics returns the counters after a tool call, and the Cloud and token rules from the acceptance list hold.
由索引模型根据 Issue 内容生成。
描述
Part of the October Challenge #997.
Why
Operators who self-host AnythingMCP want tool-call rates, error rates and latency in Grafana, next to the rest of their stack. Today they only have the Pino tool_call log lines, which they have to parse themselves, and the database (ToolInvocation). Our ROADMAP lists "per-tool latency histograms, error-rate dashboards". Most self-hosted gateways (Kong, Traefik, n8n) ship a Prometheus endpoint.
Self-hosted vs Cloud
- Self-hosted: off by default. When
METRICS_ENABLED=true, the backend starts a second, separate HTTP listener for/metrics(it does not use the main app port 4000). Available in Community; no licence gate. - Cloud (
DEPLOYMENT_MODE=cloud): the operator (us) uses the same code. Two extra rules: the listener refuses to start withoutMETRICS_TOKEN(it logs an error and metrics stay off), and per-tool/per-connector labels are always off. No series may carry an organization id, connector name, tool name or user. This prevents cross-tenant leaks and keeps cardinality bounded with thousands of workspaces. Per-org usage already exists in the database (AuditService.getBreakdowns). - Who can use it: whoever operates the instance (env vars). There are no app roles, no dashboard UI and no org scoping, because metrics are instance-wide by design. That is why they must never be reachable through the public port.
What to build
- Library: use
prom-client. Recommendation and reasons:@opentelemetry/exporter-prometheuswould hook into the existingNodeSDK(tracing.ts). But that SDK only starts whenOTEL_EXPORTER_OTLP_ENDPOINTis set, its 0.x packages must move in lockstep with@opentelemetry/sdk-node(^0.222.0), and Node runtime metrics need extra packages.prom-clientgivescollectDefaultMetrics()(event loop lag, heap, GC) for free and is trivial to unit-test (await register.metrics()returns a string). Wrap it in a small facade (src/common/metrics.ts:recordToolCall(...)), so a later switch to OTel metrics touches one file. - Env vars:
METRICS_ENABLED(defaultfalse),METRICS_PORT(default9464),METRICS_HOST(default0.0.0.0, because in Docker the Prometheus container must reach it over the compose network; the port is not published indocker-compose.yml),METRICS_TOKEN(optional on self-hosted, required on Cloud, compared withtimingSafeEqual),METRICS_TOOL_LABELS(defaultfalse, ignored on Cloud with a warning). - Listener: a plain
node:httpserver started frommain.tsafterapp.listen(), closed on shutdown. It serves onlyGET /metrics. Any other path returns 404, and a wrong or missing token returns 401. - Metrics (prefix
anythingmcp_):tool_calls_total{connector_type,status}counter (status =success|error)tool_call_duration_seconds{connector_type,status}histogram (buckets roughly 0.05 to 60 s; tool calls can take 20 s or more)tool_call_upstream_errors_total{connector_type,upstream_status_class}(4xx|5xx|none)- with
METRICS_TOOL_LABELS=true(self-hosted only), addconnector_idandtoollabels. Use the connector id (cuid), not the name. - gauges mirrored from the existing
processGauges(toolCallsInFlight,mcpRequestsInFlight,registeredTools…), using acollect()callback collectDefaultMetrics()
Where to look
packages/backend/src/mcp-server/dynamic-mcp-tools.ts:130:logToolCall()is called on both the success and the error path and already hasdurationMs,status,upstreamStatusandconnectorType. Record the metrics here.packages/backend/src/common/process-vitals.ts:246:ProcessGauges/processGauges.snapshot(), existing in-process gauges to expose.packages/backend/src/tracing.ts:26: the existing opt-in OTel setup (pattern for an env-gated, no-op-by-default module).packages/backend/src/main.ts:160:app.listen(port). Start the metrics listener after it.packages/backend/src/health/sentry-verify.controller.ts:27:sameSecret(), a constant-time token comparison to reuse.packages/backend/src/common/deployment.service.ts:10:isCloud().deploy/cloud/Caddyfile:91anddocs/deployment.md:221:/health*is proxied publicly on Cloud and self-hosted. That is why metrics must not live under/healthor on port 4000.
Acceptance criteria
- With
METRICS_ENABLEDunset, no listener starts andprom-clientdefault metrics are not collected. -
GET :9464/metricsreturns Prometheus text format, and the counters and histogram move after a tool call. - With
METRICS_TOKENset, a missing or wrongAuthorization: Bearerreturns 401. - With
DEPLOYMENT_MODE=cloudand no token, the listener does not start. WithMETRICS_TOOL_LABELS=trueon Cloud, notool/connector_idlabel appears. - Unit tests in
src/common/metrics.spec.ts(labels, Cloud rules, token check). Run withcd packages/backend && npx jest src/common/metrics. - Docs: new section "Prometheus metrics (opt-in)" in
docs/operations/observability.md, with a sample scrape config, plus the env vars in the table indocs/deployment.md(around line 338).
Out of scope
- Grafana dashboards or alert rules (a follow-up issue).
- Pushing metrics via OTLP.
- Per-organization metrics or any UI.
- Changing
docker-compose.cloud.ymlor the Cloud Caddyfile (maintainers wire scraping on Cloud).
Size
M (1–2 days)
How to claim
Comment "I'd like to work on this" and we'll assign you. Rules in #997.
- 主要语言
- TypeScript
- 星标
- 856
- 派生
- 82
- 平均合并
- 5 小时 18 分钟
- 30 天内合并 PR
- 162
环境准备
这个项目没有提供开发容器、Dockerfile 或贡献指南,环境需要你自己搭建:先看它的 README,通用步骤见我们的新手贡献指南。
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
HelpCode-ai/anythingmcp 的其他 Issue
-
Docs: document all SMTP_* and other undocumented env vars for self-hosting可能已有人在做 @notlakshitha 于 3 天前认领。 未关闭enhancement
难度 2/5 1-3 小时 新手友好度 82/100
HelpCode-ai/anythingmcp#1049 · 3 条评论 ·
维护者通常 1 天内回复
-
good first issue hacktoberfest
难度 2/5 半天 新手友好度 70/100
HelpCode-ai/anythingmcp#996 ·
维护者通常 1 天内回复
-
good first issue hacktoberfest
难度 2/5 半天 新手友好度 72/100
HelpCode-ai/anythingmcp#995 ·
维护者通常 1 天内回复
-
enhancement good first issue hacktoberfest help wanted
难度 2/5 1-3 小时 新手友好度 78/100
HelpCode-ai/anythingmcp#982 ·
维护者通常 1 天内回复
-
Upload the new social preview可能已有人在做 @helpcodeai 于 3 天前认领。 未关闭
HelpCode-ai/anythingmcp#1087 · 6 条评论 · 已指派 1 人 ·
维护者通常 1 天内回复
查看 HelpCode-ai/anythingmcp 的全部 Issue
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 62/100
umami-software/umami#4637 ·
维护者通常 3 天内回复
-
Hotkeys fire when pressed with the Ctrl key, so browser shortcuts also trigger player actions可能已有人在做 @RaphaelFakhri 今天认领。 未关闭
难度 2/5 1-3 小时 新手友好度 75/100
muxinc/media-chrome#1332 ·
维护者通常 5 天内回复
-
难度 2/5 1-3 小时 新手友好度 78/100
polarsource/polar#15580 ·
维护者通常 1 天内回复
-
accepted bug via-triage
难度 2/5 1-3 小时 新手友好度 69/100
pingdotgg/t3code#20931 · 1 条评论 ·
维护者通常 1 天内回复
-
bug/1-unconfirmed kind/bug
难度 2/5 1-3 小时 新手友好度 78/100
维护者通常 1 天内回复