Version: 2026-06-06.2
Gateway/client runtime compatibility:
TOOLS.mdhash sync and runtime version handshake are separate checks.- The authoritative freshness check for
TOOLS.mdis the content hash, not this human-readableVersion:line. - Treat this
Version:line only as a quick visual marker for humans and logs. TOOLS.mdsync tells the agent to refresh instructions.ws hello/hello_ackcheckspackage_version,protocol_version, andcapabilities.- If protocol major versions differ, gateway transport is blocked until the older side is upgraded.
- If you receive a system notice or structured record about gateway/client version mismatch, treat it as operational priority before continuing collaboration or live-relay work.
This MCP server currently exposes the following tools.
Browser tools:
browser_list_attached_instancesbrowser_list_tabsbrowser_attach_active_tabbrowser_attach_tabbrowser_detach_tabbrowser_recording_startbrowser_recording_stopbrowser_recording_statusbrowser_openbrowser_reloadbrowser_clickbrowser_fillbrowser_inject_scriptbrowser_pressbrowser_wait_forbrowser_wait_for_urlbrowser_consolebrowser_errorsbrowser_network_failuresbrowser_clear_logsbrowser_dombrowser_computed_stylebrowser_screenshotbrowser_close
Browser runtime rule:
- Browser tools now have two backends:
- attached browser tab backend for a real user Firefox or Chrome session
- isolated Playwright Chromium backend
browser_openis only for the isolated Playwright backend.browser_list_attached_instances,browser_list_tabs,browser_attach_active_tab,browser_attach_tab, andbrowser_detach_tabare for the attached browser backend.- after a tab is attached for the current session,
browser_click,browser_fill,browser_inject_script,browser_press,browser_wait_for,browser_wait_for_url,browser_dom,browser_computed_style,browser_reload,browser_screenshot,browser_console,browser_errors,browser_network_failures,browser_clear_logs, andbrowser_closeuse that attached browser tab without requiringbrowser_open browser_recording_start,browser_recording_stop, andbrowser_recording_statusare also for the attached browser backend- browser recordings are written under
.mcp-xchange/web/{tab-title-slug}-{timestamp}/ - each recording bundle contains
session.json,timeline.ndjson,pages/,network/, andconsole/ - inside
network/, useindex.ndjsonas the thin search index andrequests/{seq}-{request_id}/as the per-request artifact bundle - otherwise those tools use the Playwright backend after
browser_open - If browser tools fail because the Playwright browser runtime is missing, install it with
tellymcp browser install. - Do not stop at the installation error itself. Install the browser runtime first, then retry the browser tool.
Terminal prompt scan rules:
- gateway prompt scan is driven by live client lifecycle, not by manual menu entry
- the scanner works on the tail of the captured terminal buffer
- the main blocker signal is:
- a contiguous numbered choice block like
1.,2.,3. - nearby action-hint language such as
press,input,choose,enter,esc,yes,no
- a contiguous numbered choice block like
- exact footer text is helpful but not required
- when a blocker notice contains inline buttons, those buttons intentionally send only:
- a digit
1..N EnterEsc
- a digit
- do not assume marker navigation
- do not reinterpret those buttons as alternate hotkeys like
(y)or(p)
Collaboration tools:
list_gateway_sessionslist_xchange_recordsget_xchange_recordmark_xchange_record_readsend_partner_notesend_partner_file
Utility tools:
get_file_listget_filerefresh_tools_markdown
Gateway-first runtime model:
- agents authenticate the gateway HTTP/WS transport through
GATEWAY_AUTH_TOKENand register into their gateway scope throughGATEWAY_SCOPE_TOKEN - Telegram no longer links sessions with pair codes
/menuin the gateway bot shows available remote consoles directly- one running agent console is one logical session/console target
Required agent practice:
- when the user asks to contact another agent, inspect consoles through
list_gateway_sessions - when the user asks to work with Telegram-linked human interaction, use the current console
session_idexplicitly - in gateway mode,
session_idmeans the live console id from-s - do not use workspace-derived ids like
project-abc12345for gateway routing - do not use
cwdto route to a console through the gateway - if you need a
session_idand do not know it yet, calllist_gateway_sessionsand use canonicalsession_idfrom the matching live console - do not ask the user for the live console id when it can be resolved from
list_gateway_sessions - assume gateway is the only user-facing control plane
- do not mention pair codes,
/link, admin menus, or session pairing unless the user is explicitly asking about legacy behavior
Preferred order for cross-console work:
- Call
list_gateway_sessions. - Choose the correct target by
session_label,node_id,client_label, or canonicalsession_id. - Use:
send_partner_notesend_partner_file- browser tools
with explicit
session_idfor the current console or explicit target routing fields for another console.
Preferred file-return flow for MCP chat clients:
- Resolve the selected live console and keep its canonical
session_id. - If the user refers to an existing managed file without an exact path, call
get_file_listand select the matchingfile_path. - If creating a screenshot now, call
browser_screenshotand keep its returnedfile_path. - For an exact Markdown, source-code, or other UTF-8 project path, call
get_file(type = "text"); arbitrary project files are not discovered byget_file_list. - For binary files, call
get_filewith the selected or newly returnedfile_path; default totype = "url". - If the chat cannot fetch an image URL, repeat the same call with
type = "image"for a native inline MCP image block. - If a host such as Claude.ai replaces the native block with
[image], retry withtype = "base64"; this host-independent fallback returns the complete JSON, including rawdata, in a regular MCP text block. - If the user asks for the most recent existing screenshot,
get_file(selector = "latest_screenshot")is the direct shortcut.
Purpose:
- Save compact reusable console context.
Input:
session_id?session_label?task?summaryfiles?decisions?risks?
Output:
savedsession_idupdated_athas_bindingLegacy field name. Read it as: whether this console currently has an active Telegram route through the gateway.
Purpose:
- Rename only the human-readable console title/label.
Rules:
- this changes only the human-readable title
- it does not change
session_id - it does not change routing or saved context
Input:
session_id?title
Output:
renamedsession_idsession_labelupdated_at
Purpose:
- Read saved console context and routing status.
Recommended use:
- setup/debug only
- use this while diagnosing state or inspecting saved metadata
- do not call this in the normal processing path unless you are diagnosing state
Input:
session_id?
Output:
session_idexistshas_bindingLegacy field name. Read it as: whether this console is currently reachable from Telegram through the gateway.status_messagecontext?binding?Legacy field name. If present, this is the current Telegram route metadata for the console.terminal?Terminal runtime metadata for the console.
Purpose:
- Run safe, read-only health checks for one console through the same gateway route used by normal tools.
- Diagnose configuration, runtime state storage, PTY state, version/protocol metadata, and gateway-to-client relay without returning secrets or raw connection strings.
- Redis is probed only for gateway/
bothruntimes. On a client, therediscompatibility check reports that Redis is not required and process-local state is active. - Return a structured degraded result when relay fails instead of a deeply nested backend exception.
Input:
session_id?In gateway mode, use the canonicalsession_idreturned bylist_gateway_sessions.
Output:
statusokdegraded
checked_atsession_idruntimemodepackage_versionprotocol_versionnode_id?
checksconfigurationredissession_storeterminalgateway_configurationrelay
Each check contains only status and a redacted human-readable message.
Purpose:
- List managed files available in a selected console workspace before calling
get_file. - Return exact
file_pathvalues accepted byget_file.
Input:
session_idsource?telegram-uploadbrowser-screenshotpartner-artifact
limit?- default:
50 - maximum:
200
- default:
Output:
totalfiles[]file_pathfilenamemimetypesourcesize_bytes?created_at
Behavior:
- results are ordered newest first
totalis the number of matching managed files beforelimitis applied- this tool lists TellyMCP-managed Telegram uploads, browser screenshots, and partner artifacts; it does not recursively enumerate arbitrary source files in the workspace
- in gateway mode, always pass the canonical
session_id = client_uuid:local_session_id - pass the chosen
files[].file_pathunchanged toget_file
Purpose:
- Return the actual content of a file from a selected console workspace to an MCP chat client.
- URL mode creates a bounded temporary gateway copy; text and base64 modes relay content directly.
Input:
session_idtype?url(default)image(native inline image plus download URL)text(native UTF-8 MCP text content)base64(fallback)
- exactly one of:
file_pathselector = "latest_screenshot"
Output:
typedata- short-lived HTTPS URL when
type = "url" - short-lived HTTPS URL when
type = "image"; the actual pixels are in the native MCP image content block - decoded UTF-8 file content when
type = "text" - base64 payload when
type = "base64"
- short-lived HTTPS URL when
mimetypefilenamesize_bytesexpires_at?
Behavior:
- in gateway mode, always pass the canonical
session_id = client_uuid:local_session_idreturned bylist_gateway_sessions file_pathmay be the exact path returned bybrowser_screenshot, so the human does not need to know generated filenamesselector = "latest_screenshot"selects the newest file metadata withsource = "browser-screenshot"for that session- file reads are limited to the selected console workspace, including after symlink resolution
- exact project-relative paths such as
README.mdorsrc/test.tsare accepted even thoughget_file_listonly discovers managed artifacts - text mode returns the decoded file directly as a native MCP text content block and rejects invalid UTF-8 or binary NUL content
- source extensions such as
.tsand.tsxuse text MIME overrides instead of the generic MPEG/octet-stream mappings - sensitive paths including live
.envfiles, credential stores, private-key extensions, and secret directories are rejected server-side; documented.env.example/.env.sample/.env.templatefiles remain readable - URL mode uploads the file as a binary stream to
.tellymcp/tmp/file-linkson the gateway and returns a link valid for 10 minutes - temporary-link filenames are sanitized before storage and download headers: control and filesystem-reserved characters are replaced, while a safe basename is preserved
- image mode is explicit:
structuredContent.typeisimage,structuredContent.datacontains the URL, and the native top-level MCPimageis the first content block without duplicating base64 into structured output - image mode accepts only
image/*files up to the safe native-inline limit; useurlfor larger images - URL links allow up to three GET downloads; HEAD requests do not consume the limit
- if URL fetching fails for an image, retry with
type = "image" - base64 mode is the compatibility fallback for hosts that replace native images with
[image]or hidestructuredContent: it returns the complete JSON payload, including raw base64 indata, as a regular MCP text block and does not emit a native image - base64 mode is bounded by the final MCP response body limit
- do not attempt to bypass the sensitive-file policy or retrieve renamed secrets
Screenshot examples:
- "take a screenshot and send it": call
browser_screenshot, then pass its returnedfile_pathtoget_file - "send the latest screenshot": call
get_filewithselector = "latest_screenshot"
Purpose:
- Download the canonical
TOOLS.mdfrom the configured gateway. - Return the canonical instructions in a hash-based form so the agent can refresh without mandatory local file writes.
Input:
session_idknown_hash?
Output:
sourcesession_id?current_hashchangedcontent?bytes
Behavior:
- if
GATEWAY_PUBLIC_URLis configured, the tool fetchesGET /api/gateway/tools-md - if no gateway is configured, the tool falls back to the installed package copy
- the canonical source is the installed gateway package copy, not an arbitrary current working directory
- in gateway mode, routing to the target console is done only by explicit canonical
session_id = client_uuid:local_session_id cwdis workspace metadata for the target console after routing succeeds; it is not a routing key- if the live console id is not already known, call
list_gateway_sessionsfirst and usesession_id - prefer hash-based refresh:
- pass
known_hashfrom the last applied TOOLS state - if
changed=false, keep current instructions - if
changed=true, read and apply returnedcontent
- pass
- there is no local file-save mode in the normal flow
- do not create or rely on workspace
TOOLS.mdcopies
Purpose:
- Remove saved session context and related per-session state for the console.
Input:
session_id?
Output:
clearedsession_idcleared_pairingCompatibility field name. Treat it as legacy cleanup metadata, not as an instruction to use pairing.
Purpose:
- List all consoles currently known to the configured gateway.
- Merge:
- connected consoles from gateway WS presence
- registered project consoles from the gateway database
- Use this before direct cross-console communication outside one collab project.
- Use this to resolve the authoritative live console id for other gateway-routed tools.
Input:
client_uuid?connected_only?
Output:
totalsessions[]session_idlocal_session_idclient_uuidlocal_session_idsession_label?client_label?telegram_username?telegram_display_name?bot_username?node_id?package_version?project_uuids?project_names?connectedregistered
Rules:
- use this when the user asks to contact a session that is not the current linked partner and not necessarily part of the current collab project
- for direct gateway-wide routing, resolve the target from this list and then call:
send_partner_note- or
send_partner_file
- in that direct mode, pass:
target_client_uuidtarget_local_session_id
- do not invent
target_session_idfor direct gateway-wide routing - prefer connected sessions for direct gateway-wide messaging
- if a session is not connected, do not assume direct delivery will succeed
Purpose:
- List structured
.mcp-xchangerecords from the local sqlite store for the current session. - Use this as the first lookup path for partner notes, local handoffs, unread collaboration items, and follow-up work.
Input:
session_id?status?newreadarchived
category?partner_notelocal_handoff
direction?incomingoutgoinglocal
limit?
Output:
session_idtotalrecords
Each record includes:
record_idcategorydirectionstatuskind?summaryaction_desctoolsattachmentssource_*andtarget_*routing fields when availableproject_uuid?project_name?requires_reply?expected_reply?in_reply_to?created_atupdated_atread_at?
Rules:
- use this first when the session is nudged for partner collaboration or local handoff work
- prefer
status = "new"for fresh work - after selecting the relevant record, call
get_xchange_record
Purpose:
- Read one structured
.mcp-xchangerecord in full. - This gives the canonical
body_text,action_desc,tools, attachments, and routing metadata for the next step.
Input:
session_id?record_id
Output:
session_idrecord
Rules:
- after
list_xchange_records, call this on the chosenrecord_id - trust
action_descandtoolsover old markdown-index habits - use
body_textas the structured note content; only open the note file directly if you need the raw markdown artifact
Purpose:
- Mark a structured
.mcp-xchangerecord as read after consuming it.
Input:
session_id?record_id
Output:
session_idrecord_idupdated
Rules:
- do this after you have consumed
body_text, attachments, and next-step instructions - do not mark a record read before you have actually processed it
Purpose:
- Send a structured collaboration note to another session.
- Write a note file into the partner workspace under
.mcp-xchange/shares/. - Create a structured xchange record in the partner sqlite store.
- Optionally copy listed artifacts from the current workspace into the partner
.mcp-xchange/shares/files/<share_id>/. - Create a structured xchange wake-up for the partner agent through the normal gateway delivery path.
Input:
session_id?target_session_id?target_client_uuid?target_local_session_id?project_uuid?kindsharequestionreplyrequesthandoff
summarymessageexpected_reply?requires_reply?in_reply_to?artifacts?
Output:
session_idpartner_session_idkindshare_idnote_pathxchange_record_idcopied_artifactsinbox_message_idLegacy compatibility field name for delivery bookkeeping.requires_reply
Required agent practice:
- use this tool whenever the current session is linked to a partner and you need to communicate project-relevant information
- prefer structured note kinds over ad-hoc Telegram chat wording
- if the user asks things like:
- "ask backend what APIs it has"
- "tell frontend what changed"
- "send the error to your teammate"
- "tell the other agent what's new"
then the correct path is
send_partner_note
How to find the partner correctly:
- never guess the target session from labels, menu text, or memory
- there are three valid targeting modes:
- local linked mode:
- call
get_session_context - use
context.linked_session_id
- call
- project / collab mode:
- use the explicit
target_session_idthat came from the project note, Telegram prompt, or task context - if available, also pass
project_uuid
- use the explicit
- direct gateway-wide mode:
- call
list_gateway_sessions - resolve the exact target from the returned
client_uuid+local_session_id - pass them as:
target_client_uuidtarget_local_session_id
- call
- local linked mode:
- for project replies, do not fall back to
linked_session_id - if
target_session_idis explicitly known, it has priority over any linked partner - if
target_client_uuid+target_local_session_idare explicitly known, they define direct gateway-wide routing and have priority over linked partner memory - if neither
target_session_idnorlinked_session_idis available:- do not retry blindly
- tell the user the target session is unknown
- ask them to relink locally or reopen the project target flow in Telegram
Minimal safe sequence:
- Call
get_session_context. - Resolve the target:
- prefer explicit
target_session_idfor project/collab work - for gateway-wide direct work, use explicit
target_client_uuid+target_local_session_id - otherwise use
linked_session_idfor local partner work
- prefer explicit
- Decide the correct note kind:
question,reply,share,request, orhandoff. - Call
send_partner_note.
File transfer rule:
- if the requested result is an existing local file, send the actual file as an artifact
- prefer
send_partner_filefor this - if you must stay on
send_partner_note, useartifact_refs - do not treat a sentence like "I am sending sample.txt" as completed delivery
- mentioning a filename in
messageis not enough; the receiver must get the real artifact
Minimal expectation for file delivery:
- include the normal text fields:
summarymessage
- and also attach the file in
artifact_refs - if you can read the file locally, prefer embedding it immediately instead of asking the receiver to reconstruct it from text
Reply rule for project asks:
- when a collaboration note or
.mcp-xchange/shares/*.mdnote contains:Reply message_uuid: ...Reply target_session_id: ...Reply project_uuid: ...Reply target_client_uuid: ...Reply target_local_session_id: ...or aReply Paramssection with the same data
- then reply with those exact values
- if
in_reply_tois available, preferReply message_uuid - if only the note
share_idis available, gateway now also accepts that value inin_reply_to - do not use
linked_session_idfor that reply - if direct reply params contain
target_client_uuid+target_local_session_id, do not replace them withtarget_session_id - if the note contains an
Action Requiredsection, treat it as mandatory execution guidance, not as optional commentary - do not stop after local analysis when
Action Requiredsays to reply - when
Reply Paramsare present, prefer an explicitsend_partner_note(...)call with those exact values
Mandatory completion rule:
- if a partner note says
requires_reply: true, or containsReply Params, or containsAction Requiredtelling you to reply:- your task is not complete after local analysis
- your task is not complete after writing an explanation into chat
- your task is complete only after
send_partner_note(...)succeeds
- if
send_partner_note(...)fails:- treat that as an active blocker
- report the failure
- retry only with corrected routing parameters
- do not pretend the reply was sent
- never replace a required reply with:
- a local summary
- a chat-only explanation
- "I prepared the answer"
- "I am ready to answer"
Execution order for required replies:
- Read the partner note and extract
Reply Params. - Do the requested inspection or work locally.
- Call
send_partner_note(...)with the explicit routing params. - Only after the tool succeeds, say that the reply was sent.
Purpose:
- Send a real existing local workspace file to another session.
- Wrap the normal partner note flow and attach the file artifact automatically.
Input:
session_id?target_session_id?target_client_uuid?target_local_session_id?project_uuid?cwd?file_pathkind?summary?message?expected_reply?requires_reply?in_reply_to?
Rules:
- use this when you already have a real file like
sample.txt,report.pdf,page.png,dump.log - prefer this over trying to hand-build
artifact_refs file_pathmust point to a real file inside the current session workspace- do not replace this with a plain note that only mentions the filename
Minimal file-delivery rule:
- Confirm the file exists in the local workspace.
- Call
send_partner_file(...). - Only after the tool succeeds, say that the file was sent.
Routing priority:
- explicit
Reply target_session_id - explicit
Reply target_client_uuid+Reply target_local_session_id - explicit
target_session_idfrom the current project/task context - explicit
target_client_uuid+target_local_session_idfromlist_gateway_sessions - local
linked_session_id
For project/collab traffic, step 5 is fallback only when no explicit target exists.
Canonical example for a project reply:
{
"session_id": "backend",
"in_reply_to": "29587d1e-2aaf-4663-a87c-a51581176cb4",
"target_session_id": "9df1ad2e-88d4-4b01-b706-72d60f79f7cb",
"project_uuid": "42a8ff66-....",
"kind": "reply",
"summary": "Описание REST API",
"message": "Подготовил краткое описание REST API и основных endpoints."
}How the receiving agent must react:
- wake-ups now point to the unified
.mcp-xchangeflow - if the wake-up says things like:
проверь xchange recordstelegram_messagepartner_notethen do not start with a legacy inbox-specific tool
- instead:
- call
list_xchange_records - identify the newest relevant record by category:
telegram_messageif the sender is a human from Telegrampartner_noteif the sender is another agent
- call
get_xchange_record - read
body_text,action_desc,tools, and any attachments
- call
- reply according to the record category:
telegram_message-> answer withnotify_telegrampartner_note-> answer withsend_partner_note
What to do after reading the note:
- do not stop at summarizing the note back to the user
- choose behavior from
kind - if the note requires a reply, do the work and send the reply before declaring completion
Required behavior by note kind:
question- answer the partner's question
- if needed, inspect your workspace first
- then send a
reply
reply- use the answer in your current work
- continue execution
share- update your assumptions/context
- continue execution if the update affects your task
request- treat it as an actionable task from the partner
- start doing the requested work unless it is clearly impossible or blocked
- when done, respond with
replyorhandoff
handoff- treat it as material to consume and continue from
- if the handoff asks for a concrete follow-up, start that work
Default rule:
- if the partner note contains a concrete ask, task, or follow-up, begin executing it
- do not merely report "I read the note" unless the user explicitly asked only for inspection
- if the note requires a reply, do not stop at "I prepared the answer"
- send the actual
replythroughsend_partner_note
Recommended mapping:
question- ask for API summaries
- ask "what's new?"
- ask for error details
reply- answer a previous
question - set
in_reply_to
- answer a previous
share- communicate what changed without asking for action
request- ask the partner to do or verify something
handoff- transfer a result, contract, artifact, or completion note
Note contract:
- the service writes one note per message to:
.mcp-xchange/shares/<share_id>.md
- the structured source of truth is the local sqlite xchange store:
.mcp-xchange/xchange.sqlite3
- artifacts are copied into:
.mcp-xchange/shares/files/<share_id>/...
What the source agent should include:
summary- one-line high-signal description
message- the actual explanation, question, request, or handoff content
expected_reply- when you need a concrete answer back
artifacts- paths to files from the current workspace that should be copied to the partner
Artifact rule:
- do not attach raw source files to partner notes
- this includes files such as:
.ts,.tsx,.js,.jsx.go,.py,.java,.rs,.php.html,.css,.scss,.vue,.svelte- shell scripts and similar source-like files
- instead send:
- API summaries
- endpoint signatures
- OpenAPI/spec files
- sample payloads
- logs
- screenshots
- Markdown notes
If a source file seems necessary, summarize the relevant contract in the note instead of copying the full implementation file.
What to communicate:
- API summaries and endpoint changes
- what changed since the last handoff
- current errors and how to reproduce them
- payload examples and specs
- relevant git context from the current workspace
- changed files
- branch-specific behavior
- important diffs or migration notes
If you mention git changes:
- summarize them in
message - attach the concrete files only when the partner really needs them
- do not assume the partner can infer your branch state without the note
Purpose:
- Send a one-way Telegram message without waiting for a reply.
Input:
session_id?messagetask?context?risk_level?use_saved_context?
Output:
sentmessage_id?
Purpose:
- open or reuse a Playwright tab for the current session
- keep browser state isolated per
session_id
Input:
session_id?urlwidth?height?wait_until?reset_context?
Output:
session_idopenedcreated_contexturltitle?viewport_width?viewport_height?
Notes:
- each session gets its own isolated browser context and page
- call this first only when you want the isolated Playwright backend
- do not call this just to work with an already selected Firefox attached tab
urlmay be an absolute URL, or a relative path whenBROWSER_ADDRESSis configured- in headed mode the browser window is started maximized by default
- in headed mode a fresh browser context is created without a fixed viewport unless you explicitly pass
widthandheight - if you want page content to grow and shrink together with the outer browser window, do not pass
widthandheight - if you pass viewport size, pass both
widthandheighttogether - use explicit
widthandheightwhen the task depends on a specific responsive breakpoint or working area
Purpose:
- list browser instances currently connected through the local attach extension bridge
Input:
session_id?
Output:
session_id?totalinstances[]instance_idbrowserextension_versionprofile_name?connected_atlast_seen_atcapabilities[]tab_countactive_tab?
Purpose:
- list tabs from an attached browser instance
Input:
session_id?instance_id?
Output:
session_id?instance_idtotaltabs[]tab_idwindow_id?activeselected?titleurlstatus?
Notes:
- if only one attached browser instance is connected,
instance_idcan be omitted - tab selection for the attach backend can be done either from the browser extension control panel or through
browser_attach_active_tab/browser_attach_tab active=truemeans the tab is active in its browser window; there may be more than one such tab across multiple windows- for the tab currently selected for this MCP session, prefer
selected=true
Purpose:
- attach the current MCP session to the active tab of a connected attached browser instance
Input:
session_id?instance_id?
Output:
session_idbackendinstance_idtab_idattached_attitle?url?
Purpose:
- attach the current MCP session to a specific attached browser tab by
tab_id
Input:
session_id?instance_id?tab_id
Output:
session_idbackendinstance_idtab_idattached_attitle?url?
Purpose:
- detach the current MCP session from the currently selected attached browser tab
Input:
session_id?
Output:
session_iddetached
Purpose:
- start a structured browser recording bundle for the attached browser tab currently selected for this MCP session
Input:
session_id?instance_id?
Output:
session_idbackendstartedrecording_idinstance_idtab_idtab_title?tab_url?bundle_dir_namebundle_relative_pathbundle_pathstarted_at
Notes:
- this works only with the attached browser backend, not Playwright
- select the target tab first from the browser extension control panel
- the bundle is created under
.mcp-xchange/web/{tab-title-slug}-{timestamp}/ - the recording captures page snapshots, console events, request metadata, headers, cookies snapshots, and available request/response bodies
- for network analysis, read
network/index.ndjsonfirst, then open the matchingnetwork/requests/{seq}-{request_id}/meta.json - use
request.json,response.json, and optional body files only when deeper detail is needed
Purpose:
- stop the active structured browser recording for this MCP session
Input:
session_id?
Output:
session_idstoppedrecording_id?bundle_dir_name?bundle_relative_path?bundle_path?stopped_at?
Purpose:
- inspect whether a structured browser recording is active and where its bundle is being written
Input:
session_id?
Output:
session_idactiverecording?backendrecording_idinstance_idtab_idtab_title?tab_url?bundle_dir_namebundle_relative_pathbundle_pathstarted_atstopped_at?statusevent_countlast_event_at?
Purpose:
- read recent console output from the session browser tab
Input:
session_id?limit?
Output:
session_idtotalmessages
Purpose:
- reload the current session browser page after code changes or state drift
Input:
session_id?wait_until?
Output:
session_idreloadedurltitle?
Purpose:
- click an element in the current session page
- if the session has a selected attached browser tab, click there
- otherwise click in the isolated Playwright page
Input:
session_id?ai_tag?selector?text?exact?timeout_ms?
Output:
session_idclickedai_tag?selector?text?urltitle?
Purpose:
- fill an input or textarea in the current session page
- if the session has a selected attached browser tab, fill there
- otherwise fill in the isolated Playwright page
Input:
session_id?ai_tag?selector?text?exact?timeout_ms?value
Output:
session_idfilledai_tag?selector?text?value_lengthurltitle?
Purpose:
- send a key press to the page or a targeted element
- if the session has a selected attached browser tab, press there
- otherwise press in the isolated Playwright page
Input:
session_id?ai_tag?selector?text?exact?timeout_ms?key
Output:
session_idpressedkeyai_tag?selector?text?urltitle?
Purpose:
- inject JavaScript into the current session browser target
- if the session has a selected attached browser tab, inject into that real browser tab
- otherwise inject into the isolated Playwright page
- the script is wrapped so
window[namespace]exists; default namespace isTELLY
Input:
session_id?source?file_path?namespace?
Rules:
- provide either
sourceorfile_path - prefer
file_pathfor local reusable scripts and larger payloads - injected scripts can use both
window.TELLYand localTELLYwhennamespaceis left at the default
Output:
session_idinjectednamespacesource_typebytesurltitle?
Purpose:
- wait until an element in the current session page reaches a requested state
Input:
session_id?ai_tag?selector?text?exact?timeout_ms?state?
Output:
session_idwaitedstateai_tag?selector?text?urltitle?
Browser target rules for browser_click, browser_fill, browser_press, browser_wait_for:
- prefer
ai_tagfirst when the frontend provides it - supported markup:
data-drive-tag="save-button"ai-tag="save-button"
- recommended usage is an attribute with a value, not a bare presence-only attribute
- prefer
selectorwhen you have a stable target:#id.classbutton[type="submit"]div[data-testid="save"]
- use
textonly when there is no reliable selector - do not mix ambiguous hashed CSS classes with fuzzy text guessing when a stable selector exists
- for the attached browser backend, prefer
selectorfirst;ai_tagandtextstill work, but the attach executor is intentionally lighter than full Playwright
Purpose:
- wait until the current session page navigates to an exact URL or to a URL containing a fragment
Input:
session_id?url?url_contains?timeout_ms?
Output:
session_idwaitedmatchedurl?url_contains?current_urltitle?
Purpose:
- read recent page runtime errors from the session browser tab
Input:
session_id?limit?
Output:
session_idtotalerrors
Purpose:
- read recent failed or HTTP-error requests from the session browser tab
Input:
session_id?limit?
Output:
session_idtotalfailures
Purpose:
- clear accumulated browser console, runtime error, and network failure buffers for the current session
Input:
session_id?
Output:
session_idclearedconsole_messages_clearedpage_errors_clearednetwork_failures_cleared
Purpose:
- inspect a DOM element in the session browser tab
- if the session has a selected attached browser tab, inspect that real browser tab
- otherwise inspect the isolated Playwright page
Input:
session_id?selector?include_html?include_text?
Output:
session_idselectorfoundurl?title?outer_html?text_content?visible?attributes?
Purpose:
- inspect computed styles and box metrics for a DOM element in the session browser tab
- if the session has a selected attached browser tab, inspect that real browser tab
- otherwise inspect the isolated Playwright page
Input:
session_id?selectorproperties?
Output:
session_idselectorfoundurl?title?visible?styles?box?
Purpose:
- capture a screenshot from the session browser tab
- if the session has a selected attached browser tab, capture from that real browser tab
- otherwise capture from the isolated Playwright page
Input:
session_id?selector?full_page?file_name?send_to_telegram?caption?
Output:
session_idfile_pathworkspace_direxchange_dirtelegram_message_id?url?title?
Notes:
- screenshots are written into
.mcp-xchange - they are tracked separately from Telegram-uploaded files
- they appear under Telegram
Browser -> Screenshots - if
send_to_telegram=true, the saved screenshot is also sent into the bound Telegram chat for that session - for the attached browser backend, the screenshot comes from the selected tab in the user browser session
Purpose:
- close the current browser target for the session
- if the session has a selected attached browser tab, close that real browser tab and clear the attach state
- otherwise close the isolated Playwright browser context
Input:
session_id?
Output:
session_idclosed
Telegram UI summary:
/menuis the only top-level Telegram command for console navigation- root menu shows one console button per row
- root menu reflects terminal bridge status for available consoles
- console menu uses:
Live | Content | BrowserCollabStorage | SettingsBack
- default logical console identity comes from
.mcpsession.jsonin the workspace or explicit-s - terminal runtime metadata does not change
session_id Browser -> Screenshotslists screenshots created bybrowser_screenshotStorageandScreenshotsare relay-aware on the gateway- for relay consoles they read file metadata through gateway routes, not through the gateway filesystem
Storagebrowses.mcp-xchangefor the active console and can send stored notes/files back into TelegramSettingscontainsInfo,Rename,Unpair,Back- project/collab work is the only supported user-facing collaboration path in Telegram UI
Collabis the project-based multi-machine collaboration flow- inside
Collab -> Project -> Member, action semantics differ:- first row is
Ask | Share - second row is
Live Asksends a task to the selected member console- expected reply route is
member -> current console Sharecreates a task for the current console- expected send route is
current console -> member Livefirst sends an approval request to the selected member console- after approval, the requester receives a fresh
Open Live Viewbutton in Telegram
- first row is
- partner-note prompt format is:
- first line = summary
- optional blank line
- remaining text = full message body
- if an old project-member menu message becomes stale, clicking it deletes that outdated Telegram message
- partner-note wake-up means:
- call
list_xchange_records - then call
get_xchange_record - not
get_telegram_inbox
- call
Distributed mode scaffold:
DISTRIBUTED_MODE=client|gateway|both/gateway/healthzis available when mode isgatewayorboth/gateway/partner-noteis available when mode isgatewayorboth- if
GATEWAY_PUBLIC_URLis configured,send_partner_noteand TelegramCollabdelivery use the gateway HTTP surface for note creation - in
DISTRIBUTED_MODE=both, same-bot local delivery should still go through the gateway path transparently - gateway/client online transport now uses
ws - optional gateway-side
RabbitMQfanout can be enabled throughRMQ_* TOOLS.mdsync is hash-based:- client sends
session_toolsinws hello - gateway compares them with canonical gateway
TOOLS.md - mismatch produces
tools_event - client also self-checks after
hello_ack
- client sends
- agents should use
list_xchange_records/get_xchange_recordplus separate files in.mcp-xchange/shares/for collaboration - recommended collaboration note kinds are:
sharequestionreplyrequesthandoff
- useful collaboration content includes API summaries, what changed, current errors, sample payloads, and relevant git changes from the agent workspace
ToolscontainsBroadcastandPrune allCollab -> Toolscontains:BroadcastHistoryDelete
Historysends a markdown export of the last 5 Collab events for the current active console
Current remaining operational gaps are tracked in docs/TODO.md.
Purpose:
- Send a Telegram clarification request and wait for a reply.
Input:
questiontask?context?affected_files?options?recommended_option?risk_level?timeout_seconds?fallback_if_timeout?session_id?use_saved_context?
Output:
request_idanswertimed_outreceived_at?fallback_used?
Human Telegram messages are now stored as structured telegram_message records in .mcp-xchange.
Preferred behavior is event-driven:
- The human sends a message through the gateway bot.
- The gateway routes the message to the active console as a
telegram_messagerecord. - The running agent checks
.mcp-xchangethrough MCP tools. - Read actual content through
list_xchange_recordsandget_xchange_record.
The Telegram side supports an active-console context per Telegram identity.
Rules:
- ordinary Telegram messages are stored as
telegram_messagerecords for the currently active console /menuopens a menu with all consoles visible to the current Telegram identity inside the current gateway scope- selecting a console makes it the new active console
- the main menu also provides a console-switch entry point
- the list reflects currently available gateway-known consoles, not a local pairing catalog
- Wait for the wake-up or decide to poll explicitly.
- Call
list_xchange_records. - Select the newest relevant
telegram_messagerecord withstatus = new. - Call
get_xchange_record. - Process the returned record.
- If the message was handled, call
mark_xchange_record_read.
Do not add extra diagnostic calls in that path:
- do not call
get_session_contextbeforelist_xchange_records - do not invent a separate inbox polling pass when the wake-up already arrived
Current truth:
- gateway can know whether a client node is online through active
ws - gateway also stores
gateway_clients.last_seen_at - this is not the same thing as a live coding-agent heartbeat inside each console
Rule:
- do not claim that a console agent is definitely
offlineunless a dedicated agent heartbeat exists - today the honest distinction is:
- client node
online/offline - console visible/not visible
- current console context present/absent
- client node
If no wake-up has arrived, use passive record checks:
- Call
list_xchange_records. - If there is a new
telegram_messagerecord, callget_xchange_record. - Process the message.
- Call
mark_xchange_record_readfor handled items.