tmux-python / tmux-python/libtmux-mcp

Migrate wait_for_text off the deprecated MCP Logging capability (SEP-2577)

Open
#109 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
13
Forks
0
Avg merge
13h 52m
Merged PRs (30d)
4

Description

Summary

MCP has deprecated the protocol-level Logging capability. wait_for_text is the only tool in this server that uses it, via three _maybe_log calls that emit notifications/message. This server is stdio-only, so the specified migration path — log to stderr — is a direct swap onto the stdlib loggers the codebase already uses everywhere else.

Upstream status

MCP adopted a feature lifecycle and deprecation policy in SEP-2596, which defines Active/Deprecated/Removed states, a twelve-month minimum deprecation window, and a canonical deprecated features registry.

SEP-2577 (merged 2026-05-15) deprecates Roots, Sampling, and Logging, effective with the 2026-07-28 revision. The registry gives Logging's migration path as "Log to stderr for stdio transports; use OpenTelemetry for observability", and the rationale as "overlaps with stderr and OpenTelemetry". Earliest removal is the first revision released on or after 2027-07-28.

Separately, SEP-2575 removes logging/setLevel in the same revision and moves log level to a per-request _meta key, with a hard constraint: "servers MUST NOT emit notifications/message for requests that did not include this field". Under 2026-07-28 the current unconditional emissions are therefore not merely deprecated but non-conforming.

This is not urgent — the pinned SDK negotiates 2025-11-25 (mcp.types.LATEST_PROTOCOL_VERSION), where Logging is still Active — but the migration is small and there is no reason to carry it.

Affected code

All three call sites are in wait_for_text:

Site What it reports Already reaches the agent?
wait.py#L434 invalid regex Yes — identical text is raised as ExpectedToolError on the next line
wait.py#L730 history-limit trim-risk band No
wait.py#L838 no match before timeout Yes — WaitForTextResult.outcome == "timeout" plus elapsed

The helper is _maybe_log at wait.py#L124-L143; its own docstring names the wire method at wait.py#L133.

Why stderr rather than a result field

The two tests covering this behavior state the intent: the warnings exist "so MCP client log panels record the cause independent of the tool result" (test_pane_tools.py#L3997-L3999) and for "human log readers" (test_pane_tools.py#L4040-L4044). That is an operator/observability role, which is precisely what the spec redirects to stderr — and under stdio, MCP clients surface server stderr in those same log panels.

Promoting them to WaitForTextResult fields instead would contradict that model's stated design constraint: "Field count is load-bearing. outputSchema is re-sent on every request of every session, so a field that no agent branches on is a permanent tax." (models.py#L243-L246). Two of the three warnings are already redundant with the tool result or the raised error, and the third is documented as a caveat in the tool docstring at wait.py#L555-L561, where the agent meets it at tool-selection time.

Proposed change

  • Replace _maybe_log with stderr logging through the existing module logger; drop the transport-closed suppression, which has no analogue for stdlib logging.
  • Drop the now-unused ctx parameter from _compile_patterns.
  • Reword the ctx.warning reference in the wait_for_text docstring at wait.py#L559.
  • Rewrite the two tests to assert on captured log records rather than a recording context stub.
  • Add a MIGRATION note: MCP clients that displayed these three warnings in a log panel sourced from notifications/message will now receive them on stderr.

ctx.report_progress at wait.py#L114 is not affected. Progress notifications are not deprecated, and the 2026-07-28 draft explicitly keeps them flowing on the response stream of the request they relate to.

Out of scope

Checked and unaffected: no Roots, no Sampling, no includeContext, no HTTP+SSE transport, and no Dynamic Client Registration anywhere in src/ — the server is stdio-only at server.py#L468.

Two adjacent observations, deliberately not bundled here:

  • _meta keys minted at middleware.py#L313-L343 (error_type, expected, suggestion) are bare names. That is legal — the prefix is optional — but reverse-DNS namespacing is the spec's SHOULD, and bare names risk collision with future MCP-reserved keys.
  • SEP-2596 obliges Tier 1 SDKs to emit a runtime DeprecationWarning when a deprecated feature is exercised. pyproject.toml currently silences DeprecationWarning for libtmux.*, libtmux_mcp.*, and tests, which would mask exactly that signal once the SDK starts emitting it.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/libtmux_mcp/tools/pane_tools/wait.py, reading _maybe_log, its three call sites, _compile_patterns, and the wait_for_text docstring. Run the related tests in tests/test_pane_tools.py and inspect how they currently capture warnings. Done means stderr logging is covered by captured log-record assertions, the unused context plumbing is removed, and a MIGRATION note documents the logging destination change.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
api, backend
Issue type
Refactor
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
70/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.