McpSource.parseConfig
Java · package io.github.muthuishere:toolnexus · SPEC §2 · McpSource.java
public static Map<String, Object> parseConfig(Object input)Reads MCP configuration and normalises it into a flat map of server name → server config. It
connects to nothing — no child process is spawned, no HTTP request is made. That is the whole
point: it is the cheap check you can run before paying for load.
Three input shapes are accepted: a path to an mcp.json, a raw JSON String, and an
already-parsed Map<String, Object>. A String that is not an existing regular file is treated as
raw JSON, so the same parameter covers both.
When to use it
Section titled “When to use it”- Validate at startup or in a test, so a typo in
mcp.jsonfails 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 — then hand the resulting
Mapstraight back toload. - Accept config from somewhere other than a file — a database row, an env var, an HTTP response — without writing it to disk first.
Why this and not the alternative
Section titled “Why this and not the alternative”The returned map is not a defensive copy. When a wrapper key is present you get the caller’s own nested map back, exactly as the JS and Go references do; the bare-map form returns a shallow copy so your input is left untouched. toolnexus itself only ever reads this map — if you intend to mutate the result, copy it first.
Examples
Section titled “Examples”1. Parse the shared fixture from disk
Section titled “1. Parse the shared fixture from disk”This is examples/mcp.json, the fixture every port is tested against.
import io.github.muthuishere.toolnexus.*;import java.nio.file.Path;import java.util.List;import java.util.Map;
public class Example { public static void main(String[] args) throws Exception { // TOOLNEXUS_REPO is set by the docs test runner; in your own code just use a path. String repo = System.getenv().getOrDefault("TOOLNEXUS_REPO", "."); String fixture = Path.of(repo, "examples", "mcp.json").toString();
Map<String, Object> config = McpSource.parseConfig(fixture);
// The `mcpServers` wrapper is unwrapped — you get the servers directly. List<String> names = config.keySet().stream().sorted().toList(); if (!names.equals(List.of("everything", "example-remote"))) throw new AssertionError(names);
// Parsing does NOT filter: the disabled remote server is still here. @SuppressWarnings("unchecked") Map<String, Object> remote = (Map<String, Object>) config.get("example-remote"); if (!Boolean.FALSE.equals(remote.get("enabled"))) throw new AssertionError(remote);
System.out.println("ok: " + String.join(", ", names)); }}2. Wrapper spellings and a raw JSON string
Section titled “2. Wrapper spellings and a raw JSON string”mcpServers, servers and mcp all mean the same thing, and a raw JSON string is accepted too.
import io.github.muthuishere.toolnexus.*;import java.util.List;import java.util.Map;
public class Example { public static void main(String[] args) throws Exception { for (String raw : List.of( "{\"mcpServers\":{\"a\":{\"type\":\"local\",\"command\":[\"echo\",\"hi\"]}}}", "{\"servers\":{\"a\":{\"type\":\"local\",\"command\":[\"echo\",\"hi\"]}}}", "{\"mcp\":{\"a\":{\"type\":\"local\",\"command\":[\"echo\",\"hi\"]}}}")) { Map<String, Object> config = McpSource.parseConfig(raw); if (config.size() != 1 || !config.containsKey("a")) throw new AssertionError(config); }
// A bare map of servers works too — and the sibling toolnexus keys // (builtins/agents/a2a/mcpServer) are stripped, never read as servers. Map<String, Object> bare = McpSource.parseConfig(Map.of( "a", Map.of("type", "local", "command", List.of("echo", "hi")), "builtins", Map.of("bash", true))); if (!bare.keySet().equals(java.util.Set.of("a"))) throw new AssertionError(bare.keySet());
System.out.println("ok: 3 spellings + bare map -> a"); }}3. Validate before loading, and fail loudly
Section titled “3. Validate before loading, and fail loudly”The pattern this method exists for — check the config, then decide whether to connect.
import io.github.muthuishere.toolnexus.*;import java.util.ArrayList;import java.util.List;import java.util.Map;
public class Example { @SuppressWarnings("unchecked") static List<String> problems(String raw) { Map<String, Object> config = McpSource.parseConfig(raw); List<String> problems = new ArrayList<>(); for (Map.Entry<String, Object> e : config.entrySet()) { Map<String, Object> cfg = (Map<String, Object>) e.getValue(); // Disabled either way round: `enabled: false` or `disabled: true`. if (Boolean.TRUE.equals(cfg.get("disabled"))) continue; if (Boolean.FALSE.equals(cfg.get("enabled"))) continue;
if ("remote".equals(cfg.get("type"))) { Object url = cfg.get("url"); if (!(url instanceof String s) || s.isEmpty()) problems.add(e.getKey() + ": remote server without a url"); } else { Object command = cfg.get("command"); if (!(command instanceof List<?> l) || l.isEmpty()) problems.add(e.getKey() + ": local server without a command"); } } problems.sort(String::compareTo); return problems; }
public static void main(String[] args) throws Exception { List<String> good = problems(""" {"mcpServers":{ "ok_local":{"type":"local","command":["npx","server"]}, "ok_remote":{"type":"remote","url":"https://example.com/mcp"}, "off":{"type":"local","command":[],"enabled":false}}} """); if (!good.isEmpty()) throw new AssertionError(good);
List<String> bad = problems(""" {"mcpServers":{ "broken_remote":{"type":"remote"}, "broken_local":{"type":"local","command":[]}}} """); if (bad.size() != 2) throw new AssertionError(bad);
// Malformed JSON is a hard failure, not a silently empty config. boolean threw = false; try { McpSource.parseConfig("{\"mcpServers\": {"); } catch (RuntimeException e) { threw = e.getMessage().startsWith("Invalid JSON"); } if (!threw) throw new AssertionError("expected an Invalid JSON failure");
System.out.println("ok: problems=" + bad.size() + ", malformed rejected"); }}Accepted input
Section titled “Accepted input”| Input | Behaviour |
|---|---|
"./mcp.json" |
A path — read from disk and parsed. |
A raw JSON String |
Parsed directly (any String that is not an existing file). |
A parsed Map<String, Object> |
Normalised only. |
Wrapped under mcpServers, servers or mcp — all three are unwrapped, in that order of
precedence. A bare map of servers is accepted, with builtins / agents / a2a / mcpServer
stripped.
See also
Section titled “See also”McpSource.load— parse and connect.McpSource.loadWith— the ctx-awareload.McpSource.listMcpTools— what each server would expose, without wiring a toolkit.McpSource.ParseConfig(C#) — the same method, same spelling shape.