From 33dcb30c46c250972a34ed96422d98cba90ffa31 Mon Sep 17 00:00:00 2001 From: Ogi <86684834+obostjancic@users.noreply.github.com> Date: Thu, 10 Jul 2025 11:23:13 +0200 Subject: [PATCH 1/3] feat(mcp-insights): project flag --- src/sentry/api/serializers/models/project.py | 3 +++ src/sentry/apidocs/examples/organization_examples.py | 2 ++ src/sentry/apidocs/examples/project_examples.py | 2 ++ src/sentry/apidocs/examples/team_examples.py | 2 ++ src/sentry/models/project.py | 3 +++ src/sentry/projects/services/project/model.py | 1 + static/app/types/project.tsx | 1 + tests/sentry/api/serializers/test_project.py | 3 +++ 8 files changed, 17 insertions(+) diff --git a/src/sentry/api/serializers/models/project.py b/src/sentry/api/serializers/models/project.py index d7b4a2cd372e9a..506bad98138f11 100644 --- a/src/sentry/api/serializers/models/project.py +++ b/src/sentry/api/serializers/models/project.py @@ -285,6 +285,7 @@ class ProjectSerializerBaseResponse(_ProjectSerializerOptionalBaseResponse): hasInsightsQueues: bool hasInsightsLlmMonitoring: bool hasInsightsAgentMonitoring: bool + hasInsightsMCP: bool class ProjectSerializerResponse(ProjectSerializerBaseResponse): @@ -553,6 +554,7 @@ def serialize( "hasInsightsQueues": bool(obj.flags.has_insights_queues), "hasInsightsLlmMonitoring": bool(obj.flags.has_insights_llm_monitoring), "hasInsightsAgentMonitoring": bool(obj.flags.has_insights_agent_monitoring), + "hasInsightsMCP": bool(obj.flags.has_insights_mcp), "isInternal": obj.is_internal_project(), "isPublic": obj.public, # Projects don't have avatar uploads, but we need to maintain the payload shape for @@ -801,6 +803,7 @@ def serialize( # type: ignore[override] # intentionally different data shape hasInsightsQueues=bool(obj.flags.has_insights_queues), hasInsightsLlmMonitoring=bool(obj.flags.has_insights_llm_monitoring), hasInsightsAgentMonitoring=bool(obj.flags.has_insights_agent_monitoring), + hasInsightsMCP=bool(obj.flags.has_insights_mcp), platform=obj.platform, platforms=attrs["platforms"], latestRelease=attrs["latest_release"], diff --git a/src/sentry/apidocs/examples/organization_examples.py b/src/sentry/apidocs/examples/organization_examples.py index 5b1b6e7dbb4fdd..1331576ede8b69 100644 --- a/src/sentry/apidocs/examples/organization_examples.py +++ b/src/sentry/apidocs/examples/organization_examples.py @@ -388,6 +388,7 @@ class OrganizationExamples: "hasInsightsQueues": False, "hasInsightsLlmMonitoring": False, "hasInsightsAgentMonitoring": False, + "hasInsightsMCP": False, "platform": "node", "platforms": [], "latestRelease": None, @@ -452,6 +453,7 @@ class OrganizationExamples: "hasInsightsQueues": False, "hasInsightsLlmMonitoring": False, "hasInsightsAgentMonitoring": False, + "hasInsightsMCP": False, "latestRelease": None, } ], diff --git a/src/sentry/apidocs/examples/project_examples.py b/src/sentry/apidocs/examples/project_examples.py index 6596a2ff507059..cbc12d92e42ec2 100644 --- a/src/sentry/apidocs/examples/project_examples.py +++ b/src/sentry/apidocs/examples/project_examples.py @@ -102,6 +102,7 @@ "hasInsightsQueues": False, "hasInsightsLlmMonitoring": False, "hasInsightsAgentMonitoring": False, + "hasInsightsMCP": False, "isInternal": False, "isPublic": False, "avatar": {"avatarType": "letter_avatar", "avatarUuid": None}, @@ -343,6 +344,7 @@ "hasInsightsQueues": True, "hasInsightsLlmMonitoring": False, "hasInsightsAgentMonitoring": False, + "hasInsightsMCP": False, "platform": "node-express", "platforms": [], "latestRelease": None, diff --git a/src/sentry/apidocs/examples/team_examples.py b/src/sentry/apidocs/examples/team_examples.py index c2f66f152cdddf..ba342ad4549ea1 100644 --- a/src/sentry/apidocs/examples/team_examples.py +++ b/src/sentry/apidocs/examples/team_examples.py @@ -249,6 +249,7 @@ class TeamExamples: "hasInsightsQueues": False, "hasInsightsLlmMonitoring": False, "hasInsightsAgentMonitoring": False, + "hasInsightsMCP": False, "isInternal": False, "isPublic": False, "avatar": {"avatarType": "letter_avatar", "avatarUuid": None}, @@ -307,6 +308,7 @@ class TeamExamples: "hasInsightsQueues": False, "hasInsightsLlmMonitoring": False, "hasInsightsAgentMonitoring": False, + "hasInsightsMCP": False, "isInternal": False, "isPublic": False, "avatar": {"avatarType": "letter_avatar", "avatarUuid": None}, diff --git a/src/sentry/models/project.py b/src/sentry/models/project.py index 2707d31b30ae89..03937db51af1d8 100644 --- a/src/sentry/models/project.py +++ b/src/sentry/models/project.py @@ -349,6 +349,9 @@ class flags(TypedClassBitField): # This Project has sent insight agent monitoring spans has_insights_agent_monitoring: bool + # This Project has sent insight MCP spans + has_insights_mcp: bool + bitfield_default = 10 objects: ClassVar[ProjectManager] = ProjectManager(cache_fields=["pk"]) diff --git a/src/sentry/projects/services/project/model.py b/src/sentry/projects/services/project/model.py index 6ab01f3108e4ca..bac9e2281b1394 100644 --- a/src/sentry/projects/services/project/model.py +++ b/src/sentry/projects/services/project/model.py @@ -58,6 +58,7 @@ class RpcProjectFlags(RpcModel): has_insights_llm_monitoring: bool has_flags: bool has_insights_agent_monitoring: bool + has_insights_mcp: bool class RpcProject(RpcModel): diff --git a/static/app/types/project.tsx b/static/app/types/project.tsx index 74865cfa9d3c7e..97c85f2aaaabce 100644 --- a/static/app/types/project.tsx +++ b/static/app/types/project.tsx @@ -35,6 +35,7 @@ export type Project = { hasInsightsDb: boolean; hasInsightsHttp: boolean; hasInsightsLlmMonitoring: boolean; + hasInsightsMCP: boolean; hasInsightsQueues: boolean; hasInsightsScreenLoad: boolean; hasInsightsVitals: boolean; diff --git a/tests/sentry/api/serializers/test_project.py b/tests/sentry/api/serializers/test_project.py index adfdd6d22dcbb3..8d44ae5a3a568c 100644 --- a/tests/sentry/api/serializers/test_project.py +++ b/tests/sentry/api/serializers/test_project.py @@ -456,6 +456,7 @@ def test_has_insight_module_flags(self): assert result["hasInsightsQueues"] is False assert result["hasInsightsLlmMonitoring"] is False assert result["hasInsightsAgentMonitoring"] is False + assert result["hasInsightsMCP"] is False self.project.first_event = timezone.now() self.project.update(flags=F("flags").bitor(Project.flags.has_insights_http)) @@ -468,6 +469,7 @@ def test_has_insight_module_flags(self): self.project.update(flags=F("flags").bitor(Project.flags.has_insights_queues)) self.project.update(flags=F("flags").bitor(Project.flags.has_insights_llm_monitoring)) self.project.update(flags=F("flags").bitor(Project.flags.has_insights_agent_monitoring)) + self.project.update(flags=F("flags").bitor(Project.flags.has_insights_mcp)) result = serialize(self.project, self.user, ProjectSummarySerializer()) assert result["hasInsightsHttp"] is True @@ -480,6 +482,7 @@ def test_has_insight_module_flags(self): assert result["hasInsightsQueues"] is True assert result["hasInsightsLlmMonitoring"] is True assert result["hasInsightsAgentMonitoring"] is True + assert result["hasInsightsMCP"] is True def test_has_flags_flag(self): result = serialize(self.project, self.user, ProjectSummarySerializer()) From 8b6d023bd1e6545ece86c17dd0f150fc19b9be29 Mon Sep 17 00:00:00 2001 From: Ogi <86684834+obostjancic@users.noreply.github.com> Date: Thu, 10 Jul 2025 11:55:41 +0200 Subject: [PATCH 2/3] migrations file --- migrations_lockfile.txt | 2 +- .../0946_add_has_mcp_insights_flag.py | 68 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 src/sentry/migrations/0946_add_has_mcp_insights_flag.py diff --git a/migrations_lockfile.txt b/migrations_lockfile.txt index 582055640eb26d..138131c7e3fcdd 100644 --- a/migrations_lockfile.txt +++ b/migrations_lockfile.txt @@ -27,7 +27,7 @@ preprod: 0010_actual_drop_preprod_artifact_analysis_file_id_col replays: 0006_add_bulk_delete_job -sentry: 0945_move_discover_models +sentry: 0946_add_has_mcp_insights_flag social_auth: 0003_social_auth_json_field diff --git a/src/sentry/migrations/0946_add_has_mcp_insights_flag.py b/src/sentry/migrations/0946_add_has_mcp_insights_flag.py new file mode 100644 index 00000000000000..eeb92a744d6b1c --- /dev/null +++ b/src/sentry/migrations/0946_add_has_mcp_insights_flag.py @@ -0,0 +1,68 @@ +# Generated by Django 5.2.1 on 2025-07-10 09:55 + +from django.db import migrations + +import bitfield.models +from sentry.new_migrations.migrations import CheckedMigration + + +class Migration(CheckedMigration): + # This flag is used to mark that a migration shouldn't be automatically run in production. + # This should only be used for operations where it's safe to run the migration after your + # code has deployed. So this should not be used for most operations that alter the schema + # of a table. + # Here are some things that make sense to mark as post deployment: + # - Large data migrations. Typically we want these to be run manually so that they can be + # monitored and not block the deploy for a long period of time while they run. + # - Adding indexes to large tables. Since this can take a long time, we'd generally prefer to + # run this outside deployments so that we don't block them. Note that while adding an index + # is a schema change, it's completely safe to run the operation after the code has deployed. + # Once deployed, run these manually via: https://develop.sentry.dev/database-migrations/#migration-deployment + + is_post_deployment = False + + dependencies = [ + ("sentry", "0945_move_discover_models"), + ] + + operations = [ + migrations.AlterField( + model_name="project", + name="flags", + field=bitfield.models.BitField( + [ + "has_releases", + "has_issue_alerts_targeting", + "has_transactions", + "has_alert_filters", + "has_sessions", + "has_profiles", + "has_replays", + "has_feedbacks", + "has_new_feedbacks", + "spike_protection_error_currently_active", + "spike_protection_transaction_currently_active", + "spike_protection_attachment_currently_active", + "has_minified_stack_trace", + "has_cron_monitors", + "has_cron_checkins", + "has_sourcemaps", + "has_custom_metrics", + "has_high_priority_alerts", + "has_insights_http", + "has_insights_db", + "has_insights_assets", + "has_insights_app_start", + "has_insights_screen_load", + "has_insights_vitals", + "has_insights_caches", + "has_insights_queues", + "has_insights_llm_monitoring", + "has_flags", + "has_insights_agent_monitoring", + "has_insights_mcp", + ], + default=10, + ), + ), + ] From df05019994e364a0de623cb2a6bba3ad1df767cb Mon Sep 17 00:00:00 2001 From: Ogi <86684834+obostjancic@users.noreply.github.com> Date: Thu, 10 Jul 2025 12:00:14 +0200 Subject: [PATCH 3/3] remove fe change --- static/app/types/project.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/static/app/types/project.tsx b/static/app/types/project.tsx index 97c85f2aaaabce..74865cfa9d3c7e 100644 --- a/static/app/types/project.tsx +++ b/static/app/types/project.tsx @@ -35,7 +35,6 @@ export type Project = { hasInsightsDb: boolean; hasInsightsHttp: boolean; hasInsightsLlmMonitoring: boolean; - hasInsightsMCP: boolean; hasInsightsQueues: boolean; hasInsightsScreenLoad: boolean; hasInsightsVitals: boolean;