d2d79d65da
Complete the remaining M8 extensibility deliverables:
- MCP client with JSON-RPC 2.0 over stdio transport, protocol
lifecycle (initialize/tools-list/tools-call), and process group
management for clean shutdown
- MCP tool adapter implementing tool.Tool with mcp__{server}__{tool}
naming convention and replace_default for swapping built-in tools
- MCP manager for multi-server orchestration with parallel startup,
tool discovery, and registry integration
- Plugin system with plugin.json manifest (name/version/capabilities),
directory-based discovery (global + project scopes with precedence),
loader that merges skills/hooks/MCP configs into existing registries,
and install/uninstall/list lifecycle manager
- Config additions: MCPServerConfig, PluginsSection with opt-in/opt-out
enabled/disabled resolution
- TUI /plugins command for listing installed plugins
- 54 tests across internal/mcp and internal/plugin packages
85 lines
1.9 KiB
Go
85 lines
1.9 KiB
Go
package mcp
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"somegit.dev/Owlibou/gnoma/internal/config"
|
|
)
|
|
|
|
func TestParseServerConfigs_Valid(t *testing.T) {
|
|
raw := []config.MCPServerConfig{
|
|
{
|
|
Name: "git",
|
|
Command: "mcp-server-git",
|
|
Args: []string{"--repo", "."},
|
|
Env: map[string]string{"GIT_DIR": ".git"},
|
|
Timeout: "10s",
|
|
ReplaceDefault: []string{"bash"},
|
|
},
|
|
{
|
|
Name: "docker",
|
|
Command: "mcp-server-docker",
|
|
},
|
|
}
|
|
|
|
got, err := ParseServerConfigs(raw)
|
|
if err != nil {
|
|
t.Fatalf("ParseServerConfigs: %v", err)
|
|
}
|
|
if len(got) != 2 {
|
|
t.Fatalf("got %d configs, want 2", len(got))
|
|
}
|
|
|
|
if got[0].Name != "git" {
|
|
t.Errorf("config[0].Name = %q, want %q", got[0].Name, "git")
|
|
}
|
|
if got[0].Timeout != 10*time.Second {
|
|
t.Errorf("config[0].Timeout = %v, want %v", got[0].Timeout, 10*time.Second)
|
|
}
|
|
if len(got[0].ReplaceDefault) != 1 || got[0].ReplaceDefault[0] != "bash" {
|
|
t.Errorf("config[0].ReplaceDefault = %v, want [bash]", got[0].ReplaceDefault)
|
|
}
|
|
|
|
// Second config should get default timeout.
|
|
if got[1].Timeout != defaultTimeout {
|
|
t.Errorf("config[1].Timeout = %v, want default %v", got[1].Timeout, defaultTimeout)
|
|
}
|
|
}
|
|
|
|
func TestParseServerConfigs_Errors(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
raw []config.MCPServerConfig
|
|
}{
|
|
{
|
|
name: "missing name",
|
|
raw: []config.MCPServerConfig{{Command: "foo"}},
|
|
},
|
|
{
|
|
name: "missing command",
|
|
raw: []config.MCPServerConfig{{Name: "foo"}},
|
|
},
|
|
{
|
|
name: "duplicate name",
|
|
raw: []config.MCPServerConfig{
|
|
{Name: "foo", Command: "a"},
|
|
{Name: "foo", Command: "b"},
|
|
},
|
|
},
|
|
{
|
|
name: "bad timeout",
|
|
raw: []config.MCPServerConfig{{Name: "foo", Command: "bar", Timeout: "not-a-duration"}},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
_, err := ParseServerConfigs(tt.raw)
|
|
if err == nil {
|
|
t.Error("expected error")
|
|
}
|
|
})
|
|
}
|
|
}
|