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 rootspublic Options skills(List<SkillSource.SkillDef> v) // skills as data (§3 S1)public Options skillProvider(Supplier<List<SkillSource.SkillDef>> v) // lazy data skills, failure-isolatedpublic 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 toolspublic Options annotatedObjects(Object... v) // objects scanned via Tools.fromObjectpublic Options builtins(Object v) // on by default; false | {disabled:true} | {enabled:false} => offpublic 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 contractpublic Options disableTools(List<String> v) // drop tools by final exposed name, across every sourcepublic 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 connectCollapse 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.
When to use it
Section titled “When to use it”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).
Why this and not the alternative
Section titled “Why this and not the alternative”Examples
Section titled “Examples”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"); } }}See also
Section titled “See also”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 surfaceToolkitwires 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.