Skip to content

feat(http): send x-mcp-user-id header with the caller's DGC user UUID - #164

Draft
charlie-ang-collibra wants to merge 3 commits into
mainfrom
feat/mcp-user-id-header
Draft

charlie-ang-collibra wants to merge 3 commits into
mainfrom
feat/mcp-user-id-header

Conversation

@charlie-ang-collibra

@charlie-ang-collibra charlie-ang-collibra commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

🎯 What does this PR do?

Every request chip sends to DGC now carries an x-mcp-user-id header. It holds the UUID of the user the request's Authorization value belongs to. This lets DGC attribute MCP traffic to a user.

The UUID comes from GET /rest/2.0/users/current, called with the same credential. The result is cached per credential, so each credential costs at most one lookup per cache lifetime.

RoundTrip(request):
    drop any x-mcp-user-id sent by the MCP client
    if request has Authorization:
        id = resolve(Authorization)            # cache keyed by sha256(Authorization)
        if id != "": set x-mcp-user-id = id
    forward to DGC

resolve(authorization):
    cached, unexpired entry   -> return its id ("" for a cached failure)
    lookup already in flight  -> wait for it
    otherwise                 -> GET /rest/2.0/users/current (500 ms timeout)
        200 with id                       -> cache id for 10 min
        4xx except 408/429, 200 w/o id    -> cache "" for 10 min
        timeout, network error, 5xx,
        408, 429, bad body, panic         -> cache "" for 30 s

The lookup is abandoned after 500 milliseconds. A failed lookup sends the request without the header. The short 30 second lifetime for transient failures keeps a struggling DGC from getting a lookup on every request. It also lets the header return soon after DGC recovers.

Tracked as DEV-233767.

✅ Checklist

  • My code follows the style guidelines of this project.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have made corresponding changes to the documentation (if needed).
  • My commit messages follow the Conventional Commits standard.

Resolve the caller's DGC user UUID via GET /rest/2.0/users/current, cached per credential for 10 minutes in a bounded LRU cache, and send it as x-mcp-user-id. Failed lookups are cached too and leave the header off. The TASK_BRIEF file was not present in the workspace; the acceptance criteria were used as the specification.

Co-authored-by: betty-pr-factory-app[bot] <316249552+betty-pr-factory-app[bot]@users.noreply.github.com>
@betty-pr-factory-app

Copy link
Copy Markdown
Contributor

This pull request was created by the BETTY PR Factory App GitHub App on behalf of charlie-ang-collibra, through PESTO engineer git delegation.

PESTO run: ai-services-chip-legacy-7043d7

The commits and the pull request are authored by the engineer's own GitHub account; the App's involvement is recorded by this comment, by the betty-pr-factory-app label and by the Co-authored-by trailer on the commit.

@betty-pr-factory-app betty-pr-factory-app Bot added the betty-pr-factory-app Created by the BETTY PR Factory App GitHub App on behalf of an engineer (PESTO) label Nov 13, 2026

@charlie-ang-collibra charlie-ang-collibra left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: DEV-233767 send x-mcp-user-id header to DGC

The task brief wasn't in the workspace, so I reviewed against the acceptance criteria stated in the commit message and docs/CONFIG.md, plus docs/TOOL_CONTRIBUTION_STANDARDS.md. This change adds no MCP tool, so most of the standards don't apply. It follows 8.1 (public /rest/2.0/users/current), and the stepdown ordering is followed.

Verification: go build ./..., go vet ./cmd/... and golangci-lint run ./cmd/... (0 issues) are clean. go test ./cmd/chip/ passes, and the new tests pass 20 times in a row (-count=20). Every criterion has a test: lookup sharing, per-credential caching, TTL, LRU eviction, failure modes, no-auth, and inbound header stripping. (-count>1 on the whole package panics in the existing TestDataQualityConfigPrecedence with flag redefined: api-url. This PR didn't cause it.)

Findings (none block the merge):

  1. Transient failures turn the header off for 10 min. user_id.go resolve caches timeouts and 5xx the same way as a success. If that's intended, fine. Otherwise consider a shorter error TTL, or caching only 401/403.
  2. The first call per credential can take up to 10s longer (userIDLookupTimeout), and so can its concurrent callers. The header only serves attribution, so a shorter timeout would cap the cost. Worth mentioning in the impact analysis.
  3. No test covers the configured Api.Username/Password path. In that case the lookup runs as the service account.
  4. clients.GetCurrentUser already calls /users/current. Using it here would recurse back through this transport, so going direct is fine; add a one-line comment saying why.
  5. CONFIG.md's opening line says every request carries the header. The bullets below list the exceptions, so reword the opening to match.
  6. A panic in next.RoundTrip would skip complete and leave waiters blocked. defer the completion to close this off (low risk).

Shorten the lookup timeout to 2s, release waiters if a lookup panics, explain why clients.GetCurrentUser is not reused, test server-wide credentials and reword the CONFIG.md section. Failure caching for 10 minutes is kept as required by the acceptance criteria.

Co-authored-by: betty-pr-factory-app[bot] <316249552+betty-pr-factory-app[bot]@users.noreply.github.com>

@charlie-ang-collibra charlie-ang-collibra left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Runtime validation of PR #164 (collibra/chip): x-mcp-user-id header

What was tested

  • git rev-parse HEAD = 70be0c4ada6ae1735654d46fedb09a5d171e6a2e. git ls-remote origin refs/pull/164/head = 70be0c4ada6ae1735654d46fedb09a5d171e6a2e. They match.
  • PR binary: go build -o /tmp/v/chip-pr ./cmd/chip at that SHA. Default-branch binary: chip-main built from main @ 791fb15ae0650f9cb8b96511d706773f6c9e37b0 (git worktree).
  • DGC: http://dgc-gateway.dgc-f8e0ded1c023.svc.cluster.local:4400, taken from provision-env dgcEndpointDetails. It was reachable directly from this pod (GET /rest/2.0/users/current returned 200), so no port-forward was needed. The gateway is an nginx (Server: nginx/1.31.8, magneto-managed configmap dgc-gateway). It routes /rest/oauth/v2/* to the auth service and everything else to DGC.
  • Every chip ran as a customer runs it, with COLLIBRA_MCP_API_URL set to the URL above. Tools were called with raw MCP JSON-RPC tools/call (list_asset_types, {"limit":1}), with no model in the loop.

How chip's outbound headers were observed

chip has no log-level setting. cmd/chip/config.go has no log-level flag or env var, and the PR adds no debug line that prints x-mcp-user-id. chip's only output is INFO/WARN slog lines, and the PR's only new log line is a WARN when the lookup fails. The "chip debug log line" evidence that AC-2 and AC-7 ask for does not exist and cannot be produced without a code change. I did not change code.

Instead I used chip's existing proxy setting, COLLIBRA_MCP_API_PROXY=http://127.0.0.1:3128, pointed at a small logging HTTP forward proxy I ran locally. Because the target is plain http, the proxy sees every request chip sends upstream, with all headers. Authorization values were masked as a sha256 prefix. The proxy only forwards; it does not change requests.

Credential probe (DGC)

form request result
Basic, stack user Admin GET /rest/2.0/users/current 200, id=00000000-0000-0000-0000-000000900002, userName Admin
Bearer, grant_type=client_credentials with each global client in secret auth-global-clients-external (microsoft-copilot, databricks, openai) POST /rest/oauth/v2/token 401 invalid_client
Bearer, grant_type=password (client microsoft-copilot, user Admin) POST /rest/oauth/v2/token 200. JWT uid=00000000-0000-0000-0000-000000900002, sub=urn:usr:env:…:id:…900002, expires_in=299
Bearer from the password grant GET /rest/2.0/users/current 200, id=00000000-0000-0000-0000-000000900002 (Admin)
Register a client_credentials app: POST /rest/oauth/v2/clients (RFC 7591) with Admin's Bearer 403. I could not mint a client-application token.

chip runs (all PR build unless noted)

run mode credentials
port 18080 http (streamable, stateless) no server creds; client sends Authorization per request
port 18081 http server-wide COLLIBRA_MCP_API_USR=Admin / COLLIBRA_MCP_API_PWD=admin
port 18082 http server-wide Admin / wrongpass
port 18083 http, main build server-wide Admin / admin
stdio stdio server-wide Admin / admin

Every request chip sent upstream (from the logging proxy)

Columns: epoch ts, method, path, DGC status, Authorization (masked), x-mcp-user-id, X-MCP-Tool-Name, traceparent trace-id prefix.
Masked credential keys: fd69ab017215 = Basic Admin:admin, 9fea82925cc0 = Basic pesto-ac4-user2, 381616302245 = Basic Admin:wrongpass, 3f9e3ee6955c = Bearer bogus-token-123, 10a15071e085 = Bearer Admin, 986b11129851 = Bearer user2.

1794593537.216 GET /rest/2.0/assetTypes?excludeMeta=true&limit=1 401 auth=-                              x-mcp-user-id=<absent> tool=list_asset_types tp=9ca91bf07889
1794593549.600 GET /rest/2.0/assetTypes?excludeMeta=true&limit=1 401 auth=-                              x-mcp-user-id=<absent> tool=list_asset_types tp=51834a34eb1d
1794593549.637 GET /rest/2.0/users/current                       200 auth=Basic <sha256:fd69ab017215>    x-mcp-user-id=<absent> tool=- tp=
1794593549.691 GET /rest/2.0/assetTypes?excludeMeta=true&limit=1 200 auth=Basic <sha256:fd69ab017215>    x-mcp-user-id=00000000-0000-0000-0000-000000900002 tool=list_asset_types tp=57bd73d3f8d3
1794593549.812 GET /rest/2.0/users/current                       200 auth=Basic <sha256:9fea82925cc0>    x-mcp-user-id=<absent> tool=- tp=
1794593549.873 GET /rest/2.0/assetTypes?excludeMeta=true&limit=1 200 auth=Basic <sha256:9fea82925cc0>    x-mcp-user-id=01a1d61a-8c89-708b-815c-d22dc8becba8 tool=list_asset_types tp=4c59348349c0
1794593549.961 GET /rest/2.0/assetTypes?excludeMeta=true&limit=1 200 auth=Basic <sha256:fd69ab017215>    x-mcp-user-id=00000000-0000-0000-0000-000000900002 tool=list_asset_types tp=0dc728c3f5c8
1794593550.054 GET /rest/2.0/assetTypes?excludeMeta=true&limit=1 200 auth=Basic <sha256:9fea82925cc0>    x-mcp-user-id=01a1d61a-8c89-708b-815c-d22dc8becba8 tool=list_asset_types tp=e1f03e0ef518
1794593581.420 GET /rest/2.0/users/current                       200 auth=Bearer <sha256:10a15071e085>   x-mcp-user-id=<absent> tool=- tp=
1794593581.441 GET /rest/2.0/assetTypes?excludeMeta=true&limit=1 200 auth=Bearer <sha256:10a15071e085>   x-mcp-user-id=00000000-0000-0000-0000-000000900002 tool=list_asset_types tp=b588fd415ed1
1794593581.535 GET /rest/2.0/assetTypes?excludeMeta=true&limit=1 200 auth=Bearer <sha256:10a15071e085>   x-mcp-user-id=00000000-0000-0000-0000-000000900002 tool=list_asset_types tp=595705454676
1794593581.630 GET /rest/2.0/users/current                       200 auth=Bearer <sha256:986b11129851>   x-mcp-user-id=<absent> tool=- tp=
1794593581.688 GET /rest/2.0/assetTypes?excludeMeta=true&limit=1 200 auth=Bearer <sha256:986b11129851>   x-mcp-user-id=01a1d61a-8c89-708b-815c-d22dc8becba8 tool=list_asset_types tp=b620da75405c
1794593581.781 GET /rest/2.0/users/current                       401 auth=Bearer <sha256:3f9e3ee6955c>   x-mcp-user-id=<absent> tool=- tp=
1794593581.835 GET /rest/2.0/assetTypes?excludeMeta=true&limit=1 401 auth=Bearer <sha256:3f9e3ee6955c>   x-mcp-user-id=<absent> tool=list_asset_types tp=2767394a8d2d
1794593581.907 GET /rest/2.0/assetTypes?excludeMeta=true&limit=1 401 auth=Bearer <sha256:3f9e3ee6955c>   x-mcp-user-id=<absent> tool=list_asset_types tp=ef27f0d760bb
1794593581.982 GET /rest/2.0/users/current                       401 auth=Basic <sha256:381616302245>    x-mcp-user-id=<absent> tool=- tp=
1794593582.496 GET /rest/2.0/assetTypes?excludeMeta=true&limit=1 401 auth=Basic <sha256:381616302245>    x-mcp-user-id=<absent> tool=list_asset_types tp=1579ef3cd801
1794593583.006 GET /rest/2.0/assetTypes?excludeMeta=true&limit=1 401 auth=Basic <sha256:381616302245>    x-mcp-user-id=<absent> tool=list_asset_types tp=a452e9c9734a
1794593597.209 GET /rest/2.0/users/current                       200 auth=Basic <sha256:fd69ab017215>    x-mcp-user-id=<absent> tool=- tp=
1794593597.252 GET /rest/2.0/assetTypes?excludeMeta=true&limit=1 200 auth=Basic <sha256:fd69ab017215>    x-mcp-user-id=00000000-0000-0000-0000-000000900002 tool=list_asset_types tp=1ffee5eefb85
1794593597.335 GET /rest/2.0/assetTypes?excludeMeta=true&limit=1 200 auth=Basic <sha256:fd69ab017215>    x-mcp-user-id=00000000-0000-0000-0000-000000900002 tool=list_asset_types tp=d034350843f6
1794593597.417 GET /rest/2.0/assetTypes?excludeMeta=true&limit=1 200 auth=Basic <sha256:fd69ab017215>    x-mcp-user-id=<absent> tool=list_asset_types tp=3ec87d99246a
1794593597.460 GET /rest/2.0/users/current                       401 auth=Basic <sha256:381616302245>    x-mcp-user-id=<absent> tool=- tp=
1794593597.893 GET /rest/2.0/assetTypes?excludeMeta=true&limit=1 401 auth=Basic <sha256:381616302245>    x-mcp-user-id=<absent> tool=list_asset_types tp=ac50fa28cf39
1794593598.376 GET /rest/2.0/assetTypes?excludeMeta=true&limit=1 401 auth=Basic <sha256:381616302245>    x-mcp-user-id=<absent> tool=list_asset_types tp=fe998916ef16
1794593598.926 GET /rest/2.0/users/current                       200 auth=Basic <sha256:fd69ab017215>    x-mcp-user-id=<absent> tool=- tp=
1794593598.943 GET /rest/2.0/assetTypes?excludeMeta=true&limit=1 200 auth=Basic <sha256:fd69ab017215>    x-mcp-user-id=00000000-0000-0000-0000-000000900002 tool=list_asset_types tp=3bb50fffc4f8

Call markers (label, epoch at which the MCP call started), used to map the rows above to calls:

PROXYMARK probe-noauth 1794593537.207181607
PROXYMARK ac8-noauth-2 1794593549.593049844
PROXYMARK ac4-admin-1 1794593549.631270943
PROXYMARK ac4-user2-1 1794593549.805670531
PROXYMARK ac4-admin-2 1794593549.954463309
PROXYMARK ac4-user2-2 1794593550.047655448
PROXYMARK ac5-bearer-admin-1 1794593581.413122580
PROXYMARK ac5-bearer-admin-2 1794593581.529041449
PROXYMARK ac5-bearer-user2-1 1794593581.622806768
PROXYMARK ac7-bogus-bearer-1 1794593581.774816946
PROXYMARK ac7-bogus-bearer-2 1794593581.900908654
PROXYMARK ac7-http-wrongpw-1 1794593581.975344834
PROXYMARK ac7-http-wrongpw-2 1794593583.000064701
PROXYMARK ac2-sw-pr-1 1794593597.199664615
PROXYMARK ac2-sw-pr-2 1794593597.328402904
PROXYMARK ac6-sw-main-1 1794593597.410003623
PROXYMARK ac7-sw-wrongpw-1 1794593597.452899272
PROXYMARK ac7-sw-wrongpw-2 1794593598.368939691
PROXYMARK ac2-stdio-pr 1794593598.891721314

An example of one full set of upstream headers (the server-wide Basic tool call):

{"Host": "dgc-gateway.dgc-f8e0ded1c023.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic <sha256:fd69ab017215>", "Content-Type": "application/json", "Traceparent": "00-57bd73d3f8d34f6cd90c2f052e638eb2-9b5bfc8b33bafe6a-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "X-Mcp-User-Id": "00000000-0000-0000-0000-000000900002", "Accept-Encoding": "gzip"}

The users/current lookup itself carries User-Agent: Collibra MCP/…, but no traceparent, X-MCP-Tool-Name or x-mcp-user-id.

Raw MCP request/response for each call

Each request was POST http://localhost:<port>/ with {"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"list_asset_types","arguments":{"limit":1}}}, plus the Authorization header shown in the proxy table. Responses are trimmed to 330 chars:

### probe-noauth  2026-11-13T18:12:17.236Z  port=18080
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"Failed to authenticate request\",\"errorCode\":\"authenticationFailed\"}"}],"isError":true}}…
### ac8-noauth-2  2026-11-13T18:12:29.617Z  port=18080
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"Failed to authenticate request\",\"errorCode\":\"authenticationFailed\"}"}],"isError":true}}…
### ac4-admin-1  2026-11-13T18:12:29.793Z  port=18080
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"assetTypes\":[{\"description\":\"An asset type that represents an Azure Data Lake Container, which is a logical unit of storage containing Azure Data Lake Storage objects.\",\"displayNameEnabled\":true,\"finalType\":false,\"id\":\"00000000-0000-0000-0001-0028…
### ac4-user2-1  2026-11-13T18:12:29.942Z  port=18080
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"assetTypes\":[{\"description\":\"An asset type that represents an Azure Data Lake Container, which is a logical unit of storage containing Azure Data Lake Storage objects.\",\"displayNameEnabled\":true,\"finalType\":false,\"id\":\"00000000-0000-0000-0001-0028…
### ac4-admin-2  2026-11-13T18:12:30.034Z  port=18080
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"assetTypes\":[{\"description\":\"An asset type that represents an Azure Data Lake Container, which is a logical unit of storage containing Azure Data Lake Storage objects.\",\"displayNameEnabled\":true,\"finalType\":false,\"id\":\"00000000-0000-0000-0001-0028…
### ac4-user2-2  2026-11-13T18:12:30.124Z  port=18080
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"assetTypes\":[{\"description\":\"An asset type that represents an Azure Data Lake Container, which is a logical unit of storage containing Azure Data Lake Storage objects.\",\"displayNameEnabled\":true,\"finalType\":false,\"id\":\"00000000-0000-0000-0001-0028…
### ac5-bearer-admin-1  2026-11-13T18:13:01.516Z  port=18080
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"assetTypes\":[{\"description\":\"An asset type that represents an Azure Data Lake Container, which is a logical unit of storage containing Azure Data Lake Storage objects.\",\"displayNameEnabled\":true,\"finalType\":false,\"id\":\"00000000-0000-0000-0001-0028…
### ac5-bearer-admin-2  2026-11-13T18:13:01.609Z  port=18080
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"assetTypes\":[{\"description\":\"An asset type that represents an Azure Data Lake Container, which is a logical unit of storage containing Azure Data Lake Storage objects.\",\"displayNameEnabled\":true,\"finalType\":false,\"id\":\"00000000-0000-0000-0001-0028…
### ac5-bearer-user2-1  2026-11-13T18:13:01.760Z  port=18080
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"assetTypes\":[{\"description\":\"An asset type that represents an Azure Data Lake Container, which is a logical unit of storage containing Azure Data Lake Storage objects.\",\"displayNameEnabled\":true,\"finalType\":false,\"id\":\"00000000-0000-0000-0001-0028…
### ac7-bogus-bearer-1  2026-11-13T18:13:01.887Z  port=18080
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"The bearer token is incorrectly encoded or has wrong syntax\",\"errorCode\":\"malformedToken\"}"}],"isEr…
### ac7-bogus-bearer-2  2026-11-13T18:13:01.960Z  port=18080
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"The bearer token is incorrectly encoded or has wrong syntax\",\"errorCode\":\"malformedToken\"}"}],"isEr…
### ac7-http-wrongpw-1  2026-11-13T18:13:02.988Z  port=18080
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"Failed to authenticate request through basic credentials\",\"errorCode\":\"authenticationFailed\"}"}],"i…
### ac7-http-wrongpw-2  2026-11-13T18:13:03.513Z  port=18080
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"Failed to authenticate request through basic credentials\",\"errorCode\":\"authenticationFailed\"}"}],"i…
### ac2-sw-pr-1  2026-11-13T18:13:17.318Z  port=18081
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"assetTypes\":[{\"description\":\"An asset type that represents an Azure Data Lake Container, which is a logical unit of storage containing Azure Data Lake Storage objects.\",\"displayNameEnabled\":true,\"finalType\":false,\"id\":\"00000000-0000-0000-0001-0028…
### ac2-sw-pr-2  2026-11-13T18:13:17.399Z  port=18081
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"assetTypes\":[{\"description\":\"An asset type that represents an Azure Data Lake Container, which is a logical unit of storage containing Azure Data Lake Storage objects.\",\"displayNameEnabled\":true,\"finalType\":false,\"id\":\"00000000-0000-0000-0001-0028…
### ac6-sw-main-1  2026-11-13T18:13:17.443Z  port=18083
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"assetTypes\":[{\"description\":\"An asset type that represents an Azure Data Lake Container, which is a logical unit of storage containing Azure Data Lake Storage objects.\",\"displayNameEnabled\":true,\"finalType\":false,\"id\":\"00000000-0000-0000-0001-0028…
### ac7-sw-wrongpw-1  2026-11-13T18:13:18.359Z  port=18082
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"Failed to authenticate request through basic credentials\",\"errorCode\":\"authenticationFailed\"}"}],"i…
### ac7-sw-wrongpw-2  2026-11-13T18:13:18.886Z  port=18082
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"Failed to authenticate request through basic credentials\",\"errorCode\":\"authenticationFailed\"}"}],"i…

stdio run (initialize, notifications/initialized, tools/call): the response was {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"assetTypes\":[{…\"name\":\"ADLS Container\"… (success). Proxy rows at 1794593598.926/.943 show the lookup followed by the tool call with x-mcp-user-id=…900002.

Results per criterion

AC-1: MET. With Basic Admin:admin, GET /rest/2.0/users/current returned "id":"00000000-0000-0000-0000-000000900002". GET /rest/2.0/users?name=Admin returned {"total":1,…"results":[{"id":"00000000-0000-0000-0000-000000900002",…"userName":"Admin"…}]}. The ids are equal.

AC-2: MET on the header value; NOT MET on the stated proof. With server-wide basic creds, in both http mode (18081) and stdio mode, chip sent exactly one users/current lookup (200). Every tool call afterwards carried x-mcp-user-id: 00000000-0000-0000-0000-000000900002, the AC-1 UUID. The stated proof, "chip's debug log line", cannot be met: chip has no log-level setting and logs no such line. The value was observed on the wire through chip's existing api.proxy setting.

AC-3: NOT MET. DGC wrote no xCollibraApiLoggingFilter line for any of chip's requests, successful or failed. The whole aggregator log has 8 such lines, all from DGC-internal callers at 18:06 (Collibra-Protect/1.0, ReactorNetty, Java-http-client, Apache-HttpClient; event identity:x_collibra_api_request). Searching the aggregator log for Collibra MCP, chip's traceparent trace-ids (e.g. 57bd73d3f8d3, 1ffee5eefb85) or assetTypes found only the Filters "Returning HTTP 401 …" lines for failed calls. None of them has a user_id, user_agent or traceparent field. That filter appears to log only x-collibra-api (service-to-service) requests, not external Basic/Bearer REST calls, so this DGC log cannot show the UUID matching for chip's requests.

AC-4: MET. http mode (18080), client-supplied Basic, interleaved Admin, user2, Admin, user2. Each credential got exactly one lookup on first use. Admin's tool calls carried …900002 both times. user2's (pesto-ac4-user2) carried 01a1d61a-8c89-708b-815c-d22dc8becba8 both times. There was no cross-talk. All four tool calls returned success. The same holds for Bearer (Admin token → …900002, user2 token → 01a1d61a-…).

AC-5: MET (human account), with one gap. A Bearer token minted at /rest/oauth/v2/token (password grant) makes GET /rest/2.0/users/current return a UUID (…900002). The account is a human user (userName=Admin, apiUser:false, userSource:INTERNAL). Through chip, the Bearer tool calls carried that UUID. The stack minted no client-application token: client_credentials returned 401 invalid_client for all three global clients, and registering a client returned 403. So the behaviour for a client-application account was not tested.

AC-6: MET. The same list_asset_types call on the PR build (18081, with x-mcp-user-id) and the main build (18083, without the header; proxy row 1794593597.417 shows it absent) both returned HTTP 200 from DGC. Both returned the same successful result ("total":260, first type "ADLS Container"). DGC does not reject the unknown header.

AC-7: MET. chip has no debug level (see AC-2), but its WARN/ERROR lines, the wire and DGC's log all show the behaviour:

  • Server-wide wrong password (18082). One users/current → 401. Then 2 tool calls → 401, neither with x-mcp-user-id, and no second lookup. The MCP result for both was isError:true, "HTTP 401: {…\"userMessage\":\"Failed to authenticate request through basic credentials\"…}". DGC's log matches: 18:13:17.889 Returning HTTP 401 … 'GET /rest/2.0/users/current', then 18:13:18.313 and 18:13:18.838 … 'GET /rest/2.0/assetTypes'. The gateway log matches too (lines 196–198).
  • http mode with a client-supplied wrong-password Basic, and with Bearer bogus-token-123: same pattern. One lookup → 401, then two tool calls → 401 without the header, and the 401 surfaced as the tool error. DGC logged Invalid JWT serialization: Missing dot delimiter(s) for the bogus Bearer.
  • chip's own stderr (INFO/WARN/ERROR; no debug level exists) shows the WARN only on the first call of each bad credential. That is the single lookup; the repeat call has no WARN and so made no new lookup:
    • http mode, 18080 (bogus Bearer ×2, then wrong-password Basic ×2; earlier Bearer-success lines included):
2026/11/13 18:13:01 INFO Calling tool: list_asset_types tool_name=list_asset_types
2026/11/13 18:13:01 INFO Calling tool: list_asset_types tool_name=list_asset_types
2026/11/13 18:13:01 INFO Calling tool: list_asset_types tool_name=list_asset_types
2026/11/13 18:13:01 INFO Calling tool: list_asset_types tool_name=list_asset_types
2026/11/13 18:13:01 WARN Could not resolve the current user, x-mcp-user-id will not be sent: status 401
2026/11/13 18:13:01 ERROR error while calling tool function error="HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"The bearer token is incorrectly encoded or has wrong syntax\",\"errorCode\":\"malformedToken\"}"
2026/11/13 18:13:01 INFO Calling tool: list_asset_types tool_name=list_asset_types
2026/11/13 18:13:01 ERROR error while calling tool function error="HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"The bearer token is incorrectly encoded or has wrong syntax\",\"errorCode\":\"malformedToken\"}"
2026/11/13 18:13:01 INFO Calling tool: list_asset_types tool_name=list_asset_types
2026/11/13 18:13:02 WARN Could not resolve the current user, x-mcp-user-id will not be sent: status 401
2026/11/13 18:13:02 ERROR error while calling tool function error="HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"Failed to authenticate request through basic credentials\",\"errorCode\":\"authenticationFailed\"}"
2026/11/13 18:13:03 INFO Calling tool: list_asset_types tool_name=list_asset_types
2026/11/13 18:13:03 ERROR error while calling tool function error="HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"Failed to authenticate request through basic credentials\",\"errorCode\":\"authenticationFailed\"}"
  • server-wide wrong password, 18082:
2026/11/13 18:13:17 INFO Calling tool: list_asset_types tool_name=list_asset_types
2026/11/13 18:13:17 WARN Could not resolve the current user, x-mcp-user-id will not be sent: status 401
2026/11/13 18:13:18 ERROR error while calling tool function error="HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"Failed to authenticate request through basic credentials\",\"errorCode\":\"authenticationFailed\"}"
2026/11/13 18:13:18 INFO Calling tool: list_asset_types tool_name=list_asset_types
2026/11/13 18:13:18 ERROR error while calling tool function error="HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"Failed to authenticate request through basic credentials\",\"errorCode\":\"authenticationFailed\"}"
  • Cost: a failed Basic lookup took ~430–480 ms at DGC (see AC-9), and that time is added to the first call.

AC-8: MET. In http mode with no Authorization (2 calls), the proxy shows only GET /rest/2.0/assetTypes → 401, with no users/current lookup and no x-mcp-user-id. DGC's log shows only 18:12:17.229 and 18:12:29.612 Returning HTTP 401 Unauthorized on request 'GET /rest/2.0/assetTypes' and no users/current line. The tool result was isError:true, HTTP 401 …"Failed to authenticate request".

AC-9: MET. GET /rest/2.0/users/current from this pod through the gateway, timed with curl time_total:

  • Basic Admin, n=30, all 200: median 10.7 ms, p95 11.9 ms (min 9.3, max 17.3)
  • Bearer Admin, n=30, all 200: median 17.7 ms, p95 22.0 ms (min 15.7, max 26.0)
  • Basic wrong password, n=25, all 401: median 485.0 ms, p95 630.0 ms (min 435.4, max 634.4)

The 2 s lookup timeout is far above all three.

AC-10: MET.

  • Proxy: the only proxy in front of DGC is the magneto dgc-gateway nginx. Its config (configmap dgc-gateway) defines no log_format, so it uses nginx's default combined format. That format does not log x-mcp-user-id or any custom request header. Its log lines for chip look like 10.20.11.17 - Admin [13/Nov/2026:18:13:17 +0000] "GET /rest/2.0/assetTypes?excludeMeta=true&limit=1 HTTP/1.1" 200 856 "-" "Collibra MCP/0.0.0-SNAPSHOT". Note that it logs the Basic username ($remote_user).
  • DGC: xCollibraApiLoggingFilter lines carry the authenticated user_id plus client_address, method, uri, status_code and user_agent. They carry no request headers: there is no x-mcp-user-id field and no traceparent. Example: {"logger_name":"xCollibraApiLoggingFilter",…"uri":"/rest/2.0/license/capabilities?name=DQ_CLOUD_DATA_SOURCES","user_agent":"Java/21.0.13.0.3","user_id":"00000000-0000-0000-0000-000000900006",…}. No such line was written for chip's own requests (see AC-3).
  • There is no separate ingress in this stack.

Review observations (no code changed)

  1. No way to see the header in chip's own logs. The PR documents the behaviour but logs nothing on success, and chip has no log level. Operators cannot confirm attribution from chip's side, and the "debug log" proofs here could not be met.
  2. DGC's request log will not attribute chip traffic in this build. xCollibraApiLoggingFilter did not log external Basic/Bearer REST calls at all, so whatever consumes x-mcp-user-id must be something other than that filter.
  3. The lookup carries no traceparent or X-MCP-* headers, so it cannot be correlated with the tool call that triggered it.
  4. A failed lookup costs ~0.5 s for wrong Basic credentials, on top of the 401 tool call. That happens once per credential per 10 minutes because failures are cached, and the behaviour matched the docs.
  5. Short-lived Bearer tokens (expires_in=299) get a new cache key each time, so expect about one lookup per token per user.

Fixtures and cluster-side changes (environment is reclaimed after the run)

  • User pesto-ac4-user2, id 01a1d61a-8c89-708b-815c-d22dc8becba8, created with POST /rest/2.0/users.
  • Direct DB change: in pg-dgc (dgc.users), row 01a1d61a-8c89-708b-815c-d22dc8becba8 got Admin's password hash copied in (so its password = admin) and activated=true. I did this because PATCH /rest/2.0/users/password returned 400 "problem with parameter userId" and PATCH /rest/2.0/users/{id} with activated:true did not activate the user. No other rows were touched.
  • Responsibility 01a1d61b-b44f-751a-8e60-c31786debbf4: global role "Catalog" (00000000-0000-0000-0000-000000005042) granted to user2. Without it, login failed with NoProductPermissionsException.
  • POST /rest/oauth/v2/clients returned 403, so nothing was created.
  • I read k8s secret auth-global-clients-external to get the dev global client secrets for the token probe. They are not reproduced here.
  • Nothing was deleted. No cluster resources were changed beyond the above. The local proxy and chip processes ran only in this pod and were stopped.

Acceptance criteria evidence

AC-1 — met

HEAD 70be0c4 == refs/pull/164/head. Basic Admin:admin against the DGC gateway: GET
/rest/2.0/users/current -> 200 id=00000000-0000-0000-0000-000000900002; GET
/rest/2.0/users?name=Admin -> total 1, results[0].id=00000000-0000-0000-0000-000000900002. Equal.

AC-2 — met

Ran the PR-built chip with COLLIBRA_MCP_API_USR=Admin/PWD=admin in http and stdio mode and made raw
tools/call list_asset_types. A logging forward proxy set through chip's existing
COLLIBRA_MCP_API_PROXY shows one GET users/current (200), then each tool call with header
X-Mcp-User-Id: 00000000-0000-0000-0000-000000900002 (the AC-1 id). CAVEAT: chip has no log-level
setting and no debug line prints the header, so the stated proof (chip debug log line) cannot be
produced without a code change; the value was observed on the wire instead.

AC-3 — not met

DGC wrote no xCollibraApiLoggingFilter line for any chip request. The aggregator log has 8 such
lines in total, all DGC-internal callers at 18:06 (Collibra-Protect, ReactorNetty,
Java-http-client). Searching for 'Collibra MCP', chip traceparent ids (57bd73d3f8d3, 1ffee5eefb85)
and 'assetTypes' found only 'Filters: Returning HTTP 401' lines for failed calls, with no user_id or
user_agent. The filter appears to log only x-collibra-api service-to-service requests, so the UUID
cannot be matched in DGC's log.

AC-4 — met

http mode, client-supplied Basic, interleaved Admin/user2/Admin/user2 (created user pesto-ac4-user2
id 01a1d61a-8c89-708b-815c-d22dc8becba8). The proxy shows one lookup per credential; Admin tool
calls carried 00000000-0000-0000-0000-000000900002 and user2 tool calls carried
01a1d61a-8c89-708b-815c-d22dc8becba8 every time. No cross-talk; all calls succeeded. Bearer per user
gave the same result.

AC-5 — met

A Bearer token minted at /rest/oauth/v2/token (grant_type=password, global client microsoft-copilot)
makes GET /rest/2.0/users/current return 200 id=...900002, a HUMAN user (userName Admin,
apiUser:false, userSource INTERNAL); JWT uid matches. Through chip, the Bearer tool calls carried
that UUID. No client-application token could be minted: client_credentials -> 401 invalid_client for
all 3 global clients, and POST /rest/oauth/v2/clients -> 403. Client-app accounts were not tested.

AC-6 — met

Same tools/call list_asset_types with server-wide Admin creds: the PR build (header X-Mcp-User-Id
sent) -> DGC 200, success total=260; the main build 791fb15 (no header, per proxy) -> DGC 200,
identical result. The header is not rejected.

AC-7 — met

Server-wide wrong password and http-mode wrong Basic / Bearer bogus-token-123, 2 calls each: the
proxy shows one users/current -> 401, then 2 tool calls -> 401 with no X-Mcp-User-Id and no second
lookup. The tool result was isError:true 'HTTP 401: ...Failed to authenticate request through basic
credentials'. chip stderr shows 'WARN Could not resolve the current user, x-mcp-user-id will not be
sent: status 401' once per credential. DGC log: 18:13:17.889 401 users/current, then 18:13:18.313
and .838 401 assetTypes. chip has no debug level; INFO/WARN were used.

AC-8 — met

http mode with no Authorization, 2 calls: the proxy shows only GET /rest/2.0/assetTypes -> 401, with
no users/current and no X-Mcp-User-Id. DGC log shows only 'Returning HTTP 401 Unauthorized on
request GET /rest/2.0/assetTypes' at 18:12:17.229 and 18:12:29.612, no users/current. Tool result
isError HTTP 401.

AC-9 — met

GET /rest/2.0/users/current via the gateway, curl time_total: Basic n=30 all 200 median 10.7ms p95
11.9ms; Bearer n=30 all 200 median 17.7ms p95 22.0ms; wrong-password Basic n=25 all 401 median
485.0ms p95 630.0ms.

AC-10 — met

The only proxy is the magneto dgc-gateway nginx; its config has no log_format (default combined),
and its log lines for chip are e.g. '10.20.11.17 - Admin [...] "GET /rest/2.0/assetTypes?...
HTTP/1.1" 200 856 "-" "Collibra MCP/0.0.0-SNAPSHOT"', so it does NOT log x-mcp-user-id (it does log
the Basic username). DGC xCollibraApiLoggingFilter lines record the authenticated user_id plus
uri/status/user_agent and no request headers, but no such line was written for chip's requests (see
AC-3). No separate ingress exists.

