@@ -592,6 +592,79 @@ function describeMcpLaunch(server) {
592592 } ;
593593}
594594
595+ // ---- S5: visibility --------------------------------------------------------
596+
597+ /**
598+ * What `/mcp` shows: one row per CONFIGURED server, whether or not it is running.
599+ *
600+ * Configured-first, not running-first, on purpose. The interesting questions are "why is my server not
601+ * being used?" and "what is this repo asking to run?", and both are about servers that are absent from
602+ * the live set. A list built from live handles answers neither.
603+ *
604+ * Pure so it can be tested without the editor: the caller passes what it knows.
605+ *
606+ * @param {{servers?:Array<object>, problems?:Array<object>, active?:Array<object>,
607+ * policy?:object, trust?:object}} input
608+ * active — from mcpClient.listActive(), optionally carrying `tools` (the raw tools/list result) so
609+ * per-tool allow state can be reported; without it the row still shows a tool count.
610+ * policy — levelcode.ai.mcp.toolPolicy trust — the G1 workspaceState launch-trust map
611+ */
612+ function summarizeMcp ( input ) {
613+ const o = input || { } ;
614+ const servers = Array . isArray ( o . servers ) ? o . servers : [ ] ;
615+ const policy = ( o . policy && typeof o . policy === 'object' && ! Array . isArray ( o . policy ) ) ? o . policy : { } ;
616+ const trust = ( o . trust && typeof o . trust === 'object' && ! Array . isArray ( o . trust ) ) ? o . trust : { } ;
617+
618+ const live = new Map ( ) ;
619+ for ( const a of ( Array . isArray ( o . active ) ? o . active : [ ] ) ) {
620+ if ( a && typeof a . name === 'string' ) { live . set ( a . name , a ) ; }
621+ }
622+
623+ const rows = servers . map ( ( s ) => {
624+ const handle = live . get ( s . name ) ;
625+ const workspace = s . source !== 'settings' ;
626+ const row = {
627+ name : s . name ,
628+ source : s . source ,
629+ origin : s . origin ,
630+ running : ! ! ( handle && handle . alive ) ,
631+ // Only meaningful for a repo-authored server; a settings server needs no consent, and
632+ // reporting `false` for one would read as "blocked".
633+ trusted : workspace ? isLaunchTrusted ( s , trust ) : null ,
634+ commandLine : describeMcpLaunch ( s ) . commandLine ,
635+ tools : handle ? ( handle . toolCount || 0 ) : 0 ,
636+ allowed : null ,
637+ toolNames : [ ]
638+ } ;
639+
640+ // Names and allow state come from the SAME functions the agent uses, so the list cannot claim a
641+ // tool is allow-listed while runTool refuses it.
642+ if ( handle && Array . isArray ( handle . tools ) && handle . tools . length ) {
643+ const built = buildAgentTools ( [ { name : s . name , tools : handle . tools } ] ) ;
644+ row . tools = built . tools . length ;
645+ row . toolNames = built . tools . map ( ( t ) => t . name ) ;
646+ row . allowed = built . tools . filter ( ( t ) => {
647+ const route = built . routes . get ( t . name ) ;
648+ return classifyMcpTool ( t . name , policy , route && route . annotations ) . approve === 'allow' ;
649+ } ) . length ;
650+ }
651+ return row ;
652+ } ) ;
653+
654+ return {
655+ configured : rows . length ,
656+ running : rows . filter ( ( r ) => r . running ) . length ,
657+ // Repo-authored servers still waiting on the G1 consent card — the answer to "why is it not
658+ // running?" for the case that is a gate rather than a fault.
659+ awaitingTrust : rows . filter ( ( r ) => r . trusted === false && ! r . running ) . length ,
660+ servers : rows ,
661+ problems : ( Array . isArray ( o . problems ) ? o . problems : [ ] ) . map ( ( p ) => ( {
662+ level : String ( ( p && p . level ) || 'warn' ) ,
663+ message : String ( ( p && p . message ) || '' )
664+ } ) )
665+ } ;
666+ }
667+
595668function describeMcpCall ( name , args , route ) {
596669 const r = route || { } ;
597670 const fallback = String ( name == null ? '' : name ) . split ( NAME_SEPARATOR ) ;
@@ -605,6 +678,6 @@ module.exports = {
605678 loadServerConfig, userScopedSetting, namespaceToolName, isNamespacedToolName, assignToolNames,
606679 buildAgentTools, safeCopy,
607680 toolCountsByServer, classifyMcpTool, explainMcpRefusal, describeMcpCall,
608- launchFingerprint, isLaunchTrusted, rememberLaunchTrust, describeMcpLaunch,
681+ launchFingerprint, isLaunchTrusted, rememberLaunchTrust, describeMcpLaunch, summarizeMcp ,
609682 BUILTIN_TOOL_NAMES , MAX_TOOL_NAME , MAX_TOOL_DESC , MAX_ARG_CHARS , MAX_SERVERS , MAX_TOOLS_PER_SERVER , WORKSPACE_CONFIG_PATH
610683} ;
0 commit comments