Skip to content

refactor(mcp): tool names say which surface they act on - #706

Merged
kmonsoe merged 2 commits into
mainfrom
refactor/tool-naming-consistency
Jul 30, 2026
Merged

refactor(mcp): tool names say which surface they act on#706
kmonsoe merged 2 commits into
mainfrom
refactor/tool-naming-consistency

Conversation

@kmonsoe

@kmonsoe kmonsoe commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Reviewing the channel tools surfaced a class of naming defect across the whole MCP catalog: the unqualified names were the narrow ones. conv_test_channel and conv_send_channel_test read as universal but only resolve vendors registered in the voice/SMS adapter registry — pointing either at an email channel fails with unknown channel vendor 'smtp'. conv_create_channel read as universal but only handles email and chat. Meanwhile conv_list_channels genuinely is universal, so the absence of a qualifier carried no information.

Renames

conv — three verbs (create / setup / configure) meant the same action across three channel families, and the family qualifier sat in three different positions:

before after
conv_setup_email_channel conv_configure_email_channel
conv_send_email_test conv_send_email_channel_test
conv_widget_create_channel conv_create_widget_channel
conv_widget_update_channel conv_update_widget_channel
conv_widget_rotate_key conv_rotate_widget_key
conv_widget_rotate_identity_secret conv_rotate_widget_identity_secret
conv_configure_channel conv_configure_voice_sms_channel
conv_test_channel conv_test_voice_sms_channel
conv_send_channel_test conv_send_voice_sms_channel_test
conv_list_channel_vendors conv_list_voice_sms_vendors

Other modules — one operation carried two verbs across modules, and four singular/plural pairs were distinguished by a single letter where the rest of the surface uses get_X / list_Xs:

before after
crm_find_contact crm_lookup_contact
bookings_lookup_bookings bookings_list_guest_bookings
bookings_get_my_bookings bookings_list_my_bookings
commerce_lookup_orders commerce_list_customer_orders
commerce_get_my_orders commerce_list_my_orders
crm_change_stage crm_change_deal_stage
cms_search cms_search_entries
slack_test slack_send_test_message
kb_import_website_status kb_get_website_import_status
outreach_propose_initial outreach_propose_initial_message
analytics_get_traffic_by_source analytics_list_traffic_sources
feedback_get / feedback_create / feedback_list feedback_get_item / feedback_create_item / feedback_list_pending_items
feedback_search / feedback_vote feedback_search_roadmap / feedback_vote_on_roadmap_item

The feedback_* prefix covered two unrelated corpora with nothing in the names to say so: feedback_list reads the local outbox awaiting admin action, feedback_search queries the public Munin roadmap.

Behaviour change

conv_create_channel rejects voice and sms at the Zod schema instead of asking the model not to pass them. That path inserted a row with active: true, no adapter binding and no credential link — a channel that looked configured and could not send. conv_import still accepts all four types; importing historical conversations is not provisioning transport.

Descriptions that no longer matched behaviour

  • outreach_propose_initial_message and outreach_approve_proposal still said "email" after SMS and voice campaigns shipped. Approving now documents an email, an SMS, or an outbound call; outreach_propose_followup states that sequences are email-only (the service enforces it).
  • conv_list_channels claimed voice and sms were "reserved for upcoming adapters" — both shipped.
  • conv_request_channel_credentials is generically named and generically implemented, but claimed to be email/SMTP-only. An agent holding a pending Twilio channel would have skipped the one tool that mints its credential link.
  • conv_test_voice_sms_channel and conv_send_voice_sms_channel_test now name their scope and point at the email equivalents.

Titles

Titles are what a host shows in a permission prompt, so they now restate their tool's name instead of drifting from it. Analytics titles were noun phrases ("Top referrer hosts") where the rest of the surface is imperative, and the bookings titles said "Book a table" for a vendor-agnostic bookings contract that also serves non-restaurant venues.

The guard

packages/backend-core/src/mcp/tool-naming.test.ts boots the registry and asserts over all 197 tools that names carry a known module prefix, that titles start with that module's display prefix, and that a title's leading word matches the verb in its name.

It earned its place immediately by catching two tools the manual review missed: conv_widget_rotate_key and conv_widget_rotate_identity_secret still had the family-before-verb shape, and conv_request_callback was titled "Place a phone call…" against a name that promises a request.

Verification

  • pnpm typecheck — 31/31 packages clean
  • pnpm -F @getmunin/backend-core test — 1333 tests pass (includes a new integration test asserting voice/sms are rejected by conv_create_channel and that no channel row is created)
  • All other workspace suites pass
  • Fixtures and openapi.json regenerated; 197 tools, no old names remain
  • apps/backend's oss-signup.integration.test.ts fails locally with HTTP 429 from the signup throttler, identically on main — a repeated-local-runs artifact, not from this change

No back-compat aliases: there are no external consumers, and tool names have no persisted state to migrate.

🤖 Generated with Claude Code

https://claude.ai/code/session_011HwhYb3yU3qnGjtFRodQxm

kmonsoe and others added 2 commits July 30, 2026 17:01
Renames MCP tools so scope is legible from the name, corrects descriptions
that no longer matched behaviour, and adds tool-naming.test.ts to keep names
and titles in agreement across every registered tool.

conv_create_channel now rejects voice and sms at the schema rather than in
prose; that path produced an active row with no adapter binding and no
credential link.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011HwhYb3yU3qnGjtFRodQxm
Adds two assertions: a title must mention the object its name acts on
(skipping the 23 names with no object, e.g. feedback_approve), and no two
tools may share a title. Retitles analytics_export_config, the one tool the
object check flags.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011HwhYb3yU3qnGjtFRodQxm
@kmonsoe
kmonsoe merged commit 1461e0e into main Jul 30, 2026
6 checks passed
@kmonsoe
kmonsoe deleted the refactor/tool-naming-consistency branch July 30, 2026 16:01
kmonsoe added a commit that referenced this pull request Jul 30, 2026
4.76.0 shipped conv_send_voice_sms_channel_test, so the rename needs its
own entry rather than a correction to #706's now-consumed changeset.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011HwhYb3yU3qnGjtFRodQxm
kmonsoe added a commit that referenced this pull request Jul 31, 2026
* refactor(mcp): conv_send_voice_sms_channel_test is SMS-only

The send path dispatches on capabilities.sendTest, which only Twilio and
MessageBird set. Vapi and Threll declare sendTest: false, so a voice
channel has always answered "does not support test sends" — the
voice_sms qualifier promised a path that does not exist. Its sibling
conv_test_voice_sms_channel keeps the qualifier: every vendor implements
the credential check.

changeset rather than logged as a rename of a rename.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011HwhYb3yU3qnGjtFRodQxm

* chore(changeset): record the SMS test-send rename

4.76.0 shipped conv_send_voice_sms_channel_test, so the rename needs its
own entry rather than a correction to #706's now-consumed changeset.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011HwhYb3yU3qnGjtFRodQxm

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant