Skip to content

Commit b4aac68

Browse files
committed
feat(tui): show enabled/disabled state for MCP servers in /mcp
Decode the enabled flag odek already sends on /api/mcp and surface it on every MCP row in the tools drawer and in the server detail view, matching the built-in tools' on/off convention. README command table updated.
1 parent 708b47b commit b4aac68

4 files changed

Lines changed: 56 additions & 1 deletion

File tree

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ full command and press `⏎`.
509509
| `/memory` | Facts by target, pending-episode promote, consolidate |
510510
| `/skills` | Skill provenance badges & promote |
511511
| `/tools` | Tool registry with enabled state & MCP servers |
512-
| `/mcp` | MCP servers — `/mcp [name]` jumps to one in the tools drawer |
512+
| `/mcp` | MCP servers with enabled/disabled state — `/mcp [name]` jumps to one in the tools drawer |
513513
| `/config` | Sanitized config, lifetime usage, connections (kick) |
514514
| `/model [name]` | Switch model (opens a picker with no argument) |
515515
| `/thinking [disabled\|low\|medium\|high]` | Set reasoning depth (`on` → `medium`, `off` → `disabled`, `inherit` restores the odek serve default; bare `/thinking` opens a picker) |

‎internal/client/manage.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ type MCPServer struct {
130130
Args []string `json:"args,omitempty"`
131131
Project bool `json:"project,omitempty"`
132132
AutoApprove bool `json:"auto_approve,omitempty"`
133+
Enabled bool `json:"enabled"`
133134
TimeoutSeconds int `json:"timeout_seconds,omitempty"`
134135
MaxResponseBytes int64 `json:"max_response_bytes,omitempty"`
135136
MaxResultChars int `json:"max_result_chars,omitempty"`

‎internal/tui/mcp_command_test.go‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,50 @@ func TestMCPUnknownNotClobbered(t *testing.T) {
170170
}
171171
}
172172

173+
// TestMCPEnabledStateVisible verifies MCP rows and the detail view surface
174+
// the enabled/disabled state odek sends on /api/mcp.
175+
func TestMCPEnabledStateVisible(t *testing.T) {
176+
m := newTestModel()
177+
m.panel = panelTools
178+
m.handleMgmtMsg(mgmtMsg{tab: panelTools, mcpN: 2, tls: []client.Tool{{Name: "shell", Enabled: true}},
179+
mcp: []client.MCPServer{
180+
{Name: "fs", Command: "bun", Enabled: true},
181+
{Name: "vault", Command: "node", Enabled: false},
182+
}})
183+
if len(m.toolRows) != 3 {
184+
t.Fatalf("toolRows = %d, want 3", len(m.toolRows))
185+
}
186+
var fsRow, vaultRow *toolRow
187+
for i := range m.toolRows {
188+
switch r := &m.toolRows[i]; r.id {
189+
case "fs":
190+
fsRow = r
191+
case "vault":
192+
vaultRow = r
193+
}
194+
}
195+
if fsRow == nil || vaultRow == nil {
196+
t.Fatalf("missing mcp rows: %+v", m.toolRows)
197+
}
198+
if !strings.Contains(fsRow.dim, "enabled") || strings.Contains(fsRow.dim, "disabled") {
199+
t.Errorf("fs row dim = %q, want an \"enabled\" marker", fsRow.dim)
200+
}
201+
if !strings.Contains(vaultRow.dim, "disabled") {
202+
t.Errorf("vault row dim = %q, want \"disabled\" marker", vaultRow.dim)
203+
}
204+
// Detail view of the disabled server states the fact explicitly.
205+
for i := range m.toolRows {
206+
if m.toolRows[i].id == "vault" {
207+
m.panelSel = i
208+
}
209+
}
210+
m.panelDetail = true
211+
out := plain(m.View())
212+
if !strings.Contains(out, "disabled") {
213+
t.Errorf("mcp detail view missing \"disabled\":\n%s", out)
214+
}
215+
}
216+
173217
// TestMCPZeroServers verifies the zero-server empty state: the Tools tab
174218
// still opens (it has native tools) with a "no MCP servers configured" note.
175219
func TestMCPZeroServers(t *testing.T) {

‎internal/tui/mgmt.go‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,11 @@ func buildToolRows(tools []client.Tool, servers []client.MCPServer) []toolRow {
320320
}
321321
for _, s := range servers {
322322
detail := s.Command
323+
if s.Enabled {
324+
detail += " · enabled"
325+
} else {
326+
detail += " · disabled"
327+
}
323328
if s.Project {
324329
detail += " · project"
325330
}
@@ -935,6 +940,11 @@ func (m *Model) mgmtDetailLines(w int) []string {
935940
}
936941
out = append(out, th.acDetail.Render(cmd))
937942
var meta []string
943+
if srv.Enabled {
944+
meta = append(meta, "enabled")
945+
} else {
946+
meta = append(meta, "disabled")
947+
}
938948
if srv.Project {
939949
meta = append(meta, "project-scoped")
940950
}

0 commit comments

Comments
 (0)