Skip to content

Commit ece157d

Browse files
authored
feat(mcp-insights): Add skeleton page with placeholders (#95412)
Add navigation logic and page skeleton. - closes [TET-848: MCP skeleton page with all placeholder widgets](https://linear.app/getsentry/issue/TET-848/mcp-skeleton-page-with-all-placeholder-widgets) - closes [TET-852: Tabs: Tools/Resources/Prompts](https://linear.app/getsentry/issue/TET-852/tabs-toolsresourcesprompts)
1 parent f43fbcc commit ece157d

File tree

16 files changed

+455
-6
lines changed

16 files changed

+455
-6
lines changed

static/app/routes.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,6 +1696,11 @@ function buildRoutes() {
16961696
component={make(() => import('sentry/views/insights/sessions/views/overview'))}
16971697
/>
16981698
</Route>
1699+
<Route path={`${MODULE_BASE_URLS[ModuleName.MCP]}/`}>
1700+
<IndexRoute
1701+
component={make(() => import('sentry/views/insights/mcp/views/overview'))}
1702+
/>
1703+
</Route>
16991704
</Fragment>
17001705
);
17011706

static/app/types/project.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export type Project = {
3535
hasInsightsDb: boolean;
3636
hasInsightsHttp: boolean;
3737
hasInsightsLlmMonitoring: boolean;
38+
hasInsightsMCP: boolean;
3839
hasInsightsQueues: boolean;
3940
hasInsightsScreenLoad: boolean;
4041
hasInsightsVitals: boolean;

static/app/views/insights/agentMonitoring/utils/features.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import useMutateUserOptions from 'sentry/utils/useMutateUserOptions';
55
import useOrganization from 'sentry/utils/useOrganization';
66
import {useUser} from 'sentry/utils/useUser';
77

8-
type AgentInsightsFeatureProps = Omit<Parameters<typeof Feature>[0], 'features'>;
8+
type InsightsFeaturePropsWithoutFeatures = Omit<
9+
Parameters<typeof Feature>[0],
10+
'features'
11+
>;
912

1013
export function hasAgentInsightsFeature(organization: Organization) {
1114
return organization.features.includes('agents-insights');
@@ -41,7 +44,7 @@ export function useTogglePreferedAiModule(): [string, () => void] {
4144
return [preferedAiModule, togglePreferedModule];
4245
}
4346

44-
export function AIInsightsFeature(props: AgentInsightsFeatureProps) {
47+
export function AIInsightsFeature(props: InsightsFeaturePropsWithoutFeatures) {
4548
const preferedAiModule = usePreferedAiModule();
4649

4750
return (
@@ -53,3 +56,14 @@ export function AIInsightsFeature(props: AgentInsightsFeatureProps) {
5356
</Feature>
5457
);
5558
}
59+
60+
export function McpInsightsFeature(props: InsightsFeaturePropsWithoutFeatures) {
61+
return (
62+
<Feature
63+
features={['mcp-insights']}
64+
renderDisabled={props.renderDisabled ?? NoAccess}
65+
>
66+
{props.children}
67+
</Feature>
68+
);
69+
}

static/app/views/insights/common/components/modulesOnboarding.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,19 @@ const EMPTY_STATE_CONTENT: Record<TitleableModuleNames, EmptyStateContent> = {
314314
valuePropPoints: [],
315315
imageSrc: screenLoadsPreviewImg,
316316
},
317+
mcp: {
318+
heading: t('Model Context Providers'),
319+
description: t(
320+
'Monitor your MCP servers to ensure your AI applications have reliable access to tools, resources, and data sources they depend on.'
321+
),
322+
imageSrc: llmPreviewImg,
323+
valuePropDescription: t('MCP monitoring gives you visibility into:'),
324+
valuePropPoints: [
325+
t('Tool execution success rates and failure patterns.'),
326+
t('Resource access performance and availability.'),
327+
t('Usage patterns across different tools and prompts.'),
328+
],
329+
},
317330
'mobile-ui': {
318331
heading: t('TODO'),
319332
description: t('TODO'),

static/app/views/insights/common/queries/useHasFirstSpan.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const modulePropertyMap: Record<
2424
[ModuleName.QUEUE]: 'hasInsightsQueues',
2525
[ModuleName.SCREEN_LOAD]: 'hasInsightsScreenLoad',
2626
[ModuleName.APP_START]: 'hasInsightsAppStart',
27+
[ModuleName.MCP]: 'hasInsightsMCP',
2728
// Renamed resource to assets
2829
[ModuleName.RESOURCE]: 'hasInsightsAssets',
2930
[ModuleName.AI]: 'hasInsightsLlmMonitoring',

static/app/views/insights/common/utils/useModuleURL.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {BASE_URL as CACHE_BASE_URL} from 'sentry/views/insights/cache/settings';
77
import {BASE_URL as DB_BASE_URL} from 'sentry/views/insights/database/settings';
88
import {BASE_URL as HTTP_BASE_URL} from 'sentry/views/insights/http/settings';
99
import {BASE_URL as AI_BASE_URL} from 'sentry/views/insights/llmMonitoring/settings';
10+
import {BASE_URL as MCP_BASE_URL} from 'sentry/views/insights/mcp/settings';
1011
import {BASE_URL as APP_STARTS_BASE_URL} from 'sentry/views/insights/mobile/appStarts/settings';
1112
import {BASE_URL as SCREEN_LOADS_BASE_URL} from 'sentry/views/insights/mobile/screenload/settings';
1213
import {BASE_URL as SCREEN_RENDERING_BASE_URL} from 'sentry/views/insights/mobile/screenRendering/settings';
@@ -33,6 +34,7 @@ export const MODULE_BASE_URLS: Record<ModuleName, string> = {
3334
[ModuleName.RESOURCE]: RESOURCES_BASE_URL,
3435
[ModuleName.AI]: AI_BASE_URL,
3536
[ModuleName.AGENTS]: AGENTS_BASE_URL,
37+
[ModuleName.MCP]: MCP_BASE_URL,
3638
[ModuleName.MOBILE_UI]: MOBILE_UI_BASE_URL,
3739
[ModuleName.MOBILE_VITALS]: MOBILE_SCREENS_BASE_URL,
3840
[ModuleName.SCREEN_RENDERING]: SCREEN_RENDERING_BASE_URL,

static/app/views/insights/common/views/spans/selectors/actionSelector.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,5 +131,6 @@ const LABEL_FOR_MODULE_NAME: Record<ModuleName, ReactNode> = {
131131
'screen-rendering': t('Action'),
132132
ai: 'Action',
133133
agents: t('Action'),
134+
mcp: t('Action'),
134135
sessions: t('Action'),
135136
};
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import styled from '@emotion/styled';
2+
3+
import Panel from 'sentry/components/panels/panel';
4+
import {t} from 'sentry/locale';
5+
import {space} from 'sentry/styles/space';
6+
import {Widget} from 'sentry/views/dashboards/widgets/widget/widget';
7+
8+
const PlaceholderContent = styled('div')`
9+
display: flex;
10+
flex: 1;
11+
align-items: center;
12+
justify-content: center;
13+
color: ${p => p.theme.subText};
14+
font-size: ${p => p.theme.fontSize.md};
15+
`;
16+
17+
const TableContainer = styled('div')`
18+
margin-top: ${space(2)};
19+
`;
20+
21+
const PlaceholderTable = styled(Panel)`
22+
padding: ${space(2)};
23+
text-align: center;
24+
color: ${p => p.theme.subText};
25+
`;
26+
27+
function PlaceholderText() {
28+
return <PlaceholderContent>{t('Placeholder')}</PlaceholderContent>;
29+
}
30+
31+
export function McpTrafficWidget() {
32+
return (
33+
<Widget
34+
Title={<Widget.WidgetTitle title={t('Traffic + Error rate')} />}
35+
Visualization={<PlaceholderText />}
36+
/>
37+
);
38+
}
39+
40+
export function RequestsBySourceWidget() {
41+
return (
42+
<Widget
43+
Title={<Widget.WidgetTitle title={t('Requests by source')} />}
44+
Visualization={<PlaceholderText />}
45+
/>
46+
);
47+
}
48+
49+
export function TransportDistributionWidget() {
50+
return (
51+
<Widget
52+
Title={<Widget.WidgetTitle title={t('Transport distribution')} />}
53+
Visualization={<PlaceholderText />}
54+
/>
55+
);
56+
}
57+
58+
export function GroupedTrafficWidget({
59+
groupBy,
60+
}: {
61+
groupBy: 'tool' | 'resource' | 'prompt';
62+
}) {
63+
return (
64+
<Widget
65+
Title={<Widget.WidgetTitle title={`Traffic by ${groupBy}`} />}
66+
Visualization={<PlaceholderText />}
67+
/>
68+
);
69+
}
70+
71+
export function GroupedDurationWidget({
72+
groupBy,
73+
}: {
74+
groupBy: 'tool' | 'resource' | 'prompt';
75+
}) {
76+
return (
77+
<Widget
78+
Title={<Widget.WidgetTitle title={`Duration by ${groupBy}`} />}
79+
Visualization={<PlaceholderText />}
80+
/>
81+
);
82+
}
83+
84+
export function GroupedErrorRateWidget({
85+
groupBy,
86+
}: {
87+
groupBy: 'tool' | 'resource' | 'prompt';
88+
}) {
89+
return (
90+
<Widget
91+
Title={<Widget.WidgetTitle title={`Error rate by ${groupBy}`} />}
92+
Visualization={<PlaceholderText />}
93+
/>
94+
);
95+
}
96+
97+
export function ToolsTable() {
98+
return (
99+
<TableContainer>
100+
<PlaceholderTable>
101+
<h3>{t('Tools Table')}</h3>
102+
<p>
103+
{t(
104+
'Placeholder for tools table with metrics like requests, error rate, avg, p95'
105+
)}
106+
</p>
107+
</PlaceholderTable>
108+
</TableContainer>
109+
);
110+
}
111+
112+
export function ResourcesTable() {
113+
return (
114+
<TableContainer>
115+
<PlaceholderTable>
116+
<h3>{t('Resources Table')}</h3>
117+
<p>{t('Placeholder for resources table')}</p>
118+
</PlaceholderTable>
119+
</TableContainer>
120+
);
121+
}
122+
123+
export function PromptsTable() {
124+
return (
125+
<TableContainer>
126+
<PlaceholderTable>
127+
<h3>{t('Prompts Table')}</h3>
128+
<p>{t('Placeholder for prompts table')}</p>
129+
</PlaceholderTable>
130+
</TableContainer>
131+
);
132+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import styled from '@emotion/styled';
2+
3+
import {space} from 'sentry/styles/space';
4+
5+
const StyledGrid = styled('div')`
6+
display: grid;
7+
gap: ${space(2)};
8+
9+
grid-template-columns: minmax(0, 1fr);
10+
grid-template-rows: 300px 300px 300px;
11+
grid-template-areas:
12+
'pos1'
13+
'pos2'
14+
'pos3';
15+
16+
@media (min-width: ${p => p.theme.breakpoints.sm}) {
17+
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
18+
grid-template-rows: 300px 300px;
19+
grid-template-areas:
20+
'pos1 pos2'
21+
'pos3 pos3';
22+
}
23+
24+
@media (min-width: ${p => p.theme.breakpoints.lg}) {
25+
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr);
26+
grid-template-rows: 300px;
27+
grid-template-areas: 'pos1 pos2 pos3';
28+
}
29+
`;
30+
31+
export const WidgetGrid = Object.assign(StyledGrid, {
32+
Position1: styled('div')`
33+
grid-area: pos1;
34+
`,
35+
Position2: styled('div')`
36+
grid-area: pos2;
37+
`,
38+
Position3: styled('div')`
39+
grid-area: pos3;
40+
`,
41+
});
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import {t} from 'sentry/locale';
2+
3+
export const MODULE_TITLE = t('MCP');
4+
export const BASE_URL = 'mcp';
5+
6+
export const DATA_TYPE = t('MCP');
7+
export const DATA_TYPE_PLURAL = t('MCPs');
8+
9+
export const MODULE_DOC_LINK = 'https://docs.sentry.io/product/insights/mcp/';
10+
11+
export const MODULE_FEATURES = [];

0 commit comments

Comments
 (0)