Skip to content

create_toolkit

Python · package toolnexus · SPEC §4 · python/src/toolnexus/toolkit.py

async def create_toolkit(
mcp_config: str | dict[str, Any] | None = None,
skills_dir: str | list[str] | None = None,
skills: list[SkillDef] | None = None,
skill_provider: SkillProvider | None = None,
skills_filter: dict[str, bool] | None = None,
skill_sample_limit: int = 0,
extra_tools: list[Tool] | None = None,
builtins: BuiltinsConfig | None = None,
agents: list[Agent] | None = None,
wait_for: Any | None = None,
disable_tools: list[str] | None = None,
disable_skills: list[str] | None = None,
cancel: Any | None = None,
) -> Toolkit

create_toolkit is the other entry point: it gathers every tool source you point it at — MCP servers, a skills directory or an in-memory skill list, built-in tools, remote A2A agents, and any of your own Tools — and hands back one Toolkit with a single flat, deduplicated tool list. Pass that Toolkit into client.run(prompt, toolkit) and the model sees one uniform surface no matter how many sources fed it.

It is async because connecting MCP servers and globbing/reading skill files are I/O; the returned Toolkit is also an async context manager, and await tk.close() tears every source (MCP subprocesses/HTTP connections, served A2A handles) back down.

  • You have one or more MCP servers (mcp.json, local stdio or remote streamable-HTTP) and want every tool they expose as a plain Tool, without hand-writing the MCP client dance.
  • You have a skills/ directory (or a static/dynamic list of SkillDefs) and want them exposed as one skill tool with progressive disclosure, instead of dumping every skill’s full body into the system prompt.
  • You want built-in tools (shell, file read/write, …), your own define_tool/http_tool functions, and remote A2A agents’ skills to show up in the same tool list as the above — so the model sees one surface regardless of where a tool actually lives.
  • You need to scope that surface down per audience — a per-skill allow/deny map (skills_filter), a global disable_tools / disable_skills drop-list, or builtins=False to turn off the whole built-in source.
Option What it’s for Depth
mcp_config A path to mcp.json, or an already-parsed config dict — connects every server it names. load_mcp, parse_mcp_config
skills_dir One or more directories to glob for **/SKILL.md; exposed as one skill tool. load_skills
skills A static in-memory list of SkillDef — same skill tool, no filesystem. load_skills
skill_provider A callable (sync or async) returning list[SkillDef] on demand — for a dynamic/remote skill catalog. load_skills
skills_filter Per-skill allow/deny map, composed with disable_skills. load_skills
skill_sample_limit Caps how many example skills are sampled into the catalog summary (0 = no cap). load_skills
extra_tools Your own Tools (from define_tool / http_tool, or built by hand) merged straight in. Tool
builtins True/False/None or a {"enabled": ...} / {"disabled": ...} dict — the whole built-in source is on by default. create_builtin_tools, select_builtins
agents A list of agent(cardUrl) descriptors — each remote skill becomes an <agent>_<skill> tool (source "a2a"). agent
wait_for Same §10 suspension hook as create_client — set here if this toolkit’s own tools (not just the client’s) can suspend. WaitFor
disable_tools Drops tools by their final exposed name, across every source, after merging. —
disable_skills Drops skills by name from the skill catalog (composes with skills_filter). —
cancel An asyncio.Event — set it to abort an in-flight MCP connect/load (SPEC §2 Gap 3). —

1. The smallest useful call — no sources, just built-ins

Section titled “1. The smallest useful call — no sources, just built-ins”
import asyncio
from toolnexus import create_toolkit
async def main():
tk = await create_toolkit()
try:
names = sorted(t.name for t in tk.tools())
print("built-in tools:", names)
assert len(names) > 0 # builtins are on by default
finally:
await tk.close()
asyncio.run(main())

2. The realistic case — your own tool, a static skill, and a drop-list

Section titled “2. The realistic case — your own tool, a static skill, and a drop-list”
from toolnexus import create_toolkit, define_tool
from toolnexus.skill import SkillDef
import asyncio
async def main():
def add(a: float, b: float) -> str:
"""Add two numbers and return the sum."""
return str(a + b)
tk = await create_toolkit(
builtins=False,
extra_tools=[define_tool(add, name="add")],
skills=[
SkillDef(name="keep", description="a skill worth keeping", content="k"),
SkillDef(name="drop", description="a skill to hide from this agent", content="d"),
],
disable_skills=["drop"],
)
try:
assert tk.get("add") is not None
prompt = tk.skills_prompt()
assert "keep" in prompt and "**drop**" not in prompt
print("tools:", [t.name for t in tk.tools()])
print("skill catalog mentions 'keep':", "keep" in prompt, "| mentions 'drop':", "**drop**" in prompt)
finally:
await tk.close()
asyncio.run(main())

3. The full surface — MCP servers + skills directory + tool-name drop-list

Section titled “3. The full surface — MCP servers + skills directory + tool-name drop-list”
import asyncio
import json
import os
from toolnexus import create_toolkit
_HERE = os.path.dirname(os.path.abspath(__file__))
_EXAMPLES = os.path.normpath(os.path.join(_HERE, "..", "..", "examples"))
async def main() -> None:
tk = await create_toolkit(
mcp_config=os.path.join(_EXAMPLES, "mcp.json"),
skills_dir=os.path.join(_EXAMPLES, "skills"),
skill_sample_limit=5,
disable_tools=["everything_echo"], # drop one tool by its exposed name
)
print("MCP status:", tk.mcp_status())
print("Tools:", [f"{t.name} ({t.source})" for t in tk.tools()])
print("\nSystem-prompt skill catalog:\n" + tk.skills_prompt())
print("\nOpenAI tool schema (first 2):")
print(json.dumps(tk.to_openai()[:2], indent=2))
res = await tk.execute("skill", {"name": "hello-world"})
print("\nskill(hello-world) ->\n" + res.output)
await tk.close()
if __name__ == "__main__":
asyncio.run(main())
  • create_client — The unified client: system prompt, skills injection, parallel and chained tool calls, retries, memory.
  • load_mcp — Read an mcp.json, connect every local stdio and remote streamable-HTTP server, expose each server tool as a Tool.
  • parse_mcp_config — Parse and validate an mcp.json without connecting to anything.
  • load_skills — Glob a skills directory and expose one skill tool with progressive disclosure.
  • create_builtin_tools — Construct every built-in tool directly, outside a toolkit.
  • agent — Point at a remote agent’s card and use it exactly like a local tool.
  • WaitFor — The single hook where the host resolves a suspension — in-process prompt or durable queue, same contract.