Skip to main content
Glama
soil-dev
by soil-dev

loomiomcp

npm CI License: Apache-2.0 Glama

Model Context Protocol server for Loomio. Lets Claude (Desktop, Code, or web Projects via Custom Connector) read and write Loomio discussions, polls, comments and group memberships — and analyse member participation — in plain English. Targets Loomio's b2 API — the canonical surface documented at /help/api2 and the namespace where the controllers live in the open-source repo — as shipped in Loomio 3.8 and later (tested on 3.13.1).

Tool catalog

Every read is one upstream call unless the table says otherwise. Every collection returns Loomio's exact total and a scope note naming what was filtered, capped or not visible. Where Loomio pages a collection (its groups index and a thread's items, since Loomio 3.11.1: 50 rows by default, at most 500 per request) the connector asks for 500 at a time and follows the pages it needs, within a fixed per-call page cap; a walk the cap stops is flagged, never returned as if complete. The server also ships a ten-line routing guide as MCP instructions, so a client knows which tool answers which kind of question before it reads a single description. The whole catalogue a client downloads at session start (instructions + tools/list for the 24 full-mode tools) is about 36 KB, roughly 9 000 tokens (node scripts/catalog-size.mjs after npm run build); the long-form guidance per tool — inputs, outputs, cost, caveats, exact error texts — is in HOWTO.md.

Reads (always registered)

Tool

Purpose

Upstream calls

check_connection()

Key status, the account the key belongs to, its groups (member / pending / parent), readonly and b3 flags, fixed-wording notes. Call first when unsure what the connector can see.

the health probe (1 authenticated + 1 public GET); its groups body is reused — Loomio ≥ 3.11.1 puts only the first 50 groups in it, so +1 per further 500 member groups (groups_partial: true if that read fails)

list_groups()

The connector user's member groups (pending invitations included) with the user's own membership {accepted, admin, delegate, title}, plus each subgroup's parent flagged member: false (a parent the user belongs to is a member group, wherever Loomio's page put it). Sorted by full_name.

1 per 500 member groups (GET /b2/groups; at most 20, scope.partial beyond)

get_group(id_or_key_or_handle)

One group's full record — privacy, members_can_* permission flags, counters — with member, membership, parent, url. Works for publicly visible groups the user has not joined.

1

list_discussions(group_id, status?, limit?, offset?, description_max_chars?, strip_html?)

A group's discussions, newest activity first, each joined with its thread counters (items_count, replies_count, last_activity_at, locked_at, pinned_at, tags, …) and url. status defaults to open and is always sent. Bodies come as plain text (strip_html, default true; description_format: "text") capped at 1500 chars (description_truncated: true marks a cut).

1

get_discussion(id_or_key, strip_html?, include_items?, items_limit?, items_body_max_chars?)

One discussion with the full body (as stored; strip_html: true for plain text), thread counters, group and users. include_items: true embeds list_thread_items for the thread (same reply budget; thread_items.next_offset says where to page on).

1 (2 with include_items, +1 per further 500 embedded items)

list_polls(group_id, status?, limit?, offset?, description_max_chars?, strip_html?)

A group's polls, newest first, as SLIM rows: identity, schedule, hide_results, participation counts, stance_counts + total_score when visible (aligned with poll_options[]), current_outcome, my_stance, url. The per-voter results[] breakdown and the type knobs Loomio left null are get_poll's. status defaults to active.

1

get_poll(id_or_key, strip_html?)

One poll with poll_options, current_outcome, the user's own my_stance, thread counters and url. results_visible / results_hidden_reason apply Loomio's own visibility rule; hidden stance counts are stripped, never zeroed.

1

list_threads(limit?, offset?, group_id?, type?, since?)

Every thread the user can see across all groups, newest activity first — the cheapest "what is new" call. group_id / type / since filter client-side within the page (Loomio's route has no date filter); with since, page until scope.exhausted.

1 (GET /b2/threads)

list_thread_items(topic_id | discussion_id | poll_id, kinds?, limit?, offset?, include_reactions?, body_max_chars?, strip_html?, max_total_chars?)

One thread's structured items (comments, polls, votes, outcomes, edits, with an edit's change_note) with slimmed side-loads; comment bodies, vote reasons and outcome statements come as plain text by default (strip_html). The connector asks Loomio for exactly the offset / limit window (Loomio ≥ 3.11.1 pages this route; older Loomio returns the whole thread, which is sliced here, scope.server_paged: false), under a reply budget (max_total_chars, default 120000; truncated_by_budget + next_offset). kinds, a filter Loomio lacks, reads the thread page by page from its start (at most 5 000 items, scope.scan_truncated beyond). Replaces list_events.

1 per 500 items read (+1 by discussion_id/poll_id); kinds reads the thread

get_thread_markdown(topic_id | discussion_id | poll_id, max_chars?)

The whole thread rendered to Markdown by Loomio — front matter, body, a reply tree of comments, polls with their votes and results tables, outcomes. Best single call for a summary. Loomio ≥ 3.10's export ignores "hide results until vote", so the connector reads the thread first and refuses one holding an open until_vote poll the user has not voted in (list_thread_items and get_poll apply the rule).

2 (+1 per 500 items when the guard scans: any thread with replies, ≤ 10); refuses an open until_vote poll the user has not voted in

search_content(query | author_id, group_id?, types?, tag?, order?)

Full-text search across everything visible; author_id alone lists a user's 20 newest items. Snippets carry **match** emphasis; every hit has a deep-link url and group {id, full_name, handle} (Loomio's "Parent - Subgroup" name). A vote-reason hit on a poll whose results are hidden from the user is dropped from a query search (its existence would confirm the term; scope.hidden_stance_hits_dropped) and kept with snippet: null + snippet_hidden_reason in author mode. Loomio caps results at 20 (capped: true), no paging. tag matches only contributions explicitly tagged with that name on Loomio 3.13+ (every item of a tagged thread before).

1

get_participation_report(group_ids | group_scope, start_month?, end_month?, delegates_only?, limit?, include_inactive?)

Loomio's participation report for a group set: each user's threads, comments, polls, votes cast vs issued vs missed, outcomes, reactions, total, sorted by total; the top limit rows (default 50, max 500), users with no counted activity dropped unless include_inactive: true (total_users counts all ranked). THE tool for "who is most engaged".

1 for the whole group set

get_user_activity(user_id, group_ids, since?, until?)

One user's counts across groups (month-grained window) with by_group, plus sample_events from one author search.

N + 1 for N groups (4 in flight)

list_memberships(group_id, limit?, offset?)

