Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

agent-handshake
Check that what your agent is told matches the tools it actually has

PyPI CI License Python 3.11+

An agent's instructions and tool descriptions often name its tools, and nothing checks that those names still exist. When a tool is renamed or an MCP server adds a prefix, the model calls a tool it does not have, gets an error and often just gives up.

agent-handshake reads the request your agent sends to the model and reports where the text and the tools disagree, without calling a model.

Example

The instructions say mem_ssh; the MCP server registered the tool as mcp__mem__mem_ssh (a real case):

{
  "model": "claude-haiku-4-5",
  "max_tokens": 1024,
  "system": "Use `mem_ssh` to connect to Tower.",
  "messages": [{"role": "user", "content": "Is Tower up?"}],
  "tools": [{"name": "mcp__mem__mem_ssh", "input_schema": {"type": "object"}}]
}
$ handshake check request.json
3 rules enabled. Checked request.json (Anthropic Messages): 1 tools, 2 text segments.
✗ HS002 request.json (system): Text names `mem_ssh`; no tool has that exact name. Did you mean `mcp__mem__mem_ssh`?
1 findings (1 errors, 0 warnings).

Terminal demo: handshake check reports HS002 for a system prompt that says mem_ssh while the only tool is mcp__mem__mem_ssh, and exits 1; on a request whose tool description names webpage_read it reports an HS001 warning and exits 0, and exits 1 with --strict.

Install

pip install --pre agent-handshake

Status: beta (1.0.0b2). --pre is needed while 1.0 is in beta: without it pip installs 0.10.0, an older and different tool (see History).

Get request.json

request.json is the request body as sent to the model, in OpenAI Chat Completions, OpenAI Responses or Anthropic Messages format. If your code calls an SDK, put the call's arguments in a dict, save it as JSON and pass the same dict to the call.

OpenAI:

import json

from openai import OpenAI

request = {
    "model": "gpt-4.1",
    "messages": [
        {"role": "system", "content": "Use `get_weather` for questions about the weather."},
        {"role": "user", "content": "Is it raining in Lisbon?"},
    ],
    "tools": [
        {
            "type": "function",
            "function": {
                "name": "get_weather",
                "parameters": {
                    "type": "object",
                    "properties": {"city": {"type": "string"}},
                    "required": ["city"],
                },
            },
        }
    ],
}
with open("request.json", "w") as f:
    json.dump(request, f, indent=2)

response = OpenAI().chat.completions.create(**request)

Anthropic:

import json

from anthropic import Anthropic

request = {
    "model": "claude-sonnet-5-5",
    "max_tokens": 1024,
    "system": "Use `get_weather` for questions about the weather.",
    "messages": [{"role": "user", "content": "Is it raining in Lisbon?"}],
    "tools": [
        {
            "name": "get_weather",
            "input_schema": {
                "type": "object",
                "properties": {"city": {"type": "string"}},
                "required": ["city"],
            },
        }
    ],
}
with open("request.json", "w") as f:
    json.dump(request, f, indent=2)

response = Anthropic().messages.create(**request)

Run

handshake check request.json

Each finding names the rule, where the text is (its path in the request) and the closest real name. Errors make the command exit with code 1; warnings do not, unless --strict is given. --config points to a handshake.yaml or pyproject.toml; without it, the one in the current directory is used.

How it works: a request body as sent to the model is read into instructions, tools and response schema; rules HS001 to HS003 compare the names in the text with the tools; findings are printed, and errors make the check exit 1. No LLM calls.

In pytest

agent_handshake.check(body, config=None) runs the same rules on a dict and returns the findings (since 1.0.0b2).

import json

from agent_handshake import check


def test_request_matches_tools():
    with open("request.json") as fp:
        body = json.load(fp)  # or build it with your agent's own code
    assert not [f for f in check(body) if f.severity == "error"]

A complete example: examples/pytest/.

GitHub Actions

- run: |
    pip install --pre agent-handshake
    handshake check request.json

Rules

rule finds default
HS001 a tool named in a form that can only be a tool, that the request does not have on, warning
HS002 a tool name that is almost right (case, -/_, prefix, suffix) on, error
HS003 an argument the tool's schema does not have on, error
HS004 an example value outside a field's allowed values off
HS005 example JSON keys that do not match the call or response format off
HS007 prose that may name a tool the request does not have off

Rule pages: docs/rules/. Turning rules off, changing their severity and ignoring names: docs/configuration.md.

Known limitations

HS001 can warn about names that are not missing tools:

  • Code Mode declarations. A description that declares functions as tools.x({ ... }) is read as calls to x.
  • Negative forms. "Never attempt 'apply_patch' ... they don't exist" is read as naming apply_patch.
  • Placeholders. "I'm going to use tool_name" is read as naming tool_name.

HS001 findings are warnings, so they do not fail the check unless --strict is used. To silence them, ignore the names or turn the rule off in handshake.yaml (or [tool.handshake] in pyproject.toml):

ignore:
  - tool_name                  # never report this name
  - {rule: HS001, ref: apply_patch}
rules:
  HS001: {enabled: false}      # or turn HS001 off

Benchmark

Measured once on the frozen configuration, on three sets that share no project: dev (used to build the rules), holdout and holdout-2 (never used to tune them). Recall is over real, fixed mismatches from the projects' history; precision is over hand-labelled findings on the current code of open-source agents. Small samples are marked by their size.

dev holdout holdout-2
recall cases (projects) 25 (12) 14 (10) 4 (4)
catchable cases caught by rules on by default 8/11 4/11 2/2
precision corpus: repositories (requests) 4 (13) 3 (12) 4 (16)
HS001 precision 13/14 0/24 no findings
HS002 precision 11/11 no findings 5/5
HS003 precision 2/2 no findings no findings
known mismatches in the corpus found 22/32 6/24 6/17

The holdout HS001 findings are Code Mode function declarations of one project (tools.x({ ... })), which HS001 reads as calls. Details and the method: benchmarks/README.md; run it with make benchmark.

Roadmap

Capturing requests from a running agent, so there is no request.json to write by hand (not started).

Contributing

See CONTRIBUTING.md.

License

Apache 2.0. See LICENSE.

History (0.x)

Releases up to 0.10.0 on PyPI are a different, closed tool: contract tests between agents. Its code is in tag contract-testing-final; see why it was closed.

Metadata

Release files for agent-handshake 1.0.0b2

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

Source distribution (sdist)

Source distribution for agent-handshake 1.0.0b2
File Size Uploaded Provenance
agent_handshake-1.0.0b2.tar.gz 1.8 MB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for agent-handshake 1.0.0b2
File Interpreter ABI Platform
agent_handshake-1.0.0b2-py3-none-any.whl Python 3 none any Details

Total release size: 1.8 MB

Release files / agent_handshake-1.0.0b2.tar.gz

Download URL agent_handshake-1.0.0b2.tar.gz
Size 1.8 MB
Tags Source
SHA-256 checksum
How to use checksums
a67e6cdce727176051c7dca1663e311bcd2aa8eacc4a46ad3a88984e6b0303c3
BLAKE2b-256 checksum
How to use checksums
c7ba8d5e83cf85c7c400d6ae89ffb84fd3fec2ec215ed415057dc92013b97d59
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Release files / agent_handshake-1.0.0b2-py3-none-any.whl

Download URL agent_handshake-1.0.0b2-py3-none-any.whl
Size 39.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7b0714ed78e6c10b00be88fdf797ff81984bbafff2f385436e2b139c72445ccb
BLAKE2b-256 checksum
How to use checksums
984757883ca67dca77fa4b48cb9bce0174fabb7a5677db10c8960ca60e530250
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

Security for agent-handshake 1.0.0b2

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for agent_handshake-1.0.0b2.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 19, 2026.

Transparency log

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