This release adds 2 notable features for engineering teams evaluating rollout.
✓ No known CVEs patched in this version
Topics
+9 more
Affected surfaces
Summary
AI summaryUpdates Shared-validator hardening, Added — tests, and Response-shape robustness across a mixed release.
Full changelog
[1.1.1] - 2026-07-03
Fixed — deep-audit round 2: all-tool adversarial fuzz
A mechanical fuzz pass (every tool × every parameter × 17 garbage
values ≈ 5,000 calls) found 1,051 violations of the project's core
error contract ("tools never raise — always return a JSON envelope").
Root cause: the shared validators assumed string inputs; any non-string
type (int, list, bytes, bool, float) sent by a loose-JSON MCP client
for a string field crashed with raw TypeError/AttributeError across
essentially all 59 tools. Prior releases fuzzed only tracker tools, so
the class went unseen.
Shared-validator hardening (fixes ~all 1,051 at the root)
_require_non_empty— rejects non-string types with a clear
"must be a string (got int)" envelope instead of passing them
through to crash downstream._validate_id_shape,_validate_date,_validate_phone,
_validate_email,_validate_length,_validate_area_code,
_validate_pool_size— explicit type guards._clamp_per_page— coerces numeric strings ("250"), returns 1 on
garbage (previously raw TypeError from the<comparison).- New
_clamp_page— replaces 13 inlinemax(1, page)call sites
that raised TypeError on string/None pages. _clean_tag_list— non-list input returns [] with a warning
(mirrors_tag_names_from; previously iterated ints → TypeError).update_call/update_form_submission/create_form_submission
—tagstype-checked as list beforelen().call_eligibility_check— threshold coerced before the< 0
comparison (None/"60" previously raised TypeError).
Behavior kept honest with docstrings
delete_tag(tag_id=812)(int form) now works — the docstring has
promised "accepts string or numeric forms" since v0.6.1 but int
input crashed with TypeError. get_tag/update_tag same.search_calls_by_number(phone_number=4125551234)— a phone number
arriving as a JSON number is now coerced instead of crashing.
Wire safety
- NaN/Infinity rejected in
valueparams (update_form_submission,
create_form_submission,update_sms_thread).json.dumps
serializes non-finite floats as bareNaN/Infinitytokens —
invalid JSON on the wire to CallRail.
Response-shape robustness (client.py)
paginate()— a malformed response with a STRING where the items
array belongs wouldyield fromsingle characters; consumers then
crashed on'str'.get(). Non-list items now stop pagination with
a warning.resolve_account_id()—accountsas a dict raised raw
KeyError: 0; now a catchable CallRailError.
Added — tests
tests/test_fuzz_all_tools.py— the full adversarial matrix
(59 tools × every param × 17 garbage values, ~5k calls) is now a
permanent regression test pinning the never-raise invariant.- 6 targeted regressions (paginate string-items, dict accounts,
NaN value ×3 tools, int tag_id, JSON-number phone, clamp helpers). - Tests 320 → 385.
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
About pghdma/callrail-mcp
CallRail REST API v3 integration with 49 tools for call tracking, form submissions, transcripts, full CRUD on tags/trackers/companies/users, plus agency-specific aggregation tools (`usage_summary` for per-client cost attribution, `compare_periods` for MoM deltas, `bulk_update_calls` with dry-run, `spam_detector`, `call_eligibility_check` for Google Ads conversion debugging)
Related context
Beta — feedback welcome: [email protected]