Provider-agnostic toolkit: dynamic MCP servers + agent skills for any LLM.
Project description
toolnexus
Build an agent in a few lines. Point at an mcp.json and a skills/ folder, call run(),
and you have a working agent — MCP servers, agent skills, your own functions, and HTTP endpoints
unified as one tool set, driving any LLM.
Right-sized. Not a framework (no builders, no config to wade through), not a toy that falls over the moment you need streaming or a retry. Everything a real agent needs — the loop, hooks, streaming, retries, memory — and nothing it doesn't.
The Python port of toolnexus — the same library,
byte-identical, also in JavaScript, Go, Java, and C#. Built on the official MCP Python SDK
(the mcp package). Python ≥ 3.11.
Install
pip install toolnexus
An agent in 3 steps
The MCP SDK is async, so the toolkit is async:
import asyncio
from toolnexus import create_toolkit, create_client
async def main():
# 1. tools from an mcp.json + a skills/ folder
tk = await create_toolkit(mcp_config="./mcp.json", skills_dir="./skills")
# 2. point at any OpenAI- or Anthropic-style endpoint
agent = create_client(
base_url="https://openrouter.ai/api/v1",
style="openai", # or "anthropic"
model="openai/gpt-4o-mini",
)
# 3. run — skills injected, tools called for you, looped to an answer
res = await agent.run("Refund order 1234 for the customer.", tk)
print(res.text)
await tk.close()
asyncio.run(main())
create_client reads the key from OPENROUTER_API_KEY / OPENAI_API_KEY / ANTHROPIC_API_KEY
by default. The Toolkit is also an async context manager (async with await create_toolkit(...))
if you'd rather not call close() yourself.
Add your own tools
from toolnexus import define_tool, http_tool
# a plain function → a tool (schema inferred from the signature)
def add(a: float, b: float) -> str:
"""Add two numbers and return the sum."""
return str(a + b)
tk.register(define_tool(add, name="add"))
# a REST endpoint → a tool
tk.register(http_tool(
name="create_ticket", description="Create a ticket", method="POST",
url="https://api.example.com/tickets",
headers={"Authorization": "Bearer ${API_TOKEN}"}, # ${ENV} expands from os.environ, never logged
input_schema={"type": "object", "properties": {"title": {"type": "string"}}, "required": ["title"]},
))
URL {placeholders} are filled from args; the rest become the JSON body. Non-2xx →
ToolResult(output="HTTP <status>: <body>", is_error=True).
Bring your own loop
Don't want the host loop? Use the schema adapters and execute calls yourself:
tools = tk.to_openai() # or tk.to_anthropic() / tk.to_gemini()
system = tk.skills_prompt() # markdown skills catalog for your system prompt
# when the model returns a tool call { name, arguments }:
res = await tk.execute(name, arguments) # -> ToolResult(output, is_error, metadata)
The four sources
| Source | How |
|---|---|
| MCP servers | an mcp.json (mcpServers/servers/mcp); local stdio + remote streamable-HTTP, headers for auth |
| Agent skills | a folder of <name>/SKILL.md; a skill tool loads each on demand + a system-prompt catalog |
| Native tools | define_tool(fn) / the @tool decorator — a function becomes a tool |
| HTTP / REST | http_tool(...) — an endpoint becomes a tool, ${ENV} headers |
All four appear as one uniform Tool in tk.tools(), with source in "mcp" | "skill" | "custom".
API
| Python | Description |
|---|---|
await create_toolkit(...) |
async factory → Toolkit |
create_client(...) |
the unified host loop (await agent.run(msg, tk) / agent.stream(...)) |
tk.tools() / tk.get(name) |
the uniform tools |
await tk.execute(name, args, ctx=None) |
run a tool → ToolResult |
tk.skills_prompt() |
system-prompt skill catalog |
tk.mcp_status() |
per-server connection status |
tk.to_openai() / to_anthropic() / to_gemini() |
provider tool schemas |
tk.register(*tools) |
add native/http/custom tools |
await tk.close() |
disconnect MCP servers |
More
Full docs, the other four language ports, the shared behavior spec, and runnable examples: https://github.com/muthuishere/toolnexus
MIT licensed.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file toolnexus-0.1.2.tar.gz.
File metadata
- Download URL: toolnexus-0.1.2.tar.gz
- Upload date:
- Size: 79.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
169b9a6ce941738a9a4a4228231a62b0bbd9074c787abe61baf46ef3c196874c
|
|
| MD5 |
bfac87501b97e37a681840842a5d44fd
|
|
| BLAKE2b-256 |
a4dc2bbe7169f65e857e0dac0e73e3c1396040907a2136ac7167a1a8c1688cab
|
Provenance
The following attestation bundles were made for toolnexus-0.1.2.tar.gz:
Publisher:
release.yml on muthuishere/toolnexus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
toolnexus-0.1.2.tar.gz -
Subject digest:
169b9a6ce941738a9a4a4228231a62b0bbd9074c787abe61baf46ef3c196874c - Sigstore transparency entry: 2010523254
- Sigstore integration time:
-
Permalink:
muthuishere/toolnexus@3e5532767a9ea34c6a3f11b74471b5898d95ab83 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/muthuishere
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3e5532767a9ea34c6a3f11b74471b5898d95ab83 -
Trigger Event:
release
-
Statement type:
File details
Details for the file toolnexus-0.1.2-py3-none-any.whl.
File metadata
- Download URL: toolnexus-0.1.2-py3-none-any.whl
- Upload date:
- Size: 26.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4fb7ce99febfdb2f554be0deab0708dd2096a792f58a7ef592fed220efd8fa96
|
|
| MD5 |
fd72975ca0ad0501333e52c5fc5e4ee2
|
|
| BLAKE2b-256 |
0bdb4258f4337c4317f298c6b0c6421587a860b2b0b4900b5de61163d9438d92
|
Provenance
The following attestation bundles were made for toolnexus-0.1.2-py3-none-any.whl:
Publisher:
release.yml on muthuishere/toolnexus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
toolnexus-0.1.2-py3-none-any.whl -
Subject digest:
4fb7ce99febfdb2f554be0deab0708dd2096a792f58a7ef592fed220efd8fa96 - Sigstore transparency entry: 2010523362
- Sigstore integration time:
-
Permalink:
muthuishere/toolnexus@3e5532767a9ea34c6a3f11b74471b5898d95ab83 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/muthuishere
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3e5532767a9ea34c6a3f11b74471b5898d95ab83 -
Trigger Event:
release
-
Statement type: