Skip to content

[Feature]: Let external plugins register AI Assistant tools without bundling the agent SDK #2296

Description

@ttntbn

Before you start

What problem does this solve?

registerAssistantTool (GeoLibreAppAPI) lets a plugin expose a tool to the in-app
AI Assistant, but it requires passing a real Tool instance built with
@strands-agents/sdk's tool() + a zod inputSchema. Built-in plugins
(packages/plugins/src/plugins/*) can do this because that package already
depends on the SDK and zod directly.

External plugins (loaded from a manifest/zip URL, see docs/plugin-api.md)
are standalone ES modules built independently of the app, and deliberately
avoid bundling the full agent SDK — it carries a partly server-oriented
surface (e.g. an MCP client, tracing) that isn't relevant in a browser
context and would bloat what's meant to be a lightweight drop-in. Because of
that, an external plugin today has no way at all to contribute an
assistant tool, even when it already has a working "fetch data + mutate the
map" action wired to a UI button.

Concretely: a plugin panel with an "Add to X" button that already fetches
data and calls app.addGeoJsonLayer(...) cannot let the AI Assistant trigger
that same action — the user must always click through the panel manually,
even though the plugin already contains everything needed to do it itself.

Proposed solution

Add a second, JSON-Schema-based registration entry point to GeoLibreAppAPI,
alongside the existing registerAssistantTool:

type AssistantToolSpec = {
  name: string;
  description: string;
  inputSchema?: JSONSchema; // plain JSON Schema, not a zod schema
  callback: (input: unknown) => unknown | Promise<unknown>;
};

registerAssistantToolSpec?: (
  spec: AssistantToolSpec,
  ownerPluginId?: string,
) => () => void;

The host builds a real Tool from the spec using the SDK's existing
JSON-Schema tool() overload (FunctionToolConfig, which already accepts a
plain JSON Schema instead of a zod schema — no SDK change needed) and
forwards it to the existing registerAssistantTool. Both paths land in the
same single registry (assistant-tool-registry.ts); nothing changes in how
apps/geolibre-desktop/src/lib/assistant/tools.ts consumes tools
(listAssistantTools() keeps working as-is).

One behavior to document clearly: unlike a zod-backed Tool, a JSON-Schema
spec's input is not validated automatically before the callback runs
(FunctionTool doesn't parse) — the callback must validate its own input.
This should be called out in the JSDoc for AssistantToolSpec.

Also worth documenting registerAssistantToolSpec in docs/plugin-api.md,
since registerAssistantTool itself currently has no mention there either.

I'm happy to open a PR with this change if the approach sounds right.

Estimated scope

Small: a minor tweak, small UI change, or text adjustment

Alternatives considered

  • Requiring external plugins to bundle @strands-agents/sdk + zod themselves
    and call registerAssistantTool directly. Rejected: pulls a large, partly
    server-oriented SDK surface into what's meant to be a lightweight drop-in
    bundle, and hand-constructing an SDK Tool duck-types undocumented internals
    (e.g. ToolResultBlock) with no test coverage for third-party bundles.

  • A second, independent tool registry just for external plugins. Rejected:
    would let the same user-facing action be reachable through two disconnected
    code paths with no shared state — exactly the inconsistency
    registerAssistantTool already avoids for built-in plugins (one function
    backs both a panel's button and the assistant tool).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions