-
Couldn't load subscription status.
- Fork 117
mcp: add configured header attribtues to metrics and spans #1342
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Codecov Report❌ Patch coverage is
❌ Your project status has failed because the head coverage (77.13%) is below the target coverage (86.00%). You can increase the head coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #1342 +/- ##
==========================================
+ Coverage 77.03% 77.13% +0.09%
==========================================
Files 125 126 +1
Lines 16136 16204 +68
==========================================
+ Hits 12431 12499 +68
Misses 3080 3080
Partials 625 625 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is "ok" but I think we are misunderstanding the session concept here. So attributes like session.id will need to be propagated as request meta, just like trace IDs are. This change will not be able to be used for all kinds of session because it relies on header which are typically not set in the smaller scope of JSON rpc messages.
internal/metrics/mcp_metrics.go
Outdated
| initializationDuration metric.Float64Histogram | ||
| capabilitiesNegotiated metric.Float64Counter | ||
| progressNotifications metric.Float64Counter | ||
| requestHeaderLabelMapping map[string]string // maps HTTP headers to metric label names. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please use the word attribute as this is otel and not just prom. label is a prom thing and applied at export
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. I've also added a commit to rename it in the chat completion and embeddings metrics implementations
internal/mcpproxy/handlers.go
Outdated
| } | ||
|
|
||
| span := m.tracer.StartSpanAndInjectMeta(ctx, req, p) | ||
| // TODO: headers with multiple values |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
curious why not pass http.Header type instead of converting it to a map like this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly to keep a similar interface to the chat completions one: https://github.com/envoyproxy/ai-gateway/blob/main/internal/tracing/api/api.go#L81
But I can change to http.Header if we think that'd be better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it isn't too important just I must have started a problem with copying in maps? if we don't need to copy we shouldn't especailly as headers are case insentive in http
|
another way out would be to do the HTTP tier in case some attribute maps apply, just don't close out MCP mapping until there's another attribute for request meta -> span attributes. Doing things at the HTTP layer will apply attributes to multiple traces (to to the JSON-RPC messaging layer), which is different than normal HTTP where one request is a single span. modelcontextprotocol/modelcontextprotocol#414 is the details about why trace propagation is at meta level, and so likely should be other things like session.id. |
|
I thought about the client = AsyncOpenAI(base_url="...", api_key="...", default_headers=CUSTOM_HEADERS)
provider = OpenAIProvider(openai_client=client)So, for people already using custom headers for LLM, initializing the MCP client using the same configuration looks like something reasonable that will just work with current setups, as the clients are already accepting custom headers. mcp = MCPServerStreamableHttp(name="Envoy AI Gateway MCP",
params={"url": mcp_url, "timeout": 300, "headers": CUSTOM_HEADERS},
cache_tools_list=True,
)Having the tracing implementation extract these custom attributes from the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nacx it is ok to go forward with this, my main point was to highlight that it is solving a different scope than why that feature was added (session.id). That said, the issue you are mentioning is at a higher scope "user.region" which this would solve.
To do session.id properly we need to extract attributes from the json-rpc messages, and that can happen later. I don't think you can predict this will never happen because already in order to try new MCP spec additions people put things in meta.
Anyway, since this change is about "X-Tracing-Enrichment-User-Region"-> "user.region" I recommend using exactly those values in your unit tests. Much better than arbitrary and explains the motivation!
|
opened #1348 so we don't have to go around on this one. It is ok to skip if you don't feel it is straightforward to implement and wait for someone to complain. Just it takes several months sometimes for an aigw release. Meanwhile any late request can use the docker images built from main branch anyway! |
|
@codefromthecrypt in 1420dbb I'm reading custom attributes from the metadata as well, those taking precedence over the HTTP header ones. WDYT? |
|
/retest |
| if headerValue, ok := headers[headerName]; ok { | ||
| attrs = append(attrs, attribute.String(attrName, headerValue)) | ||
| for srcName, targetName := range m.attributeMappings { | ||
| // Check if the attribute is present in the metadata first, as this is the common place to add custom attributes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great idea!
|
fyi I am working on goose to do propagation at the moment and will probably try to get it to propagate its session id when done |
|
is this ready or on hold? |
|
This is ready! :) |
internal/mcpproxy/handlers.go
Outdated
| headerMap := make(map[string]string, len(headers)) | ||
| for header := range headers { | ||
| headerMap[header] = headers.Get(header) | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
non blocking nit comment (as i think the impact is negligible): maybe skip this header copying when the header-to-attribute thingy is not configured at the startup time
internal/tracing/mcp.go
Outdated
| // Check if the attribute is present in the metadata first, as this is the common place to add custom attributes | ||
| // in MCP requests. Fall back to headers if not found in metadata. | ||
| // If the attribute is not found there, check if there is any custom header to map. | ||
| if metaValue, ok := param.GetMeta()[srcName]; ok { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we need to make sure that we do downcase based comparison as unlike http these are case sensitive, but we should clobber anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or any other normal way to do case insensitive map (probably need to scan and decide to pick first or last even though maps aren't ordered in go..)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nacx could you address this adrian's comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done!
|
fyi for testing block/goose#5165 |
Signed-off-by: Ignasi Barrera <ignasi@tetrate.io>
Signed-off-by: Ignasi Barrera <ignasi@tetrate.io>
Signed-off-by: Ignasi Barrera <ignasi@tetrate.io>
Signed-off-by: Ignasi Barrera <ignasi@tetrate.io>
Signed-off-by: Ignasi Barrera <ignasi@tetrate.io>
Signed-off-by: Ignasi Barrera <ignasi@tetrate.io>
|
/retest |
…y#1342) **Description** The LLM metrics and trace providers allow for the configuration of custom headers to be included as metric attributes and span attributes. The MCP implementation, however, did not honor those values. This PR updates the MCP metrics and tracer configurations to also take this configuration into account. * The tracer method to create a span now receives the request headers. * To avoid having to propagate request headers all the way down inside the MCP Proxy so that they can be taken into account when recording metrics, I've opted to instantiate an MCPProxy on each request, and have it set up with the request attributes from the beginning. This allows reusing the metrics implementation in a clean way. **Related Issues/PRs (if applicable)** Fixes envoyproxy#1335 Fixes envoyproxy#1348 **Special notes for reviewers (if applicable)** N/A --------- Signed-off-by: Ignasi Barrera <ignasi@tetrate.io>
…y#1342) **Description** The LLM metrics and trace providers allow for the configuration of custom headers to be included as metric attributes and span attributes. The MCP implementation, however, did not honor those values. This PR updates the MCP metrics and tracer configurations to also take this configuration into account. * The tracer method to create a span now receives the request headers. * To avoid having to propagate request headers all the way down inside the MCP Proxy so that they can be taken into account when recording metrics, I've opted to instantiate an MCPProxy on each request, and have it set up with the request attributes from the beginning. This allows reusing the metrics implementation in a clean way. **Related Issues/PRs (if applicable)** Fixes envoyproxy#1335 Fixes envoyproxy#1348 **Special notes for reviewers (if applicable)** N/A --------- Signed-off-by: Ignasi Barrera <ignasi@tetrate.io> Signed-off-by: Hrushikesh Patil <hrushikesh.patil@nutanix.com>
…y#1342) **Description** The LLM metrics and trace providers allow for the configuration of custom headers to be included as metric attributes and span attributes. The MCP implementation, however, did not honor those values. This PR updates the MCP metrics and tracer configurations to also take this configuration into account. * The tracer method to create a span now receives the request headers. * To avoid having to propagate request headers all the way down inside the MCP Proxy so that they can be taken into account when recording metrics, I've opted to instantiate an MCPProxy on each request, and have it set up with the request attributes from the beginning. This allows reusing the metrics implementation in a clean way. **Related Issues/PRs (if applicable)** Fixes envoyproxy#1335 Fixes envoyproxy#1348 **Special notes for reviewers (if applicable)** N/A --------- Signed-off-by: Ignasi Barrera <ignasi@tetrate.io> Signed-off-by: Hrushikesh Patil <hrushikesh.patil@nutanix.com>
…y#1342) **Description** The LLM metrics and trace providers allow for the configuration of custom headers to be included as metric attributes and span attributes. The MCP implementation, however, did not honor those values. This PR updates the MCP metrics and tracer configurations to also take this configuration into account. * The tracer method to create a span now receives the request headers. * To avoid having to propagate request headers all the way down inside the MCP Proxy so that they can be taken into account when recording metrics, I've opted to instantiate an MCPProxy on each request, and have it set up with the request attributes from the beginning. This allows reusing the metrics implementation in a clean way. **Related Issues/PRs (if applicable)** Fixes envoyproxy#1335 Fixes envoyproxy#1348 **Special notes for reviewers (if applicable)** N/A --------- Signed-off-by: Ignasi Barrera <ignasi@tetrate.io> Signed-off-by: Hrushikesh Patil <hrushikesh.patil@nutanix.com>
…y#1342) **Description** The LLM metrics and trace providers allow for the configuration of custom headers to be included as metric attributes and span attributes. The MCP implementation, however, did not honor those values. This PR updates the MCP metrics and tracer configurations to also take this configuration into account. * The tracer method to create a span now receives the request headers. * To avoid having to propagate request headers all the way down inside the MCP Proxy so that they can be taken into account when recording metrics, I've opted to instantiate an MCPProxy on each request, and have it set up with the request attributes from the beginning. This allows reusing the metrics implementation in a clean way. **Related Issues/PRs (if applicable)** Fixes envoyproxy#1335 Fixes envoyproxy#1348 **Special notes for reviewers (if applicable)** N/A --------- Signed-off-by: Ignasi Barrera <ignasi@tetrate.io>
Description
The LLM metrics and trace providers allow for the configuration of custom headers to be included as metric attributes and span attributes. The MCP implementation, however, did not honor those values.
This PR updates the MCP metrics and tracer configurations to also take this configuration into account.
Related Issues/PRs (if applicable)
Fixes #1335
Fixes #1348
Special notes for reviewers (if applicable)
N/A