feat(mcp): distribution-channel client marker in MCP telemetry (#706)

* feat(mcp): distribution-channel client marker in MCP telemetry

Record an optional client marker on mcp.tool_called, mcp.tools_list_called
and mcp.resource_read events so per-channel adoption (e.g. the OpenClaw
skill) is measurable in event_log (180-day TTL).

- Server reads X-Gnubok-Client header, falling back to a ?client= query
  param on the endpoint URL. Sanitized ([A-Za-z0-9._-]{1,64}, lowercased),
  telemetry-only — same trust level as Mcp-Session-Id, never auth.
- The query param works with the already-published gnubok-mcp 1.0.1 via
  GNUBOK_URL, so no npm release is required to start measuring.
- Bridge 1.1.0 additionally forwards GNUBOK_CLIENT as X-Gnubok-Client.

OAuth-path attribution via DCR client_name is a possible follow-up — DCR
is stateless today, so client_name isn't recoverable at token time.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(mcp): address PR #706 compliance findings

- ropa.yaml: declare the distribution-channel marker in the mcp.telemetry
  processing activity (GDPR Art. 30 — RoPA was drifting from actual flow)
- bridge: mirror the server's allow-list on GNUBOK_CLIENT so an invalid
  value degrades to no header instead of fetch() rejecting every request
- lib/events/types.ts: annotate client as client-supplied/telemetry-only
- test: pin that the allow-list runs on the percent-decoded ?client= value

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-06-10 14:33:24 +02:00
committed by GitHub
co-authored by Claude Fable 5
parent f9ea9c0082
commit 205610d200
7 changed files with 139 additions and 10 deletions
+4
View File
@@ -168,6 +168,8 @@ export type CoreEvent =
userId: string
companyId: string
sessionId: string | null // from Mcp-Session-Id header; null if absent
client: string | null // distribution-channel marker (X-Gnubok-Client header / ?client= param, e.g. 'openclaw').
// Client-supplied (allow-list-sanitized) — telemetry only, never identity or authz.
}}
// tools/list — informs us whether agents are using progressive discovery
// (gnubok_search_tools) or pulling the full list. Tool counts vary with
@@ -182,6 +184,7 @@ export type CoreEvent =
userId: string
companyId: string
sessionId: string | null // from Mcp-Session-Id header; null if absent
client: string | null // distribution-channel marker; null if absent
}}
// resources/read — informs us which skills/widgets/data resources actually
// get loaded by agents. `kind` discriminates by URI scheme so we can
@@ -199,6 +202,7 @@ export type CoreEvent =
userId: string
companyId: string
sessionId: string | null // from Mcp-Session-Id header; null if absent
client: string | null // distribution-channel marker; null if absent
}}
// Workflow lifecycle — agents declare "I'm starting month-end-close" via
// gnubok_load_skill (or implicitly by following a skill's recommended tool