Skip to content

Commit 6aceae9

Browse files
Print the tools TELEGRAM_EXPOSED_TOOLS hides at startup
read-only mode unregisters tools silently, so the only way to learn which names the + allowlist accepts was reading the annotations in the source. Print them to stderr, like the other startup messages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 21d43b2 commit 6aceae9

3 files changed

Lines changed: 32 additions & 1 deletion

File tree

‎README.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,9 @@ TELEGRAM_EXPOSED_TOOLS=read-only+send_message,reply_to_message,send_file
261261
An unknown name in the allowlist aborts startup, so a typo cannot silently
262262
degrade into a narrower surface that looks like it worked.
263263

264+
At startup the server prints the names of the tools the mode hides to stderr;
265+
those are the names the `+` list accepts.
266+
264267
This is an MCP tool-surface restriction, not a Telegram session sandbox or
265268
reduced Telegram account permission. The Telegram session string still has its
266269
normal authority inside the server process; read-only mode only prevents

‎telegram_mcp/runner.py‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,15 @@ def main() -> None:
275275
# send_file is not exposed is a valid configuration, so the name check
276276
# has to see the full tool set.
277277
_runtime._apply_file_extension_overrides()
278-
_runtime._apply_exposed_tools_mode()
278+
hidden = _runtime._apply_exposed_tools_mode()
279+
if hidden:
280+
# These names are the menu for the "+" allowlist; without this line the
281+
# only way to find them is reading the tool annotations in the source.
282+
print(
283+
f"TELEGRAM_EXPOSED_TOOLS hides {len(hidden)} tool(s); list any of them "
284+
f"after '+' to expose it: {', '.join(sorted(hidden))}",
285+
file=sys.stderr,
286+
)
279287
_transcription.validate_transcription_config()
280288
_session_lock_shared() # fail loudly at startup on a bad toggle
281289
asyncio.run(_main())

‎tests/test_runner.py‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,3 +434,23 @@ def test_cli_host_and_port_flags(monkeypatch):
434434
runtime._configure_allowed_roots_from_cli(["--host", "0.0.0.0", "--port", "9000"])
435435
assert runtime._CLI_HOST == "0.0.0.0"
436436
assert runtime._CLI_PORT == 9000
437+
438+
439+
def test_main_prints_the_tools_exposure_hides(monkeypatch, capsys):
440+
"""The hidden names are what a user copies into read-only+<tool>."""
441+
monkeypatch.setattr(runner, "_configure_allowed_roots_from_cli", lambda *a, **k: None)
442+
monkeypatch.setattr(runner._runtime, "_apply_file_extension_overrides", lambda: None)
443+
monkeypatch.setattr(
444+
runner._runtime,
445+
"_apply_exposed_tools_mode",
446+
lambda: ["send_message", "export_chat_invite"],
447+
)
448+
monkeypatch.setattr(runner._transcription, "validate_transcription_config", lambda: None)
449+
monkeypatch.setattr(runner, "_session_lock_shared", lambda: None)
450+
monkeypatch.setattr(runner.asyncio, "run", lambda coro: coro.close())
451+
452+
runner.main()
453+
454+
err = capsys.readouterr().err
455+
assert "hides 2 tool(s)" in err
456+
assert "export_chat_invite, send_message" in err

0 commit comments

Comments
 (0)