@charlie-ang-collibra charlie-ang-collibra left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validate-ac — collibra/chip PR #164 (x-mcp-user-id header) on a Magneto stack

Bytes under test

  • git rev-parse HEAD → 70be0c4ada6ae1735654d46fedb09a5d171e6a2e
  • git ls-remote origin 'refs/pull/164/*' → 70be0c4ada6ae1735654d46fedb09a5d171e6a2e refs/pull/164/head — matches.
  • PR build: go build -o /tmp/v/chip-pr ./cmd/chip at that SHA. Default-branch build: worktree at merge-base 791fb15 (main), go build -o /tmp/v/chip-main ./cmd/chip. (No local main ref existed; 791fb15 is the merge-base, the commit right before the PR's two commits.) chip's code was not changed.

Environment and how it was reached

  • DGC from provision-env dgcEndpointDetails: http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400, user Admin. It was directly reachable from this pod (curl -u Admin:admin …/rest/2.0/users/current → 200), so no port-forward was needed. DGC aggregator 2027.01.261, auth service 0.153.10, gateway nginx:alpine (magneto-managed dgc-gateway).
  • Every chip run: COLLIBRA_MCP_API_URL=<above>, COLLIBRA_MCP_API_PROXY=http://127.0.0.1:3128, where 3128 is a small Python logging forward proxy started in this pod that writes each request's method, URL, all request headers, status, latency, and response body as JSONL.
  • MCP driven with raw JSON-RPC (initialize, notifications/initialized, tools/call list_asset_types), with no model in the loop. stdio mode: server-wide COLLIBRA_MCP_API_USR/PWD. http mode: COLLIBRA_MCP_MODE=http COLLIBRA_MCP_HTTP_PORT=8091 with no server credentials, and the client sends Authorization on each POST.

Credential forms DGC accepts (GET /rest/2.0/users/current)

Form Result
Authorization: Basic Admin:admin 200, id=00000000-0000-0000-0000-000000900002
Authorization: Bearer from POST /rest/oauth/v2/token grant_type=password&username=Admin&password=admin (no client auth needed) token minted (RS256, iss=http://auth.iam, expires_in=299); users/current 200 with the same id …900002
POST /rest/oauth/v2/token grant_type=client_credentials (no client) 401; with Basic Admin → 401 invalid_client "Client authentication failed: client_id"
Basic Admin:wrong 401 authenticationFailed
Bearer bogus 401 malformedToken

Fixtures created (nothing else was created or deleted)

  1. DGC user pesto-vac-user2, id 01a1d684-7fce-71cd-a1fc-fd153d0ec38b, created with POST /rest/2.0/users as Admin (201).
  2. Global responsibility id 01a1d688-343e-7566-8a19-72c88e51f278: role Catalog 00000000-0000-0000-0000-000000005042 → pesto-vac-user2, created with POST /rest/2.0/responsibilities (201). Without it, login failed with NoProductPermissionsException.
  3. DB write on that fixture row only (deviation from "through /rest/2.0"): this DGC build exposes no admin set-password REST call. PATCH /rest/2.0/users/changePassword → 400 (DGC log shows the path is routed as PATCH /users/{userId} → PathParamException). /users/{id}/password|passwordChange|changePassword|resetPassword|credentials → 404/405 for POST/PATCH/PUT. /rest/1.0/user/{id}/password → 404. POST /rest/2.0/internal/users/password/request_reset email=… → 200, but the key is emailed and not stored in the DB. Through magneto magneto_query_database (pg-dgc, db dgc) I ran: update dgc.users u set password=a.password, salt=a.salt, activated=true from dgc.users a where a.username='Admin' and u.id='01a1d684-7fce-71cd-a1fc-fd153d0ec38b' and u.username='pesto-vac-user2' → UPDATE 1. pesto-vac-user2's password is therefore admin. Admin's row was only read.
  4. Side effect: a password-reset request was issued for pesto-vac-user2 (the email went nowhere observable).

Cluster-side changes (not reverted; the stack is disposable)

  • ConfigMap dgc-gateway (ns dgc-af3a7b6ebec4), key nginx.conf, patched with magneto k8s_patch_resource (→ patched:true). Before: the original config had no log_format and no access_log, so nginx's default combined format applied. Added at http{} level:
    log_format mcpuid '$remote_addr [$time_local] "$request" $status x_mcp_user_id="$http_x_mcp_user_id" x_mcp_tool="$http_x_mcp_tool_name"';
    access_log /dev/stdout mcpuid;
    
    and inside location / {}: add_header X-Seen-User-Id $http_x_mcp_user_id always;. Nothing else changed. The full before config is the "BEFORE" block at the end of this file; after = before + those 3 lines.
  • Reload: magneto k8s_exec_in_pod is unavailable (exec is not available in k8s provisioner mode: it needs a SPDY connection upgrade), so nginx -s reload could not be run. nginx was reloaded by deleting pod dgc-gateway-675d9f5979-l4667 with magneto k8s_delete_pod (deleted:true). The Deployment recreated it as dgc-gateway-675d9f5979-fjhbz, which read the patched ConfigMap. Check: curl -D- -H 'x-mcp-user-id: probe-curl' …/users/current → X-Seen-User-Id: probe-curl.

Acceptance criteria

AC-1: MET. With Basic Admin:admin, GET /rest/2.0/users/current → "id":"00000000-0000-0000-0000-000000900002","userName":"Admin", and GET /rest/2.0/users?name=Admin → {"total":1,…"results":[{"id":"00000000-0000-0000-0000-000000900002",…"userName":"Admin"…}]}. The ids are equal. Also for pesto-vac-user2: users/current (as user2) and users?name=pesto-vac-user2 (as Admin) both return 01a1d684-7fce-71cd-a1fc-fd153d0ec38b.

AC-2: MET. In stdio mode with COLLIBRA_MCP_API_USR=Admin COLLIBRA_MCP_API_PWD=admin, two tools/call list_asset_types produced in the proxy: GET /rest/2.0/users/current 200 (no x-mcp-user-id on the lookup), then two GET /rest/2.0/assetTypes… each with X-Mcp-User-Id: 00000000-0000-0000-0000-000000900002, which is exactly the AC-1 UUID. There was one lookup for two calls (cached). The same happened in the http-mode Basic Admin run (Run 2a).

AC-3: MET. After the nginx patch above, one chip stdio call (Basic Admin) and one http-mode call (Basic pesto-vac-user2) produced these lines in the gateway pod dgc-gateway-675d9f5979-fjhbz stdout access log (magneto k8s_get_pod_logs):

10.20.0.180 [13/Nov/2026:20:11:43 +0000] "GET /rest/2.0/users/current HTTP/1.1" 200 x_mcp_user_id="-" x_mcp_tool="-"
10.20.0.180 [13/Nov/2026:20:11:43 +0000] "GET /rest/2.0/assetTypes?excludeMeta=true&limit=1&name=Business+Term HTTP/1.1" 200 x_mcp_user_id="00000000-0000-0000-0000-000000900002" x_mcp_tool="list_asset_types"
10.20.0.180 [13/Nov/2026:20:11:43 +0000] "GET /rest/2.0/assetTypes?excludeMeta=true&limit=1&name=Business+Term HTTP/1.1" 200 x_mcp_user_id="01a1d684-7fce-71cd-a1fc-fd153d0ec38b" x_mcp_tool="list_asset_types"

In the proxy record for the same calls, chip sent 00000000-0000-0000-0000-000000900002 and 01a1d684-7fce-71cd-a1fc-fd153d0ec38b, the same UUIDs nginx logged.

AC-4: MET. In http mode against one chip process with no server credentials, client Authorization: Basic alternated Admin:admin and pesto-vac-user2:admin (2 sequential rounds), followed by 3 concurrent Admin/user2 pairs. The proxy recorded 10 tool requests, and a script that decodes each request's Authorization user and compares X-Mcp-User-Id to that user's UUID reported tool requests=10 mismatches=0. Every Admin request carried …900002 and every user2 request carried 01a1d684-…ec38b. user2's credential caused exactly one users/current lookup. All tool calls returned results (no isError).

AC-5: MET (recorded). The stack mints a usable Bearer token at /rest/oauth/v2/token with grant_type=password. Claims: sub=urn:usr:env:00000000-0000-0000-0000-000000000001:id:00000000-0000-0000-0000-000000900002, uid=…900002, iss=http://auth.iam, aud=collibra, 75 scopes, 300 s lifetime. GET /rest/2.0/users/current with that Bearer → 200 and returns a UUID: {'id': '00000000-0000-0000-0000-000000900002', 'userName': 'Admin', 'apiUser': False, 'system': False, 'guestUser': False, 'userSource': 'INTERNAL'}. That is a human user account (apiUser:false, urn:usr subject), not a client-application account. Through chip in http mode with that Bearer (Run 2b), the lookup returned 200 and both tool requests carried X-Mcp-User-Id: 00000000-0000-0000-0000-000000900002. Not tested: a client-application (client_credentials) token. The stack has no registered OAuth client: client_credentials → invalid_client, and /rest/oauth/v2/applications|register → 404. What users/current returns for a client-app token on this stack is therefore unknown.

AC-6: MET. The same two tools/call list_asset_types ran in stdio mode with server-wide Basic Admin, first with the PR build and then with the default-branch build (791fb15). PR: tool requests carried X-Mcp-User-Id: …900002, DGC answered 200/200, and neither result had isError. Default: no header, 200/200. A JSON comparison of the two runs' result objects reported results identical: True. DGC did not reject or alter the response because of the unknown header.

AC-7: MET. (a) http mode, client Basic Admin:wrong, two calls. Proxy: GET /rest/2.0/users/current 401 (once), then assetTypes 401, assetTypes 401, neither carrying x-mcp-user-id. Both tool results were "isError":true with text HTTP 401: {"statusCode":401,…"userMessage":"Failed to authenticate request through basic credentials","errorCode":"authenticationFailed"}. chip log: WARN Could not resolve the current user, x-mcp-user-id will not be sent: status 401. DGC log (dgc-aggregator) shows exactly one users/current for that credential: 20:08:17.611 Returning HTTP 401 Unauthorized on request 'GET /rest/2.0/users/current', then 20:08:18.052 … 'GET /rest/2.0/assetTypes' and 20:08:18.541 … 'GET /rest/2.0/assetTypes', each preceded by User failed to log in: userName='Admin'. (b) http mode, Bearer bogus.token.value: one users/current 401, then two assetTypes 401 without the header, and tool errors with "errorCode":"malformedToken". (c) stdio mode, server-wide Admin/wrong: one users/current 401 and two assetTypes 401 without the header (DGC log 20:10:37.389 users/current, 20:10:37.814 and 20:10:38.289 assetTypes). Both tool results were isError:true with HTTP 401. The repeat call made no second lookup in all three cases. Latency note: each wrong-password Basic request takes ~425–445 ms at DGC (LDAP realm attempt), and the failed lookup adds one of those to the first call.

AC-8: MET. In http mode with no Authorization, two calls. Proxy record: only GET /rest/2.0/assetTypes… 401 ×2 (Authorization absent, no X-Mcp-User-Id) and no /rest/2.0/users/current request. Tool results: isError:true with HTTP 401 … "Failed to authenticate request".

AC-9: MET. GET /rest/2.0/users/current was timed from this pod via the gateway with curl -w %{time_total}, 25 calls per form, all 200. Basic: median 14.4 ms, p95 16.2 ms (min 12.9, max 24.4). Bearer: median 18.1 ms, p95 21.6 ms (min 15.5, max 22.9). Through chip and the proxy, the cold lookups observed were 16.8–55.6 ms (20 ms through proxy+gateway is typical). A failed Basic lookup costs ~430 ms.

AC-10: MET, with one qualification. (1) DGC's log does not record x-mcp-user-id. A search of the whole dgc-aggregator log for (?i)x-mcp|mcp-user|mcp_user|X-MCP-Tool|list_asset_types|probe-curl → No matches found … in 2379 log lines, after all chip calls had been made. Successful chip requests to /rest/2.0/assetTypes produce no request-log line at all. Only 401s are logged (Returning HTTP 401 Unauthorized on request 'GET /rest/2.0/assetTypes'), without headers. DGC logs its own authenticated_id field on some lines (e.g. User logged in … authenticated_id:01a1d684-…). (2) nginx's default format does not record it. The gateway config before the patch had no log_format or access_log, so nginx's built-in combined format applied. A line from the unpatched gateway for a request: 10.20.0.180 - pesto-vac-user2 [13/Nov/2026:20:06:31 +0000] "GET /rest/2.0/users/current HTTP/1.1" 401 228 "-" "curl/7.76.1" has no custom request headers. (3) Allowlist to change: in this stack it is the log_format of the nginx.conf key in ConfigMap dgc-gateway (ns dgc-af3a7b6ebec4). It needs $http_x_mcp_user_id (as done for AC-3), plus an access_log that uses that format. Qualification: this stack has no other gateway, so the name or location of a production gateway's header/log allowlist (outside magneto) could not be observed here and is not verified.

Other observations (not in the ACs)

  • In all 28 recorded tool requests, in both stdio and http (stateless) modes, chip sent X-Mcp-Session-Id: "" (an empty value). This also happens on the default branch, so the PR did not cause it. Downstream consumers should not treat the session id as a correlation key.
  • The users/current lookup itself carries no X-Mcp-Tool-Name/traceparent, and nginx logs it with x_mcp_user_id="-". If you correlate traffic at the gateway, the lookup appears as an anonymous-attribution request.
  • In http mode, a credential's result (including a failure) is cached for 10 min per chip process. A user who fixes a wrong password keeps getting no header until the TTL expires (by design per docs/CONFIG.md).

Raw evidence

The raw MCP JSON-RPC requests and responses, and the logging-proxy record, for every run follow. Long tool results and the initialize response are truncated. Authorization values are the stack's disposable test credentials.

Run 1 — stdio, server-wide Basic (COLLIBRA_MCP_API_USR=Admin/PWD=admin), PR build

MCP JSON-RPC (raw):

>>> {"jsonrpc": "2.0", "id": 0, "method": "initialize", "params": {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "validate-ac", "v …(initialize, truncated)
<<< {"jsonrpc":"2.0","id":0,"result":{"capabilities":{"logging":{},"tools":{"listChanged":true}},"instructions":"# Collibra MCP Server\n\nThis server connects y …(initialize, truncated)
>>> {"jsonrpc": "2.0", "method": "notifications/initialized"}
>>> {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "list_asset_types", "arguments": {"limit": 2}}}
<<< {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"assetTypes\":[{\"description\":\"An asset type that represents an Azure Data Lake Container, which is a logical unit of storage containing Azure Data Lake Storage objects.\",\"displayNameEnabled\":true,\"finalType\":false,\"id\":\"00000000-0000-0000-0001-002800000003\",\"name\":\"ADLS Container\",\"product\":\"CATALOG\",\"publicId\":\"AdlsContainer\",\"ratingEnabled\":false,\"system\":true},{\"description\":\"An asset type that represents Azure Data Lake Storage file system.\",\"displayNameEnabled\":true,\"finalType\":false,\"id\":\"00000000-0000-0000-0001-002800000001\",\"name\":\"ADLS File System\",\"product\":\"CATALOG\",\"publicId\":\"AdlsFileSystem\",\"ratingEnabled\":false,\"system\":true}],\"limit\":2,\"offset\":0,\"resultsTruncated\":false,\"scanned\":0,\"status\":\"success\",\"total\":260}"}],"structuredC
>>> {"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "list_asset_types", "arguments": {"limit": 1, "name": "Business Term"}}}
<<< {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"{\"assetTypes\":[{\"description\":\"A word or phrase that is used to describe something or to express a concept in a particular kind of language or branch of business.\\n\\nExamples: Customer, Person Purchase Count, Loan Amount.\",\"displayNameEnabled\":false,\"finalType\":false,\"id\":\"00000000-0000-0000-0000-000000011001\",\"name\":\"Business Term\",\"product\":\"GLOSSARY\",\"publicId\":\"BusinessTerm\",\"ratingEnabled\":false,\"system\":true}],\"limit\":1,\"offset\":0,\"resultsTruncated\":false,\"scanned\":0,\"status\":\"success\",\"total\":1}"}],"structuredContent":{"assetTypes":[{"description":"A word or phrase that is used to describe something or to express a concept in a particular kind of language or branch of business.\n\nExamples: Customer, Person Purchase Count, Loan Amount.","displayNameEnabled":false,"
2026/11/13 20:07:42 WARN Using a single basic auth header for all requests is not recommended as it will result in all actions being attributed to the same account. Consider setting an appropriate basic auth header for each request.
2026/11/13 20:07:42 INFO Calling tool: list_asset_types tool_name=list_asset_types
2026/11/13 20:07:42 INFO Calling tool: list_asset_types tool_name=list_asset_types

Logging proxy record (chip -> DGC):

GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/users/current -> 200 (55.6ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Accept": "application/json", "Authorization": "Basic QWRtaW46YWRtaW4=", "Content-Type": "application/json", "Accept-Encoding": "gzip"}
  resp body: {"id":"00000000-0000-0000-0000-000000900002","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-000000900001","lastModifiedOn":1794600222152,"system":fa
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=2 -> 200 (77.5ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic QWRtaW46YWRtaW4=", "Content-Type": "application/json", "Traceparent": "00-bdfab97472aa1a4f90ded03e1b21e63f-92d17ff262a47005-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "X-Mcp-User-Id": "00000000-0000-0000-0000-000000900002", "Accept-Encoding": "gzip"}
  resp body: {"total":260,"offset":0,"limit":2,"results":[{"id":"00000000-0000-0000-0001-002800000003","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-00000090000
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1&name=Business+Term -> 200 (36.9ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic QWRtaW46YWRtaW4=", "Content-Type": "application/json", "Traceparent": "00-caf31e415be5d060986c8ef1dd99b54f-5d31d10f0aca1f5d-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "X-Mcp-User-Id": "00000000-0000-0000-0000-000000900002", "Accept-Encoding": "gzip"}
  resp body: {"total":1,"offset":0,"limit":1,"results":[{"id":"00000000-0000-0000-0000-000000011001","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-000000900001"

Run 1b — same, DEFAULT-BRANCH build (791fb15)

MCP JSON-RPC (raw):

>>> {"jsonrpc": "2.0", "id": 0, "method": "initialize", "params": {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "validate-ac", "v …(initialize, truncated)
<<< {"jsonrpc":"2.0","id":0,"result":{"capabilities":{"logging":{},"tools":{"listChanged":true}},"instructions":"# Collibra MCP Server\n\nThis server connects y …(initialize, truncated)
>>> {"jsonrpc": "2.0", "method": "notifications/initialized"}
>>> {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "list_asset_types", "arguments": {"limit": 2}}}
<<< {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"assetTypes\":[{\"description\":\"An asset type that represents an Azure Data Lake Container, which is a logical unit of storage containing Azure Data Lake Storage objects.\",\"displayNameEnabled\":true,\"finalType\":false,\"id\":\"00000000-0000-0000-0001-002800000003\",\"name\":\"ADLS Container\",\"product\":\"CATALOG\",\"publicId\":\"AdlsContainer\",\"ratingEnabled\":false,\"system\":true},{\"description\":\"An asset type that represents Azure Data Lake Storage file system.\",\"displayNameEnabled\":true,\"finalType\":false,\"id\":\"00000000-0000-0000-0001-002800000001\",\"name\":\"ADLS File System\",\"product\":\"CATALOG\",\"publicId\":\"AdlsFileSystem\",\"ratingEnabled\":false,\"system\":true}],\"limit\":2,\"offset\":0,\"resultsTruncated\":false,\"scanned\":0,\"status\":\"success\",\"total\":260}"}],"structuredC
>>> {"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "list_asset_types", "arguments": {"limit": 1, "name": "Business Term"}}}
<<< {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"{\"assetTypes\":[{\"description\":\"A word or phrase that is used to describe something or to express a concept in a particular kind of language or branch of business.\\n\\nExamples: Customer, Person Purchase Count, Loan Amount.\",\"displayNameEnabled\":false,\"finalType\":false,\"id\":\"00000000-0000-0000-0000-000000011001\",\"name\":\"Business Term\",\"product\":\"GLOSSARY\",\"publicId\":\"BusinessTerm\",\"ratingEnabled\":false,\"system\":true}],\"limit\":1,\"offset\":0,\"resultsTruncated\":false,\"scanned\":0,\"status\":\"success\",\"total\":1}"}],"structuredContent":{"assetTypes":[{"description":"A word or phrase that is used to describe something or to express a concept in a particular kind of language or branch of business.\n\nExamples: Customer, Person Purchase Count, Loan Amount.","displayNameEnabled":false,"
2026/11/13 20:07:50 WARN Using a single basic auth header for all requests is not recommended as it will result in all actions being attributed to the same account. Consider setting an appropriate basic auth header for each request.
2026/11/13 20:07:50 INFO Calling tool: list_asset_types tool_name=list_asset_types
2026/11/13 20:07:50 INFO Calling tool: list_asset_types tool_name=list_asset_types

Logging proxy record (chip -> DGC):

GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=2 -> 200 (39.0ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic QWRtaW46YWRtaW4=", "Content-Type": "application/json", "Traceparent": "00-fd713b593305c8ec25e1b4f442cb6141-40bb457cc17d28fa-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "Accept-Encoding": "gzip"}
  resp body: {"total":260,"offset":0,"limit":2,"results":[{"id":"00000000-0000-0000-0001-002800000003","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-00000090000
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1&name=Business+Term -> 200 (25.5ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic QWRtaW46YWRtaW4=", "Content-Type": "application/json", "Traceparent": "00-98bbe41250622ad1947798af9e9ee7b4-ca9abd114cdaf464-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "Accept-Encoding": "gzip"}
  resp body: {"total":1,"offset":0,"limit":1,"results":[{"id":"00000000-0000-0000-0000-000000011001","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-000000900001"

Run 2a — http mode, client Authorization: Basic Admin

MCP JSON-RPC (raw):

>>> POST http://localhost:8091/ headers={"Content-Type": "application/json", "Accept": "application/json, text/event-stream", "Authorization": "Basic QWRtaW46YWRtaW4="}
>>> {"jsonrpc": "2.0", "id": 0, "method": "initialize", "params": {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "validate-ac", "v …(initialize, truncated)
<<< HTTP 200
<<< event: message
data: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{"logging":{},"tools":{"listChanged":true}},"instructions":"# Collibra MCP Server\n\nThis server connects …(initialize, truncated)
>>> POST http://localhost:8091/ headers={"Content-Type": "application/json", "Accept": "application/json, text/event-stream", "Authorization": "Basic QWRtaW46YWRtaW4="}
>>> {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "list_asset_types", "arguments": {"limit": 1}}}
<<< HTTP 200
<<< event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"assetTypes\":[{\"description\":\"An asset type that represents an Azure Data Lake Container, which is a logical unit of storage containing Azure Data Lake Storage objects.\",\"displayNameEnabled\":true,\"finalType\":false,\"id\":\"00000000-0000-0000-0001-002800000003\",\"name\":\"ADLS Container\",\"product\":\"CATALOG\",\"publicId\":\"AdlsContainer\",\"ratingEnabled\":false,\"system\":true}],\"limit\":1,\"offset\":0,\"resultsTruncated\":false,\"scanned\":0,\"status\":\"success\",\"total\":260}"}],"structuredContent":{"assetTypes":[{"description":"An asset type that represents an Azure Data Lake Container, which is a logical unit of storage containing Azure Data Lake Storage objects.","displayNameEnabled":true,"finalType":false,"id":"00000000-0000-0000-0001-002800000003","name":"ADLS Container","product":"CATALOG
>>> POST http://localhost:8091/ headers={"Content-Type": "application/json", "Accept": "application/json, text/event-stream", "Authorization": "Basic QWRtaW46YWRtaW4="}
>>> {"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "list_asset_types", "arguments": {"limit": 1, "name": "Business Term"}}}
<<< HTTP 200
<<< event: message
data: {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"{\"assetTypes\":[{\"description\":\"A word or phrase that is used to describe something or to express a concept in a particular kind of language or branch of business.\\n\\nExamples: Customer, Person Purchase Count, Loan Amount.\",\"displayNameEnabled\":false,\"finalType\":false,\"id\":\"00000000-0000-0000-0000-000000011001\",\"name\":\"Business Term\",\"product\":\"GLOSSARY\",\"publicId\":\"BusinessTerm\",\"ratingEnabled\":false,\"system\":true}],\"limit\":1,\"offset\":0,\"resultsTruncated\":false,\"scanned\":0,\"status\":\"success\",\"total\":1}"}],"structuredContent":{"assetTypes":[{"description":"A word or phrase that is used to describe something or to express a concept in a particular kind of language or branch of business.\n\nExamples: Customer, Person Purchase Count, Loan Amount.","displayNameEnabled":false

Logging proxy record (chip -> DGC):

GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/users/current -> 200 (51.0ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Accept": "application/json", "Authorization": "Basic QWRtaW46YWRtaW4=", "Content-Type": "application/json", "Accept-Encoding": "gzip"}
  resp body: {"id":"00000000-0000-0000-0000-000000900002","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-000000900001","lastModifiedOn":1794600222152,"system":fa
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1 -> 200 (24.3ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic QWRtaW46YWRtaW4=", "Content-Type": "application/json", "Traceparent": "00-077e29395b99ab1d5a72bf3c3699bc8c-d935f9cc73b080ce-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "X-Mcp-User-Id": "00000000-0000-0000-0000-000000900002", "Accept-Encoding": "gzip"}
  resp body: {"total":260,"offset":0,"limit":1,"results":[{"id":"00000000-0000-0000-0001-002800000003","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-00000090000
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1&name=Business+Term -> 200 (23.3ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic QWRtaW46YWRtaW4=", "Content-Type": "application/json", "Traceparent": "00-73caf963404e30365d0558f779fe91ee-ee7eea2f11246592-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "X-Mcp-User-Id": "00000000-0000-0000-0000-000000900002", "Accept-Encoding": "gzip"}
  resp body: {"total":1,"offset":0,"limit":1,"results":[{"id":"00000000-0000-0000-0000-000000011001","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-000000900001"

Run 2b — http mode, client Authorization: Bearer (password-grant token, Admin)

MCP JSON-RPC (raw):

>>> POST http://localhost:8091/ headers={"Content-Type": "application/json", "Accept": "application/json, text/event-stream", "Authorization": "Bearer eyJra..."}
>>> {"jsonrpc": "2.0", "id": 0, "method": "initialize", "params": {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "validate-ac", "v …(initialize, truncated)
<<< HTTP 200
<<< event: message
data: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{"logging":{},"tools":{"listChanged":true}},"instructions":"# Collibra MCP Server\n\nThis server connects …(initialize, truncated)
>>> POST http://localhost:8091/ headers={"Content-Type": "application/json", "Accept": "application/json, text/event-stream", "Authorization": "Bearer eyJra..."}
>>> {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "list_asset_types", "arguments": {"limit": 1}}}
<<< HTTP 200
<<< event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"assetTypes\":[{\"description\":\"An asset type that represents an Azure Data Lake Container, which is a logical unit of storage containing Azure Data Lake Storage objects.\",\"displayNameEnabled\":true,\"finalType\":false,\"id\":\"00000000-0000-0000-0001-002800000003\",\"name\":\"ADLS Container\",\"product\":\"CATALOG\",\"publicId\":\"AdlsContainer\",\"ratingEnabled\":false,\"system\":true}],\"limit\":1,\"offset\":0,\"resultsTruncated\":false,\"scanned\":0,\"status\":\"success\",\"total\":260}"}],"structuredContent":{"assetTypes":[{"description":"An asset type that represents an Azure Data Lake Container, which is a logical unit of storage containing Azure Data Lake Storage objects.","displayNameEnabled":true,"finalType":false,"id":"00000000-0000-0000-0001-002800000003","name":"ADLS Container","product":"CATALOG
>>> POST http://localhost:8091/ headers={"Content-Type": "application/json", "Accept": "application/json, text/event-stream", "Authorization": "Bearer eyJra..."}
>>> {"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "list_asset_types", "arguments": {"limit": 1, "name": "Business Term"}}}
<<< HTTP 200
<<< event: message
data: {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"{\"assetTypes\":[{\"description\":\"A word or phrase that is used to describe something or to express a concept in a particular kind of language or branch of business.\\n\\nExamples: Customer, Person Purchase Count, Loan Amount.\",\"displayNameEnabled\":false,\"finalType\":false,\"id\":\"00000000-0000-0000-0000-000000011001\",\"name\":\"Business Term\",\"product\":\"GLOSSARY\",\"publicId\":\"BusinessTerm\",\"ratingEnabled\":false,\"system\":true}],\"limit\":1,\"offset\":0,\"resultsTruncated\":false,\"scanned\":0,\"status\":\"success\",\"total\":1}"}],"structuredContent":{"assetTypes":[{"description":"A word or phrase that is used to describe something or to express a concept in a particular kind of language or branch of business.\n\nExamples: Customer, Person Purchase Count, Loan Amount.","displayNameEnabled":false

Logging proxy record (chip -> DGC):

GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/users/current -> 200 (21.1ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Accept": "application/json", "Authorization": "Bearer eyJraWQiOiJkZ\u2026", "Content-Type": "application/json", "Accept-Encoding": "gzip"}
  resp body: {"id":"00000000-0000-0000-0000-000000900002","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-000000900001","lastModifiedOn":1794600222152,"system":fa
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1 -> 200 (31.4ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Bearer eyJraWQiOiJkZ\u2026", "Content-Type": "application/json", "Traceparent": "00-14e571afde5600904fcbdcdfed87fd32-b805f76dab02c624-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "X-Mcp-User-Id": "00000000-0000-0000-0000-000000900002", "Accept-Encoding": "gzip"}
  resp body: {"total":260,"offset":0,"limit":1,"results":[{"id":"00000000-0000-0000-0001-002800000003","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-00000090000
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1&name=Business+Term -> 200 (27.1ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Bearer eyJraWQiOiJkZ\u2026", "Content-Type": "application/json", "Traceparent": "00-db844befbae2fd679571a94d0ed9bcf1-26cbcc96e9841ed3-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "X-Mcp-User-Id": "00000000-0000-0000-0000-000000900002", "Accept-Encoding": "gzip"}
  resp body: {"total":1,"offset":0,"limit":1,"results":[{"id":"00000000-0000-0000-0000-000000011001","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-000000900001"

Run 2c — http mode, NO Authorization

MCP JSON-RPC (raw):

>>> POST http://localhost:8091/ headers={"Content-Type": "application/json", "Accept": "application/json, text/event-stream"}
>>> {"jsonrpc": "2.0", "id": 0, "method": "initialize", "params": {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "validate-ac", "v …(initialize, truncated)
<<< HTTP 200
<<< event: message
data: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{"logging":{},"tools":{"listChanged":true}},"instructions":"# Collibra MCP Server\n\nThis server connects …(initialize, truncated)
>>> POST http://localhost:8091/ headers={"Content-Type": "application/json", "Accept": "application/json, text/event-stream"}
>>> {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "list_asset_types", "arguments": {"limit": 1}}}
<<< HTTP 200
<<< event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"Failed to authenticate request\",\"errorCode\":\"authenticationFailed\"}"}],"isError":true}}
>>> POST http://localhost:8091/ headers={"Content-Type": "application/json", "Accept": "application/json, text/event-stream"}
>>> {"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "list_asset_types", "arguments": {"limit": 1, "name": "Business Term"}}}
<<< HTTP 200
<<< event: message
data: {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"Failed to authenticate request\",\"errorCode\":\"authenticationFailed\"}"}],"isError":true}}

Logging proxy record (chip -> DGC):

GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1 -> 401 (11.6ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Content-Type": "application/json", "Traceparent": "00-f93a0656c36e003583ec679f89862af9-06984ff55e41d187-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "Accept-Encoding": "gzip"}
  resp body: {"statusCode":401,"titleMessage":"Unauthorized","helpMessage":"Please check the application logs for more information.","userMessage":"Failed to authenticate request","errorCode":"authenticationFailed"}
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1&name=Business+Term -> 401 (9.9ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Content-Type": "application/json", "Traceparent": "00-f8dae126d2e0c84b1c7a716a90074035-69e0277e8cb322c5-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "Accept-Encoding": "gzip"}
  resp body: {"statusCode":401,"titleMessage":"Unauthorized","helpMessage":"Please check the application logs for more information.","userMessage":"Failed to authenticate request","errorCode":"authenticationFailed"}

Run 2d — http mode, Basic Admin:wrong

MCP JSON-RPC (raw):

>>> POST http://localhost:8091/ headers={"Content-Type": "application/json", "Accept": "application/json, text/event-stream", "Authorization": "Basic QWRtaW46d3Jvbmc="}
>>> {"jsonrpc": "2.0", "id": 0, "method": "initialize", "params": {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "validate-ac", "v …(initialize, truncated)
<<< HTTP 200
<<< event: message
data: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{"logging":{},"tools":{"listChanged":true}},"instructions":"# Collibra MCP Server\n\nThis server connects …(initialize, truncated)
>>> POST http://localhost:8091/ headers={"Content-Type": "application/json", "Accept": "application/json, text/event-stream", "Authorization": "Basic QWRtaW46d3Jvbmc="}
>>> {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "list_asset_types", "arguments": {"limit": 1}}}
<<< HTTP 200
<<< event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"Failed to authenticate request through basic credentials\",\"errorCode\":\"authenticationFailed\"}"}],"isError":true}}
>>> POST http://localhost:8091/ headers={"Content-Type": "application/json", "Accept": "application/json, text/event-stream", "Authorization": "Basic QWRtaW46d3Jvbmc="}
>>> {"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "list_asset_types", "arguments": {"limit": 1, "name": "Business Term"}}}
<<< HTTP 200
<<< event: message
data: {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"Failed to authenticate request through basic credentials\",\"errorCode\":\"authenticationFailed\"}"}],"isError":true}}

Logging proxy record (chip -> DGC):

GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/users/current -> 401 (437.6ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Accept": "application/json", "Authorization": "Basic QWRtaW46d3Jvbmc=", "Content-Type": "application/json", "Accept-Encoding": "gzip"}
  resp body: {"statusCode":401,"titleMessage":"Unauthorized","helpMessage":"Please check the application logs for more information.","userMessage":"Failed to authenticate request through basic credentials","errorCode":"authentication
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1 -> 401 (439.1ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic QWRtaW46d3Jvbmc=", "Content-Type": "application/json", "Traceparent": "00-c02e95a001c681c1768aa910ac2a0b78-8711d47d0b21da09-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "Accept-Encoding": "gzip"}
  resp body: {"statusCode":401,"titleMessage":"Unauthorized","helpMessage":"Please check the application logs for more information.","userMessage":"Failed to authenticate request through basic credentials","errorCode":"authentication
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1&name=Business+Term -> 401 (445.2ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic QWRtaW46d3Jvbmc=", "Content-Type": "application/json", "Traceparent": "00-1d2a7a6a9bd9d034ddec33556c16911f-090b61755d221d16-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "Accept-Encoding": "gzip"}
  resp body: {"statusCode":401,"titleMessage":"Unauthorized","helpMessage":"Please check the application logs for more information.","userMessage":"Failed to authenticate request through basic credentials","errorCode":"authentication

Run 2e — http mode, Bearer bogus.token.value

MCP JSON-RPC (raw):

>>> POST http://localhost:8091/ headers={"Content-Type": "application/json", "Accept": "application/json, text/event-stream", "Authorization": "Bearer bogus.token.value"}
>>> {"jsonrpc": "2.0", "id": 0, "method": "initialize", "params": {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "validate-ac", "v …(initialize, truncated)
<<< HTTP 200
<<< event: message
data: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{"logging":{},"tools":{"listChanged":true}},"instructions":"# Collibra MCP Server\n\nThis server connects …(initialize, truncated)
>>> POST http://localhost:8091/ headers={"Content-Type": "application/json", "Accept": "application/json, text/event-stream", "Authorization": "Bearer bogus.token.value"}
>>> {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "list_asset_types", "arguments": {"limit": 1}}}
<<< HTTP 200
<<< event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"The bearer token is incorrectly encoded or has wrong syntax\",\"errorCode\":\"malformedToken\"}"}],"isError":true}}
>>> POST http://localhost:8091/ headers={"Content-Type": "application/json", "Accept": "application/json, text/event-stream", "Authorization": "Bearer bogus.token.value"}
>>> {"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "list_asset_types", "arguments": {"limit": 1, "name": "Business Term"}}}
<<< HTTP 200
<<< event: message
data: {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"The bearer token is incorrectly encoded or has wrong syntax\",\"errorCode\":\"malformedToken\"}"}],"isError":true}}

Logging proxy record (chip -> DGC):

GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/users/current -> 401 (10.5ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Accept": "application/json", "Authorization": "Bearer bogus.token.value", "Content-Type": "application/json", "Accept-Encoding": "gzip"}
  resp body: {"statusCode":401,"titleMessage":"Unauthorized","helpMessage":"Please check the application logs for more information.","userMessage":"The bearer token is incorrectly encoded or has wrong syntax","errorCode":"malformedTo
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1 -> 401 (8.4ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Bearer bogus.token.value", "Content-Type": "application/json", "Traceparent": "00-34ea4b5319b7e47e19a01bdbb96da00d-2d850bc4bedc614f-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "Accept-Encoding": "gzip"}
  resp body: {"statusCode":401,"titleMessage":"Unauthorized","helpMessage":"Please check the application logs for more information.","userMessage":"The bearer token is incorrectly encoded or has wrong syntax","errorCode":"malformedTo
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1&name=Business+Term -> 401 (11.1ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Bearer bogus.token.value", "Content-Type": "application/json", "Traceparent": "00-1a93bab87ffc09847eb10335b6e73562-aec6340a2086f389-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "Accept-Encoding": "gzip"}
  resp body: {"statusCode":401,"titleMessage":"Unauthorized","helpMessage":"Please check the application logs for more information.","userMessage":"The bearer token is incorrectly encoded or has wrong syntax","errorCode":"malformedTo

Run 3 — http mode, Admin and pesto-vac-user2 interleaved + 3 concurrent pairs (proxy record)

Logging proxy record (chip -> DGC):

GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1 -> 200 (36.1ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic QWRtaW46YWRtaW4=", "Content-Type": "application/json", "Traceparent": "00-aa872324f0597e0bdf62c33f4c70709c-074382aa947dac05-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "X-Mcp-User-Id": "00000000-0000-0000-0000-000000900002", "Accept-Encoding": "gzip"}
  resp body: {"total":260,"offset":0,"limit":1,"results":[{"id":"00000000-0000-0000-0001-002800000003","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-00000090000
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/users/current -> 200 (16.8ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Accept": "application/json", "Authorization": "Basic cGVzdG8tdmFjLXVzZXIyOmFkbWlu", "Content-Type": "application/json", "Accept-Encoding": "gzip"}
  resp body: {"id":"01a1d684-7fce-71cd-a1fc-fd153d0ec38b","createdBy":"00000000-0000-0000-0000-000000900002","createdOn":1794600370126,"lastModifiedBy":"00000000-0000-0000-0000-000000900002","lastModifiedOn":1794600370126,"system":fa
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1 -> 200 (23.8ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic cGVzdG8tdmFjLXVzZXIyOmFkbWlu", "Content-Type": "application/json", "Traceparent": "00-2523028e4a44c5e497c78834312632dd-e238ad36930211bd-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "X-Mcp-User-Id": "01a1d684-7fce-71cd-a1fc-fd153d0ec38b", "Accept-Encoding": "gzip"}
  resp body: {"total":260,"offset":0,"limit":1,"results":[{"id":"00000000-0000-0000-0001-002800000003","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-00000090000
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1 -> 200 (20.6ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic QWRtaW46YWRtaW4=", "Content-Type": "application/json", "Traceparent": "00-4c4a1cdcc22791a31d65d35f76fab65c-1b3fa00cadce4bd7-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "X-Mcp-User-Id": "00000000-0000-0000-0000-000000900002", "Accept-Encoding": "gzip"}
  resp body: {"total":260,"offset":0,"limit":1,"results":[{"id":"00000000-0000-0000-0001-002800000003","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-00000090000
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1 -> 200 (25.0ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic cGVzdG8tdmFjLXVzZXIyOmFkbWlu", "Content-Type": "application/json", "Traceparent": "00-f8a669173cf5ce85c1bb76ec909bd77b-98d29627f5dbe2ab-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "X-Mcp-User-Id": "01a1d684-7fce-71cd-a1fc-fd153d0ec38b", "Accept-Encoding": "gzip"}
  resp body: {"total":260,"offset":0,"limit":1,"results":[{"id":"00000000-0000-0000-0001-002800000003","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-00000090000
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1 -> 200 (23.2ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic cGVzdG8tdmFjLXVzZXIyOmFkbWlu", "Content-Type": "application/json", "Traceparent": "00-6521bf9053eaba644fdb98de533f7c08-10124c08bd19dd1b-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "X-Mcp-User-Id": "01a1d684-7fce-71cd-a1fc-fd153d0ec38b", "Accept-Encoding": "gzip"}
  resp body: {"total":260,"offset":0,"limit":1,"results":[{"id":"00000000-0000-0000-0001-002800000003","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-00000090000
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1 -> 200 (44.4ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic QWRtaW46YWRtaW4=", "Content-Type": "application/json", "Traceparent": "00-b4ecbcb3f6bc476d7e8b778280122ee0-32c14aaafea897aa-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "X-Mcp-User-Id": "00000000-0000-0000-0000-000000900002", "Accept-Encoding": "gzip"}
  resp body: {"total":260,"offset":0,"limit":1,"results":[{"id":"00000000-0000-0000-0001-002800000003","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-00000090000
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1 -> 200 (22.8ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic QWRtaW46YWRtaW4=", "Content-Type": "application/json", "Traceparent": "00-8fbeffb0d15a4f0a59753794351a1fcc-38b8a4c0704ea282-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "X-Mcp-User-Id": "00000000-0000-0000-0000-000000900002", "Accept-Encoding": "gzip"}
  resp body: {"total":260,"offset":0,"limit":1,"results":[{"id":"00000000-0000-0000-0001-002800000003","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-00000090000
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1 -> 200 (26.7ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic cGVzdG8tdmFjLXVzZXIyOmFkbWlu", "Content-Type": "application/json", "Traceparent": "00-1a75d6694d604b557800b9a26bb09d0b-61f647c933f7fb32-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "X-Mcp-User-Id": "01a1d684-7fce-71cd-a1fc-fd153d0ec38b", "Accept-Encoding": "gzip"}
  resp body: {"total":260,"offset":0,"limit":1,"results":[{"id":"00000000-0000-0000-0001-002800000003","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-00000090000
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1 -> 200 (32.6ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic cGVzdG8tdmFjLXVzZXIyOmFkbWlu", "Content-Type": "application/json", "Traceparent": "00-7bc628f5707e4513a6ab5c8d28b09bf9-dabfbc4581750708-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "X-Mcp-User-Id": "01a1d684-7fce-71cd-a1fc-fd153d0ec38b", "Accept-Encoding": "gzip"}
  resp body: {"total":260,"offset":0,"limit":1,"results":[{"id":"00000000-0000-0000-0001-002800000003","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-00000090000
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1 -> 200 (29.7ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic QWRtaW46YWRtaW4=", "Content-Type": "application/json", "Traceparent": "00-226ea4fdbcf0726016c75f5acaf4ef94-7c80b02c81a77770-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "X-Mcp-User-Id": "00000000-0000-0000-0000-000000900002", "Accept-Encoding": "gzip"}
  resp body: {"total":260,"offset":0,"limit":1,"results":[{"id":"00000000-0000-0000-0001-002800000003","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-00000090000

Run 4 — stdio, server-wide Basic Admin:wrong, two calls

MCP JSON-RPC (raw):

>>> {"jsonrpc": "2.0", "id": 0, "method": "initialize", "params": {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "validate-ac", "v …(initialize, truncated)
<<< {"jsonrpc":"2.0","id":0,"result":{"capabilities":{"logging":{},"tools":{"listChanged":true}},"instructions":"# Collibra MCP Server\n\nThis server connects y …(initialize, truncated)
>>> {"jsonrpc": "2.0", "method": "notifications/initialized"}
>>> {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "list_asset_types", "arguments": {"limit": 1}}}
<<< {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"Failed to authenticate request through basic credentials\",\"errorCode\":\"authenticationFailed\"}"}],"isError":true}}
>>> {"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "list_asset_types", "arguments": {"limit": 1}}}
<<< {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"HTTP 401: {\"statusCode\":401,\"titleMessage\":\"Unauthorized\",\"helpMessage\":\"Please check the application logs for more information.\",\"userMessage\":\"Failed to authenticate request through basic credentials\",\"errorCode\":\"authenticationFailed\"}"}],"isError":true}}
2026/11/13 20:10:36 WARN Using a single basic auth header for all requests is not recommended as it will result in all actions being attributed to the same account. Consider setting an appropriate basic auth header for each request.
2026/11/13 20:10:36 INFO Calling tool: list_asset_types tool_name=list_asset_types
2026/11/13 20:10:37 WARN Could not resolve the current user, x-mcp-user-id will not be sent: status 401
2026/11/13 20:10:37 INFO Calling tool: list_asset_types tool_name=list_asset_types

Logging proxy record (chip -> DGC):

GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/users/current -> 401 (426.5ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Accept": "application/json", "Authorization": "Basic QWRtaW46d3Jvbmc=", "Content-Type": "application/json", "Accept-Encoding": "gzip"}
  resp body: {"statusCode":401,"titleMessage":"Unauthorized","helpMessage":"Please check the application logs for more information.","userMessage":"Failed to authenticate request through basic credentials","errorCode":"authentication
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1 -> 401 (423.8ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic QWRtaW46d3Jvbmc=", "Content-Type": "application/json", "Traceparent": "00-4dfcf3a74f87d99bf36ca299e6163ab4-64709ad4ca000844-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "Accept-Encoding": "gzip"}
  resp body: {"statusCode":401,"titleMessage":"Unauthorized","helpMessage":"Please check the application logs for more information.","userMessage":"Failed to authenticate request through basic credentials","errorCode":"authentication
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1 -> 401 (429.6ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic QWRtaW46d3Jvbmc=", "Content-Type": "application/json", "Traceparent": "00-182986fe2030ba3ee0017aab6efe987f-a774c126e028f5d0-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "Accept-Encoding": "gzip"}
  resp body: {"statusCode":401,"titleMessage":"Unauthorized","helpMessage":"Please check the application logs for more information.","userMessage":"Failed to authenticate request through basic credentials","errorCode":"authentication

Run 5 — after nginx patch: stdio Basic Admin, then http Basic pesto-vac-user2

MCP JSON-RPC (raw):

>>> {"jsonrpc": "2.0", "id": 0, "method": "initialize", "params": {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "validate-ac", "v …(initialize, truncated)
<<< {"jsonrpc":"2.0","id":0,"result":{"capabilities":{"logging":{},"tools":{"listChanged":true}},"instructions":"# Collibra MCP Server\n\nThis server connects y …(initialize, truncated)
>>> {"jsonrpc": "2.0", "method": "notifications/initialized"}
>>> {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "list_asset_types", "arguments": {"limit": 1, "name": "Business Term"}}}
<<< {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"assetTypes\":[{\"description\":\"A word or phrase that is used to describe something or to express a concept in a particular kind of language or branch of business.\\n\\nExamples: Customer, Person Purchase Count, Loan Amount.\",\"displayNameEnabled\":false,\"finalType\":false,\"id\":\"00000000-0000-0000-0000-000000011001\",\"name\":\"Business Term\",\"product\":\"GLOSSARY\",\"publicId\":\"BusinessTerm\",\"ratingEnabled\":false,\"system\":true}],\"limit\":1,\"offset\":0,\"resultsTruncated\":false,\"scanned\":0,\"status\":\"success\",\"total\":1}"}],"structuredContent":{"assetTypes":[{"description":"A word or phrase that is used to describe something or to express a concept in a particular kind of language or branch of business.\n\nExamples: Customer, Person Purchase Count, Loan Amount.","displayNameEnabled":false,"
2026/11/13 20:11:43 WARN Using a single basic auth header for all requests is not recommended as it will result in all actions being attributed to the same account. Consider setting an appropriate basic auth header for each request.
2026/11/13 20:11:43 INFO Calling tool: list_asset_types tool_name=list_asset_types

>>> POST http://localhost:8091/ headers={"Content-Type": "application/json", "Accept": "application/json, text/event-stream", "Authorization": "Basic cGVzdG8tdmFjLXVzZXIyOmFkbWlu"}
>>> {"jsonrpc": "2.0", "id": 0, "method": "initialize", "params": {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "validate-ac", "v …(initialize, truncated)
<<< HTTP 200
<<< event: message
data: {"jsonrpc":"2.0","id":0,"result":{"capabilities":{"logging":{},"tools":{"listChanged":true}},"instructions":"# Collibra MCP Server\n\nThis server connects …(initialize, truncated)
>>> POST http://localhost:8091/ headers={"Content-Type": "application/json", "Accept": "application/json, text/event-stream", "Authorization": "Basic cGVzdG8tdmFjLXVzZXIyOmFkbWlu"}
>>> {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "list_asset_types", "arguments": {"limit": 1, "name": "Business Term"}}}
<<< HTTP 200
<<< event: message
data: {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"assetTypes\":[{\"description\":\"A word or phrase that is used to describe something or to express a concept in a particular kind of language or branch of business.\\n\\nExamples: Customer, Person Purchase Count, Loan Amount.\",\"displayNameEnabled\":false,\"finalType\":false,\"id\":\"00000000-0000-0000-0000-000000011001\",\"name\":\"Business Term\",\"product\":\"GLOSSARY\",\"publicId\":\"BusinessTerm\",\"ratingEnabled\":false,\"system\":true}],\"limit\":1,\"offset\":0,\"resultsTruncated\":false,\"scanned\":0,\"status\":\"success\",\"total\":1}"}],"structuredContent":{"assetTypes":[{"description":"A word or phrase that is used to describe something or to express a concept in a particular kind of language or branch of business.\n\nExamples: Customer, Person Purchase Count, Loan Amount.","displayNameEnabled":false

Logging proxy record (chip -> DGC):

GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/users/current -> 200 (25.4ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Accept": "application/json", "Authorization": "Basic QWRtaW46YWRtaW4=", "Content-Type": "application/json", "Accept-Encoding": "gzip"}
  resp body: {"id":"00000000-0000-0000-0000-000000900002","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-000000900001","lastModifiedOn":1794600222152,"system":fa
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1&name=Business+Term -> 200 (22.5ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic QWRtaW46YWRtaW4=", "Content-Type": "application/json", "Traceparent": "00-91d27483b4455f305dfac7c28ac0ccba-029c6055c322b6d6-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "X-Mcp-User-Id": "00000000-0000-0000-0000-000000900002", "Accept-Encoding": "gzip"}
  resp body: {"total":1,"offset":0,"limit":1,"results":[{"id":"00000000-0000-0000-0000-000000011001","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-000000900001"
GET http://dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400/rest/2.0/assetTypes?excludeMeta=true&limit=1&name=Business+Term -> 200 (21.7ms)
  req headers: {"Host": "dgc-gateway.dgc-af3a7b6ebec4.svc.cluster.local:4400", "User-Agent": "Collibra MCP/0.0.0-SNAPSHOT", "Authorization": "Basic cGVzdG8tdmFjLXVzZXIyOmFkbWlu", "Content-Type": "application/json", "Traceparent": "00-40832b3f149a11b148ad4f3454245793-f11d26023e42a226-01", "X-Mcp-Session-Id": "", "X-Mcp-Tool-Name": "list_asset_types", "X-Mcp-User-Id": "01a1d684-7fce-71cd-a1fc-fd153d0ec38b", "Accept-Encoding": "gzip"}
  resp body: {"total":1,"offset":0,"limit":1,"results":[{"id":"00000000-0000-0000-0000-000000011001","createdBy":"00000000-0000-0000-0000-000000900001","createdOn":1794599788313,"lastModifiedBy":"00000000-0000-0000-0000-000000900001"

nginx.conf BEFORE (ConfigMap dgc-gateway)

events { worker_connections 1024; }
http {
  map $http_host $forwarded_port {
    ~:(?<port>[0-9]+)$ $port;
    default 80;
  }
  map $upstream_http_content_security_policy $csp_without_upgrade {
    '~*^(?<before>(?:.*;)?\s*)upgrade-insecure-requests\s*;\s*(?<after>.*)$' $before$after;
    '~*^(?:(?<before>.*?)\s*;)?\s*upgrade-insecure-requests\s*$' $before;
    default $upstream_http_content_security_policy;
  }
  map $http_upgrade $connection_upgrade {
    default upgrade;
    '' '';
  }
  client_max_body_size 256m;
  proxy_read_timeout 120s;
  proxy_send_timeout 120s;
  server {
    listen 4400;
    location = /rest/oauth/v2 {
      proxy_pass http://auth.dgc-af3a7b6ebec4.svc.cluster.local:4557;
      proxy_http_version 1.1;
      proxy_set_header Host $http_host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection $connection_upgrade;
      proxy_hide_header Strict-Transport-Security;
      proxy_hide_header Content-Security-Policy;
      add_header Content-Security-Policy $csp_without_upgrade always;
    }
    location /rest/oauth/v2/ {
      proxy_pass http://auth.dgc-af3a7b6ebec4.svc.cluster.local:4557;
      proxy_http_version 1.1;
      proxy_set_header Host $http_host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection $connection_upgrade;
      proxy_hide_header Strict-Transport-Security;
      proxy_hide_header Content-Security-Policy;
      add_header Content-Security-Policy $csp_without_upgrade always;
    }
    location / {
      proxy_pass http://dgc.dgc-af3a7b6ebec4.svc.cluster.local:4400;
      proxy_http_version 1.1;
      proxy_set_header Host $http_host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection $connection_upgrade;
      proxy_hide_header Strict-Transport-Security;
      proxy_hide_header Content-Security-Policy;
      add_header Content-Security-Policy $csp_without_upgrade always;
      proxy_set_header X-Forwarded-Host $http_host;
      proxy_set_header X-Forwarded-Port $forwarded_port;
    }
  }
}

Shorten the current-user lookup timeout to 500ms and cap the response at 1 MiB. Definitive failures (4xx other than 408/429, 200 without an id) stay cached for 10 minutes; transient ones (timeout, transport error, 5xx, 408, 429, undecodable or oversized body, panic) are cached for 30 seconds. 401/403 are logged at debug level. A panic in the lookup is recovered and still releases waiters. A non-2xx/non-4xx status other than 5xx (e.g. 3xx) is treated as transient, since the requirements only name 4xx as definitive.

Co-authored-by: betty-pr-factory-app[bot] <316249552+betty-pr-factory-app[bot]@users.noreply.github.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

betty-pr-factory-app Created by the BETTY PR Factory App GitHub App on behalf of an engineer (PESTO)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant