Skip to content

feat(chat): let tools claim the names a server derives from them - #7185

Draft
Haroenv wants to merge 8 commits into
masterfrom
fix/chat-tool-name-resolution
Draft

feat(chat): let tools claim the names a server derives from them#7185
Haroenv wants to merge 8 commits into
masterfrom
fix/chat-tool-name-resolution

Conversation

@Haroenv

@Haroenv Haroenv commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Stacked on #7172, addressing #7172 (comment).

That thread chased a tiebreak rule for overlapping tool names, but the rule isn't the problem — the guessing is. a_b is genuinely ambiguous between the tool a_b and the tool a addressing b, and nothing in the string tells you which. Every rule is therefore wrong for someone, which is exactly what the thread found:

Rejecting overlapping names doesn't work either: algolia_search_index and a hypothetical algolia_search_index_v2 are both legitimate registrations.

So this applies the same move #7172 already makes for rendering — the registration site declares intent, the resolver stops inferring. shouldRender lets a tool opt out of rendering; matchesToolName lets it opt in to a name.

  • Add shouldRender's sibling matchesToolName?: (toolName: string) => boolean to ClientSideTool; it reaches consumers through the existing UserClientSideTool omit.
  • findTool resolves an exact registration first, and otherwise only considers tools that claim the name. Two overlapping registrations no longer collide at all — which answers "can we reject overlapping names?" with "they're no longer overlapping".
  • Among claimants the most specific one wins and a dev warning names the conflict, so the winner never depends on registration order.
  • The Algolia MCP Server's per-index search tool names are declared in connectChat rather than guessed.
  • chat.tsx held a third copy of the resolution rule (still first-match-wins, and unreachable — the connector keys its tools the same way the widget does). It goes through findTool now.
  • A dev warning fires when a name goes unresolved while a registered tool is a prefix of it, pointing at matchesToolName.

Why connectChat and not the default tools

