-
Notifications
You must be signed in to change notification settings - Fork 74
feat(mcp): add anonymized MCP attribution to tool-call telemetry #1131
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
base: main
Are you sure you want to change the base?
Changes from all commits
51c926a
2bdc2bf
5fa6b91
8dc2982
d434d90
999a336
ec429a9
a048bc9
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 |
|---|---|---|
|
|
@@ -70,7 +70,11 @@ | |
| from starlette.requests import Request | ||
|
|
||
| from airbyte._util.meta import set_mcp_mode | ||
| from airbyte._util.telemetry import DO_NOT_TRACK, PYAIRBYTE_APP_TRACKING_KEY | ||
| from airbyte._util.telemetry import ( | ||
| DO_NOT_TRACK, | ||
| PYAIRBYTE_APP_TRACKING_KEY, | ||
| _get_analytics_id, | ||
| ) | ||
| from airbyte.constants import AIRBYTE_OFFLINE_MODE, _str_to_bool, is_hosted_mcp_mode | ||
| from airbyte.mcp._config import load_secrets_to_env_vars | ||
| from airbyte.mcp._tool_utils import ( | ||
|
|
@@ -305,6 +309,7 @@ def _create_auth() -> AuthProvider | None: | |
|
|
||
|
|
||
| SEGMENT_WRITE_KEY_ENV = "AIRBYTE_MCP_SEGMENT_WRITE_KEY" | ||
| ANONYMIZATION_SALT_ENV = "AIRBYTE_TELEMETRY_ANONYMIZATION_SALT" | ||
|
Contributor
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. 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win Align the salt environment-variable contract. Could we read 🤖 Prompt for AI Agents
Contributor
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. ☑️ Real mismatch, but the description is what's stale — fixing that, not the code. The env var name is a deployment contract with the Ops repo: |
||
|
|
||
| SEGMENT_USER_ID = "airbyte-mcp" | ||
| """Identifies the PyAirbyte MCP server as the event source. | ||
|
|
@@ -327,6 +332,27 @@ def _segment_write_key() -> str | None: | |
| return _env_or_default(SEGMENT_WRITE_KEY_ENV, PYAIRBYTE_APP_TRACKING_KEY) or None | ||
|
|
||
|
|
||
| def _mcp_extra_properties() -> dict[str, bool]: | ||
| """Return PyAirbyte-specific telemetry properties for an MCP tool call.""" | ||
| return {"is_hosted_mcp": is_hosted_mcp_mode()} | ||
|
|
||
|
|
||
| def _mcp_anonymization_salt() -> str | None: | ||
| """Return the configured salt or the persisted analytics ID.""" | ||
| return os.environ.get(ANONYMIZATION_SALT_ENV) or _get_analytics_id() | ||
|
|
||
|
|
||
| def _mcp_segment_anonymous_id() -> str | None: | ||
| """Return the local analytics ID; hosted identity comes from caller hashes. | ||
|
|
||
| A hosted container's analytics ID is per-revision and shared across callers, | ||
| so emitting it would invent a fake user instead of representing a caller. | ||
| """ | ||
| if is_hosted_mcp_mode(): | ||
| return None | ||
| return _get_analytics_id() | ||
|
|
||
|
|
||
| set_mcp_mode() | ||
| load_secrets_to_env_vars() | ||
|
|
||
|
|
@@ -360,7 +386,10 @@ def _segment_write_key() -> str | None: | |
| package_name="airbyte", | ||
| segment_write_key=segment_write_key, | ||
| segment_user_id=SEGMENT_USER_ID, | ||
| extra_properties=lambda: {"is_hosted_mcp": is_hosted_mcp_mode()}, | ||
| segment_anonymous_id=_mcp_segment_anonymous_id, | ||
| extra_properties=_mcp_extra_properties, | ||
| known_public_mcp_domains=("airbyte.ai", "airbyte.com", "airbyte.io"), | ||
| anonymization_salt=_mcp_anonymization_salt, | ||
| ), | ||
| ) | ||
| """The Airbyte MCP Server application instance.""" | ||
|
|
||
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 the probably the right home for anything that can't live generically in the telemetry.py module (without importing fastmcp) and which can't live in fastmcp-extensions.
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.
👍 On it. Putting it in
server.pydirectly rather than a new private module — it's the only consumer, and a separate module would be a third home for code that's headed upstream to fastmcp-extensions#113 anyway. If it turns out to bloatserver.pybadly I'll use a private module in the same package and say so here.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.
☑️ Resolved in 999a336. No
fastmcpimport anywhere under_util/— verified by importingairbyte._util.telemetrywithfastmcpblocked fromsys.modules, transitively as well as directly.