Skip to content

Tool collections: coarse-grained grouping with progressive disclosure #255

Description

@sdras

In a large JS application, the number of registered tools grows fast. Tools accumulate across routes, across features, and across client/server boundaries. A moderately complex app can easily surface 100+ tools (see also #88, where an MCP bridge exposes 50+ from a single server). Today getTools() hands the agent one flat list, which means:

  • The agent must read every tool description to find the 3 it needs, spending tokens on tools that are irrelevant to the current task.
  • As the tool list grows, the accuracy of the model selection will decrease. This was a well-documented failure mode for large flat tool lists in backend MCP, and it's why progressive disclosure patterns (tool search, toolsets) have emerged in backend MCP.
  • There's no unit between "one tool" and "every tool on the page."

Applications already have natural coarse-grained groupings: an auth flow (sign-in, sign-out, verify-2fa, recover-account), a cart flow (add-to-cart, remove-from-cart, apply-promo, begin-checkout), a video experience (play, seek, set-captions, set-speed). What's missing is a way to expose that structure so an agent can read a handful of group descriptions first, pick the relevant one, and only then drill into those tools.

There's a second problem this addresses. When a tool execution spans a client/server interaction, or across tabs, the tool call triggers a navigation or a server roundtrip, and tools re-register on the way back. We've sometimes observed agents failing to re-find the relevant tools after the roundtrip. A collection whose identity persists (from cart count/data to server to success/error) improves continuity.

Strawman

Keep registerTool() as-is and add a collection primitive alongside it:

document.modelContext.registerCollection({
  name: "cart-checkout",
  description: "Create cart, process order, report to user",
  tools: ["cart-inventory", "confirm-payment", "process-cart-order", "get-order-status", "cancel-order", "cart-success", "cart-error"],
});

Benefits:

  • Backwards compatible, and keeps the existing API simple. Agents that don't understand collections call getTools() and get the flat list they get today. Tools registered without a collection remain top-level. Collections are additive structures that preserve the current API.
  • Deterministic. A collection is a defined set, defined by the application owner. It doesn’t require natural-language interpretation. Devs can define this according to how they would like their application to be used.
  • Cheap for the agent. The initial payload is smaller, the agent doesn’t have to process non-related tools for task completion.
  • Can help with cross-document, and cross-tab actions. We need tools to outlive a single document view: Tool discovery should not be limited to a single traversable navigable #227 extends discovery across the browsing context group, and Cross-document tool response mechanism #135 discusses cross-document tool responses. A collection could provide cohesion beyond a navigation or server roundtrip. The agent could have some continuity of defined tools instead of re-reading an entire flat list.

Frameworks

Filing this partly as a sub-topic of the frameworks meta issue (#199, section 2: scoped, composable registries). This could fit well into Framework mental-models.

Alternatives considered: Skills (#161)

#161 proposes skills that bundle tools with workflow instructions and structured context, and grouping falls out of that as a side effect (tools: ["searchProducts", "addToCart", ...]). Skills are solving a real problem, but I don't think they're the right home for grouping, for two reasons:

  1. Creates two API surface areas to think about: If grouping only exists via skills, a developer who just wants to expose their cart tools has to reach for a primitive whose main job is behavioral guidance, and every framework integration has to reason about both surfaces. Grouping is structural and could live with tool registration itself, where you’re already defining it.
  2. Determinism: As I alluded to above, a skill's instructions field leaves room for LLM-based agents to interpret, which clients may weigh differently or ignore completely. Developers need a consistent, enumerable way to expose groupings that behave the same in every client.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions