@@ -19,7 +19,7 @@ import {
1919import { inflateRawSync } from "node:zlib" ;
2020import yaml from "yaml" ;
2121import { parse as parseToml } from "smol-toml" ;
22- import { qwenworkMcpPath } from "../mcp/agent-config.ts" ;
22+ import { qwenworkMcpPath , workbuddyMcpPaths } from "../mcp/agent-config.ts" ;
2323
2424/**
2525 * Where an item comes from. Everything discovered on disk today is `local`;
@@ -398,7 +398,9 @@ function transportOf(entry: Record<string, unknown>): {
398398 const url = typeof entry . url === "string" ? entry . url : undefined ;
399399 if ( url ) {
400400 const type = typeof entry . type === "string" ? entry . type . toLowerCase ( ) : "" ;
401- return { transport : type === "sse" ? "sse" : "http" , detail : url } ;
401+ const transportField = typeof entry . transport === "string" ? entry . transport . toLowerCase ( ) : "" ;
402+ if ( type === "sse" || transportField === "sse" ) return { transport : "sse" , detail : url } ;
403+ return { transport : "http" , detail : url } ;
402404 }
403405 return { transport : "unknown" , detail : "" } ;
404406}
@@ -475,7 +477,44 @@ export function listMcpServers(home: string = homedir()): McpServerInfo[] {
475477 if ( gemini ) collectMcpMap ( gemini . mcpServers , "gemini" , "global" , out , true ) ;
476478
477479 const openclaw = readJsonSafe ( join ( home , ".openclaw" , "openclaw.json" ) ) ;
478- if ( openclaw ) collectMcpMap ( openclaw . mcpServers , "openclaw" , "global" , out , true ) ;
480+ if ( openclaw ) {
481+ const mcp = asRecord ( openclaw . mcp ) ;
482+ collectMcpMap ( mcp ?. servers ?? openclaw . mcpServers , "openclaw" , "global" , out , true ) ;
483+ }
484+
485+ const zcode = readJsonSafe ( join ( home , ".zcode" , "cli" , "config.json" ) ) ;
486+ if ( zcode ) collectMcpMap ( asRecord ( zcode . mcp ) ?. servers , "zcode" , "global" , out , true ) ;
487+
488+ for ( const file of workbuddyMcpPaths ( home ) ) {
489+ const workbuddy = readJsonSafe ( file ) ;
490+ if ( workbuddy ) collectMcpMap ( workbuddy . mcpServers , "workbuddy" , file , out , true ) ;
491+ }
492+
493+ const dshPatch = readText ( join ( home , ".dsh" , "cordis.patch.yml" ) ) ;
494+ if ( dshPatch ) {
495+ try {
496+ const parsed = yaml . parse ( dshPatch ) as unknown ;
497+ const items = Array . isArray ( parsed ) ? parsed : [ ] ;
498+ const dshServers : Record < string , unknown > = { } ;
499+ for ( const item of items ) {
500+ const entries = asRecord ( item ) ?. insert ;
501+ const list = Array . isArray ( entries ) ? entries : [ item ] ;
502+ for ( const entry of list ) {
503+ const record = asRecord ( entry ) ;
504+ const config = record ? asRecord ( record . config ) : undefined ;
505+ if (
506+ record ?. name === "@deepseek-ai/dsh-mcp-client" &&
507+ typeof config ?. serverName === "string"
508+ ) {
509+ dshServers [ config . serverName ] = config ;
510+ }
511+ }
512+ }
513+ collectMcpMap ( dshServers , "deepseek-harness" , "global" , out , false ) ;
514+ } catch {
515+ /* ignore malformed yaml */
516+ }
517+ }
479518
480519 const claudeDesktop = readJsonSafe ( claudeDesktopConfigPath ( home ) ) ;
481520 if ( claudeDesktop ) collectMcpMap ( claudeDesktop . mcpServers , "claude-desktop" , "global" , out , true ) ;
@@ -514,6 +553,8 @@ function claudeDesktopConfigPath(home: string): string {
514553interface McpWriteTarget {
515554 file : string ;
516555 mapKey : string ;
556+ /** When set, the server map lives at parentKey.mapKey (e.g. mcp.servers). */
557+ parentKey ?: string ;
517558 /** Claude stores project-scoped servers under projects[scope][mapKey]. */
518559 projectScoped : boolean ;
519560}
@@ -581,6 +622,20 @@ function mcpWriteTarget(source: string, scope: string, home: string): McpWriteTa
581622 if ( source === "openclaw" )
582623 return {
583624 file : join ( home , ".openclaw" , "openclaw.json" ) ,
625+ mapKey : "servers" ,
626+ parentKey : "mcp" ,
627+ projectScoped : false ,
628+ } ;
629+ if ( source === "zcode" )
630+ return {
631+ file : join ( home , ".zcode" , "cli" , "config.json" ) ,
632+ mapKey : "servers" ,
633+ parentKey : "mcp" ,
634+ projectScoped : false ,
635+ } ;
636+ if ( source === "workbuddy" )
637+ return {
638+ file : workbuddyMcpPaths ( home ) [ 0 ] ?? join ( home , ".workbuddy-ai" , "mcp.json" ) ,
584639 mapKey : "mcpServers" ,
585640 projectScoped : false ,
586641 } ;
@@ -623,6 +678,45 @@ function unmaskMcpConfig(submitted: unknown, stored: unknown): unknown {
623678 return submitted ;
624679}
625680
681+ function mcpMapContainer (
682+ root : Record < string , unknown > ,
683+ target : McpWriteTarget ,
684+ scope : string ,
685+ ) : Record < string , unknown > | undefined {
686+ let container : Record < string , unknown > | undefined = root ;
687+ if ( target . projectScoped ) {
688+ const projects = asRecord ( root . projects ) ;
689+ container = projects ? asRecord ( projects [ scope ] ) : undefined ;
690+ }
691+ if ( ! container ) return undefined ;
692+ if ( target . parentKey ) {
693+ const parent = asRecord ( container [ target . parentKey ] ) ;
694+ return parent ;
695+ }
696+ return container ;
697+ }
698+
699+ function ensureMcpMapContainer (
700+ root : Record < string , unknown > ,
701+ target : McpWriteTarget ,
702+ scope : string ,
703+ ) : Record < string , unknown > {
704+ let container : Record < string , unknown > = root ;
705+ if ( target . projectScoped ) {
706+ const projects = asRecord ( root . projects ) ?? { } ;
707+ root . projects = projects ;
708+ const proj = asRecord ( projects [ scope ] ) ?? { } ;
709+ projects [ scope ] = proj ;
710+ container = proj ;
711+ }
712+ if ( target . parentKey ) {
713+ const parent = asRecord ( container [ target . parentKey ] ) ?? { } ;
714+ container [ target . parentKey ] = parent ;
715+ return parent ;
716+ }
717+ return container ;
718+ }
719+
626720/** Create or update one MCP server entry, writing back to its source file. */
627721export function writeMcpServer (
628722 source : string ,
@@ -639,14 +733,7 @@ export function writeMcpServer(
639733 if ( ! cfg ) throw new Error ( "Config must be a JSON object." ) ;
640734
641735 const root = readJsonSafe ( target . file ) ?? { } ;
642- let container : Record < string , unknown > = root ;
643- if ( target . projectScoped ) {
644- const projects = asRecord ( root . projects ) ?? { } ;
645- root . projects = projects ;
646- const proj = asRecord ( projects [ scope ] ) ?? { } ;
647- projects [ scope ] = proj ;
648- container = proj ;
649- }
736+ const container = ensureMcpMapContainer ( root , target , scope ) ;
650737 const map = asRecord ( container [ target . mapKey ] ) ?? { } ;
651738 container [ target . mapKey ] = map ;
652739
@@ -667,11 +754,7 @@ export function deleteMcpServer(
667754 if ( ! target ) throw new Error ( "This MCP source is read-only and cannot be edited here." ) ;
668755 const root = readJsonSafe ( target . file ) ;
669756 if ( ! root ) throw new Error ( "Config file not found." ) ;
670- let container : Record < string , unknown > | undefined = root ;
671- if ( target . projectScoped ) {
672- const projects = asRecord ( root . projects ) ;
673- container = projects ? asRecord ( projects [ scope ] ) : undefined ;
674- }
757+ const container = mcpMapContainer ( root , target , scope ) ;
675758 const map = container ? asRecord ( container [ target . mapKey ] ) : undefined ;
676759 if ( ! map || ! ( name in map ) ) throw new Error ( "Server not found: " + name ) ;
677760 delete map [ name ] ;
0 commit comments