Skip to main content
Glama

messages_send

Destructive

Send a message to a thread, channel, or contact. Supports Telegram, Email, LinkedIn, and other connected channels. For LinkedIn posts (comment_thread kind), this posts a comment on the post. Can automatically resolve recipients and channels when not specified. Can send files/images/documents as attachments — pass attachments=[file_id, ...] with integer file IDs obtained from collections.list_files, search.files, or files.search. text is optional when attachments are provided.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
textNoMessage text to send. Optional if attachments provided.
formatNoMessage formattext
silentNoSend without notification
buttonsNoInline reply buttons shown under the message, one per row (max 10). Each item is {"label": "<visible button text>"} plus exactly one target: "value" (a string sent back as a normal incoming message when tapped, defaults to the label, max 64 bytes), "url" (opens a link) or "web_app" (opens an https page as a Telegram Mini App). Only on channels that support buttons (Telegram bot accounts); can accompany at most one attachment, and when the text is too long for a media caption the buttons arrive with the text as a second message.
templateNoWhatsApp Business API only. An approved template to send instead of free text, in Meta's shape: {name, language, components:[{type:'body', parameters:[{type:'text', text:...}]}, ...]}; `language` is the code as a string ('en_US'). Required when the 24-hour window of the conversation is closed (the send answers WINDOW_EXPIRED otherwise). Read the approved templates and their variables with channels.list(account_id=...).
thread_idNoTarget thread. OMIT to reply in the same chat you received the triggering message from — the backend defaults to the current thread. Pass an explicit value ONLY to reply in a DIFFERENT thread, and only use: (a) a numeric DB thread id from search.threads, or (b) a channel_ref like 'telegram:-12345'. To write to a phone number nobody has a thread with yet: 'whatsapp_business:+<number in international format>' opens a WhatsApp conversation from a WhatsApp Business API number (from_account_id picks which one; the first message must be a `template`), and 'sms:+<number>' texts it from a workspace phone number that has SMS turned on (from_account_id picks which one), landing in that number's conversation with the recipient, next to their calls. NEVER use a chat-type word (dm, group, channel, livechat) — those are category labels from the SITUATION block, not ids.
attachmentsNoArray of integer file IDs to send as attachments (images, documents, any files). Get file IDs from collections.list_files (field `file_id`), search.files (field `file_id`), or files.search — only ids returned by those calls in this workspace. The file must already exist in the workspace (status=ready) — no separate upload step needed. When attachments are provided, `text` becomes optional (a caption can be included alongside).
in_workspaceNoRun this one call in this workspace id instead of the session's. Nothing is stored; other sessions are not affected.
private_replyNoComment threads only (Instagram, Facebook): answer the comment in the commenter's private messages instead of publicly under the post (Meta's Private Replies). One message per comment, within 7 days of the comment. The message is filed in the person's private conversation. To also answer publicly, make a second call without this flag. OMIT for a normal send.
recipient_nameNoName of person to send to (e.g., 'Jane', 'John'). Tool will auto-resolve channel. Optional if thread_id provided.
from_account_idNoWhich of the workspace's accounts on this channel SENDS the message, i.e. the number or handle the recipient sees. Only meaningful when starting a NEW conversation — an existing thread already belongs to an account and that one is used. OMIT and the platform picks the most recently active account, which is a coin flip in a workspace with several numbers: pass it whenever one of them must not be used to open conversations (a personal line, or one under a spam restriction). An account that is not active in this workspace is refused, never silently swapped.
recipient_usernameNoTelegram @username to message (e.g. '@some_username'). Use this for a Telegram user NOT yet in contacts — it resolves the handle, adds the contact, and creates the thread. Telegram only; for existing contacts prefer thread_id or recipient_name.
reply_to_message_idNoID of message to reply to (optional)

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed3 schema fields changed
    • changedInput schema / properties / template / description
      Previous value: -"WhatsApp Business API only. An approved template to send instead of free text, in Meta's shape: {name, language, components:[{type:'body', parameters:[{type:'text', text:...}]}, ...]}; `language` may be the code as a string ('en_US') or Meta's {code:'en_US'}. Required when the 24-hour window of the conversation is closed (the send answers WINDOW_EXPIRED otherwise). Read the approved templates and their variables with channels.list(account_id=...)."New value: +"WhatsApp Business API only. An approved template to send instead of free text, in Meta's shape: {name, language, components:[{type:'body', parameters:[{type:'text', text:...}]}, ...]}; `language` is the code as a string ('en_US'). Required when the 24-hour window of the conversation is closed (the send answers WINDOW_EXPIRED otherwise). Read the approved templates and their variables with channels.list(account_id=...)."
    • addedInput schema / properties / template / properties
      Added value: +{
      +  "components": {
      +    "description": "Variable values per component, in Meta's shape.",
      +    "items": {
      +      "properties": {
      +        "parameters": {
      +          "items": {
      +            "properties": {
      +              "text": {
      +                "description": "The value of this variable.",
      +                "type": "string"
      +              },
      +              "type": {
      +                "description": "'text'.",
      +                "type": "string"
      +              }
      +            },
      +            "type": "object"
      +          },
      +          "type": "array"
      +        },
      +        "type": {
      +          "description": "'body', 'header' or 'button'.",
      +          "type": "string"
      +        }
      +      },
      +      "type": "object"
      +    },
      +    "type": "array"
      +  },
      +  "language": {
      +    "description": "Template language code, e.g. 'en_US'.",
      +    "type": "string"
      +  },
      +  "name": {
      +    "description": "Template name as approved at Meta.",
      +    "type": "string"
      +  }
      +}
    • changedInput schema / properties / thread_id / description
      Previous value: -"Target thread. OMIT to reply in the same chat you received the triggering message from — the backend defaults to the current thread. Pass an explicit value ONLY to reply in a DIFFERENT thread, and only use: (a) a numeric DB thread id from search.threads, or (b) a channel_ref like 'telegram:-12345'. To text a phone number, pass 'sms:+<number in international format>': the message goes out from a workspace phone number that has SMS turned on (from_account_id picks which one) and lands in that number's conversation with the recipient, next to their calls. NEVER use a chat-type word (dm, group, channel, livechat) — those are category labels from the SITUATION block, not ids."New value: +"Target thread. OMIT to reply in the same chat you received the triggering message from — the backend defaults to the current thread. Pass an explicit value ONLY to reply in a DIFFERENT thread, and only use: (a) a numeric DB thread id from search.threads, or (b) a channel_ref like 'telegram:-12345'. To write to a phone number nobody has a thread with yet: 'whatsapp_business:+<number in international format>' opens a WhatsApp conversation from a WhatsApp Business API number (from_account_id picks which one; the first message must be a `template`), and 'sms:+<number>' texts it from a workspace phone number that has SMS turned on (from_account_id picks which one), landing in that number's conversation with the recipient, next to their calls. NEVER use a chat-type word (dm, group, channel, livechat) — those are category labels from the SITUATION block, not ids."
  2. Changed1 schema field changed
    • addedInput schema / properties / template
      Added value: +{
      +  "description": "WhatsApp Business API only. An approved template to send instead of free text, in Meta's shape: {name, language, components:[{type:'body', parameters:[{type:'text', text:...}]}, ...]}; `language` may be the code as a string ('en_US') or Meta's {code:'en_US'}. Required when the 24-hour window of the conversation is closed (the send answers WINDOW_EXPIRED otherwise). Read the approved templates and their variables with channels.list(account_id=...).",
      +  "type": "object"
      +}
  3. Changed1 schema field changed
    • addedInput schema / properties / in_workspace
      Added value: +{
      +  "description": "Run this one call in this workspace id instead of the session's. Nothing is stored; other sessions are not affected.",
      +  "type": "integer"
      +}
  4. Changed1 schema field changed
    • changedInput schema / properties / thread_id / description
      Previous value: -"Target thread. OMIT to reply in the same chat you received the triggering message from — the backend defaults to the current thread. Pass an explicit value ONLY to reply in a DIFFERENT thread, and only use: (a) a numeric DB thread id from search.threads, or (b) a channel_ref like 'telegram:-12345'. To text a phone number that has no thread yet, pass 'sms:+<number in international format>' (the workspace needs a phone number with SMS turned on; from_account_id picks which one). NEVER use a chat-type word (dm, group, channel, livechat) — those are category labels from the SITUATION block, not ids."New value: +"Target thread. OMIT to reply in the same chat you received the triggering message from — the backend defaults to the current thread. Pass an explicit value ONLY to reply in a DIFFERENT thread, and only use: (a) a numeric DB thread id from search.threads, or (b) a channel_ref like 'telegram:-12345'. To text a phone number, pass 'sms:+<number in international format>': the message goes out from a workspace phone number that has SMS turned on (from_account_id picks which one) and lands in that number's conversation with the recipient, next to their calls. NEVER use a chat-type word (dm, group, channel, livechat) — those are category labels from the SITUATION block, not ids."
  5. Changed1 schema field changed
    • changedInput schema / properties / thread_id / description
      Previous value: -"Target thread. OMIT to reply in the same chat you received the triggering message from — the backend defaults to the current thread. Pass an explicit value ONLY to reply in a DIFFERENT thread, and only use: (a) a numeric DB thread id from search.threads, or (b) a channel_ref like 'telegram:-12345'. NEVER use a chat-type word (dm, group, channel, livechat) — those are category labels from the SITUATION block, not ids."New value: +"Target thread. OMIT to reply in the same chat you received the triggering message from — the backend defaults to the current thread. Pass an explicit value ONLY to reply in a DIFFERENT thread, and only use: (a) a numeric DB thread id from search.threads, or (b) a channel_ref like 'telegram:-12345'. To text a phone number that has no thread yet, pass 'sms:+<number in international format>' (the workspace needs a phone number with SMS turned on; from_account_id picks which one). NEVER use a chat-type word (dm, group, channel, livechat) — those are category labels from the SITUATION block, not ids."
  6. Added
  7. Removed
  8. Changed1 schema field changed
    • addedInput schema / properties / recipient_username
      Added value: +{
      +  "description": "Telegram @username to message (e.g. '@some_username'). Use this for a Telegram user NOT yet in contacts — it resolves the handle, adds the contact, and creates the thread. Telegram only; for existing contacts prefer thread_id or recipient_name.",
      +  "type": "string"
      +}
  9. Changed8 schema fields changed
    • changedInput schema / properties / attachments / description
      Previous value: -"Array of integer file IDs to send as attachments (images, documents, any files). Get file IDs from collections.list_files (field `file_id`), workspace.search with scope=['files'] (field `file_id`), or files.search. Example: [302237]. The file must already exist in the workspace (status=ready) — no separate upload step needed. When attachments are provided, `text` becomes optional (a caption can be included alongside)."New value: +"Array of integer file IDs to send as attachments (images, documents, any files). Get file IDs from collections.list_files (field `file_id`), search.files (field `file_id`), or files.search. Example: [302237]. The file must already exist in the workspace (status=ready) — no separate upload step needed. When attachments are provided, `text` becomes optional (a caption can be included alongside)."
    • removedInput schema / properties / bcc
      Removed value: -{
      -  "description": "Email addresses to BCC (blind carbon copy). Only for email channel.",
      -  "items": {
      -    "type": "string"
      -  },
      -  "type": "array"
      -}
    • removedInput schema / properties / cc
      Removed value: -{
      -  "description": "Email addresses to CC (carbon copy). Only for email channel.",
      -  "items": {
      -    "type": "string"
      -  },
      -  "type": "array"
      -}
    • removedInput schema / properties / channel
      Removed value: -{
      -  "description": "Channel hint (e.g. 'telegram'). Required when using recipient_username. Only 'telegram' is currently accepted for handle-based routing.",
      -  "type": "string"
      -}
    • removedInput schema / properties / recipient_email
      Removed value: -{
      -  "description": "Email address to send to (e.g., 'john@example.com'). Creates new email thread. Only for email channel.",
      -  "type": "string"
      -}
    • removedInput schema / properties / recipient_username
      Removed value: -{
      -  "description": "Telegram @handle (e.g. '@smartdeveloper' or 'smartdeveloper'). Resolves or opens a DM without needing a DB thread_id. Requires channel='telegram'. Only Telegram supported in this release.",
      -  "type": "string"
      -}
    • removedInput schema / properties / subject
      Removed value: -{
      -  "description": "Email subject line. Required for new emails, optional for replies (auto-generates 'Re: ...').",
      -  "type": "string"
      -}
    • changedInput schema / properties / thread_id / description
      Previous value: -"Target thread. OMIT to reply in the same chat you received the triggering message from — the backend defaults to the current thread. Pass an explicit value ONLY to reply in a DIFFERENT thread, and only use: (a) a numeric DB thread id from threads.list / workspace.search, or (b) a channel_ref like 'telegram:-12345'. NEVER use a chat-type word (dm, group, channel, livechat) — those are category labels from the SITUATION block, not ids."New value: +"Target thread. OMIT to reply in the same chat you received the triggering message from — the backend defaults to the current thread. Pass an explicit value ONLY to reply in a DIFFERENT thread, and only use: (a) a numeric DB thread id from search.threads, or (b) a channel_ref like 'telegram:-12345'. NEVER use a chat-type word (dm, group, channel, livechat) — those are category labels from the SITUATION block, not ids."
  10. Changed2 schema fields changed
    • removedInput schema / properties / silent / default
      Removed value: -false
    • removedInput schema / properties / text / default
      Removed value: -""
  11. First observed

TDQS

A3.5/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare destructiveHint=true, openWorldHint=true and idempotentHint=false, so the send-is-an-irreversible-external-write profile is covered structurally. The description adds some operational context — attachments must already exist in the workspace, text becomes optional when attachments are present — but says nothing about rate limits, delivery failures, or what the call returns.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose is front-loaded in the first sentence and the follow-on sentences each cover a distinct capability (channels, comment_thread, auto-resolution, attachments). Some of the attachment detail duplicates the schema, but the paragraph stays tight and scannable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 13-parameter, nested-schema, open-world send tool, the description covers the important axes: what it sends to, cross-channel scope, LinkedIn comment semantics, attachment sourcing, and recipient auto-resolution. The notable remaining gap is the absence of any sibling routing (vs messages_send_email) and no mention of what a successful send returns, which matters more here because there is no output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the per-parameter descriptions are unusually rich (thread_id, from_account_id, template, private_reply), so the schema already carries the semantics. The description largely restates the attachments and optional-text behavior that the schema documents in more detail, adding little beyond the baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Send a message to a thread, channel, or contact') and enumerates the supported channels plus the special LinkedIn comment_thread behavior, so the core action is unambiguous. It does not, however, disambiguate itself from the sibling messages_send_email, even though it claims Email support, which is a real routing ambiguity an agent must resolve elsewhere.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives conditional guidance for attachments (obtain file IDs from collections.list_files, search.files, files.search) and notes that recipients/channels can be auto-resolved. But the detailed when-to-use rules — omit thread_id to reply in the current chat, explicit thread_id only for a different thread, never use chat-type words — live in the schema, not the description, and no alternative tool (messages_send_email, messages_forward, messages_edit) is named as a routing alternative.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources