Skip to main content

tooltangle: find out which tools your LLM agent mixes up, and fix their descriptions with proof

PyPI Python license: MIT

Connect five MCP servers to an agent and the model is choosing between forty tools on every message. Each server's author tested their own tools, but nobody tested the mix you ended up with.

tooltangle tests that mix. It writes realistic user messages for every tool, runs your model on them with all the tools attached, and shows which tools get confused with which. It can then rewrite the confusing descriptions, keeping a rewrite only if the model does measurably better on messages the rewrite was never shown.

Here is part of a real run against the reference time server, which has just two tools:

ERROR  time.convert_time → time.get_current_time  44% (7 of 16)
       e.g. "Our CEO wants to host an all-hands at 9:00 AM Eastern Time next Tuesday. What would that time correspond to in Dubai?"

accuracy  0.82 [0.67, 0.91] on 38 messages with gemini-3.5-flash-lite

Install

You need Python 3.12 or newer.

uvx tooltangle --help     # run it without installing anything
pip install tooltangle    # or install it into a project

Set GOOGLE_API_KEY in your environment or in a .env file in the directory you run from. Gemini is the default because its free tier is enough to try the tool.

To test OpenAI or Anthropic models, install the matching extra, set OPENAI_API_KEY or ANTHROPIC_API_KEY, and name the model:

pip install "tooltangle[openai]"        # or "tooltangle[anthropic]"
tooltangle check mcp.json --model openai:gpt-5.4-mini
tooltangle check mcp.json --model anthropic:claude-haiku-4-5

The test messages are still written by Gemini unless you also pass --generator <model>. Claude works well as the model under test but isn't recommended as the generator yet. Both integrations are new and not battle-tested. Other providers that LangChain supports can be tried the same way after installing their LangChain package, but they are untested.

Usage

tooltangle tools claude-desktop   # list your tools and run the checks that need no model
tooltangle check claude-desktop   # measure which tools the model confuses
tooltangle fix claude-desktop     # rewrite confusing descriptions, keep the ones that hold up
tooltangle check claude-desktop   # measure again with the rewrites applied

The argument says where the tools come from: claude-desktop, claude-code, cursor or vscode, a path to an MCP config file, or a Python target such as my_agent/tools.py:TOOLS with LangChain tools or plain functions. MCP servers are started only to list their tools. tooltangle never calls a tool.

The repository has two examples to try first:

tooltangle tools examples/demo.mcp.json            # five reference MCP servers, 38 tools
tooltangle check examples/support_tools.py:TOOLS   # six LangChain tools of a support agent

To pick a model, compare runs the same messages through several and names the cheapest one that a paired test can't show to be worse than the best:

tooltangle compare mcp.json -m google_genai:gemini-3.8-flash -m google_genai:gemini-3.5-flash-lite

How it works

  1. It loads the tools from all servers and runs the checks that need no model: duplicate names, missing or very short descriptions, deprecated tools, the token cost of the definitions.
  2. A generator model writes messages a real user might send: some for each tool, tricky ones for pairs that are easy to mix up, and some that need no tool. Messages that name the tool or repeat its description are dropped. A second pass labels every message with all the tools that would be a reasonable first call, so a message with two valid answers never counts as a mistake.
  3. The messages are split into dev and holdout halves and saved to .tooltangle/queries.jsonl, which you can read, edit and commit.
  4. The model under test gets each message with all the tools attached, and the first tool it calls is recorded.
  5. The report lists directed confusions with a rate and an example, needless tool calls, and accuracy with a 95% Wilson interval, in the terminal and in .tooltangle/report.html.

Looking before acting is not a confusion. Calling a read-only tool from the same server first, such as git_status before git_commit, is reported as INFO. This uses the MCP readOnlyHint annotation.

How fix decides what to keep

fix asks the generator to rewrite the descriptions of the most confused pairs, then tests each rewrite:

  • The generator sees mistakes only from the dev half. The rewrite is scored on the holdout half.
  • It is kept only if a one-sided exact McNemar test gives p < 0.05 and, among related tools, it broke at most one more message than it fixed.
  • The 0.05 is split across attempts, and a retry is told only the counts, never the messages.
  • If a pair has too few mistakes to prove anything (fewer than six on holdout by default), it first writes more messages for that pair, and says so if that still isn't enough.

Accepted rewrites go to tooltangle.overrides.yaml, and the next check applies them. To use them in your own agent:

from tooltangle import apply_overrides

tools = apply_overrides(tools)  # LangChain tools; reads tooltangle.overrides.yaml

Apps like Claude Desktop or Cursor take descriptions straight from the server, so there a rewrite is a suggestion to pass on to the server's author.

CI

tooltangle check mcp.json --yes works as a CI step. Its exit codes:

Code Meaning
0 nothing at ERROR level
1 a tool loses at least 20% of its messages (3 or more) to one wrong choice, two tools share a name, or a tool has no description
2 the run failed: a bad config, a model that can't be created, or every request failing
3 the daily quota or the account's credit ran out

Commit .tooltangle/queries.jsonl so every run scores the same messages, and cache .tooltangle/cache.sqlite so a run with no changes makes no model calls. See examples/github-workflow.yml.

Configuration

Settings are optional and go in tooltangle.toml in the directory you run from. The ones you are most likely to change, with their defaults:

model = "google_genai:gemini-3.5-flash-lite"   # the model under test
generator = "google_genai:gemini-3.8-flash"    # writes and labels the test messages
language = "English"                           # language of the test messages
queries_per_tool = 10
requests_per_minute = 120

There are also generator_fallbacks, contrast_pairs, queries_per_contrast, concurrency, system_prompt, model_options and a [prices] table; see settings.py.

Cost

  • Every model answer is cached, so running again without changes is free and an interrupted run picks up where it stopped.
  • Before making more than 25 model calls it prints an estimate and asks. --yes skips the question. Prices come from the public list in the genai-prices package; a [prices] table in tooltangle.toml overrides them.
  • Requests are paced and retried on rate limits. When the generator's quota runs out, the next model in generator_fallbacks takes over.

License

MIT

Metadata

Release files for tooltangle 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tooltangle 0.4.0
File Size Uploaded Provenance
tooltangle-0.4.0.tar.gz 43.2 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for tooltangle 0.4.0
File Interpreter ABI Platform
tooltangle-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 94.6 kB

Release files / tooltangle-0.4.0.tar.gz

Download URL tooltangle-0.4.0.tar.gz
Size 43.2 kB
Tags Source
SHA-256 checksum
How to use checksums
7b2ea14a17c4829cb965efd6c8d9ceed9b13905507140ec6a839bb15df8f3e1a
BLAKE2b-256 checksum
How to use checksums
5d6fd29a75e98fca6d9f43bd3ac243614204046a8c04333297cdc71714576065
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / tooltangle-0.4.0-py3-none-any.whl

Download URL tooltangle-0.4.0-py3-none-any.whl
Size 51.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ab269414e96f1c80fbfe0b7eee4b4a6bc93e58dec373a5cb59a698f03478d921
BLAKE2b-256 checksum
How to use checksums
4bad2fb562ad16a534f58746776590b250cdcaf7f372b8dcb8be52a9fcae2c19
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.7 {"installer":{"name":"uv","version":"0.12.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for tooltangle 0.4.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page