Skip to content

chore(release): v0.22.1 — guard fixes - #69

Merged
kaiser-data merged 4 commits into
mainfrom
release/v0.22.1
Oct 1, 2026
Merged

kaiser-data merged 4 commits into
mainfrom
release/v0.22.1

Conversation

@kaiser-data

Copy link
Copy Markdown
Owner

Patch release: three fixes to the v0.22.0 action guard.

  • URL paths treated as file paths: in shell commands, a URL's path was read as a file path. For example, curl https://pypi.org/simple/ produced /simple, which could fail the write-root check. The whole URL is now removed first.
  • IPv6 literals misread: http://[::1]:8080/ was read as the host [, so it was blocked even with ::1 on the allowlist. Bracketed literals are now parsed. This failed safe: a false block, never a bypass.
  • Approval prompts could hang: a prompt with no answer could stall the tool call forever, because some clients never render the form. It now times out after 120 s and counts as a no. Clients that don't advertise elicitation (e.g. Claude Desktop) aren't prompted, and the refusal message says to allow the tool in the policy instead. The README documents which clients show the prompt.

Verified locally

  • The wheel builds as kitsune_mcp-0.22.1.
  • A fresh-venv install resolves mcp 1.30.0, and all 9 lean tools register.
  • 914 passed, 2 skipped (5 new tests); ruff is clean.

After merge: tag v0.22.1, then dispatch npm, then the MCP Registry.

🤖 Generated with Claude Code

kaiser-data and others added 4 commits October 1, 2026 21:37
In free-text arguments only the scheme and host of a URL were stripped
before path extraction, so `curl https://pypi.org/simple/` yielded the
path `/simple` and could fail the write-root check. Remove the whole URL
first.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The URL host pattern stopped at the first ":", so http://[::1]:8080/ gave the
host "[" and was blocked even with ::1 on network.allow. Match [..] literals
first and compare without the brackets. It failed safe (false block, never a
bypass), but it hit every IPv6 loopback or literal URL.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…an't ask

_ask_human awaited the elicitation with no limit, and some clients leave a
form unrendered with the request open, so the tool call stalled. Waiting now
ends after ASK_TIMEOUT (120 s) as a no. Clients that don't advertise the
elicitation capability are not sent a prompt; the call is refused as before,
and the message points to allowing the tool in the policy instead.

README: which clients show the prompt (Claude Code, Cursor yes; Claude
Desktop no, so ask acts as block there), and that a client-side hook can
answer for the user. CHANGELOG also notes the IPv6 fix from 214a35b.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Bump pyproject.toml, package.json and server.json (3 occurrences) to 0.22.1
and relock. CHANGELOG's [Unreleased] becomes [0.22.1].

Three guard fixes against v0.22.0, all in stage 1 / the approval path:
- a URL's path in a shell command was read as a file path (false write-root
  blocks, e.g. `curl https://pypi.org/simple/`);
- bracketed IPv6 literals were read as the host "[" (false blocks);
- an unanswered approval prompt could stall the call forever; it now times
  out after 120 s as a no, and clients without elicitation aren't prompted.

Verified: wheel builds as kitsune_mcp-0.22.1; fresh-venv install of
`kitsune-mcp[ws]` resolves mcp 1.30.0 and the stdio server registers all 9
lean tools; [::1] allowed when listed; 914 passed / 2 skipped; ruff clean.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

🫀 flight recorder py3.12 — newest last, each line ~15s apart

t+  0s log=573B frozen=0s | loadavg=0.34 0.30 0.13 2/261 2301 | psi_io=avg10=1.10 psi_mem=avg10=0.00 | MemTotal=15989M MemFree=12571M MemAvailable=14922M SwapTotal=3071M SwapFree=3071M Committed_AS=1993M
t+8s CLEAN EXIT — suite finished
collected test.log tail
am_with_nl_task_returns_empty PASSED [ 97%]
tests/test_v019_intent_router.py::TestInferArgsEnhanced::test_repo_param_extracts_owner_repo PASSED [ 97%]
tests/test_v019_intent_router.py::TestInferArgsEnhanced::test_repo_param_no_match_returns_empty PASSED [ 97%]
tests/test_v019_works_now.py::TestWorksNowScore::test_zero_config_official_stdio_is_highest PASSED [ 98%]
tests/test_v019_works_now.py::TestWorksNowScore::test_missing_creds_lowers_score PASSED [ 98%]
tests/test_v019_works_now.py::TestWorksNowScore::test_smithery_http_scores_lower_than_official_stdio PASSED [ 98%]
tests/test_v019_works_now.py::TestWorksNowScore::test_source_tier_ordering PASSED [ 98%]
tests/test_v019_works_now.py::TestWorksNowScore::test_stdio_beats_http_same_source PASSED [ 98%]
tests/test_v019_works_now.py::TestWorksNowScore::test_high_token_cost_penalized PASSED [ 98%]
tests/test_v019_works_now.py::TestWorksNowScore::test_score_never_exceeds_one PASSED [ 98%]
tests/test_v019_works_now.py::TestWorksNowScore::test_score_non_negative PASSED [ 98%]
tests/test_v019_works_now.py::TestWorksNowScore::test_unknown_source_gets_zero_tier_bonus PASSED [ 98%]
tests/test_v019_works_now.py::TestCandidateSortUsesWorksNow::test_works_now_score_exported_from_registry PASSED [ 99%]
tests/test_v019_works_now.py::TestCandidateSortUsesWorksNow::test_official_stdio_sorts_before_smithery_http PASSED [ 99%]
tests/test_v0201_fixes.py::TestAuthServerIdGuard::test_server_id_with_value_returns_error PASSED [ 99%]
tests/test_v0201_fixes.py::TestAuthServerIdGuard::test_server_id_with_value_does_not_write_env PASSED [ 99%]
tests/test_v0201_fixes.py::TestAuthServerIdGuard::test_server_id_logout_does_not_write_env PASSED [ 99%]
tests/test_v0201_fixes.py::TestAuthServerIdGuard::test_plain_env_var_all_caps_still_saves PASSED [ 99%]
tests/test_v0201_fixes.py::TestAuthServerIdGuard::test_env_var_no_hyphen_passes_guard PASSED [ 99%]
tests/test_v0201_fixes.py::TestAutoCompositeRank::test_relevant_beats_merely_operable PASSED [ 99%]
tests/test_v0201_fixes.py::TestAutoCompositeRank::test_works_now_breaks_ties_among_equally_relevant PASSED [ 99%]
tests/test_v0201_fixes.py::TestAutoCompositeRank::test_exact_name_match_dominates PASSED [100%]

