tmux-python / tmux-python/libtmux-mcp
Migrate wait_for_text off the deprecated MCP Logging capability (SEP-2577)
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_logwith stderr logging through the existing module logger; drop the transport-closed suppression, which has no analogue for stdlib logging. - Drop the now-unused
ctxparameter from_compile_patterns. - Reword the
ctx.warningreference in thewait_for_textdocstring atwait.py#L559. - Rewrite the two tests to assert on captured log records rather than a recording context stub.
- Add a
MIGRATIONnote: MCP clients that displayed these three warnings in a log panel sourced fromnotifications/messagewill 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:
_metakeys minted atmiddleware.py#L313-L343(error_type,expected,suggestion) are bare names. That is legal — the prefix is optional — but reverse-DNS namespacing is the spec'sSHOULD, and bare names risk collision with future MCP-reserved keys.- SEP-2596 obliges Tier 1 SDKs to emit a runtime
DeprecationWarningwhen a deprecated feature is exercised.pyproject.tomlcurrently silencesDeprecationWarningforlibtmux.*,libtmux_mcp.*, andtests, which would mask exactly that signal once the SDK starts emitting it.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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