refactor(mcp): tool names say which surface they act on - #706
Merged
Conversation
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
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reviewing the channel tools surfaced a class of naming defect across the whole MCP catalog: the unqualified names were the narrow ones.
conv_test_channelandconv_send_channel_testread as universal but only resolve vendors registered in the voice/SMS adapter registry — pointing either at an email channel fails withunknown channel vendor 'smtp'.conv_create_channelread as universal but only handles email and chat. Meanwhileconv_list_channelsgenuinely 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:conv_setup_email_channelconv_configure_email_channelconv_send_email_testconv_send_email_channel_testconv_widget_create_channelconv_create_widget_channelconv_widget_update_channelconv_update_widget_channelconv_widget_rotate_keyconv_rotate_widget_keyconv_widget_rotate_identity_secretconv_rotate_widget_identity_secretconv_configure_channelconv_configure_voice_sms_channelconv_test_channelconv_test_voice_sms_channelconv_send_channel_testconv_send_voice_sms_channel_testconv_list_channel_vendorsconv_list_voice_sms_vendorsOther 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:crm_find_contactcrm_lookup_contactbookings_lookup_bookingsbookings_list_guest_bookingsbookings_get_my_bookingsbookings_list_my_bookingscommerce_lookup_orderscommerce_list_customer_orderscommerce_get_my_orderscommerce_list_my_orderscrm_change_stagecrm_change_deal_stagecms_searchcms_search_entriesslack_testslack_send_test_messagekb_import_website_statuskb_get_website_import_statusoutreach_propose_initialoutreach_propose_initial_messageanalytics_get_traffic_by_sourceanalytics_list_traffic_sourcesfeedback_get/feedback_create/feedback_listfeedback_get_item/feedback_create_item/feedback_list_pending_itemsfeedback_search/feedback_votefeedback_search_roadmap/feedback_vote_on_roadmap_itemThe
feedback_*prefix covered two unrelated corpora with nothing in the names to say so:feedback_listreads the local outbox awaiting admin action,feedback_searchqueries the public Munin roadmap.Behaviour change
conv_create_channelrejectsvoiceandsmsat the Zod schema instead of asking the model not to pass them. That path inserted a row withactive: true, no adapter binding and no credential link — a channel that looked configured and could not send.conv_importstill accepts all four types; importing historical conversations is not provisioning transport.Descriptions that no longer matched behaviour
outreach_propose_initial_messageandoutreach_approve_proposalstill said "email" after SMS and voice campaigns shipped. Approving now documents an email, an SMS, or an outbound call;outreach_propose_followupstates that sequences are email-only (the service enforces it).conv_list_channelsclaimedvoiceandsmswere "reserved for upcoming adapters" — both shipped.conv_request_channel_credentialsis 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_channelandconv_send_voice_sms_channel_testnow 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.tsboots 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_keyandconv_widget_rotate_identity_secretstill had the family-before-verb shape, andconv_request_callbackwas titled "Place a phone call…" against a name that promises a request.Verification
pnpm typecheck— 31/31 packages cleanpnpm -F @getmunin/backend-core test— 1333 tests pass (includes a new integration test assertingvoice/smsare rejected byconv_create_channeland that no channel row is created)openapi.jsonregenerated; 197 tools, no old names remainapps/backend'soss-signup.integration.test.tsfails locally with HTTP 429 from the signup throttler, identically onmain— a repeated-local-runs artifact, not from this changeNo 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