================================ tests coverage ================================
_______________ coverage: platform linux, python 3.12.14-final-0 _______________

Coverage XML written to file coverage.xml
======================== 914 passed, 2 skipped in 7.39s ========================

recent kmsg
(quiet)

@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

🫀 flight recorder py3.13 — newest last, each line ~15s apart

t+  0s log=0B frozen=0s | loadavg=0.68 0.19 0.06 2/260 2444 | psi_io=avg10=14.35 psi_mem=avg10=0.00 | MemTotal=15989M MemFree=12508M MemAvailable=14848M SwapTotal=3071M SwapFree=3071M Committed_AS=2014M
t+12s CLEAN EXIT — suite finished
collected test.log tail
am_with_nl_task_returns_empty PASSED [ 97%]
tests/test_v019_intent_router.py::TestInferArgsEnhanced::test_repo_param_extracts_owner_repo PASSED [ 97%]
tests/test_v019_intent_router.py::TestInferArgsEnhanced::test_repo_param_no_match_returns_empty PASSED [ 97%]
tests/test_v019_works_now.py::TestWorksNowScore::test_zero_config_official_stdio_is_highest PASSED [ 98%]
tests/test_v019_works_now.py::TestWorksNowScore::test_missing_creds_lowers_score PASSED [ 98%]
tests/test_v019_works_now.py::TestWorksNowScore::test_smithery_http_scores_lower_than_official_stdio PASSED [ 98%]
tests/test_v019_works_now.py::TestWorksNowScore::test_source_tier_ordering PASSED [ 98%]
tests/test_v019_works_now.py::TestWorksNowScore::test_stdio_beats_http_same_source PASSED [ 98%]
tests/test_v019_works_now.py::TestWorksNowScore::test_high_token_cost_penalized PASSED [ 98%]
tests/test_v019_works_now.py::TestWorksNowScore::test_score_never_exceeds_one PASSED [ 98%]
tests/test_v019_works_now.py::TestWorksNowScore::test_score_non_negative PASSED [ 98%]
tests/test_v019_works_now.py::TestWorksNowScore::test_unknown_source_gets_zero_tier_bonus PASSED [ 98%]
tests/test_v019_works_now.py::TestCandidateSortUsesWorksNow::test_works_now_score_exported_from_registry PASSED [ 99%]
tests/test_v019_works_now.py::TestCandidateSortUsesWorksNow::test_official_stdio_sorts_before_smithery_http PASSED [ 99%]
tests/test_v0201_fixes.py::TestAuthServerIdGuard::test_server_id_with_value_returns_error PASSED [ 99%]
tests/test_v0201_fixes.py::TestAuthServerIdGuard::test_server_id_with_value_does_not_write_env PASSED [ 99%]
tests/test_v0201_fixes.py::TestAuthServerIdGuard::test_server_id_logout_does_not_write_env PASSED [ 99%]
tests/test_v0201_fixes.py::TestAuthServerIdGuard::test_plain_env_var_all_caps_still_saves PASSED [ 99%]
tests/test_v0201_fixes.py::TestAuthServerIdGuard::test_env_var_no_hyphen_passes_guard PASSED [ 99%]
tests/test_v0201_fixes.py::TestAutoCompositeRank::test_relevant_beats_merely_operable PASSED [ 99%]
tests/test_v0201_fixes.py::TestAutoCompositeRank::test_works_now_breaks_ties_among_equally_relevant PASSED [ 99%]
tests/test_v0201_fixes.py::TestAutoCompositeRank::test_exact_name_match_dominates PASSED [100%]

================================ tests coverage ================================
_______________ coverage: platform linux, python 3.13.15-final-0 _______________

Coverage XML written to file coverage.xml
======================== 914 passed, 2 skipped in 9.31s ========================

recent kmsg
(quiet)

@kaiser-data
kaiser-data merged commit a5012a9 into main Oct 1, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant