🎃 Add an opt-in Prometheus /metrics endpoint for tool calls
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 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
- 平均マージ
- 6時間 15分
- マージ済み PR(30日)
- 141
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
HelpCode-ai/anythingmcp のほかの issue
-
Docs: document all SMTP_* and other undocumented env vars for self-hosting対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープンenhancement
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
HelpCode-ai/anythingmcp#1049 · コメント 2 件 ·
メンテナーはふだん 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 日以内に返信
-
good first issue hacktoberfest
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
HelpCode-ai/anythingmcp#745 ·
メンテナーはふだん 1 日以内に返信
HelpCode-ai/anythingmcp の issue をすべて見る
似ている issue
-
e2e-failure ready-to-code
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
redhat-developer/rhdh-plugin-export-overlays#4391 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 1/5 1時間未満 初心者へのやさしさ 82/100
Luligu/matterbridge#655 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
software-mansion/argent#1457 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
schmayterling/hibi#245 ·
メンテナーはふだん 1 日以内に返信
-
After a failed save, the red error banner stays on every page until the app is reloaded対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープンai-driven-qa bug claude Minor
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
linagora/twake-contacts-frontend#241 ·
メンテナーはふだん 1 日以内に返信