connectChat is the Algolia-specific layer — it already sends the x-algolia-* headers — and it's the single normalization point both flavors and the renderer read their tools from. Putting the MCP naming there means:

  • instantsearch-ui-components ends up with zero tool-name knowledge, which is stronger than what feat(chat): let tools control their own rendering #7172 achieves on its own.
  • Direct connectChat / useChat users keep working unchanged. A defaults-only approach (declaring it in each flavor's createDefaultTools) would have quietly broken them, since they never go through the widget's defaults.

It's a default, not a branch: an explicit matchesToolName wins, and so does a tool registered under the derived name itself.

Public API

Additive — one optional property, no breaking change (semver minor):

<Chat
  tools={{
    my_tool: {
      layoutComponent: MyPanel,
      // The server exposes this tool once per index, suffixed with the index name.
      matchesToolName: (toolName) => toolName.startsWith('my_tool_'),
    },
  }}
/>

Behaviour change to note

#7172's "the name-suffix shim is generic now" widening is reverted, and narrowed past master: the loader and connector previously prefix-matched any tool through findTool, undocumented and accidental. That now requires an explicit claim. The dev warning covers the migration by naming the tool and the property to add. The one real case — algolia_search_index — is unaffected, and its existing test passes unmodified.

Tests

  • findTool unit tests rewritten around the new contract, including both failure cases from the thread.
  • Two connectChat tests: a tool claiming derived names, and one not claiming them.
  • A common-widgets test across JS + React: hello claims hello_products and renders; goodbye doesn't claim goodbye_products and doesn't.
  • The existing "adds a compatibility layer for Algolia MCP Server search tool" test passes unmodified.

yarn type-check, yarn lint:changed and the full chat surface (153 suites, 2111 tests) are green.

🤖 Generated with Claude Code

shaejaz and others added 7 commits August 18, 2026 12:58
Resolving a tool part by prefix is a guess: `a_b` is ambiguous between the
tool `a_b` and the tool `a` addressing `b`, and no naming rule tells those
apart. Preferring the shorter key renders `foo_bar_products` with `foo` when
both `foo` and `foo_bar` are registered; preferring the longer one hands
`search_index` on the `products` index to `search_index_products`. Which one
wins is the registration site's call, not something the resolver can infer.

Add `matchesToolName` to the tool contract, so a tool opts in to the names a
server derives from it. `findTool` resolves an exact registration first and
otherwise only considers tools that claim the name, so two overlapping
registrations no longer collide at all. Among claimants the most specific one
wins and a dev warning names the conflict, so the winner never depends on
registration order.

The Algolia MCP Server's per-index search tool names are declared in
`connectChat` rather than guessed: it is the Algolia-specific layer both
flavors and the renderer read their tools from, and it covers users who go
through the connector without the widget's default tools. That leaves
`instantsearch-ui-components` with no tool-name knowledge at all.

`chat.tsx` held a third copy of the resolution rule, still first-match-wins
and unreachable since the connector keys its tools the same way the widget
does; it goes through `findTool` now. A dev warning fires when a name goes
unresolved while a registered tool is a prefix of it, pointing at
`matchesToolName`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codacy-production

codacy-production Bot commented Aug 20, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 13 complexity

Metric Results
Complexity 13

View in Codacy

TIP This summary will be updated as you push new changes.

@Haroenv
Haroenv requested review from anjola-adeuyi and a lite review from Copilot August 20, 2026 15:13
@pkg-pr-new

pkg-pr-new Bot commented Aug 20, 2026

Copy link
Copy Markdown
More templates

algoliasearch-helper

npm i https://pkg.pr.new/algolia/instantsearch/algoliasearch-helper@7185

instantsearch-ui-components

npm i https://pkg.pr.new/algolia/instantsearch/instantsearch-ui-components@7185

instantsearch.css

npm i https://pkg.pr.new/algolia/instantsearch/instantsearch.css@7185

instantsearch.js

npm i https://pkg.pr.new/algolia/instantsearch/instantsearch.js@7185

react-instantsearch

npm i https://pkg.pr.new/algolia/instantsearch/react-instantsearch@7185

react-instantsearch-core

npm i https://pkg.pr.new/algolia/instantsearch/react-instantsearch-core@7185

react-instantsearch-nextjs

npm i https://pkg.pr.new/algolia/instantsearch/react-instantsearch-nextjs@7185

react-instantsearch-router-nextjs

npm i https://pkg.pr.new/algolia/instantsearch/react-instantsearch-router-nextjs@7185

vue-instantsearch

npm i https://pkg.pr.new/algolia/instantsearch/vue-instantsearch@7185

commit: 28ebb06

@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown

Size Change: +1.1 kB (+0.09%)

Total Size: 1.24 MB

📦 View Changed
Filename Size Change
packages/instantsearch.js/dist/instantsearch.development.js 305 kB +753 B (+0.25%)
packages/instantsearch.js/dist/instantsearch.production.min.js 143 kB +98 B (+0.07%)
packages/react-instantsearch-core/dist/umd/ReactInstantSearchCore.min.js 68.3 kB +144 B (+0.21%)
packages/react-instantsearch/dist/umd/ReactInstantSearch.min.js 114 kB +108 B (+0.09%)
ℹ️ View Unchanged
Filename Size Change
packages/algolia-experiences/dist/algolia-experiences.development.js 179 kB 0 B
packages/algolia-experiences/dist/algolia-experiences.production.min.js 81.4 kB -1 B (0%)
packages/algoliasearch-helper/dist/algoliasearch.helper.js 44.3 kB 0 B
packages/algoliasearch-helper/dist/algoliasearch.helper.min.js 13.9 kB 0 B
packages/instantsearch.css/components/ai-mode-button.css 1.75 kB 0 B
packages/instantsearch.css/components/autocomplete-min.css 4.31 kB 0 B
packages/instantsearch.css/components/autocomplete.css 4.66 kB 0 B
packages/instantsearch.css/components/button.css 1.96 kB 0 B
packages/instantsearch.css/components/chat-min.css 6.36 kB 0 B
packages/instantsearch.css/components/chat.css 6.83 kB 0 B
packages/instantsearch.css/components/filter-suggestions.css 1.88 kB 0 B
packages/instantsearch.css/themes/algolia-min.css 10.8 kB 0 B
packages/instantsearch.css/themes/algolia.css 11.5 kB 0 B
packages/instantsearch.css/themes/nova-min.css 10.9 kB 0 B
packages/instantsearch.css/themes/nova.css 11.6 kB 0 B
packages/instantsearch.css/themes/reset-min.css 1.3 kB 0 B
packages/instantsearch.css/themes/reset.css 1.38 kB 0 B
packages/instantsearch.css/themes/satellite-min.css 11.6 kB 0 B
packages/instantsearch.css/themes/satellite.css 12.6 kB 0 B
packages/vue-instantsearch/vue2/cjs/index.js 19.9 kB 0 B
packages/vue-instantsearch/vue2/umd/index.js 73.4 kB -7 B (-0.01%)
packages/vue-instantsearch/vue3/cjs/index.js 20.6 kB 0 B
packages/vue-instantsearch/vue3/umd/index.js 73.8 kB -3 B (0%)

compressed-size-action

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Chat tool-resolution contract so that derived / suffixed tool names (e.g. my_tool_products) are no longer inferred from string prefixes, and instead must be explicitly “claimed” by the registered tool via a new optional matchesToolName(toolName) predicate. This centralizes resolution in findTool, avoids ambiguous overlaps like a_b vs a + b, and keeps the Algolia MCP Server naming convention declared at the Algolia-specific layer (connectChat) rather than guessed in UI components.

Changes:

  • Add matchesToolName?: (toolName: string) => boolean to the ClientSideTool contract and update findTool to only resolve derived names when a tool explicitly claims them (with deterministic conflict handling + dev warnings).
  • Declare the Algolia MCP Server’s per-index search-tool naming (algolia_search_index_<index>) in connectChat via a default matchesToolName for algolia_search_index.
  • Update widget/connector/common tests to cover claimed vs unclaimed derived names and overlapping registrations.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/common/widgets/chat/options.tsx Adds common-widgets coverage ensuring only tools that claim derived names render for suffixed tool parts.
packages/instantsearch.js/src/widgets/chat/chat.tsx Removes bespoke prefix-guessing and delegates tool lookup to shared findTool.
packages/instantsearch.js/src/lib/chat/index.ts Introduces matchesSearchIndexToolName helper for Algolia MCP Server’s suffixed search tool naming.
packages/instantsearch.js/src/connectors/chat/connectChat.ts Injects a default matchesToolName for algolia_search_index when not explicitly set by the user.
packages/instantsearch.js/src/connectors/chat/tests/connectChat-test.ts Adds connector tests for claimed vs unclaimed derived tool names during tool-call dispatch.
packages/instantsearch-ui-components/src/lib/utils/chat.ts Updates findTool resolution logic to prefer exact matches, otherwise require explicit claims; adds dev warnings for conflicts/unresolved prefixes.
packages/instantsearch-ui-components/src/lib/utils/tests/chat-test.ts Rewrites findTool unit tests around the new claim-based contract and warnings.
packages/instantsearch-ui-components/src/components/chat/types.ts Extends ClientSideTool public type with matchesToolName documentation and signature.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +106 to +110
`No tool is registered for "${toolName}". The registered ${prefixes
.map((key) => `"${key}"`)
.join(
', '
)} is a prefix of it, but a prefix alone doesn't resolve: declare \`matchesToolName\` on it to also handle "${toolName}".`

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in 28ebb06. The message now branches on the count:

  • one prefix: The registered tool "foo" is a prefix of it
  • several: The registered tools "foo", "foo_bar" are prefixes of it

The tail also changed from "declare matchesToolName on it" to "on the tool that should handle …", since "it" no longer refers to a single tool. Added a unit test for the plural case.

— via Claude

The message named every registered prefix but always read "The registered
… is a prefix of it", which is wrong as soon as two tools qualify. Branch on
the count, and point at "the tool that should handle" it rather than "it",
which no longer refers to a single tool.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Haroenv
Haroenv force-pushed the fix/chat-tool-name-resolution branch from a588d5e to 28ebb06 Compare August 20, 2026 15:20
Base automatically changed from feat/chat-generic-rendering-control to master August 20, 2026 15:24
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.

3 participants