Skip to content

Toolkit.create

Java · package io.github.muthuishere:toolnexus · SPEC §4 · Toolkit.java

public static Toolkit create(Options opts)
// Toolkit.Options — the full surface, grouped:
public Options mcpConfig(Object v) // path, raw JSON string, or a parsed Map
public Options skillsDir(String... v) // one or more filesystem skill roots
public Options skills(List<SkillSource.SkillDef> v) // skills as data (§3 S1)
public Options skillProvider(Supplier<List<SkillSource.SkillDef>> v) // lazy data skills, failure-isolated
public Options skillsFilter(Map<String, Boolean> v) // per-agent skill allowlist (§3 S2)
public Options skillSampleLimit(int v) // sibling-file sample cap (§3 S5)
public Options extraTools(Tool... v) // custom/native/http tools
public Options annotatedObjects(Object... v) // objects scanned via Tools.fromObject
public Options builtins(Object v) // on by default; false | {disabled:true} | {enabled:false} => off
public Options agents(A2A.Agent... v) // remote A2A agents — each skill becomes a tool
public Options waitFor(Function<Request, Answer> v) // §10 — bridges MCP elicitation onto the suspension contract
public Options disableTools(List<String> v) // drop tools by final exposed name, across every source
public Options disableSkills(List<String> v) // drop skills by name (sugar over skillsFilter)
public Options cancelSignal(McpSource.CancelSignal v) // §2 Gap 3 — cancel a slow/hung MCP connect

Collapse MCP servers, skills, built-ins, A2A agents and your own functions into one tool list behind a single Toolkit. It connects every enabled MCP server (in parallel, failures isolated), globs and parses the skill directories, resolves the built-in file/shell/web tools, fetches each configured A2A agent’s card, and de-dupes everything by final tool name (first source wins) into one Map<String, Tool> you can hand straight to LlmClient.run/stream, or adapt to any provider’s tool schema.

Call Toolkit.create once per agent configuration — wherever you’d otherwise wire MCP, skills, built-ins and your own functions up separately. It is the toolkit’s only constructor: every option is optional, so Toolkit.create(new Toolkit.Options()) alone still gives you a working (empty, all-builtins-on) toolkit. Toolkit is AutoCloseable — closing it disconnects every connected MCP client, so build it in a try-with-resources (or hold it for the process lifetime and close it on shutdown).

1. The smallest useful call — built-ins only, no MCP, no skills

Section titled “1. The smallest useful call — built-ins only, no MCP, no skills”
import io.github.muthuishere.toolnexus.*;
public class Example {
public static void main(String[] args) {
try (Toolkit tk = Toolkit.create(new Toolkit.Options())) {
// No mcpConfig / skillsDir set — builtins default to ON, so the ten
// built-in tools (bash/read/write/edit/grep/glob/webfetch/question/
// apply_patch/todowrite) are the whole list.
if (tk.tools().isEmpty()) throw new AssertionError("expected the built-ins");
if (tk.get("bash") == null) throw new AssertionError("expected the bash builtin");
System.out.println("ok: " + tk.tools().size() + " tools, no MCP/skills configured");
}
}
}

2. The realistic case — skills from disk plus a hand-written tool, builtins off

Section titled “2. The realistic case — skills from disk plus a hand-written tool, builtins off”
import io.github.muthuishere.toolnexus.*;
import java.util.Map;
public class Example {
public static void main(String[] args) {
Tool add = NativeTool.of("add", "Add two numbers", null, callArgs -> {
Number a = (Number) callArgs.get("a");
Number b = (Number) callArgs.get("b");
return String.valueOf(a.intValue() + b.intValue());
});
try (Toolkit tk = Toolkit.create(new Toolkit.Options()
.skillsDir("examples/skills") // the shared cross-language fixture
.extraTools(add)
.builtins(false))) {
if (tk.get("skill") == null) throw new AssertionError("expected the skill tool");
if (tk.get("add") == null) throw new AssertionError("expected the custom tool");
if (tk.get("bash") != null) throw new AssertionError("builtins(false) should drop bash");
ToolResult r = tk.execute("add", Map.of("a", 2, "b", 3));
System.out.println("ok: skillsPrompt has hello-world="
+ tk.skillsPrompt().contains("hello-world") + ", add(2,3)=" + r.output());
}
}
}

3. The full surface — MCP, disableTools/disableSkills, and a per-agent skills filter together

Section titled “3. The full surface — MCP, disableTools/disableSkills, and a per-agent skills filter together”
import io.github.muthuishere.toolnexus.*;
import java.util.List;
import java.util.Map;
public class Example {
public static void main(String[] args) {
// A bare server map, every server disabled — hermetic: no process spawned, no network reached.
Map<String, Object> mcpConfig = Map.of(
"srv", Map.of("type", "remote", "url", "https://example.invalid/mcp", "enabled", false));
try (Toolkit tk = Toolkit.create(new Toolkit.Options()
.mcpConfig(mcpConfig)
.builtins(false)
.skills(List.of(
new SkillSource.SkillDef("keep", "Keep this one", "body"),
new SkillSource.SkillDef("drop", "Dropped via disableSkills", "body")))
.disableSkills("drop")
.disableTools("nonexistent_tool"))) {
// MCP: config parsed, server disabled, no tools contributed — no network reached.
if (!"disabled".equals(tk.mcpStatus().get("srv"))) throw new AssertionError(tk.mcpStatus());
String prompt = tk.skillsPrompt();
if (!prompt.contains("keep")) throw new AssertionError("expected \"keep\" in: " + prompt);
if (prompt.contains("**drop**")) throw new AssertionError("disableSkills should drop it: " + prompt);
System.out.println("ok: mcpStatus=" + tk.mcpStatus() + ", skills filtered as expected");
}
}
}
  • McpSource.load — Read an mcp.json, connect every local stdio and remote streamable-HTTP server, expose each server tool as a Tool.
  • SkillSource.load — Glob a skills directory and expose one skill tool with progressive disclosure.
  • SkillSource.loadWith — The data/provider/filter/sample-limit surface Toolkit wires skills through.
  • BuiltinTools.select — Pick which built-in file/shell/web tools are in play, by name or by source toggle.
  • LlmClient.waitFor — The single hook where a suspension (including bridged MCP elicitation) is resolved.
  • LlmClient.create — The unified client this toolkit’s tools get called through.