A group's roster: ids, names, usernames, admin / delegate, title, accepted_at, and (Loomio 3.10+) the vote weight. Member emails only as user_email on a row, and only where the connector's user is a group admin; users[] never carry emails (the connector account's own, which Loomio adds, is removed). A non-member gets an empty list, not a 403 — the connector annotates it.

1 (compact=1)

Writes (registered unless LOOMIO_MCP_READONLY=1)

Tool

Purpose

Upstream calls

create_discussion(title, group_id, …)

Start a thread. Nested {discussion: {…}} body (a flat body silently loses group_id and private). Throws if Loomio's echo lands in another group.

1

update_discussion(id_or_key, …)

Edit title, body (replaced), privacy, comment / reaction / concurrent-poll settings, or add recipients.

1

delete_discussion(id_or_key)

Soft-discard: Loomio blanks the thread and keeps the records; an admin can restore.

1

create_poll(title, poll_type, options, closing_at, group_id | discussion_id | topic_id, …)

Any Loomio 3.8 poll type — proposal, poll, count, check, question, score, ranked_choice, stv, meeting, dot_vote — standalone or inside a discussion. Without closing_at Loomio saves an unopened draft (opened: false + warning); an opening poll is announced to every eligible voter unless notify_on_open: false. A group_id given with a thread reference is cross-checked before anything is written.

1 (2 with discussion_id, or with topic_id + group_id)

update_poll(id_or_key, …)

Edit an open poll: title, details, closing time (the next full hour ends voting within the hour), options to ADD (Loomio's PATCH replaces the set and deletes unlisted options with their votes, so the connector reads the current names first and sends the union), hide_results (tighten only), voters.

1 (2 with options)

delete_poll(id_or_key)

Soft-discard a poll.

1

create_comment(discussion_id | parent_id + parent_type, body, body_format?)

Comment on a thread (discussion_id as numeric id or short key), or reply to a comment, poll, stance or outcome. Send body_format: "html" for HTML bodies (Loomio stores an omitted format as Markdown).

1 (2 with a short key)

update_comment(id, body, body_format?)

Replace a comment's body.

1

delete_comment(id)

Soft-discard a comment.

1

manage_memberships(group_id, emails, remove_absent?)

Invite by email; with remove_absent: true remove everyone not listed. Group admin only. Read SECURITY.md first.

1

Instance-operator tools (b3; registered when LOOMIO_B3_API_KEY is set and not read-only)

Tool

Purpose

Upstream calls

deactivate_user(id)

Deactivate an account instance-wide (Loomio runs it asynchronously; on Loomio 3.11+ votes already cast in open polls are kept).

1

reactivate_user(id)

Reactivate an account and restore the memberships and (Loomio 3.10+) open-poll ballots the deactivation revoked.

1

get_user(id | identity_type + uid)

One account by id or linked external identity — email included.

1

list_users(is_admin?)

Every account on the instance — emails included, unpaginated.

1

list_instance_groups(limit?, offset?)

Every group on the instance — secret ones included, member or not — as {id, key, name, handle, parent_id, group_privacy}, ordered by id. Loomio 3.13+.

1 per page of ≤ 50

list_instance_memberships(group_id, limit?, offset?)

One group's direct members and pending invitations — emails included — with the membership id the two role / revoke tools take. Loomio 3.13+.

1 per page of ≤ 50

add_group_members(group_id, emails)

Add up to 50 addresses to any group, all-or-nothing: unknown addresses get a new unverified account, everyone added gets Loomio's invitation email. Additive only — remove_absent is never sent. Loomio 3.13+.

1

update_membership_roles(id, admin?, delegate?)

Set or clear a member's group admin / delegate role by membership id. Loomio 3.13+.

1

revoke_membership(id)

Remove a person from a group and its subgroups and threads, revoking their open-poll votes; no notification. add_group_members restores the group only, without the admin role. Loomio 3.13+.

1

The b3 secret authenticates the server, not a user: it is validated against ENV['B3_API_KEY'] on the Loomio instance and sees every account and, on Loomio 3.13+, every group and every roster. Loomio records no actor for the b3 membership writes (and a b3 deactivation reads as the user deactivating themself), so the connector's own tool.call log line is the only trail of who asked. All nine tools are therefore for single-tenant deployments only (one organisation per Loomio instance), registered only when the deployment is writable; on a shared instance — or any open-DCR deployment — leave LOOMIO_B3_API_KEY unset (the HTTP server refuses to start with it in open-DCR mode on a public URL unless read-only). The five provisioning tools need Loomio 3.13.0 or later (an older instance answers 404) and were verified against Loomio's source, tests and documentation only.

Deliberately not exposed

  • b3 update / destroy / redact users (PATCH / DELETE / POST …/redact on /b3/users, and their identity/{type}/{uid} variants). is_admin is a permitted field, so an update can mint instance admins; an email change hands the login to whoever owns the new address; destroy and redact delete or scrub personal data for good; and Loomio records no actor identity for b3 calls, so nobody could later tell which agent session did it. Deactivate / reactivate cover the operational need and are reversible.

  • POST /b3/users (Loomio 3.13+). is_admin is permitted there too, so it creates instance admins; with a name and DEFAULT_ONBOARDING_GROUP_ID set it also silently joins a group. add_group_members already creates the accounts it needs.

  • remove_absent on the b3 membership sync (POST /b3/memberships, Loomio 3.13+). An empty or wrong list removes everyone absent from it — admins included, across every subgroup — with no admin check, in one transaction. add_group_members builds its body field by field and its schema is strict, so the key can never be sent. (The b2 manage_memberships keeps its documented, admin-gated remove_absent.)

  • The deprecated PUT aliases Rails generates beside each b3 PATCH.

  • b2 chatbots / webhooks (/b2/chatbots). Group-admin configuration whose serializer returns the webhook URL and secret; nothing an AI caller needs, and a leak surface if it were readable.

  • The participation report's base and countries sections. Instance-wide totals and per-country breakdowns; not answerable per group the way the users section is, and not what members see.

Related MCP server: capsulemcp

Efficiency

Loomio 3.8 exposes aggregates its earlier releases lacked; this connector uses them so every read costs as few upstream calls and bytes as the API allows. Since Loomio 3.11.1 two of them are paged (groups index, thread items); the costs below are per 500 rows there.

Question

Before (0.0.11)

Now (0.0.16)

"Which groups can you see?"

list_groups probed GET /b2/polls?group_id=N per id: 50–500 calls, blind to groups without polls

1 call per 500 member groups on the native GET /b2/groups; parents included; no blind spots

"How active was user X?"

get_user_activity walked every discussion's event stream: ~200 calls, and 0 on Loomio ≥ 3.4 (endpoint removed)

N + 1 calls for N groups on Loomio's participation report + one author search

"Who is most engaged in these groups?"

Not answerable without reconstructing from polls × memberships

get_participation_report: 1 call for the whole group set

"Read / summarise this thread"

list_events per discussion, paginated, bodies in full

get_thread_markdown (2 calls, +1 per 500 items when the thread has replies; Loomio renders) or get_discussion with include_items (2 calls, +1 per further 500 embedded items)

"What is new anywhere?"

list_discussions per group

list_threads: 1 call across every visible group

"Find the thread about …"

Not available

search_content: 1 call, 20 hits with deep links

Payload size is handled the same way:

  • Side-load profiles. Lists send exclude_types=group parent membership reaction translation (the topics root — where the thread counters live — is kept and joined client-side); shows keep the group for its name and privacy; list_threads sends compact minus tag; rosters, search and thread items by bare topic_id use compact=1, while thread items for a thread whose record is already in hand also exclude discussion (no second copy of the opening post). tag is never excluded where topic rows are read: Loomio gates the rows' tags FIELD on it, not just the side-loaded root. Writes never carry these parameters.

  • Slimming. Users become {id, name, username} (+ email only on the b3 tools); groups keep identity, privacy and counters; reactions, attachment and link-preview metadata are dropped unless asked for; list_polls rows leave the per-voter results[] to get_poll.

  • Body caps with explicit flags. description_max_chars (lists, default 1500) and body_max_chars (thread items, default 4000) cap a record's text and mark it *_truncated: true with the original *_chars; 0 omits the field (*_omitted: true), -1 returns everything. An HTML body longer than its cap is first stripped of tag attributes (Loomio stores target / rel on every link and an id on every heading — 17 % of a capped plain body, over half of a link-dense one; href and alt stay) so the capped characters carry content. max_total_chars (thread items, default 120000) budgets the whole reply and hands back next_offset. max_chars on get_thread_markdown (default 60000) caps the whole document from the END with top-level truncated and chars; -1 returns it whole and 0 is refused. get_* return full text.

  • Compact JSON to the model. Tool results are serialised without indentation (measured 10–37 % smaller than pretty-printed over the 3.8.1 fixtures); LOOMIO_MCP_PRETTY_JSON=1 restores indentation for a human reading a stdio session.

  • Exact totals. Every collection surfaces Loomio's meta.total as total, so "how many" never needs a second page.

Example questions

  • "Check the Loomio connection and tell me which groups you can see." → check_connection

  • "What is new in Loomio since yesterday?" → list_threads

  • "Summarise the discussion about the budget." → search_content, then get_thread_markdown

  • "Who are the coordinators of the Finance group?" → list_groups, list_memberships

  • "Rank the members of these three groups by participation this year." → get_participation_report

  • "How active has Ada Example been since March?" → get_user_activity

  • "What was decided in the last five closed proposals?" → list_polls with status: closed

  • "Post a status update in the release thread." → create_comment (writable mode)

HOWTO.md has longer walk-throughs.

Quick start (stdio, local)

LOOMIO_API_KEY=… npx loomiomcp

Add it to your Claude Desktop / Claude Code config the same way you would any stdio MCP server.

Remote (HTTP)

See DEPLOY.md for Cloud Run. The HTTP server also exposes an unauthenticated GET /health that reports whether Loomio still accepts the connector's API key — 200 {"status":"ok","key_status":"valid",…} or 503 with key_status rejected / unreachable. Point an uptime check at it with content match "key_status":"valid" (DEPLOY.md has the recommended setup); a rotated key is otherwise invisible until someone notices every call failing. The probe behind the page runs under an 8 s deadline, so a hung Loomio becomes a 503 from the connector before the checker gives up. The check_connection tool reports the same verdict to the AI caller.

Auth

Loomio authenticates by API key sent in an HTTP bearer header:

Authorization: Bearer <API_KEY>

The connector injects it server-side; it never reaches the MCP client. Copy the key from the user's API access page in Loomio (/profile/api_access).

The key is not permanent. Loomio regenerates a user's API key whenever that user's password changes (and Loomio 3.3.1, August 2026, rotated every user's key once). When that happens every call answers 403 {"error":"You are not authorized to access this page."}; the connector recognises that body, says "key rejected — probably rotated" with the remediation, and (HTTP) turns /health red. Fetch the current key from the API access page and update LOOMIO_API_KEY. No API can read another user's key.

Keys passed in the query string (?api_key=…) are rejected — Loomio removed that scheme in July 2026 because URLs are retained in browser history, proxy logs, and monitoring systems. A request carrying its key that way is treated as unauthenticated and 403s.

The optional b3 admin namespace uses the same bearer header with a different secret (validated against ENV['B3_API_KEY'] on the Loomio server, >16 chars). Only relevant if you operate a Loomio instance.

What the connector's user can see

Every read runs as the user whose API key is configured. Loomio ≥ 3.8 lets any authenticated user read publicly visible groups' public threads, so get_group, list_discussions, list_polls, list_threads and search_content reach beyond the user's member groups — but list_groups (Loomio's current_user.groups) and the participation report do not, and list_memberships answers a non-member with an empty list. Instance is_admin widens nothing. Poll results follow Loomio's own rule for that user: an open until_vote poll the user has not voted in has its counts stripped (results_visible: false, results_hidden_reason: "until_vote"), the same rule hides vote reasons in search_content hits (snippet_hidden_reason), get_thread_markdown refuses such a thread on Loomio 3.10+ (whose Markdown export stopped applying that rule; on 3.8 / 3.9 the export applies it and nothing is refused), and anonymous polls never reveal who voted what.

Loomio compatibility

Tested against Loomio 3.13.1 (TESTED_LOOMIO_VERSION in src/version.ts; every request shape was first verified on 3.8.1, and the 3.9 → 3.13.1 changes — paged groups and thread items, serializer fields, the request-body handling, the b3 provisioning routes — were checked against Loomio's source and tests and, for the b2 reads, sanitized live captures). Older 3.x releases are still served: where Loomio before 3.11.1 ignores limit / offset and returns a whole collection, the connector detects it and makes one request instead of paging, and on 3.8 / 3.9 — whose Markdown export still hides until_vote results itself — get_thread_markdown skips its vote-visibility guard (when a health probe confirmed the version within the last minute). The five b3 provisioning tools need Loomio 3.13.0 or later. Loomio publishes no API compatibility or deprecation policy and ships tags often, so the connector reads the instance's version from the public GET /api/v1/boot/version at startup and logs a one-time loomio.version_drift warning when the major.minor differs; check_connection repeats the warning in its notes. Every outbound request carries User-Agent: loomiomcp/<version> — if your Loomio sits behind a CDN/WAF, allow that user-agent (the connector recognises a WAF 403 and says so instead of blaming the key).

Read-only mode

Set LOOMIO_MCP_READONLY=1 to register only the 14 read tools. All write tools (create_*, update_*, delete_*, manage_*) and the b3 tools are skipped at server-init time, and the client (loomioPost / loomioPatch / loomioDelete) refuses a write before any request is made even if one were reached. This is the mode the Cloud Run deployment runs in.

Docs map

File

When to read

INSTALL.md

"I want to use this locally with Claude Desktop / Code today"

DEPLOY.md

"I want to run this as a remote HTTP/OAuth endpoint"

HOWTO.md

"I want example prompts and use cases"

DESIGN.md

"I want to understand the load-bearing choices"

NOTES-ON-LOOMIO-API.md

"I'm hitting a weird Loomio behaviour, or want the line-by-line endpoint reference"

SECURITY.md

"I'm doing a security review or rotating secrets"

OPTIMIZATIONS.md

"I want to know what each tool costs upstream, and the observability queries"

CONTRIBUTING.md

"I want to add a tool or send a PR"

CHANGELOG.md

"What changed?"

License

Apache-2.0

Available Tools

24 tools
check_connectionA
Read-onlyIdempotent

Call FIRST when unsure whether the connector works or what it can see: 1 health probe, no input. Returns key_status ('valid' | 'rejected' | 'unreachable'), the key's user, its groups[] with member_state, readonly, b3_enabled and notes[].

NameRequiredDescriptionDefault

No parameters

TDQS

A4.7/5.0
Behavior5/5

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

Beyond the annotations (readOnlyHint, openWorldHint, idempotentHint, destructiveHint false), the description details the exact return values: key_status enum, the key's user, groups[] with member_state, readonly, b3_enabled, and notes[]. This adds substantial behavioral context, especially since there is no output schema.

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

Conciseness5/5

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

The description is a single, densely informative sentence. It front-loads the primary usage instruction ('Call FIRST when unsure...') and then succinctly lists return fields, with no wasted words.

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 simple, no-input health probe, the description provides complete context: when to use it, what it returns, and the key statuses. With annotations covering safety and idempotency, and no output schema, nothing essential is missing for an agent to call it correctly.

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

Parameters4/5

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

The tool takes zero parameters, so the baseline is 4. The description confirms 'no input', which is consistent with the empty schema, but naturally there are no parameters to explain further.

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?

The description states a specific verb ('health probe') and resource ('the connector', 'the key'), and clarifies it checks whether the connector works or what it can see. It is clearly distinguishable from all sibling tools, which deal with polls, discussions, comments, and content.

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

Usage Guidelines4/5

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

It explicitly says to call it FIRST when unsure whether the connector works or what it can see, giving clear situational guidance. However, it lacks explicit when-not-to-use guidance or mention of alternatives, though none are really needed for a unique diagnostic.

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

create_commentA

Post a comment in a thread: 1 call (2 when discussion_id is a short key). Required body (+ body_format: 'html' for HTML; Loomio stores an omitted format as Markdown) and a target: discussion_id for a top-level comment, or parent_id + parent_type ('Comment' for a threaded reply; 'Poll' / 'Stance' / 'Outcome'). A new thread: create_discussion.

NameRequiredDescriptionDefault
bodyYes
parent_idNoComment, Poll, Stance or Outcome id to reply to (needs parent_type).
body_formatNoDefault 'md'; 'html' when `body` is HTML.
parent_typeNoType of parent_id; required with it.
discussion_idNoDiscussion id or short key (+1 call) for a top-level comment; or use parent_id.

TDQS

A4.5/5.0
Behavior4/5

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

Annotations cover the safety profile (readOnly false, destructive false, openWorld true), and the description adds non-derivable operational context: call cost (1 call, 2 when discussion_id is a short key) and the server-side default that an omitted body_format is stored as Markdown. It does not warn about duplicate posts (idempotentHint=false), which is the main remaining gap.

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?

Front-loaded with the action, then packs target rules into one dense sentence with no filler. The telegraphic shorthand ('2 when...', '+1 call') is efficient but slightly compressed relative to plain prose.

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 5-parameter mutation tool with no output schema, the description covers purpose, both target paths, the format default, and the sibling alternative. Only the idempotency/duplicate-posting behavior and any required permissions are unaddressed.

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

Parameters4/5

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

With 80% schema coverage the baseline is 3, but the description adds value beyond the schema by mapping parent_type values to intent ('Comment' for a threaded reply; 'Poll'/'Stance'/'Outcome') and by cross-referencing the dependency between parent_id and parent_type in prose.

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 ('Post a comment in a thread') and immediately scopes it against siblings by naming create_discussion for new threads. The target variants (top-level vs threaded reply) further pin down what this tool does that create_poll/update_comment do not.

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?

Explicitly states which parameter combination selects which behavior: discussion_id for a top-level comment, parent_id + parent_type for a reply, and create_discussion for a brand-new thread. An agent can route correctly without opening any schema.

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

create_discussionA

Start a discussion in a group: 1 call. Required title, group_id; optional description (+ description_format: 'html' for HTML), private (omit for the group's default), tags, recipients. Returns id, key, url, topic_id. Check list_discussions for duplicates first; to add to a thread use create_comment.

NameRequiredDescriptionDefault
tagsNo
titleYes
privateNotrue = members only, false = public. Default: the group's setting.
group_idYes
descriptionNo
recipient_emailsNoEmails to notify; non-members become guests.
notify_recipientsNofalse = add recipients without emailing them.
recipient_messageNoText for the notification.
description_formatNoDefault 'md'; 'html' when `description` is HTML.
recipient_audienceNo'group' = notify the whole group.
recipient_user_idsNoUser ids to notify.

TDQS

A4.4/5.0
Behavior4/5

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

Annotations already declare non-read-only, non-idempotent, non-destructive, open-world, so the safety profile is covered. The description adds real value beyond that by listing the returned fields (id, key, url, topic_id) despite there being no output schema, and by noting the '1 call' cost and that omitting private falls back to the group default. It does not explicitly state that recipients are emailed by default (notify_recipients semantics), which the schema carries instead.

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

Conciseness5/5

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

A single dense paragraph that front-loads the action, then parameters, then return values, then routing. Every clause carries information; nothing is filler.

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 an 11-parameter mutation tool with no output schema, the description supplies the return fields and the required/optional split, which fills the biggest gap. It is slightly thin on the recipient-notification behavior and permissions/auth needs, but nothing essential for a correct call is missing.

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 coverage is 64% and the description groups parameters into required (title, group_id) and optional, and clarifies description_format usage and the private default. However, most of that repeats defaults already in the schema, and it says only 'recipients' for the four recipient-related parameters (emails, ids, audience, message, notify flag), leaving the notification surface to the schema.

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?

States a specific verb and resource ('Start a discussion in a group') and immediately scopes it against siblings by naming create_comment as the tool for threading and list_discussions for dedupe. An agent can distinguish this from create_poll or create_comment without opening any 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?

Explicitly tells the agent to check list_discussions for duplicates first and to use create_comment to add to an existing thread. That is a clear when-to-use precondition plus named alternatives, which is exactly what routing guidance should look like.

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

create_pollA

Create a poll: 1 call (2 with discussion_id). Required title, poll_type, options (NO default options; only 'question' takes none) and group_id (standalone) or discussion_id / topic_id (inside a thread). closing_at (ISO-8601, future) is effectively REQUIRED: without it Loomio saves the poll UNOPENED and the result carries opened: false and a warning. An opening poll is announced because notify_on_open defaults to TRUE; pass notify_on_open: false for a quiet create.

NameRequiredDescriptionDefault
tagsNoTags for a standalone poll's thread.
titleYes
detailsNo
optionsNoOption names in order. REQUIRED except 'question'; ranked_choice/stv need 2+.
group_idNoGroup of a STANDALONE poll (else only cross-checked).
topic_idNoThread id (`topic_id` on any thread row); no extra call.
anonymousNoPermanent. Needs closing_at; not for count, question, meeting.
max_scoreNoscore: max per option (default 5); meeting: 2.
min_scoreNoscore: min per option (default 0).
poll_typeYes'question' has no options; 'meeting' options are ISO-8601 times; 'stv' takes stv_seats.
stv_seatsNostv: seats to fill.
closing_atNoISO-8601, future. Effectively REQUIRED: omitted = unopened draft.
hide_resultsNoDefault 'off'; anonymous polls force 'until_closed'.
discussion_idNoDiscussion to attach to (id or key; +1 call). Prefer topic_id.
reason_promptNoPrompt above the reason box.
details_formatNoDefault 'md'; 'html' when `details` is HTML.
notify_on_openNoAnnounce (`poll_announced`) on opening; Loomio's default is TRUE.
dots_per_personNodot_vote: points per voter (default 8).
shuffle_optionsNoRandomise option order per voter.
meeting_durationNomeeting: slot minutes. No default through the API.
recipient_emailsNoEmails to invite; non-members become guests.
can_respond_maybeNomeeting: allow 'maybe'. API default is false.
notify_recipientsNoEmail/push the named recipients; SEPARATE from notify_on_open.
recipient_messageNoText for the notification.
recipient_audienceNo'group' = every member (needs announce permission).
recipient_user_idsNoUser ids to invite / notify.
specified_voters_onlyNoOnly the named recipients may vote.
maximum_stance_choicesNoMax options per voter (poll: 1; raise for multi-choice).
minimum_stance_choicesNoMin options per voter (ranked_choice: ranks, default 3).
notify_on_closing_soonNo24h closing reminder; API default is 'nobody'.
show_none_of_the_aboveNoOffer 'none of the above' (poll, ranked_choice).
stance_reason_requiredNoDefault 'optional'; anonymous polls force 'disabled'.

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already cover safety profile, but the description adds critical behaviors beyond them: call count, that omitting closing_at produces an unopened poll with opened:false and a warning, and that notify_on_open defaults to TRUE requiring explicit false for a quiet create. These are meaningful operational disclosures.

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

Conciseness5/5

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

Four dense sentences front-load the creation summary and required parameters with no filler, using inline code for parameter names. Appropriately sized for a 32-parameter tool.

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?

Given the rich schema and annotations, the description covers the critical creation-time behavioral traps (unopened draft, default announcement) and call-count implications. No output schema exists, but the description mentions key result fields, making it complete for an agent's needs.

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

Parameters4/5

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

Schema description coverage is 94%, so baseline is 3. The description adds operational meaning for requiredness of options/closing_at and the quiet-create toggle, plus the call-count impact of discussion_id, though much of the parameter guidance duplicates existing schema descriptions.

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?

States a specific verb ('Create') and resource ('poll'), distinguishes standalone vs thread call counts, and names the required fields. Sibling tools are other poll operations (get/update/delete/list), so the creation intent is unambiguous.

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

Usage Guidelines4/5

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

Provides clear context for when poll creation applies, including standalone vs inside-thread routes and the effectively-required closing_at condition. It does not explicitly name alternatives or when-not-to-use scenarios, so it falls short of full routing guidance.

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

delete_commentA
DestructiveIdempotent

Discard a comment by numeric id: 1 call. Loomio's SOFT delete: the body is blanked and the comment leaves the thread view, but nothing is permanently erased (restorable in Loomio). For a wording fix prefer update_comment.

NameRequiredDescriptionDefault
idYes

TDQS

A4.7/5.0
Behavior5/5

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

Annotations declare destructiveHint=true and idempotentHint=true, but the description adds crucial nuance the annotations cannot: this is a Loomio SOFT delete, the body is blanked, the comment leaves the thread view, and the change is restorable. That materially changes how an agent should reason about irreversibility.

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

Conciseness5/5

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

Three short sentences, front-loaded with the action, then the soft-delete semantics, then the alternative. No filler; each clause carries distinct information.

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?

No output schema, but annotations cover the safety profile and the description covers the rest: action, target, reversibility, and the sibling alternative. An agent has everything needed to call this correctly and to avoid it when updating text instead.

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 coverage is 0% and there is one parameter. The description identifies `id` as a numeric comment identifier, which matches the schema's integer type and clarifies the target entity, but adds no format, range, or lookup detail beyond that. With one trivially typed param, this is adequate but not compensatory.

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?

States a specific verb and resource ('Discard a comment by numeric `id`') and differentiates from the sibling update_comment as well as the broader delete_* family. An agent can identify the operation 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?

Explicitly routes the agent: 'For a wording fix prefer update_comment' names the alternative and the condition that selects it. Usage is thereby bounded rather than left to inference.

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

delete_discussionA
DestructiveIdempotent

Discard a discussion by id_or_key: 1 call. Loomio's SOFT delete: it leaves every list and its text is blanked, but nothing is permanently erased (a group admin can restore it). For a text mistake prefer update_discussion.

NameRequiredDescriptionDefault
id_or_keyYesDiscussion id or short key.

TDQS

A4.5/5.0
Behavior5/5

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

Annotations only say destructive/idempotent/not-read-only; the description goes well beyond them by disclosing that this is a SOFT delete, that the discussion leaves every list, that its text is blanked, that nothing is permanently erased, and that a group admin can restore it. That reversibility and side-effect detail is exactly what an agent needs before invoking a destructive tool.

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

Conciseness5/5

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

Two tight sentences, front-loaded with the verb and target, then the soft-delete semantics, then the alternative. No filler; every clause carries operational information.

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 single-parameter destructive tool with no output schema, the description covers effect (blanked text, removed from lists), reversibility (admin restore), and the sibling alternative. Nothing an agent needs in order to call it correctly is missing.

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 single parameter is documented there ('Discussion id or short key'), so the schema already carries the semantics. The description references `id_or_key` but adds no format or lookup detail, so the baseline 3 applies.

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?

States a specific verb ('Discard') and resource ('a discussion') plus the identifier used to target it. It also distinguishes itself from a sibling ('prefer update_discussion' for text mistakes), so an agent can separate delete from update without opening schemas.

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

Usage Guidelines4/5

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

Explicitly names the alternative (update_discussion) and the condition selecting it (a text mistake), which is real routing guidance. It lacks broader when-not guidance (e.g., delete vs. archive/lock, or confirm-before-delete norms), so it stops short of fully explicit when/when-not coverage.

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

delete_pollA
DestructiveIdempotent

Discard a poll by id_or_key: 1 call. Loomio's SOFT delete: the poll and its votes leave the thread view, but nothing is permanently erased (a group admin can restore it). To end voting set closing_at to the next full hour with update_poll; no immediate close exists.

NameRequiredDescriptionDefault
id_or_keyYesPoll id or short key.

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare destructiveHint=true and idempotentHint=true, so the safety profile is known. The description adds real value by qualifying that destruction: it is a SOFT delete, items leave the thread view but are recoverable by a group admin. It does not explain the response body, but the destructive-scope nuance is the important missing piece and it is supplied.

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

Conciseness5/5

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

Two tightly packed sentences: the action and its soft-delete consequence come first, then the alternative routing. The '1 call' fragment is the only mildly extraneous element.

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?

No output schema exists, and none is needed for a delete. The description covers what is removed, what is recoverable, by whom, and how to achieve the adjacent goal (closing a poll), which is everything an agent needs before invoking it.

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 coverage is 100% and the single parameter is already documented as 'Poll id or short key.' The description only names id_or_key without adding format, accepted-key, or lookup semantics, so it does not exceed the schema baseline.

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?

States a specific verb and resource ('Discard a poll') plus the identifying parameter, and immediately clarifies the scope of the operation. An agent can distinguish this from delete_comment, delete_discussion, and update_poll 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?

Explicitly routes the agent away from this tool for a common mis-use case: ending voting is done via update_poll with closing_at, and it notes 'no immediate close exists'. That is a concrete when-not-to-use plus the named alternative.

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

get_discussionA
Read-onlyIdempotent

One discussion by numeric id or short key: 1 call (2+ with include_items). Returns the full body, topic_id, counters, url; include_items: true embeds list_thread_items as thread_items (items_limit; next_offset says when to page on). Prose: get_thread_markdown; a group's threads: list_discussions.

NameRequiredDescriptionDefault
id_or_keyYesDiscussion id or short key.
strip_htmlNoPlain text instead of HTML; default false.
items_limitNoWith include_items: items to embed (default 200, max 1000).
include_itemsNoAlso embed the thread's items as `thread_items` (+1 call per 500 items).
items_body_max_charsNoWith include_items: chars per body (default 4000; 0 omits, -1 full).

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already cover read-only, idempotent, non-destructive, open-world, so the safety bar is met without the text. The description adds genuinely useful behavior beyond them: a call-cost model ('1 call (2+ with include_items)', '+1 call per 500 items') and the fact that next_offset signals when to page — real operational context an agent needs.

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

Conciseness5/5

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

Front-loads the resource and call cost, then return shape, then opt-in embedding, then sibling routing. Extremely dense with no filler; every clause carries a fact about identity, cost, return value, or alternative.

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?

With no output schema, the description still enumerates what comes back (full body, topic_id, counters, url) and how paging works. For a 5-param read tool with full schema coverage and annotation coverage, nothing an agent needs to call it correctly is missing.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3, but the description adds cross-parameter behavior the schema cannot express: include_items embeds list_thread_items output as thread_items, and items_limit interacts with the returned next_offset for paging. id_or_key and strip_html semantics are left to the schema, which fully documents them.

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?

States a specific verb+resource ('One discussion by numeric id or short key') and immediately notes the single-item scope vs. the batched variant. It is instantly distinguishable from list_discussions, get_thread_markdown, and the create/update/delete siblings.

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

Usage Guidelines4/5

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

Explicitly routes to two alternatives: get_thread_markdown for prose and list_discussions for a group's threads. The conditions are stated but in compressed telegraphic form ('Prose: get_thread_markdown'), leaving the agent to infer exactly when markdown beats the HTML body.

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

get_groupA
Read-onlyIdempotent

One group by numeric id, short key or URL handle: 1 call; works for public groups the user has not joined. Returns the full record (members_can_* flags included, billing subscription dropped) plus member, membership, parent, url. 403 'Not authorized to show Group.' = hidden from this user; unknown = 404.

NameRequiredDescriptionDefault
id_or_key_or_handleYesGroup numeric id, short key or URL handle.

TDQS

A4.5/5.0
Behavior5/5

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

Adds substantial behavior beyond the annotations: cost (1 call), visibility rules for unjoined public groups, the exact field set returned (members_can_* included, billing subscription dropped, plus member/membership/parent/url), and error semantics (403 = hidden from user, 404 = unknown). This is exactly the destructive/edge-case context the readOnly/idempotent hints don't cover.

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

Conciseness5/5

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

Three dense sentences, front-loaded with the identifier forms, then scope, then return shape, then error codes. No filler; every clause carries load-bearing information.

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?

With no output schema, the description fully covers the return payload and the error taxonomy, plus the non-member visibility nuance. Nothing an agent needs to call and interpret the result is missing.

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 coverage is 100% and the description's 'numeric id, short key or URL handle' essentially restates the schema's parameter description. No additional syntax or format detail is added, so the baseline 3 applies.

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?

States a specific resource (a single group) and the accepted identifier forms (numeric id, short key, URL handle), making it clearly distinguishable from the sibling list_groups ('One group' vs a list). An agent can select it correctly 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 Guidelines4/5

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

Provides clear usage context that it 'works for public groups the user has not joined,' which tells the agent this is usable even without membership. It does not explicitly name list_groups as the alternative for multiple groups, so it stops short of full when/when-not routing.

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

get_participation_reportA
Read-onlyIdempotent

Rank the members of a group set: 1 call returns Loomio's own report, a row per user sorted by total desc. Scope by group_ids (1-50, counted together) or group_scope: 'my'; top limit rows (default 50, max 500), zero-activity users dropped unless include_inactive. Anonymous polls are excluded; a vote counts in the month its counted stance row was created.

NameRequiredDescriptionDefault
limitNoRows after sorting by total desc (1-500). Default 50.
end_monthNoLast month, YYYY-MM. Default: the current month.
group_idsNoGroup ids counted together (1-50); required unless group_scope 'my'.
group_scopeNo'custom' (default) = given group_ids; 'my' = the user's groups.
start_monthNoFirst month, YYYY-MM. Default: 11 months before end_month.
delegates_onlyNoOnly users with an active delegate role. Default false.
include_inactiveNoAlso list users with total 0 (ever-members). Default false.

TDQS

A3.7/5.0
Behavior4/5

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

Annotations already cover safety (readOnly, idempotent, non-destructive), so the description adds genuinely useful behavior beyond them: anonymous polls excluded, zero-activity users dropped unless include_inactive, and the non-obvious counting rule that a vote counts in the month its counted stance row was created. These are real disclosure wins for a reporting tool.

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?

Two dense sentences pack the ranking behavior, scoping options, and counting caveat up front with essentially no filler. The semicolon-chained clauses are information-dense rather than wasteful, though slightly cramped.

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?

No output schema exists, so the description carries return-shape burden: it tells the agent it gets one row per user with a `total` value sorted descending, which is the key information. Full column set is not enumerated, but for a 7-param report this is close to sufficient.

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 coverage is 100%, so the schema already documents all 7 parameters including ranges and defaults. The description largely restates them ('default 50, max 500', group_ids 1-50) and only marginally adds the 'counted together' relationship, so baseline 3 is warranted.

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+resource ('rank the members of a group set') and clarifies it returns Loomio's own report, one row per user sorted by `total` desc. It is unambiguous, though it does not explicitly name a sibling such as get_user_activity to differentiate the two activity-style tools.

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?

Explains how to scope the report ('group_ids' counted together or 'group_scope: my'), which implies usage, but never states when to choose this tool over alternatives like get_user_activity or get_group. Inclusion rules (include_inactive, zero-activity dropped) give some operational guidance but no true when-to-use framing.

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

get_pollA
Read-onlyIdempotent

One poll by numeric id or short key: 1 call. Returns poll (full details, topic_id), poll_options[], current_outcome, my_stance, url; votes as items: list_thread_items with its topic_id. Results gate: results_visible / results_hidden_reason say whether tallies are present; absent counts mean HIDDEN, never zero.

NameRequiredDescriptionDefault
id_or_keyYesPoll id or short key.
strip_htmlNoPlain text instead of HTML; default false.

TDQS

A4.4/5.0
Behavior5/5

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

Annotations already cover the safety profile (readOnly/openWorld/idempotent/non-destructive), and the description adds behavior annotations cannot: the results-gating fields and the critical gotcha that 'absent counts mean HIDDEN, never zero.' This is exactly the kind of disclosure that prevents misinterpretation.

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?

Front-loaded with purpose, then return shape, then sibling routing, then the gating caveat — dense and waste-free. The return-field enumeration is verbose but earned given the absence of an output schema.

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?

With no output schema, the description carries the return-value burden and does so fully: it names the key fields, the gating fields, and how to fetch votes. Nothing an agent needs to call and interpret this tool is missing.

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%, so both parameters are already documented ('Poll id or short key', 'Plain text instead of HTML; default false'). The description restates the id/key duality but adds no syntax or format detail beyond the schema, so the baseline 3 applies.

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?

States a specific verb+resource ('One poll') and its lookup modes ('by numeric id or short key'), immediately distinguishing it from list_polls, create_poll, and update_poll. An agent knows exactly what it retrieves 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 Guidelines4/5

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

Clear context for single-item retrieval and a concrete routing hint for related data ('votes as items: list_thread_items with its topic_id'), which steers the agent to the right sibling for votes. However, it never explicitly contrasts itself with list_polls or states exclusions.

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

get_thread_markdownA
Read-onlyIdempotent

The best single call for 'summarise / read this thread': 2 calls by topic_id, discussion_id or poll_id (pass exactly one; +1 per 500 items when the thread has replies) return Loomio's server-rendered Markdown (markdown, chars, truncated). REFUSED while an open poll hides results until the user votes: use list_thread_items. max_chars cuts the END: truncated: true = newest items dropped.

NameRequiredDescriptionDefault
poll_idNoPoll id or short key.
topic_idNoThread id (`topic_id` on any thread row, NOT `id`).
max_charsNoCap in chars; default 60000, -1 = all. Cuts the END; `truncated` flags it.
discussion_idNoDiscussion id or short key.

TDQS

A4.8/5.0
Behavior5/5

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

Beyond annotations (readOnly/idempotent/openWorld), it discloses the call-cost multiplier, a hard refusal condition tied to poll result hiding, and precise truncation semantics — max_chars cuts the END and `truncated: true` means the newest items were dropped. That is real operational context an agent cannot get 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.

Conciseness4/5

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

Front-loaded with the primary use case and tightly written with no filler. It is telegraphic to the point of density (backticked fragments, parentheticals), which slightly taxes first-read comprehension, but nothing is wasted.

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?

With no output schema, the description still names the return fields (`markdown`, `chars`, `truncated`), covers the failure mode, and explains the truncation contract. Nothing an agent needs to invoke this correctly is missing.

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

Parameters4/5

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

Schema coverage is 100%, so baseline is 3; the description adds the crucial exclusivity rule ('pass exactly one') that the schema's flat property list does not enforce, plus the cost impact of thread size. The max_chars explanation largely restates the schema text, keeping it below a 5.

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?

States a specific verb and resource ('read/summarise this thread') and names exactly what it returns: Loomio server-rendered Markdown plus `markdown`, `chars`, `truncated`. It also distinguishes itself from the sibling `list_thread_items`, so an agent can choose without opening either 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?

Explicit routing rules: 'pass exactly one' of topic_id/discussion_id/poll_id, and an explicit when-not case — REFUSED while an open poll hides results, use list_thread_items instead. It also gives the cost model (2 calls, +1 per 500 items) which informs the use/don't-use decision.

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

get_user_activityA
Read-onlyIdempotent

One person's participation across groups: 1 call per group + 1. Required user_id (from list_memberships or a participation row) and group_ids. Returns counts, by_group, sample_events (newest items with urls, not a full list). MONTH-GRAINED: since / until widen to whole months; anonymous polls never count; a vote counts in the month its counted stance row was created.

NameRequiredDescriptionDefault
sinceNoISO-8601 start, rounded down to its month. Default: all history.
untilNoISO-8601 end (exclusive), rounded out to a whole month. Default: now.
user_idYesLoomio user id.
group_idsYesGroup ids (1-50); 1 call each.

TDQS

A4.1/5.0
Behavior5/5

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

Annotations already declare this as a safe idempotent read, so the bar is lower, yet the description adds substantial domain behavior: month-graining that widens since/until to whole months, the rule that anonymous polls never count, and the stance-row month attribution rule for votes. It also discloses that sample_events are newest items with urls, not a full list.

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?

Front-loaded with purpose, then required params, then return shape, then the MONTH-GRAINED rules; every clause carries information. It is dense and heavy on terse fragments, but nothing is wasted.

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 four-parameter read tool with no output schema, the description covers the return fields (counts, by_group, sample_events), the date-windowing semantics, the counting rules, and where inputs are sourced. An agent has everything needed to call it correctly.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3, but the description adds meaning beyond the schema by specifying the source of user_id (list_memberships or a participation row) and the month-widening consequence of since/until. The '1 call each' note on group_ids largely restates the schema.

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: retrieving one person's participation across groups. The '1 call per group + 1' batching detail is distinctive and helps an agent understand the call pattern. It implies but does not explicitly name the contrast with get_participation_report, so it stops short of full sibling differentiation.

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?

It tells the agent where required values come from (user_id from list_memberships or a participation row), which is useful workflow guidance. However, it never says when to prefer this per-user view over the sibling get_participation_report, nor states any exclusions, leaving the when-to-use decision implied.

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

list_discussionsA
Read-onlyIdempotent

One group's discussions, newest activity first: 1 call. status defaults to 'open'; description_max_chars (default 1500) caps bodies, description_truncated: true marking a cut; strip_html (default true) gives plain text. Every visible group: list_threads; keywords: search_content. Subgroups are excluded.

NameRequiredDescriptionDefault
limitNoPage size, 1-200. Default 50.
offsetNoPage offset. Default 0.
statusNo'open' (default) = unlocked, 'closed' = locked, 'all'.
group_idYes
strip_htmlNoPlain text instead of HTML; default true.
description_max_charsNoChars per `description`; default 1500, 0 omits it, -1 = full.

TDQS

A3.6/5.0
Behavior3/5

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

Annotations already cover readOnlyHint, idempotentHint, openWorldHint, and destructiveHint=false, so the safety profile is clear. The description adds behavioral details: default status filtering, pagination hints via '1 call', and default strip_html. However, return format and pagination behavior are only partially addressed. Baseline 3 appropriate given annotations carry the core traits.

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?

Efficient and front-loaded: the primary purpose and scoping are stated first, followed by parameter defaults and related tools. No wasted words. Could be tightened further but is well structured.

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

Completeness3/5

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

For a list tool with annotations covering safety and schema covering most parameters, the description provides useful defaults and scoping. However, it does not mention pagination behavior (offset/limit interaction) or return structure, and there is no output schema. Adequate but incomplete for an agent needing to call correctly.

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 coverage is 83%, so most parameters are already described. The description adds behavioral nuance for status default, description_max_chars truncation behavior (with description_truncated flag), and strip_html default, which goes beyond schema. This is slightly above baseline but not rich enough for a 4.

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+resource ('One group's discussions') and a scope constraint (subgroups excluded). Does not explicitly name the related sibling get_discussion or list_threads, though it does reference list_threads in a different context. Distinguishable from siblings but not maximally so.

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

Usage Guidelines4/5

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

Names alternatives: 'Every visible group: list_threads' and 'keywords: search_content', giving clear when-to-use context for related tasks. Lacks explicit when-not or differentiation from get_discussion / list_polls. Adequate but not comprehensive.

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

list_groupsA
Read-onlyIdempotent

The connector user's groups, pending invitations included: 1 call, no input. Subgroup parents are appended with member: false. get_group reads one record, even a public group this list omits.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.4/5.0
Behavior4/5

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

Annotations already declare readOnly/idempotent/non-destructive/openWorld, so safety is covered. The description adds return-shape behavior not in structured fields: pending invitations are included and subgroup parents are emitted with `member: false`. It does not discuss auth requirements or pagination, but adds genuine value beyond annotations.

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

Conciseness5/5

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

Three tight clauses, output scope front-loaded, then the call cost, then the sibling contrast. No filler anywhere.

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?

No output schema exists, so the description carries the return-value burden, and it does explain two notable return behaviors. Pagination and ordering are unaddressed, which is a minor remaining gap for a list tool.

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

Parameters4/5

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

Zero parameters, so the baseline is 4; the description confirms 'no input', which matches the empty schema. Nothing further is needed.

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?

States a specific resource (the connector user's groups) with scope detail (pending invitations included) and explicitly distinguishes itself from the sibling get_group, which 'reads one record'. An agent can pick between list_groups and get_group without opening either schema.

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

Usage Guidelines4/5

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

The contrast with get_group ('get_group reads one record, even a public group this list omits') gives clear routing guidance for the list-vs-single-record choice. It does not spell out broader when-not-to-use cases, but the key sibling decision is covered.

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

list_membershipsA
Read-onlyIdempotent

A group's members: 1 call. Returns memberships[] (user_email only where the connector's user is a group admin), users[], total. Use it to resolve a name to a user_id and ALWAYS before manage_memberships with remove_absent. A non-member gets an EMPTY list, not 403: never report it as an empty group.

NameRequiredDescriptionDefault
limitNoPage size, 1-200. Default 50.
offsetNoPage offset. Default 0.
group_idYesGroup id (a non-member gets an empty list, not 403).

TDQS

A4.4/5.0
Behavior5/5

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

Annotations already cover the safety profile (readOnly, idempotent, non-destructive), and the description adds real behavioral nuance beyond them: user_email is only populated when the caller is a group admin, and a non-member receives an empty list rather than 403 — plus an explicit instruction not to misreport that as an empty group. This is exactly the kind of context annotations cannot convey.

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?

Dense but front-loaded: scope, return shape, then usage rules, then the empty-list caveat. Nearly every clause earns its place, though the return-shape enumeration is tightly packed and slightly cluttered with backticks.

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?

With no output schema, the description compensates by naming the returned keys (memberships[], users[], total) and the admin-conditional field, plus the critical empty-list edge case. An agent has everything needed to call and interpret this correctly.

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 coverage is 100%, and the schema already documents limit, offset, and the group_id non-member/empty-list behavior. The description's group_id remark largely restates the schema, adding no new syntax or format detail. Baseline 3 applies when the schema carries the parameter burden.

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 resource and scope ('A group's members: 1 call') with a clear read verb implied by 'list_memberships'. It is distinguishable from list_groups/get_group, though the differentiation from manage_memberships comes through the usage note rather than the purpose statement itself.

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?

Explicitly states two use cases: resolving a name to a user_id, and the mandatory pre-step before manage_memberships with remove_absent. Names the sibling tool and the exact condition that requires this call first.

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

list_pollsA
Read-onlyIdempotent

One group's polls, newest first: 1 call. status defaults to 'active' ('closed', 'all'); description_max_chars caps details; strip_html (default true). Slim rows with poll_options[] and stance_counts when visible (trust results_visible). For member activity use get_participation_report, not this plus list_memberships.

NameRequiredDescriptionDefault
limitNoPage size, 1-200. Default 50.
offsetNoPage offset. Default 0.
statusNo'active' (default) = open, 'closed', 'all'.
group_idYes
strip_htmlNoPlain text instead of HTML; default true.
description_max_charsNoChars per `details`; default 1500, 0 omits it, -1 = full.

TDQS

A4.4/5.0
Behavior4/5

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

Annotations already declare readOnly/idempotent/non-destructive, so the bar is lower, and the description adds real behavioral detail beyond them: newest-first ordering, slim rows, conditional presence of poll_options[] and stance_counts gated by results_visible, and the effect of strip_html on details. It still doesn't state pagination behavior or total counts, so not a 5.

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?

Dense and front-loaded, with the scope/ordering statement first and the routing caveat last. The telegraphic fragment style ('1 call', semicolon-joined clauses) is efficient but slightly cryptic and costs a point on readability.

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?

With no output schema, the description compensates by describing the row shape (poll_options[], stance_counts, results_visible) and the `details` field. Combined with schema-documented paging and defaults, an agent has everything needed to call it correctly.

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

Parameters4/5

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

Schema coverage is 83%, so the baseline is 3. The description adds semantic links the schema doesn't make on its own: that description_max_chars caps `details` specifically, and that strip_html controls whether details come back as HTML.

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?

States a specific verb and resource with scope and ordering: 'One group's polls, newest first: 1 call.' It is immediately distinguishable from siblings like get_poll (single poll), create_poll, and list_groups.

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

Usage Guidelines4/5

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

Explicitly routes the agent away from a tempting wrong path: 'For member activity use get_participation_report, not this plus list_memberships.' That is a clear when-not plus a named alternative, though it covers only this one scenario and says nothing about paging or when to prefer get_poll.

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

list_thread_itemsA
Read-onlyIdempotent

One thread as structured items: 1 call per 500 items by topic_id (+1 by discussion_id or poll_id; pass exactly one). Returns items[] in thread order plus the comments, polls, stances (votes), outcomes and users they reference. Paged by limit / offset under the max_total_chars budget (kinds reads the whole thread); next_offset says where to continue.

NameRequiredDescriptionDefault
kindsNoItem kinds to keep (e.g. new_comment, stance_created); 'other' = unlisted kinds.
limitNoItems after the kinds filter; default 200, max 1000.
offsetNoItems to skip. Default 0.
poll_idNoPoll id or short key (+1 call).
topic_idNoThread id (`topic_id` on any thread row, NOT `id`); no extra call.
strip_htmlNoPlain text instead of HTML bodies, reasons, outcomes; default true.
discussion_idNoDiscussion id or short key (+1 call); prefer `topic_id`.
body_max_charsNoChars per `body`; default 4000, 0 omits it, -1 = full.
max_total_charsNoReply budget in chars; default 120000, -1 = none. See next_offset.
include_reactionsNoAlso return emoji `reactions`. Default false.

TDQS

A4/5.0
Behavior4/5

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

Even with annotations covering readOnly/idempotent safety, the description adds real behavioral context: the extra API call cost per 500 items for topic_id versus +1 for discussion/poll id, the `max_total_chars` reply budget, and default `strip_html` behavior. It doesn't disclose error behavior when multiple ids are passed, so it stops short of 5.

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?

Front-loaded with the core purpose and return shape, then pagination. Dense with parenthetical clauses, but each earns its place; only the stacking of several asides makes it slightly harder to scan than ideal.

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 10-parameter read tool with no output schema, the description covers return shape, ordering, paging semantics, and budget behavior. Gaps remain around item ordering guarantees and multi-id error handling, but nothing critical is missing for a correct call.

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

Parameters4/5

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

Schema coverage is 100%, so baseline is 3; the description earns above that by stating the otherwise-invisible constraint that exactly one id must be passed (schema marks zero required), plus the call-cost differential and the note that `kinds` ignores paging. This adds genuine meaning beyond the schema.

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+resource: returns 'one thread as structured items,' distinguishing it implicitly from the markdown-oriented sibling by emphasizing structured `items[]` plus referenced comments/polls/stances/users. It does not explicitly name an alternative sibling, which keeps it from a 5.

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

Usage Guidelines4/5

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

Gives a concrete selection rule for the three id parameters ('pass exactly one', 'prefer topic_id') and explains pagination continuation via `next_offset`. It lacks explicit when-not guidance against siblings like get_thread_markdown or search_content, so not a 5.

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

list_threadsA
Read-onlyIdempotent

The cheapest 'what is new across everything I can see': 1 call lists every visible thread, newest activity first (topic_id, type, title, group_id, counters, url; no bodies). group_id / type filter client-side within the page (one group: list_discussions / list_polls). No date filter upstream: pass since and page with offset until scope.exhausted.

NameRequiredDescriptionDefault
typeNoKeep only Discussion or Poll threads (client-side).
limitNoThreads per page, default 20, max 100.
sinceNoISO-8601 cutoff on last_activity_at; page until scope.exhausted.
offsetNoThreads to skip. Default 0.
group_idNoOnly this group's threads (client-side; `total` stays instance-wide).

TDQS

A4.6/5.0
Behavior4/5

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

Annotations already cover the safety profile (readOnly, idempotent, non-destructive), yet the description adds real behavioral context: no bodies returned, `group_id`/`type` filtering happens client-side within the page, and `total` stays instance-wide. What it does not fully disclose is what `scope.exhausted` actually contains or how many pages a typical traversal needs.

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?

Front-loaded with the value proposition and return shape, then the caveats; no filler sentences. The parenthetical stacking makes it dense and slightly hard to parse, but nothing is padding.

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?

With no output schema, the description carries the return-value burden and does so by listing the key fields and noting the absence of bodies. Combined with the paging loop and client-side filter caveats, an agent has enough to call it correctly.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3, but the description goes further by explaining that `group_id`/`type` are client-side filters and that `since`/`offset` drive a page-until-exhausted loop. This adds operational meaning (especially that `total` is not scoped to the filter) beyond the per-parameter schema text.

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?

States a concrete verb+resource ('lists every visible thread, newest activity first') and enumerates the returned fields, so the agent knows exactly what comes back. It also distinguishes itself from the single-group siblings by naming list_discussions / list_polls as the narrower alternatives.

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?

Positions the tool as 'the cheapest what-is-new across everything I can see' and explicitly routes single-group lookups to list_discussions / list_polls. It even prescribes the paging pattern (pass `since`, page with `offset` until `scope.exhausted`), so when and how to use it is unambiguous.

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

manage_membershipsA
Destructive

Invite users to a group by email and optionally REMOVE members not in the list: 1 call; the connector's user must be an admin of THAT group. Default is additive: new addresses are invited, nobody is touched. DANGEROUS: remove_absent: true removes every member whose email is absent, own user included, with no undo: list_memberships first, then confirm the diff with a human.

NameRequiredDescriptionDefault
emailsYesAddresses to ensure are members; new ones are invited.
group_idYesGroup id; the connector's user must be its admin.
remove_absentNoDANGEROUS: also revokes every member absent from `emails`, own user included. Default false.

TDQS

A4.6/5.0
Behavior5/5

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

Goes well beyond annotations (which only flag destructive=true, idempotent=false): it names the admin-of-that-group auth requirement, the self-removal hazard ('own user included'), and the irreversibility ('no undo'). This is exactly the safety context an agent needs before a destructive mutation.

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?

Front-loaded with the core action and default, then the danger. It is dense but each clause earns its place; the '1 call' phrase is slightly superfluous but minor.

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 3-param destructive mutation with no output schema, the description covers auth requirements, default vs destructive semantics, self-inclusion, irreversibility, and a recommended pre-check workflow. Nothing an agent needs to invoke it safely is missing.

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 coverage is 100%, and the schema already documents emails ('ensure are members'), group_id (admin requirement), and remove_absent (dangerous/default false). The description largely restates that, so it earns the baseline 3 rather than adding new parameter meaning.

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?

States specific verbs (invite, remove) and resource (group memberships by email), and explicitly contrasts the additive default against the destructive remove mode. An agent can distinguish it from list_memberships without opening either 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?

Explicitly tells the agent that the default is additive ('nobody is touched') and that remove_absent:true triggers destructive removal, then prescribes a workflow: call list_memberships first and confirm the diff with a human. When-to-use and when-to-be-careful are both stated.

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

search_contentA
Read-onlyIdempotent

Keyword search over everything the connector's user can see: 1 call. Pass query (prefix match; !word excludes), author_id alone for 'what did X post', or both. Hard cap: at most 20 results, no paging; capped: true means narrow the search. Stance hits on polls with hidden results are DROPPED from a query search; never use search to probe hidden vote reasons.

NameRequiredDescriptionDefault
tagNoOnly items tagged with this exact name (Loomio < 3.13: every item of a tagged thread).
orderNoauthored_at_desc (default), authored_at_asc, relevance.
queryNoFull-text query (prefix match; `!word` excludes).
typesNoRecord types to include; default all (e.g. ['Outcome']).
group_idNoRestrict to one group (not its subgroups).
author_idNoRestrict to one author; alone = their 20 newest items.

TDQS

A4.3/5.0
Behavior3/5

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

Annotations already declare readOnlyHint, idempotentHint and destructiveHint=false, so the safety profile is covered structurally. The description adds genuinely new behavior beyond that: a hard cap of 20 results with no paging, the meaning of `capped: true`, and that stance hits on polls with hidden results are DROPPED from query searches. That is real added value, though the return shape of individual results is left unstated.

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?

Dense and front-loaded: scope, then parameter modes, then the cap, then the exclusion caveat. Each sentence carries information, though the phrasing is telegraphic enough that some clauses (e.g. 'Stance hits on polls with hidden results are DROPPED') require a second read.

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?

There is no output schema, so the description reasonably must mention return behavior — it covers the 20-result cap and the `capped` flag, but does not describe what a result item looks like or how `relevance` ordering interacts with the cap. For a six-parameter search tool with full schema coverage, this is close to sufficient.

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

Parameters4/5

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

Schema description coverage is 100%, so the per-parameter baseline is 3, but the description adds combination semantics the schema does not: `author_id` alone yields that author's 20 newest items, and query+author_id compose. It also reinforces the prefix-match and `!word` exclusion syntax and ties the result cap to the `capped` flag.

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?

States a specific verb (keyword search) and resource scope ('everything the connector's user can see'), and explicitly contrasts with the enumerate-style siblings by noting it is a single call reaching all record types. An agent can immediately tell this apart from list_discussions/list_polls/get_* tools.

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?

Gives concrete invocation modes: pass `query`, pass `author_id` alone for 'what did X post', or both. It also states an explicit prohibition ('never use search to probe hidden vote reasons'), which is the kind of when-not guidance most descriptions omit.

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

update_commentA
DestructiveIdempotent

Edit a comment: 1 call. Required numeric id and body (REPLACES the whole text: read it with list_thread_items first), optional body_format. Only the author, or a thread admin where the group allows it, may edit; the thread must be unlocked.

NameRequiredDescriptionDefault
idYes
bodyYesNew body; REPLACES the whole text.
body_formatNoOmitted = the STORED format stays; send 'html' when the text is HTML.

TDQS

A4.3/5.0
Behavior5/5

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

Goes well beyond the annotations by disclosing that the body REPLACES the entire text (matching destructiveHint=true), the authorization model, and the thread-lock constraint. Annotations flag safety profile; the description fills in the operational consequences an agent needs.

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?

Front-loaded with the verb and '1 call', then packs replace-semantics, params, and auth into one dense passage. Slightly redundant with schema wording on the body-replacement rule, but no wasted sentences.

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 mutation tool with no output schema, it covers auth requirements, destructive replace behavior, and prerequisites adequately. Return-value expectations are left implicit, which is acceptable given the tool's simple edit semantics.

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 coverage is 67% and the body/body_format descriptions already appear in the schema, so the description largely repeats structured data ('REPLACES the whole text', 'html' example). It adds identification of the undocumented id as 'numeric' and required, but little else beyond the baseline.

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?

States a specific verb+resource ('Edit a comment') and immediately disambiguates it against the create/delete_comment siblings by describing an in-place edit that REPLACES the whole body. An agent can select it confidently 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 Guidelines4/5

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

Provides clear preconditions: author/thread-admin permission, thread must be unlocked, and routes the agent to list_thread_items to read the text before replacing it. No explicit 'use X instead' exclusion, but the context is concrete and actionable.

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

update_discussionA
DestructiveIdempotent

Edit a discussion by id_or_key: 1 call. Pass only what changes: title, description (REPLACES the body: read it first, send the whole text), private, allow_* flags, recipients to add. Cannot move a thread or change its tags; a discarded thread cannot be edited.

NameRequiredDescriptionDefault
titleNo
privateNotrue = members only; must fit `discussion_privacy_options`.
id_or_keyYesDiscussion id or short key.
descriptionNoNew body; REPLACES the whole description.
allow_commentsNoWhether members may comment.
allow_reactionsNoWhether members may react.
recipient_emailsNoEmails to invite as readers; non-members become guests.
notify_recipientsNofalse = add recipients without emailing them.
recipient_messageNoNotification text; records a visible 'edited' item.
description_formatNoOmitted = the STORED format stays; send 'html' when the text is HTML.
recipient_audienceNo'group' = notify the whole group (needs announce permission).
recipient_user_idsNoUser ids to add as readers and notify.
allow_concurrent_pollsNoAllow several open polls at once.

TDQS

A4.6/5.0
Behavior5/5

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

Annotations already declare destructiveHint and idempotentHint, but the description adds critical specifics beyond them: description fully REPLACES the body (forcing a read-first, send-whole-text workflow), threads cannot be moved, tags cannot be changed, and discarded threads cannot be edited. These negative constraints are exactly the context an agent needs before mutating.

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

Conciseness5/5

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

One dense, well-ordered sentence: identification, partial-update rule, the destructive replacement warning, then the impossibility constraints. No filler, and the highest-risk behavior (body replacement) is front-loaded.

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 mutation tool with no output schema and strong annotation coverage, the description covers the essential behavioral traps. Minor gaps remain around recipient/notify effects and permission requirements, but the core editing contract is complete.

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

Parameters4/5

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

Schema coverage is 92%, so the schema carries most parameter meaning. The description still adds value by grouping the changeable fields ('allow_* flags, recipients to add') and emphasizing the replace-not-merge semantics of description, which the schema only states tersely.

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?

States a specific verb and resource ('Edit a discussion by id_or_key'), identifies the key parameter, and scopes it as a single call. An agent can distinguish this from create_discussion or update_comment 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 Guidelines4/5

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

'Pass only what changes' plus the 'read it first' workflow for description give clear operational context, and the closing sentence lists what the tool cannot do. It stops short of naming a sibling alternative (e.g. update_comment) the way a 5 would, but the usage context is unambiguous.

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

update_pollA
DestructiveIdempotent

Edit an OPEN poll by id_or_key: 1 call (2 with options). Pass only what changes: title, details (REPLACES the text), closing_at (future; opening a draft announces unless notify_on_open is false), options (names to ADD: merged with the current list, so it never removes an option it saw; not atomic), settings. Cannot change poll_type, anonymous, group or tags; a closed poll answers 403.

NameRequiredDescriptionDefault
titleNo
detailsNo
optionsNoNames to ADD (+1 call): merged with the stored names, so it never removes an option it saw. Not atomic.
id_or_keyYesPoll id or short key.
max_scoreNoscore: max per option (default 5); meeting: 2.
min_scoreNoscore: min per option (default 0).
stv_seatsNostv: seats to fill.
closing_atNoISO-8601, future; omitted = unchanged. To end voting soon pass the next full hour.
hide_resultsNoCannot leave 'until_closed' once set.
reason_promptNoPrompt above the reason box.
details_formatNoDefault 'md'; 'html' when `details` is HTML.
notify_on_openNoAnnounce (`poll_announced`) on opening; Loomio's default is TRUE.
dots_per_personNodot_vote: points per voter (default 8).
shuffle_optionsNoRandomise option order per voter.
meeting_durationNomeeting: slot minutes. No default through the API.
recipient_emailsNoEmails to invite; non-members become guests.
can_respond_maybeNomeeting: allow 'maybe'. API default is false.
notify_recipientsNoEmail/push the named recipients; SEPARATE from notify_on_open.
recipient_messageNoText for the notification.
recipient_audienceNo'group' = every member (needs announce permission).
recipient_user_idsNoUser ids to invite / notify.
specified_voters_onlyNoOnly the named recipients may vote.
maximum_stance_choicesNoMax options per voter (poll: 1; raise for multi-choice).
minimum_stance_choicesNoMin options per voter (ranked_choice: ranks, default 3).
notify_on_closing_soonNo24h closing reminder; API default is 'nobody'.
show_none_of_the_aboveNoOffer 'none of the above' (poll, ranked_choice).
stance_reason_requiredNoDefault 'optional'; anonymous polls force 'disabled'.

TDQS

A4.5/5.0
Behavior5/5

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

Goes well beyond the annotations: discloses that details REPLACES existing text, options are ADD-only and merged (never removes), the merge is not atomic, opening a draft announces a poll, and a closed poll returns 403. These are exactly the destructive/stateful traits an agent needs and the annotations alone (destructiveHint, idempotentHint) do not convey.

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?

A single dense paragraph that is front-loaded with the action and the call-count note, then lists the mutability contract and exclusions. Every sentence carries information; only the count of packed clauses keeps it from being maximally clean.

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 27-parameter tool with high schema coverage, a safety profile in annotations, and no output schema, the description supplies the missing behavioral context (overwrite, merge, non-atomicity, 403, announce-on-open). Return values needn't be described since no output schema exists.

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

Parameters4/5

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

With 93% schema coverage the baseline is 3, but the description adds real semantic value: 'Pass only what changes', details overwrite behavior, option merge semantics, and future-only closing_at. It clarifies the update-patch contract that the schema type constraints alone don't express.

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?

States a specific verb (Edit) and resource (an OPEN poll) with the identifying parameter (id_or_key), and the constraint that it must be open immediately differentiates it from create_poll/delete_poll siblings. An agent can identify the operation 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 Guidelines4/5

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

Gives clear when-to-use conditions (only open polls; closed returns 403) and explicit when-not boundaries (cannot change poll_type, anonymous, group, tags). It doesn't name a sibling alternative by name, but the create/delete/update triad is unambiguous from context.

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

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updatesv0.0.16
    • Changedget_discussion1 field changed
      • changedInput schema / properties / include_items / description
        Previous value: -"Also embed the thread's items as `thread_items` (+1 call)."New value: +"Also embed the thread's items as `thread_items` (+1 call per 500 items)."
    • Changedget_thread_markdown3 fields changed
      • changedInput schema / properties / discussion_id / description
        Previous value: -"Discussion id or short key (+1 call); prefer `topic_id`."New value: +"Discussion id or short key."
      • changedInput schema / properties / poll_id / description
        Previous value: -"Poll id or short key of a STANDALONE poll (+1 call)."New value: +"Poll id or short key."
      • changedInput schema / properties / topic_id / description
        Previous value: -"Thread id (`topic_id` on any thread row, NOT `id`); no extra call."New value: +"Thread id (`topic_id` on any thread row, NOT `id`)."
    • Changedlist_thread_items1 field changed
      • changedInput schema / properties / poll_id / description
        Previous value: -"Poll id or short key of a STANDALONE poll (+1 call)."New value: +"Poll id or short key (+1 call)."
    • Changedsearch_content1 field changed
      • changedInput schema / properties / tag / description
        Previous value: -"Only threads carrying this tag (exact name)."New value: +"Only items tagged with this exact name (Loomio < 3.13: every item of a tagged thread)."
  2. 24 tool updatesv0.0.13
    • Addedcheck_connection
    • Changedcreate_comment10 fields changed
      • removedInput schema / properties / body / description
        Removed value: -"Comment body (required)."
      • changedInput schema / properties / body_format / description
        Previous value: -"Format of `body`. Defaults to Loomio's group default when omitted."New value: +"Default 'md'; 'html' when `body` is HTML."
      • addedInput schema / properties / discussion_id / anyOf
        Added value: +[
        +  {
        +    "pattern": "^[A-Za-z0-9_-]+$",
        +    "type": "string"
        +  },
        +  {
        +    "exclusiveMinimum": 0,
        +    "maximum": 9007199254740991,
        +    "type": "integer"
        +  }
        +]
      • changedInput schema / properties / discussion_id / description
        Previous value: -"ID of the discussion to comment on."New value: +"Discussion id or short key (+1 call) for a top-level comment; or use parent_id."
      • removedInput schema / properties / discussion_id / exclusiveMinimum
        Removed value: -0
      • removedInput schema / properties / discussion_id / maximum
        Removed value: -9007199254740991
      • removedInput schema / properties / discussion_id / type
        Removed value: -"integer"
      • addedInput schema / properties / parent_id
        Added value: +{
        +  "description": "Comment, Poll, Stance or Outcome id to reply to (needs parent_type).",
        +  "exclusiveMinimum": 0,
        +  "maximum": 9007199254740991,
        +  "type": "integer"
        +}
      • addedInput schema / properties / parent_type
        Added value: +{
        +  "description": "Type of parent_id; required with it.",
        +  "enum": [
        +    "Discussion",
        +    "Comment",
        +    "Poll",
        +    "Stance",
        +    "Outcome"
        +  ],
        +  "type": "string"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "discussion_id",
        -  "body"
        -]New value: +[
        +  "body"
        +]
    • Addedcreate_discussion
    • Changedcreate_poll39 fields changed
      • changedInput schema / properties / anonymous / description
        Previous value: -"If true, hide voter identities."New value: +"Permanent. Needs closing_at; not for count, question, meeting."
      • addedInput schema / properties / can_respond_maybe
        Added value: +{
        +  "description": "meeting: allow 'maybe'. API default is false.",
        +  "type": "boolean"
        +}
      • changedInput schema / properties / closing_at / description
        Previous value: -"ISO-8601 timestamp at which the poll closes."New value: +"ISO-8601, future. Effectively REQUIRED: omitted = unopened draft."
      • removedInput schema / properties / details / description
        Removed value: -"Optional poll body / context."
      • changedInput schema / properties / details_format / description
        Previous value: -"Format of `details`. Defaults to 'md'."New value: +"Default 'md'; 'html' when `details` is HTML."
      • addedInput schema / properties / discussion_id / anyOf
        Added value: +[
        +  {
        +    "pattern": "^[A-Za-z0-9_-]+$",
        +    "type": "string"
        +  },
        +  {
        +    "exclusiveMinimum": 0,
        +    "maximum": 9007199254740991,
        +    "type": "integer"
        +  }
        +]
      • changedInput schema / properties / discussion_id / description
        Previous value: -"Attach the poll to an existing discussion. When set, group_id is taken from the discussion."New value: +"Discussion to attach to (id or key; +1 call). Prefer topic_id."
      • removedInput schema / properties / discussion_id / exclusiveMinimum
        Removed value: -0
      • removedInput schema / properties / discussion_id / maximum
        Removed value: -9007199254740991
      • removedInput schema / properties / discussion_id / type
        Removed value: -"integer"
      • addedInput schema / properties / dots_per_person
        Added value: +{
        +  "description": "dot_vote: points per voter (default 8).",
        +  "exclusiveMinimum": 0,
        +  "maximum": 9007199254740991,
        +  "type": "integer"
        +}
      • changedInput schema / properties / group_id / description
        Previous value: -"Group the poll belongs to. Required when not attaching to an existing discussion via discussion_id."New value: +"Group of a STANDALONE poll (else only cross-checked)."
      • changedInput schema / properties / hide_results / description
        Previous value: -"Results visibility policy. Defaults to 'off'."New value: +"Default 'off'; anonymous polls force 'until_closed'."
      • addedInput schema / properties / max_score
        Added value: +{
        +  "description": "score: max per option (default 5); meeting: 2.",
        +  "maximum": 9007199254740991,
        +  "minimum": -9007199254740991,
        +  "type": "integer"
        +}
      • addedInput schema / properties / maximum_stance_choices
        Added value: +{
        +  "description": "Max options per voter (poll: 1; raise for multi-choice).",
        +  "exclusiveMinimum": 0,
        +  "maximum": 9007199254740991,
        +  "type": "integer"
        +}
      • addedInput schema / properties / meeting_duration
        Added value: +{
        +  "description": "meeting: slot minutes. No default through the API.",
        +  "exclusiveMinimum": 0,
        +  "maximum": 9007199254740991,
        +  "type": "integer"
        +}
      • addedInput schema / properties / min_score
        Added value: +{
        +  "description": "score: min per option (default 0).",
        +  "maximum": 9007199254740991,
        +  "minimum": -9007199254740991,
        +  "type": "integer"
        +}
      • addedInput schema / properties / minimum_stance_choices
        Added value: +{
        +  "description": "Min options per voter (ranked_choice: ranks, default 3).",
        +  "maximum": 9007199254740991,
        +  "minimum": 0,
        +  "type": "integer"
        +}
      • changedInput schema / properties / notify_on_closing_soon / description
        Previous value: -"Who Loomio notifies as the closing date approaches. Defaults to 'nobody'."New value: +"24h closing reminder; API default is 'nobody'."
      • changedInput schema / properties / notify_on_closing_soon / enum
        Previous value: -[
        -  "nobody",
        -  "author",
        -  "voters",
        -  "undecided_voters",
        -  "all_members"
        -]New value: +[
        +  "nobody",
        +  "author",
        +  "undecided_voters",
        +  "voters"
        +]
      • addedInput schema / properties / notify_on_open
        Added value: +{
        +  "description": "Announce (`poll_announced`) on opening; Loomio's default is TRUE.",
        +  "type": "boolean"
        +}
      • changedInput schema / properties / notify_recipients / description
        Previous value: -"If false, suppress the initial notification email. Defaults to false."New value: +"Email/push the named recipients; SEPARATE from notify_on_open."
      • changedInput schema / properties / options / description
        Previous value: -"Voting options. `proposal` has built-in agree/disagree/abstain options; for poll / count / score / ranked_choice / meeting / dot_vote you MUST supply your own."New value: +"Option names in order. REQUIRED except 'question'; ranked_choice/stv need 2+."
      • changedInput schema / properties / poll_type / description
        Previous value: -"Poll type. One of: proposal, poll, count, score, ranked_choice, meeting, dot_vote."New value: +"'question' has no options; 'meeting' options are ISO-8601 times; 'stv' takes stv_seats."
      • changedInput schema / properties / poll_type / enum
        Previous value: -[
        -  "proposal",
        -  "poll",
        -  "count",
        -  "score",
        -  "ranked_choice",
        -  "meeting",
        -  "dot_vote"
        -]New value: +[
        +  "proposal",
        +  "poll",
        +  "count",
        +  "score",
        +  "ranked_choice",
        +  "meeting",
        +  "dot_vote",
        +  "check",
        +  "question",
        +  "stv"
        +]
      • addedInput schema / properties / reason_prompt
        Added value: +{
        +  "description": "Prompt above the reason box.",
        +  "type": "string"
        +}
      • addedInput schema / properties / recipient_audience / description
        Added value: +"'group' = every member (needs announce permission)."
      • addedInput schema / properties / recipient_emails / description
        Added value: +"Emails to invite; non-members become guests."
      • changedInput schema / properties / recipient_emails / items / pattern
        Previous value: -"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"New value: +"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
      • addedInput schema / properties / recipient_message / description
        Added value: +"Text for the notification."
      • addedInput schema / properties / recipient_user_ids / description
        Added value: +"User ids to invite / notify."
      • addedInput schema / properties / show_none_of_the_above
        Added value: +{
        +  "description": "Offer 'none of the above' (poll, ranked_choice).",
        +  "type": "boolean"
        +}
      • changedInput schema / properties / shuffle_options / description
        Previous value: -"If true, shuffle option display order."New value: +"Randomise option order per voter."
      • changedInput schema / properties / specified_voters_only / description
        Previous value: -"If true, only users in recipient_user_ids / recipient_emails can vote."New value: +"Only the named recipients may vote."
      • addedInput schema / properties / stance_reason_required
        Added value: +{
        +  "description": "Default 'optional'; anonymous polls force 'disabled'.",
        +  "enum": [
        +    "disabled",
        +    "optional",
        +    "required",
        +    "required_for_disagree_or_block",
        +    "required_for_block"
        +  ],
        +  "type": "string"
        +}
      • addedInput schema / properties / stv_seats
        Added value: +{
        +  "description": "stv: seats to fill.",
        +  "exclusiveMinimum": 0,
        +  "maximum": 9007199254740991,
        +  "type": "integer"
        +}
      • addedInput schema / properties / tags
        Added value: +{
        +  "description": "Tags for a standalone poll's thread.",
        +  "items": {
        +    "minLength": 1,
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • removedInput schema / properties / title / description
        Removed value: -"Poll title (required)."
      • addedInput schema / properties / topic_id
        Added value: +{
        +  "description": "Thread id (`topic_id` on any thread row); no extra call.",
        +  "exclusiveMinimum": 0,
        +  "maximum": 9007199254740991,
        +  "type": "integer"
        +}
    • Addeddelete_comment
    • Addeddelete_discussion
    • Addeddelete_poll
    • Addedget_discussion
    • Addedget_group
    • Addedget_participation_report
    • Addedget_poll
    • Addedget_thread_markdown
    • Changedget_user_activity4 fields changed
      • changedInput schema / properties / group_ids / description
        Previous value: -"Groups to scan. Required — pass the result of `list_groups` (or a subset of it) to make the cost explicit. ~1 outbound HTTP request per discussion in scope, plus one list_discussions call per group; ~100-300 calls is typical for a wide scan. Capped at 50 groups per call."New value: +"Group ids (1-50); 1 call each."
      • changedInput schema / properties / since / description
        Previous value: -"ISO-8601 timestamp; ignore events before this time. Rejected if unparseable (so a typo can't silently widen the scan to all history)."New value: +"ISO-8601 start, rounded down to its month. Default: all history."
      • changedInput schema / properties / until / description
        Previous value: -"ISO-8601 timestamp; ignore events at or after this time. Rejected if unparseable."New value: +"ISO-8601 end (exclusive), rounded out to a whole month. Default: now."
      • changedInput schema / properties / user_id / description
        Previous value: -"Loomio user id whose activity to summarise."New value: +"Loomio user id."
    • Changedlist_discussions6 fields changed
      • addedInput schema / properties / description_max_chars
        Added value: +{
        +  "description": "Chars per `description`; default 1500, 0 omits it, -1 = full.",
        +  "maximum": 9007199254740991,
        +  "minimum": -1,
        +  "type": "integer"
        +}
      • removedInput schema / properties / group_id / description
        Removed value: -"ID of the Loomio group whose discussions to list (required)."
      • changedInput schema / properties / limit / description
        Previous value: -"Page size. Loomio defaults to 50."New value: +"Page size, 1-200. Default 50."
      • changedInput schema / properties / offset / description
        Previous value: -"Page offset. Defaults to 0."New value: +"Page offset. Default 0."
      • changedInput schema / properties / status / description
        Previous value: -"Filter by status. 'open' = unlocked, 'closed' = locked, 'all' = every kept discussion. Loomio defaults to 'open'."New value: +"'open' (default) = unlocked, 'closed' = locked, 'all'."
      • addedInput schema / properties / strip_html
        Added value: +{
        +  "description": "Plain text instead of HTML; default true.",
        +  "type": "boolean"
        +}
    • Changedlist_groups3 fields changed
      • removedInput schema / properties / end_id
        Removed value: -{
        -  "description": "Last group_id to probe (inclusive). Defaults to 200. A single call may scan at most 500 ids; use multiple calls for wider ranges.",
        -  "maximum": 10000,
        -  "minimum": 1,
        -  "type": "integer"
        -}
      • removedInput schema / properties / start_id
        Removed value: -{
        -  "description": "First group_id to probe (inclusive). Defaults to 1.",
        -  "maximum": 9007199254740991,
        -  "minimum": 1,
        -  "type": "integer"
        -}
      • removedInput schema / properties / stop_after_consecutive_misses
        Removed value: -{
        -  "description": "Early-exit heuristic: stop probing after this many consecutive 404/403 misses. Saves wall time on sparse id ranges. Defaults to 50.",
        -  "maximum": 500,
        -  "minimum": 1,
        -  "type": "integer"
        -}
    • Changedlist_memberships3 fields changed
      • changedInput schema / properties / group_id / description
        Previous value: -"ID of the Loomio group whose memberships to list (required). The connector's bot user must be an admin (coordinator) of the group — Loomio only returns the member list, including email addresses, to group admins. For a non-admin bot this returns a clear 403 explaining the role requirement; names/usernames/ids (not emails) are still reachable via get_user_activity / list_events."New value: +"Group id (a non-member gets an empty list, not 403)."
      • changedInput schema / properties / limit / description
        Previous value: -"Page size. Loomio defaults to 50."New value: +"Page size, 1-200. Default 50."
      • changedInput schema / properties / offset / description
        Previous value: -"Page offset. Defaults to 0."New value: +"Page offset. Default 0."
    • Changedlist_polls6 fields changed
      • addedInput schema / properties / description_max_chars
        Added value: +{
        +  "description": "Chars per `details`; default 1500, 0 omits it, -1 = full.",
        +  "maximum": 9007199254740991,
        +  "minimum": -1,
        +  "type": "integer"
        +}
      • removedInput schema / properties / group_id / description
        Removed value: -"ID of the Loomio group whose polls to list (required)."
      • changedInput schema / properties / limit / description
        Previous value: -"Page size. Loomio defaults to 50."New value: +"Page size, 1-200. Default 50."
      • changedInput schema / properties / offset / description
        Previous value: -"Page offset. Defaults to 0."New value: +"Page offset. Default 0."
      • changedInput schema / properties / status / description
        Previous value: -"Filter polls by status. Loomio defaults to 'active'."New value: +"'active' (default) = open, 'closed', 'all'."
      • addedInput schema / properties / strip_html
        Added value: +{
        +  "description": "Plain text instead of HTML; default true.",
        +  "type": "boolean"
        +}
    • Addedlist_thread_items
    • Addedlist_threads
    • Changedmanage_memberships4 fields changed
      • changedInput schema / properties / emails / description
        Previous value: -"Email addresses to ensure are members. Each address that isn't already a member is invited / added."New value: +"Addresses to ensure are members; new ones are invited."
      • changedInput schema / properties / emails / items / pattern
        Previous value: -"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"New value: +"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
      • changedInput schema / properties / group_id / description
        Previous value: -"ID of the Loomio group to modify (required). Caller must be a group admin."New value: +"Group id; the connector's user must be its admin."
      • changedInput schema / properties / remove_absent / description
        Previous value: -"DANGEROUS. When true, Loomio REMOVES every existing member whose email is NOT in `emails`. Empty-emails (after dedupe) effectively removes the entire group. Default false. Only set true after reading list_memberships and confirming the diff with a human."New value: +"DANGEROUS: also revokes every member absent from `emails`, own user included. Default false."
    • Addedsearch_content
    • Addedupdate_comment
    • Addedupdate_discussion
    • Addedupdate_poll
  3. 4 tool updatesv0.0.10
    • Removedcreate_discussion
    • Removedget_discussion
    • Removedget_poll
    • Removedlist_events
  4. 12 tool updatesv0.0.6
    • First observedcreate_comment
    • First observedcreate_discussion
    • First observedcreate_poll
    • First observedget_discussion
    • First observedget_poll
    • First observedget_user_activity
    • First observedlist_discussions
    • First observedlist_events
    • First observedlist_groups
    • First observedlist_memberships
    • First observedlist_polls
    • First observedmanage_memberships

TDQS

A4/5.0

Scored across 24 tools

Disambiguation4/5

Most tools map cleanly to a distinct resource and action, with explicit cross-references guiding selection. There is some overlap among thread-reading tools (get_discussion with include_items, list_thread_items, get_thread_markdown) and between list_threads and the group-specific list_discussions/list_polls tools, but descriptions make the intended boundaries mostly clear.

Naming Consistency5/5

Every tool name follows a predictable snake_case verb_noun pattern such as create_poll, get_thread_markdown, list_memberships, and manage_memberships. There are no mixed conventions or vague standalone names.

Tool Count3/5

At 24 tools, the surface is at the upper end of practical and feels heavy for an agent to navigate, although most tools have a legitimate role. Some reading-related helpers could likely be consolidated without losing functionality.

Completeness3/5

The set covers CRUD for discussions, polls, and comments, plus group/membership reads and activity reports. Notable gaps include any way to vote or create/update stances on polls, create/update/delete groups, or manage poll outcomes, which leaves core Loomio participation workflows incomplete.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers