Skip to main content
Glama

send_message

Deliver a role-addressed channel message to one recipient, several roles, or all others, with per-recipient addenda and tracked obligations that stay open until resolved.

Instructions

Send a message on the channel. 'to' is one role name, a LIST of role names, or '' for every other role. Sending to yourself is rejected. A multi-recipient message is ONE message with one body and one id: one thread, one set of acknowledgements, and — the reason it exists — one text that is byte-identical for everyone, so four roles cannot end up voting on four slightly different drafts. Read state is tracked per recipient. Only kind='proc' and kind='status' may go to several roles, and action_required=true is REFUSED for them: a debt needs exactly one owner, or 'closed' stops being a definite state and the resolve→confirm loop has nothing to hang on. Need work from three roles — send three messages, one debt each. 'addenda' ({role: text}) carries a personal tail per recipient inside the same message: the shared body stays identical while the part that legitimately differs travels with it instead of in four follow-up letters. Each recipient sees theirs as 'addendum'. Optional 'kind' tags the namespace (bug/feat/proc/status/question/answer — use 'answer' with reply_to for replies to a question); optional 'work_status' tags progress (proposed/in_progress/done_local/needs_you/done/blocked). These fields REPLACE the old text conventions — do not duplicate them as 'bug:' topic prefixes or '[status]' tags in the body. As the work progresses, move the status on the SAME message with set_work_status instead of sending new messages. Choosing the mechanism: need a formal decision from the other side → kind='proc' (surfaces in awaiting_ack); need work/action done → action_required=true (surfaces in open_obligations, starts with status='open', closed via resolve_message). A REPLY to a multi-recipient message may keep the same recipients whatever its kind: answering four roles is not a broadcast you chose, it is the audience the question already had — sending four separate letters instead is exactly the copy-paste this channel asks you to avoid. action_required stays single-recipient. kind='proc' REQUIRES TWO EXPLICIT ANSWERS, and omitting either is refused at send time rather than discovered later: (1) 'pin_key' — the pin this proposal changes, or null if it changes none. With a key the link is structural rather than inferred from wording, so pin_set(approved_by=this) cannot fail afterwards for not naming the key, the proposal is findable with list_messages(pin_key=...), and a successful pin_set retires the drafts it settles. A proposal that names a pin must be addressed to EVERY other role — everyone READS a pin round — and must also declare 'voters': the roles whose 'agree' it needs. voters='' is every role (the classic rule); voters=['x','y'] scopes the decision to the roles it is actually between, and the rest still receive it, may still vote, and simply do not block. What you declare is stored on the message and is the exact rule pin_set will check, so a round can no longer collect a full quorum and then be refused. Omitting 'voters' is refused: in a channel with a part-time member an unscoped round cannot close, and silence must not be mistaken for a veto. (2) 'about_message_id' — the message this one is about, or null if it stands on its own. A nudge ('still need your vote on #1403') asks for a decision about ANOTHER message, so it does not become a decision of its own: it stays out of awaiting_ack and retires when its target is voted on, superseded or deleted. 'decision_requested=false' opens a proposal for READING rather than voting ('ten points, tell me what is wrong, I am not collecting votes') — it stays out of awaiting_ack while acknowledge still works for anyone who wants to weigh in. 'topic' is at most 80 characters. The body has no server limit, but there is a limit on what you can TYPE in one call: if the text is a document rather than a message, put it in with upload_content + seal_content and pass body_ref= instead of 'body' — that also gives the document its own sha256, separate from the digest of the letter carrying it. Returns the new message id and created_at timestamp.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
toYes
bodyNo
kindNo
topicYes
votersNo
addendaNo
pin_keyNo
body_refNo
reply_toNo
work_statusNo
action_requiredNo
about_message_idNo
decision_requestedNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Schema Changelog

Changes observed during successful MCP inspections.

  1. First observedv0.1.0

TDQS

A4.8/5.0
Behavior5/5

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

With no annotations, the description carries the full burden and does so: self-sends are rejected, multi-recipient + action_required is REFUSED, kind='proc' requires pin_key and about_message_id or it fails at send time, voters gets stored and is the exact rule pin_set checks, about_message_id keeps a nudge out of awaiting_ack. These are non-obvious refusals and lifecycle behaviors that the agent could not derive from the schema.

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

Conciseness3/5

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

The core instruction and the mechanism-choice rule are front-loaded, which is good, but the middle devolves into discursive justification ('the reason it exists', 'silence must not be mistaken for a veto', 'exactly the copy-paste this channel asks you to avoid'). The information is valuable but the density of editorializing costs it; a tighter version would keep every rule and drop the rhetoric.

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

Completeness5/5

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

For a 13-parameter mutation tool with no annotations, this is complete: it covers refusals, required companion fields for kind='proc', the reply/multi-recipient interaction, and the document handoff path. An output schema exists, and the description still notes the two returned values (id, created_at) without over-explaining them.

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

Parameters5/5

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

Schema description coverage is 0%, so the description must define all 13 parameters — and it does: to (role/list/'*'), kind (with the bug/feat/proc/status/question/answer enum), work_status (six values), voters ('*' vs scoped list), addenda ({role:text}, seen as 'addendum'), pin_key, about_message_id, decision_requested, reply_to, body_ref, topic (max 80 chars). It even explains the failure modes of omitting pin_key/voters, far beyond schema-level typing.

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

Purpose5/5

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

Opens with a specific verb+resource ('Send a message on the channel') and immediately scopes it against siblings: set_work_status for progress on an existing message, upload_content + seal_content for documents, resolve_message for closing obligations. An agent can distinguish this from every other write tool in the family without opening the schema.

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

Usage Guidelines5/5

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

Routes explicitly: 'need a formal decision → kind=proc' vs 'need work done → action_required=true', with the surfacing consequence (awaiting_ack vs open_obligations) for each. It names when NOT to use it (don't send a new message to advance status; use set_work_status) and specifies the multi-recipient gotcha (send three messages, one debt each). Nothing is left to inference.

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