Skip to content

ParseMcpConfig

Go · module github.com/muthuishere/toolnexus/golang · SPEC §2 · golang/mcp.go

func ParseMcpConfig(input any) (McpConfig, error)

Reads MCP server configuration and normalises it into a flat map of server name → config. It connects to nothing — no child processes, no HTTP. That is the whole point: it is the cheap check you can run before paying for LoadMcp.

  • Validate at startup or in a test, so a typo in mcp.json fails immediately rather than halfway through connecting to five servers.
  • Inspect or modify config before loading — filter servers by environment, inject a header, disable one in CI.
  • Accept config from somewhere other than a file — a database, an env var, an API response.

The input is any because it accepts a path string, raw JSON bytes, an already-typed McpConfig, or a parsed map[string]any.

This is examples/mcp.json, the fixture every port is tested against.

package main
import (
"fmt"
"log"
"os"
"path/filepath"
"sort"
toolnexus "github.com/muthuishere/toolnexus/golang"
)
func main() {
// TOOLNEXUS_REPO is set by the docs test runner; in your own code just use a path.
fixture := filepath.Join(os.Getenv("TOOLNEXUS_REPO"), "examples", "mcp.json")
config, err := toolnexus.ParseMcpConfig(fixture)
if err != nil {
log.Fatal(err)
}
// The `mcpServers` wrapper is unwrapped — you get the servers directly.
var names []string
for name := range config {
names = append(names, name)
}
sort.Strings(names)
if len(names) != 2 || names[0] != "everything" || names[1] != "example-remote" {
log.Fatalf("unexpected servers: %v", names)
}
// Disabled servers are still returned — parsing does not filter.
fmt.Println("ok:", names)
}

mcpServers, servers and mcp all mean the same thing, and raw JSON bytes are accepted too.

package main
import (
"fmt"
"log"
toolnexus "github.com/muthuishere/toolnexus/golang"
)
func main() {
for _, raw := range []string{
`{"mcpServers":{"a":{"type":"local","command":["echo","hi"]}}}`,
`{"servers":{"a":{"type":"local","command":["echo","hi"]}}}`,
`{"mcp":{"a":{"type":"local","command":["echo","hi"]}}}`,
} {
config, err := toolnexus.ParseMcpConfig([]byte(raw))
if err != nil {
log.Fatal(err)
}
if len(config) != 1 {
log.Fatalf("expected one server, got %d", len(config))
}
if _, ok := config["a"]; !ok {
log.Fatalf("expected server 'a' in %v", config)
}
}
fmt.Println("ok: 3 spellings -> a")
}

3. Validate before loading, and surface the error

Section titled “3. Validate before loading, and surface the error”

The pattern this function exists for — check the config, then decide whether to connect.

package main
import (
"fmt"
"log"
"sort"
toolnexus "github.com/muthuishere/toolnexus/golang"
)
func validate(raw string) (enabled []string, problems []string, err error) {
config, err := toolnexus.ParseMcpConfig([]byte(raw))
if err != nil {
return nil, nil, err
}
for name, cfg := range config {
// Disabled either way round. BOTH flags are *bool, so nil means "not set"
// — dereference only after a nil check.
if (cfg.Disabled != nil && *cfg.Disabled) || (cfg.Enabled != nil && !*cfg.Enabled) {
continue
}
enabled = append(enabled, name)
if cfg.Type == "remote" {
if cfg.URL == "" {
problems = append(problems, name+": remote server without a url")
}
} else if len(cfg.Command) == 0 {
problems = append(problems, name+": local server without a command")
}
}
sort.Strings(enabled)
sort.Strings(problems)
return enabled, problems, nil
}
func main() {
enabled, problems, err := validate(`{"mcpServers":{
"ok_local":{"type":"local","command":["npx","server"]},
"ok_remote":{"type":"remote","url":"https://example.com/mcp"},
"off":{"type":"local","command":["x"],"enabled":false}}}`)
if err != nil {
log.Fatal(err)
}
if len(enabled) != 2 || len(problems) != 0 {
log.Fatalf("unexpected: %v %v", enabled, problems)
}
_, badProblems, err := validate(`{"mcpServers":{
"broken_remote":{"type":"remote"},
"broken_local":{"type":"local","command":[]}}}`)
if err != nil {
log.Fatal(err)
}
if len(badProblems) != 2 {
log.Fatalf("expected 2 problems, got %v", badProblems)
}
// Malformed JSON is an error, not a panic and not a partial config.
if _, err := toolnexus.ParseMcpConfig([]byte(`{not json`)); err == nil {
log.Fatal("expected a parse error")
}
fmt.Println("ok:", enabled, "| problems:", len(badProblems))
}
Input Behaviour
"./mcp.json" (string) Read from disk and unmarshalled. Error if missing or malformed.
[]byte(...) Raw JSON, unmarshalled directly.
McpConfig Returned as-is.
map[string]any Normalised.

Wrapped under mcpServers, servers or mcp — all three are unwrapped.