-
Notifications
You must be signed in to change notification settings - Fork 81
feat(mcp): capture resource discovery and reads #928
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
Changes from all commits
b245a3c
a341587
62caaf1
6dea4de
88ebc29
0eb054c
df1c814
21f9f55
e5892c9
2b11efc
1d37981
65ccc76
5f5a65e
36e175e
98abc7b
b50cd56
1999e40
f0c6c57
3419fe3
757aa3c
e9e82dd
916eadf
19a696c
6c980c6
84655d0
67a76c6
2502fb8
d867631
b7179b9
d3ec6c8
b760dc6
7371ec7
d05b16e
f70061d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| pypi/posthog: minor | ||
| --- | ||
|
|
||
| Capture MCP resource discovery and reads from instrumented servers. URL credential redaction (userinfo, credential-named query and fragment parameters) now applies to every captured string, including existing `$mcp_tool_call` parameters, responses and error messages, so URLs in existing tool-call data will show `%5Bredacted%5D` values after upgrading. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,10 +2,9 @@ | |
| # Copyright (c) 2025 MCPcat | ||
| # Licensed under the MIT License: https://github.com/MCPCat/mcpcat-typescript-sdk/blob/main/LICENSE | ||
|
|
||
| """Shared tool-call / tools-list / initialize lifecycle used by both the FastMCP | ||
| and low-level server adapters. The adapters resolve transport-specific details | ||
| (client info, session id, raw result shape) and delegate the analytics flow here | ||
| so both stay in sync.""" | ||
| """Shared MCP request lifecycles used by both the FastMCP and low-level server | ||
| adapters. The adapters resolve transport-specific details (client info, session | ||
| id, raw result shape) and delegate analytics policy here so both stay in sync.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
|
|
@@ -904,3 +903,55 @@ async def record_tools_list( | |
| fire_and_forget(capture_event(data, event), data) | ||
| except Exception as err: # noqa: BLE001 - isolate analytics from the tool path | ||
| log(f"record_tools_list failed (event dropped): {err}") | ||
|
|
||
|
|
||
| def resource_listing_response(event_type: str, result: Any) -> Any: | ||
| """The result an adapter should capture as the event ``response``. A listing | ||
| (``resources/list``, ``resources/templates/list``) is metadata — names, uris, | ||
| mime types — so it is captured; a read's result is the resource body itself, | ||
| which this SDK never captures.""" | ||
| if event_type != MCPAnalyticsEventType.MCP_RESOURCES_LIST: | ||
| return None | ||
| return _to_jsonable(result) | ||
|
|
||
|
|
||
| async def record_resource_request( | ||
| data: MCPAnalyticsData, | ||
| session_id: str, | ||
| *, | ||
| event_type: str, | ||
| request: Dict[str, Any], | ||
| response: Any = None, | ||
| error: Any = None, | ||
| duration_ms: Optional[float] = None, | ||
| client_name: Optional[str] = None, | ||
| client_version: Optional[str] = None, | ||
| protocol_version: Optional[str] = None, | ||
| extra: Optional[Dict[str, Any]] = None, | ||
| ) -> None: | ||
| """Record a resources listing or read without affecting dispatch.""" | ||
| try: | ||
| params = request.get("params") | ||
| uri = params.get("uri") if isinstance(params, dict) else None | ||
| event: Dict[str, Any] = { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in df1c814. Listing events now carry the listing result as |
||
| "event_type": event_type, | ||
| "session_id": session_id, | ||
| "resource_name": uri | ||
| if event_type == MCPAnalyticsEventType.MCP_RESOURCES_READ | ||
| else None, | ||
| "parameters": build_captured_mcp_parameters(request), | ||
| "response": _wrap_response(response) if response is not None else None, | ||
| "duration": duration_ms, | ||
| "client_name": client_name, | ||
| "client_version": client_version, | ||
| "protocol_version": protocol_version, | ||
| "is_error": error is not None, | ||
| "timestamp": datetime.now(timezone.utc), | ||
| } | ||
| if error is not None: | ||
| event["error"] = capture_exception(error) | ||
| await _apply_event_properties(data, event, request, extra) | ||
| stamp_transport_identity(event, extra) | ||
| fire_and_forget(capture_event(data, event), data) | ||
| except Exception as err: # noqa: BLE001 - isolate analytics from the request path | ||
| log(f"record_resource_request failed (event dropped): {err}") | ||
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.
resources/templates/listis not instrumented in any adapter. MCP resource discovery has two methods:resources/listandresources/templates/list. This wrapper covers onlyListResourcesRequest/ReadResourceRequest, and_RESOURCE_METHODSin_instrument_v2.pylists onlyresources/listandresources/read. FastMCP registers aListResourceTemplatesRequesthandler, and templated resources (@mcp.resource("users://{id}")) are advertised only through it. A template-only server therefore shows reads with no matching discovery events, while the README and changeset claim resource discovery is captured. No test registers a templated resource either. Suggestion: wrapListResourceTemplatesRequest/resources/templates/listin both adapters, or scope the README claim.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.
Fixed in df1c814.
resources/templates/listis wrapped on every adapter (low-level v1, FastMCP, v2) and emitted as$mcp_resources_list;$mcp_parameters.request.methodseparates it fromresources/list. Tests register a templatedusers://{user_id}/profileresource on each adapter.