Skip to content

Releases: muthuishere/toolnexus

v0.12.0 — single-turn translation (all six ports)

Choose a tag to compare

@muthuishere muthuishere released this 30 Jul 04:30

Single-turn translation — the inbound half of the adapters

SPEC.md §0 item 7 pinned the format adapters as schema only: toOpenAI/toAnthropic/toGemini translated tool declarations outbound, and nothing in the library read a provider's tool calls back inbound. Every user of those public functions hit the same wall — they could tell a provider about their tools, but not receive the calls it made.

New: translate (idiomatic naming per port). Exactly one provider call, returned in OpenAI shape. No agent loop, no tool execution, no conversation state — every call is self-contained, so it runs statelessly and scales horizontally.

  • Request takes the OpenAI messages, tools and tool_choice verbatim, or an ordinary toolkit (MCP tools, skills, native functions, A2A agents, builtins) which is declared and never executed. They compose.
  • Inbound preserves the tool structure a text flattening destroys: assistant tool_calls → native tool-use blocks with arguments re-parsed to objects; tool results → tool-result blocks keyed by tool_call_id, merged into one user turn when consecutive; system hoisted to the provider's own field; content-parts flattened. Both arguments wire forms accepted.
  • Outbound returns arguments as a JSON string (the wire form, echoable byte-for-byte) plus a mapped finishReasonany tool call wins, giving "tool_calls".
  • Shares the loop's retries/backoff, request-param merging and llm metric. beforeLLM/afterLLM fire once; tool hooks never fire.

Use it when the caller owns the conversation and executes tools itself — the standard OpenAI function-calling posture. golang/examples/translator is a stateless OpenAI-compatible proxy in ~60 lines.

Parity verified by byte-diff, not assumed

One adversarial fixture hitting every §11 rule at once was run through all six ports and diffed against Go: js, python, java, csharp and elixir are byte-identical, first diff, no corrections. Committed at docs/spikes/0003-translation-parity-fixture.json.

Tests: js 13 · python 20 · golang 12 · java 13 · csharp 20 · elixir 27. Every port's full suite green; Elixir coverage 96.9%.

Fixed: python was broken for new installs

pyproject allowed mcp>=1.0.0, but mcp 2.x renamed streamablehttp_client, so mcp_source.py failed at import — a fresh resolve could not load the package at all. Now pinned mcp>=1.0.0,<2.0.0.

Relay tools + durable resume — golang ONLY, a preview

golang also gains relay (declaration-only) tools and an answer-carrying durable resume, built on the §10 suspension primitive (ADR-0010, #37). The other five ports do not implement this yet, so it is deliberately not part of the SPEC.md §0 conformance contract — the §10 subsections carry a status banner, and the remaining ports are tracked in openspec/changes/add-tool-relay-mode/tasks.md.

If you want cross-port behaviour, use §11 translation. ADR-0011 explains the split: translation for the pass-through posture (the caller owns the conversation), relay for proxy-managed memory (toolnexus owns it).

Full detail in CHANGELOG.md. Closes #37 via #38.

v0.10.0 — the agent release

Choose a tag to compare

@muthuishere muthuishere released this 19 Jul 16:23
bc4d95a

Both agent archetypes ship on a shared actor-model runtime: coding (sub-agents) and persona (agent home), with compaction to keep either alive over long sessions. All six ports — JavaScript, Python, Go, Java, C#, Elixir — byte-parity against the shared examples/ fixtures.

Added

Agent runtime + sub-agents (SPEC.md §7D) — a new agents namespace per port: agent(name, { does, uses, soul, team, budget, … }) with .run() and .asTool(). An Agent is a Tool. Delegation runs through a built-in team-scoped task tool: isolated child transcript, one tool message back, usage roll-up, parallel calls. Underneath — a Handle state machine with six host verbs (spawn/post/wake/wait/interrupt/close), two delivery rails, three loud backpressure gates, hierarchical live-enforced budgets, §10 suspension escalation with durable resume by task-key reattachment.

Persona agents / agent home (SPEC.md §7E) — fromDir(dir) composes the bootstrap files (AGENTS/SOUL/IDENTITY/USER/TOOLS/HEARTBEAT/MEMORY.md, 2 MB cap each) into a frozen soul snapshot at session start. A file-backed memory builtin writes to disk and loads at the start of the next session — never mutating the live prompt, keeping a long-lived persona cache-stable. Plus startAgent(…, { everyMs }), a coalescing heartbeat where a HEARTBEAT_OK reply stays silent.

Context compaction (SPEC.md §7F) — an opt-in compactor({ maxTokens, keepTail, summarize, … }) returning a beforeLLM hook. Below budget it is a byte-identical no-op. Two invariants: the retained tail begins at a user turn (tool-pair safety — a tool result is never orphaned from its tool_call_id), and a leading system prompt survives verbatim.

Docs — sub-agent and persona pages, plus five end-to-end scenario builds (coding agent, persona assistant, research orchestrator, support agent, self-improving agent), each with all six language tabs and an honest "what you bring / what this isn't" section.

Changed — action may be required

RunResult.status gains "incomplete". A maxTurns stop that still had tool calls in flight now returns status: "incomplete" plus limit: "maxTurns" instead of a silent "done". Any limit stop (turns, tokens, tool calls, wall clock) is now loud and names its limit; partial work and the transcript are preserved.

Code matching status === "done" after a maxTurns stop must be updated to also handle "incomplete".

Install

npm install toolnexus@0.10.0
pip install toolnexus==0.10.0
go get github.com/muthuishere/toolnexus/golang@v0.10.0
dotnet add package Toolnexus --version 0.10.0
<dependency><groupId>io.github.muthuishere</groupId><artifactId>toolnexus</artifactId><version>0.10.0</version></dependency>
{:toolnexus, "~> 0.10"}

Docs: https://muthuishere.github.io/toolnexus/

v0.9.5 — resilience policy + cross-port parity guard

Choose a tag to compare

@muthuishere muthuishere released this 15 Jul 17:45

All six ports at 0.9.5.

Resilience policy (§8). Host-configurable onError(info) -> retry | fail classifier for LLM-call failures — widen or narrow the retryable set, or fail fast. Absent ⇒ byte-identical to prior behavior. No suspend tier (suspension stays user-action, §10). Landed in all six ports with a per-port resilience matrix + default-parity test.

JS parity fix. Added disableTools/disableSkills to the JS toolkit — five other ports shipped these in 0.9.0 but the JS reference lacked them.

Cross-port option-parity CI guard. A new conformance job asserts every ClientOptions/ToolkitOptions field exists in all six ports, so option drift fails CI instead of shipping.

Resilience benchmark. New mock-LLM failure-mode matrix (8 scenarios × 4 ports, all graceful) + SPEC §2 note pinning the per-server MCP load bound.

Additive; no breaking changes.

v0.9.4 — Elixir port (sixth language) on Hex.pm

Choose a tag to compare

@muthuishere muthuishere released this 14 Jul 20:11

First release including the Elixir port (elixir/, Hex package toolnexus): full SPEC §0 conformance, in-house MCP client (stdio + streamable-HTTP/SSE) with the complete §10 elicitation bridge (form + URL), 274 hermetic tests at 96.7% coverage (CI gate ≥95%). All six ports publish at this version; behavior unchanged for the existing five.

v0.9.3 — unified release + Go stdio fix

Choose a tag to compare

@muthuishere muthuishere released this 14 Jul 01:16

Unified 0.9.3 across all five registries.

Same feature surface as 0.9.0 (ADR-0001: the seven rag_go consumer gaps — client request shaping §8 + MCP load lifecycle §2 — plus DisableTools/DisableSkills), now including the Go stdio child-lifetime fix (a v0.9.0 regression where local stdio MCP servers were killed right after connect; fixed via context.Background() for the stdio/SSE transport start, with a real outbound-stdio regression test).

This collapses the messy Go-only interim tags (golang/v0.9.1 was a dud, v0.9.2 the patch) into one clean version for every language. No API changes vs 0.9.0; additive and backward-compatible.

v0.9.0 — rag_go consumer needs (7 gaps) + host controls

Choose a tag to compare

@muthuishere muthuishere released this 13 Jul 20:34
138b36d

Implements all seven gaps from ADR-0001 (docs/adr/0001-rag-go-consumer-needs.md) across all five ports (js/python/golang/java/csharp), byte-identical, additive and backward-compatible. A caller passing none of the new options gets identical behavior to 0.8.0.

Client request shaping (§8)

  • RequestParams (caller-wins body merge; messages/tools/stream forbidden) + BodyTransform (rewrite the assembled body last, no proxy needed)
  • Injectable HTTP transport for LLM calls
  • Omit empty tool keys (tools/tool_choice dropped when the toolkit is empty)
  • ConversationStore accessor (read/rewind the transcript; no shadow store)

MCP load lifecycle (§2)

  • ctx/cancellation-aware load (propagates through connect/init/list; bounded SSE start; parent-cancel aborts the build, per-server timeout isolates)
  • Per-server tool allowlist (tools map; builtins/skills semantics; original names)
  • List-only inventory (ListMcpTools — connect, list, disconnect; unfiltered + per-server status)

Toolkit ergonomics

  • DisableTools / DisableSkills — flat name lists to turn things off across every source

All five CI suites green. Also includes a fix: MCP load never orphans a stdio child on an aborted/timed-out connect.

v0.8.0 — host-facing skill source

Choose a tag to compare

@muthuishere muthuishere released this 13 Jul 17:46

Extends the skill tool source so a host application can use it beyond on-disk directories — byte-identical across all five ports (js/python/golang/java/csharp), additive and backward-compatible.

  • S1 injectable sources: skills as data (SkillDef) + a lazy provider, composing with SkillsDir (first-wins dedupe); provider failure isolated.
  • S2 per-agent allowlist (SkillsFilter): same semantics as the MCP tools filter.
  • S3 list/validate inventory (ListSkills): parsed skills + typed skip reasons (missing-name | malformed-frontmatter | duplicate-name | unreadable).
  • S4 logical base (skill://name/) for data/served skills; on-disk output stays byte-identical.
  • S5 configurable sibling-file sample cap.

Docs: new API Reference section (five-language synced tabs + per-port pages). SPEC.md §3 extended.

A SkillsDir-only caller is unchanged in every port. See PR #11.

v0.7.0 — human-in-the-loop

Choose a tag to compare

@muthuishere muthuishere released this 12 Jul 13:19

The human-in-the-loop release. Ships the suspension/elicitation feature work that
0.6.0 predated — complete across all five ports (JavaScript, Python, Go, Java, C#).

Added

  • The question built-in now suspends — returns a kind:"question" Request; when served, maps to A2A input-required. The agent can pause and ask you mid-run, and resume where it left off.
  • MCP elicitation bridge — a downstream MCP server's elicitation request is mapped onto the one waitFor suspension mechanism.

Fixed / hardened (suspension correctness — parity across ports)

  • Suspensions are not tool errors (G2).
  • Concurrent suspensions surface the first deterministically (G3), including the streaming loops.
  • De-flaked the A2A cancel-mid-poll tests (C#, Go).

Docs

  • New Astro/Starlight docs site with a narrated launch video, and LLM-friendly llms.txt.

All behavior is verified byte-identical against the shared examples/ fixtures in CI.

v0.6.0

Choose a tag to compare

@muthuishere muthuishere released this 09 Jul 13:13

§10 Suspension (Pending / waitFor) across all five ports. See SPEC.md §10.

v0.5.0 — MCP server (inbound)

Choose a tag to compare

@muthuishere muthuishere released this 03 Jul 11:08
955b759

Expose a toolkit as an MCP server — the inbound mirror of the A2A serve.

Add an mcp profile to toolkit.serve(...) that re-exposes the toolkit's full unified Tool[] (mcp · skill · native · http · builtin · a2a) as a streamable-HTTP MCP server at POST /mcp. toolnexus becomes a universal MCP gateway: aggregate N servers + skills + your own tools, then serve the union as one MCP server any MCP client can call. tools/call dispatches straight to Tool.execute — no client, no tasks, no store. Optional mcp.tools filter, onCall callback, and a top-level mcpServer config block. Built on each port's existing MCP SDK in server mode.

Transport is streamable-HTTP; a stdio transport (local clients / Claude Desktop) is a documented follow-up.

All five ports (JS · Python · Go · Java · C#), suites green. SPEC §7C. PR #6.