@@ -15,6 +15,12 @@ export default function AnalyticsPage() {
1515 security ?: {
1616 eventCount : number ;
1717 events ?: { timestamp : string ; eventType : string ; ipAddress : string ; clientId ?: string ; details : string } [ ] ;
18+ byOrganization ?: { organization : string ; eventType : string ; severity : string ; eventCount : number ; avgRiskScore : number } [ ] ;
19+ privilegeEscalations ?: { userName : string ; userEmail : string ; eventType : string ; riskScore : number ; timestamp : string ; details : Record < string , unknown > } [ ] ;
20+ } ;
21+ enterprise ?: {
22+ usersByMCPServer ?: { userName : string ; userEmail : string ; mcpServerName : string ; mcpServerIdentifier : string } [ ] ;
23+ toolUsage ?: { toolName : string ; mcpMethod : string ; usageCount : number ; uniqueUsers : number } [ ] ;
1824 } ;
1925 lastUpdated ?: string ;
2026 timeRange ?: string ;
@@ -163,55 +169,132 @@ export default function AnalyticsPage() {
163169 </ div >
164170 ) }
165171
166- { /* Security Events */ }
167- < div className = "analytics-section" >
168- < h2 > 🔒 Security Events</ h2 >
169- < div className = "security-overview" >
170- < div className = "metric-card security-card" >
171- < h4 > Total Events</ h4 >
172- < p className = "metric-value" > { data . security ?. eventCount || 0 } </ p >
173- </ div >
174- { ( ! data . security ?. eventCount || data . security . eventCount === 0 ) && (
175- < p className = "no-events" > No security events detected in the selected time range</ p >
172+
173+ { /* Enterprise Analytics */ }
174+ { data . enterprise && (
175+ < div className = "analytics-section" >
176+ < h2 > Enterprise Analytics</ h2 >
177+
178+ { /* Which MCP servers are users using? */ }
179+ { data . enterprise . usersByMCPServer && data . enterprise . usersByMCPServer . length > 0 && (
180+ < div className = "enterprise-subsection" >
181+ < h3 > Which MCP servers are users using?</ h3 >
182+ < div className = "user-mcp-list" >
183+ { data . enterprise . usersByMCPServer . map ( ( item , index ) => (
184+ < div key = { index } className = "user-mcp-item" >
185+ < div className = "user-info" >
186+ < strong > { item . userName } </ strong >
187+ < span className = "user-email" > { item . userEmail } </ span >
188+ </ div >
189+ < div className = "mcp-info" >
190+ < span className = "mcp-name" > { item . mcpServerName } </ span >
191+ < span className = "mcp-identifier" > { item . mcpServerIdentifier } </ span >
192+ </ div >
193+ </ div >
194+ ) ) }
195+ </ div >
196+ </ div >
197+ ) }
198+
199+ { /* MCP Tool Usage */ }
200+ { data . enterprise . toolUsage && data . enterprise . toolUsage . length > 0 && (
201+ < div className = "enterprise-subsection" >
202+ < h3 > Most Used MCP Tools</ h3 >
203+ < div className = "tool-usage-list" >
204+ { data . enterprise . toolUsage . map ( ( tool , index ) => (
205+ < div key = { index } className = "tool-usage-item" >
206+ < span className = "tool-rank" > #{ index + 1 } </ span >
207+ < div className = "tool-details" >
208+ < strong > { tool . toolName } </ strong >
209+ < span className = "tool-method" > { tool . mcpMethod } </ span >
210+ </ div >
211+ < div className = "tool-stats" >
212+ < span > { tool . usageCount } calls</ span >
213+ < span > { tool . uniqueUsers } users</ span >
214+ </ div >
215+ </ div >
216+ ) ) }
217+ </ div >
218+ </ div >
176219 ) }
177220 </ div >
178- { data . security ?. events && data . security . events . length > 0 && (
179- < div className = "security-events" >
180- < h4 > Recent Security Events</ h4 >
181- { data . security . events . slice ( 0 , 10 ) . map ( ( event , index ) => (
182- < div key = { index } className = "security-event" >
183- < div className = "event-header" >
184- < span className = "event-type" > { event . eventType } </ span >
185- < span className = "event-time" > { new Date ( event . timestamp ) . toLocaleString ( ) } </ span >
186- </ div >
187- < div className = "event-details" >
188- < span > IP: { event . ipAddress } </ span >
189- { event . clientId && < span > Client: { event . clientId } </ span > }
190- </ div >
191- < div className = "event-description" > { event . details } </ div >
221+ ) }
222+
223+ { /* Enhanced Security Events */ }
224+ { data . security && (
225+ < div className = "analytics-section" >
226+ < h2 > Security Monitoring</ h2 >
227+
228+ { /* Security events by organization */ }
229+ { data . security . byOrganization && data . security . byOrganization . length > 0 && (
230+ < div className = "enterprise-subsection" >
231+ < h3 > Security events by organization</ h3 >
232+ < div className = "security-org-list" >
233+ { data . security . byOrganization . map ( ( item , index ) => (
234+ < div key = { index } className = "security-org-item" >
235+ < div className = "org-name" > { item . organization } </ div >
236+ < div className = "event-details" >
237+ < span className = { `event-type ${ item . severity } ` } > { item . eventType } </ span >
238+ < span className = "event-count" > { item . eventCount } events</ span >
239+ < span className = "risk-score" > Risk: { item . avgRiskScore } </ span >
240+ </ div >
241+ </ div >
242+ ) ) }
192243 </ div >
193- ) ) }
244+ </ div >
245+ ) }
246+
247+ { /* Privilege escalation attempts */ }
248+ { data . security . privilegeEscalations && data . security . privilegeEscalations . length > 0 && (
249+ < div className = "enterprise-subsection" >
250+ < h3 > Users with elevated privilege attempts (Last 7 days)</ h3 >
251+ < div className = "privilege-escalation-list" >
252+ { data . security . privilegeEscalations . map ( ( item , index ) => (
253+ < div key = { index } className = "privilege-escalation-item" >
254+ < div className = "user-info" >
255+ < strong > { item . userName } </ strong >
256+ < span className = "user-email" > { item . userEmail } </ span >
257+ </ div >
258+ < div className = "escalation-details" >
259+ < span className = "risk-score" > Risk Score: { item . riskScore } </ span >
260+ < span className = "timestamp" > { new Date ( item . timestamp ) . toLocaleString ( ) } </ span >
261+ </ div >
262+ </ div >
263+ ) ) }
264+ </ div >
265+ </ div >
266+ ) }
267+
268+ { /* Overall security summary */ }
269+ < div className = "security-overview" >
270+ < div className = "metric-card security-card" >
271+ < h4 > Total Events</ h4 >
272+ < p className = "metric-value" > { data . security ?. eventCount || 0 } </ p >
273+ </ div >
274+ { ( ! data . security ?. eventCount || data . security . eventCount === 0 ) && (
275+ < p className = "no-events" > No security events detected in the selected time range</ p >
276+ ) }
194277 </ div >
195- ) }
196- </ div >
278+ </ div >
279+ ) }
197280
198281 { /* System Info */ }
199282 < div className = "analytics-section" >
200- < h2 > 📈 System Information</ h2 >
283+ < h2 > System Information</ h2 >
201284 < div className = "info-grid" >
202285 < div className = "info-card" >
203- < h4 > Database Analytics</ h4 >
204- < p > ✅ Persistent PostgreSQL storage </ p >
205- < p > ✅ Real-time performance tracking </ p >
206- < p > ✅ Optimized with batching & indexing </ p >
207- < p > ✅ Production-ready for Vercel </ p >
286+ < h4 > Enhanced Analytics</ h4 >
287+ < p > Enterprise user tracking with SSO context </ p >
288+ < p > MCP server and tool usage monitoring </ p >
289+ < p > Real-time security threat detection </ p >
290+ < p > Role-based access analytics </ p >
208291 </ div >
209292 < div className = "info-card" >
210- < h4 > Data Collection </ h4 >
211- < p > 📊 Request analytics with geographic data </ p >
212- < p > 🔒 Security event monitoring</ p >
213- < p > ⚡ Non-blocking collection with batching </ p >
214- < p > 🗄️ Automatic cleanup after 30 days </ p >
293+ < h4 > Security Features </ h4 >
294+ < p > Privilege escalation detection </ p >
295+ < p > Geographic anomaly monitoring</ p >
296+ < p > Rate limiting and brute force protection </ p >
297+ < p > Token reuse and audience validation </ p >
215298 </ div >
216299 </ div >
217300 </ div >
@@ -467,6 +550,132 @@ export default function AnalyticsPage() {
467550 font-weight: bold;
468551 }
469552
553+ /* Enterprise Analytics Styles */
554+ .enterprise-subsection {
555+ margin-bottom: 2rem;
556+ padding: 1rem;
557+ background: #f8f9fa;
558+ border-radius: 6px;
559+ border-left: 4px solid #007bff;
560+ }
561+
562+ .enterprise-subsection h3 {
563+ color: #333;
564+ margin-bottom: 1rem;
565+ font-size: 1.1rem;
566+ }
567+
568+ .user-mcp-list, .tool-usage-list, .security-org-list, .privilege-escalation-list {
569+ display: flex;
570+ flex-direction: column;
571+ gap: 0.75rem;
572+ }
573+
574+ .user-mcp-item, .tool-usage-item, .security-org-item, .privilege-escalation-item {
575+ display: flex;
576+ justify-content: space-between;
577+ align-items: center;
578+ padding: 0.75rem;
579+ background: white;
580+ border-radius: 6px;
581+ border-left: 3px solid #28a745;
582+ }
583+
584+ .user-info {
585+ display: flex;
586+ flex-direction: column;
587+ }
588+
589+ .user-email {
590+ font-size: 0.8rem;
591+ color: #666;
592+ font-family: monospace;
593+ }
594+
595+ .mcp-info {
596+ display: flex;
597+ flex-direction: column;
598+ text-align: right;
599+ }
600+
601+ .mcp-name {
602+ font-weight: bold;
603+ color: #333;
604+ }
605+
606+ .mcp-identifier {
607+ font-size: 0.8rem;
608+ color: #666;
609+ font-family: monospace;
610+ }
611+
612+ .tool-rank {
613+ font-weight: bold;
614+ color: #007bff;
615+ margin-right: 1rem;
616+ min-width: 30px;
617+ }
618+
619+ .tool-details {
620+ flex: 1;
621+ display: flex;
622+ flex-direction: column;
623+ }
624+
625+ .tool-method {
626+ font-size: 0.8rem;
627+ color: #666;
628+ font-family: monospace;
629+ }
630+
631+ .tool-stats {
632+ display: flex;
633+ flex-direction: column;
634+ text-align: right;
635+ font-size: 0.9rem;
636+ color: #495057;
637+ }
638+
639+ .org-name {
640+ font-weight: bold;
641+ color: #333;
642+ }
643+
644+ .event-details {
645+ display: flex;
646+ gap: 1rem;
647+ align-items: center;
648+ }
649+
650+ .event-type {
651+ padding: 0.25rem 0.5rem;
652+ border-radius: 4px;
653+ font-size: 0.8rem;
654+ font-weight: bold;
655+ color: white;
656+ }
657+
658+ .event-type.low { background: #28a745; }
659+ .event-type.medium { background: #ffc107; color: #333; }
660+ .event-type.high { background: #fd7e14; }
661+ .event-type.critical { background: #dc3545; }
662+
663+ .event-count, .risk-score {
664+ font-size: 0.9rem;
665+ color: #495057;
666+ }
667+
668+ .escalation-details {
669+ display: flex;
670+ flex-direction: column;
671+ text-align: right;
672+ }
673+
674+ .timestamp {
675+ font-size: 0.8rem;
676+ color: #666;
677+ }
678+
470679 @media (max-width: 768px) {
471680 .metrics-grid {
472681 grid-template-columns: 1fr;
0 commit comments