Making your plugin agent-enabled

Rozenite plugins can expose agent tools so coding agents (CLI, Cursor, Codex) can inspect and control your plugin’s domain via Rozenite for Agents. Tools are grouped under a domain that matches your plugin’s ID, and agents discover them the same way they use built-in domains like console or network.

This page describes what you need to expose tools from your plugin package.

Prerequisites

  • You have a Rozenite plugin with a React Native side (code that runs inside the app). See Plugin Development if you are new to plugins.
  • Your plugin is already integrated so its React Native entry runs when the app is in development.

Add the agent bridge dependency

In your plugin package, install @rozenite/agent-bridge:

npm
yarn
pnpm
bun
deno
npm install @rozenite/agent-bridge

The bridge provides the same message protocol and tool lifecycle as in-app tools, but registers tools under your plugin ID instead of the app domain.

Use the plugin agent tool hook

From your plugin’s React Native code, call useRozenitePluginAgentTool with:

  • pluginId – Your plugin’s public ID (e.g. @rozenite/react-navigation-plugin). This becomes the domain agents use to list and call your tools.
  • tool – An AgentTool: name, description, and inputSchema (JSON Schema–style).
  • handler – A function that receives the tool’s arguments and returns a serializable result (or throws).

Each tool is qualified as {pluginId}.{tool.name}. The hook registers the tool when the component mounts and unregisters it on unmount.

Example: echo tool

import { useRozenitePluginAgentTool, type AgentTool } from '@rozenite/agent-bridge';

const PLUGIN_ID = '@my-org/my-rozenite-plugin';

const echoTool: AgentTool = {
  name: 'echo',
  description: 'Return the provided value.',
  inputSchema: {
    type: 'object',
    properties: {
      value: { type: 'string' },
    },
    required: ['value'],
  },
};

export function MyPluginAgentTools() {
  useRozenitePluginAgentTool({
    pluginId: PLUGIN_ID,
    tool: echoTool,
    handler: ({ value }: { value: string }) => ({ value }),
  });

  return null;
}

Mount MyPluginAgentTools from your plugin’s React Native entry (e.g. next to your DevTools panel registration) so it runs when the app is connected. Agents will see a domain equal to PLUGIN_ID and a tool named echo (qualified as @my-org/my-rozenite-plugin.echo).

Example: plugin-specific tool

The React Navigation plugin exposes tools such as get-root-state, navigate, and go-back. Each is defined as an AgentTool and registered with useRozenitePluginAgentTool using pluginId: '@rozenite/react-navigation-plugin'. The handler calls into the plugin’s refs and callbacks (e.g. navigation ref, state getters). For example:

useRozenitePluginAgentTool({
  pluginId: '@rozenite/react-navigation-plugin',
  tool: getRootStateTool,
  handler: () => {
    const state = getCurrentState();
    return { state, hasState: !!state };
  },
});

Other official plugins that expose agent tools include:

  • @rozenite/mmkv-plugin – MMKV storage inspection and mutation
  • @rozenite/storage-plugin – Generic storage adapter inspection and mutation
  • @rozenite/controls-plugin – Controls inspection and interaction
  • @rozenite/redux-devtools-plugin – Redux store inspection and curated history control

You can mirror this pattern: define one or more AgentTool objects, then register each with useRozenitePluginAgentTool in a component that is mounted when your plugin is active.

Tool shape and lifecycle

  • pluginId: Must match the plugin ID you use in Rozenite.
  • name: Use kebab-case. The full tool name seen by agents is {pluginId}.{name}.
  • description and inputSchema: Same as for in-app tools. Good descriptions and schema help agents call your tools correctly.
  • handler: Can be async. Return serializable data; on throw, the bridge sends an error result to the agent.

Tools are registered when the component that calls useRozenitePluginAgentTool mounts, and unregistered when it unmounts. If your plugin’s UI or logic is conditional, keep the agent tools in a component that stays mounted whenever the plugin is “active” so the domain and tools remain visible to agents.

Optional: publish typed SDK descriptors

Your plugin does not need this to be agent-enabled. Agents can already discover and call your tools through the plugin domain. Publishing ./sdk is a TypeScript convenience for users of @rozenite/agent-sdk, so they can write:

await session.tools.call(myPluginTools.echo, { value: 'hello' });

instead of manually spelling out the plugin domain and tool name.

The pattern used by Rozenite's official plugins is:

  1. Put the public tool contract in a non-React module such as src/shared/agent-tools.ts.
  2. Define each tool with defineAgentToolContract<TArgs, TResult>(...) from @rozenite/agent-shared.
  3. Reuse those shared definitions both in your React Native registration hook and in sdk.ts.
  4. In sdk.ts, call defineAgentToolDescriptors(pluginId, toolDefinitions) and export the resulting descriptor object.

That split keeps one source of truth for:

  • the plugin ID
  • tool names and descriptions
  • input schemas
  • public argument/result types

1. Define shared tool contracts

import {
  defineAgentToolContract,
  type AgentToolContract,
} from '@rozenite/agent-shared';

export const MY_PLUGIN_ID = '@my-org/my-rozenite-plugin';

export type EchoArgs = {
  value: string;
};

export type EchoResult = {
  value: string;
};

export const myToolDefinitions = {
  echo: defineAgentToolContract<EchoArgs, EchoResult>({
    name: 'echo',
    description: 'Return the provided value.',
    inputSchema: {
      type: 'object',
      properties: {
        value: { type: 'string' },
      },
      required: ['value'],
    },
  }),
} as const satisfies Record<string, AgentToolContract<unknown, unknown>>;

2. Reuse the same contracts at runtime

import { useRozenitePluginAgentTool } from '@rozenite/agent-bridge';
import { MY_PLUGIN_ID, myToolDefinitions } from '../shared/agent-tools';

export function MyPluginAgentTools() {
  useRozenitePluginAgentTool({
    pluginId: MY_PLUGIN_ID,
    tool: myToolDefinitions.echo,
    handler: ({ value }) => ({ value }),
  });

  return null;
}

3. Publish typed descriptors from sdk.ts

import { defineAgentToolDescriptors } from '@rozenite/agent-shared';
import { MY_PLUGIN_ID, myToolDefinitions } from './src/shared/agent-tools.js';

export { MY_PLUGIN_ID, myToolDefinitions };

export const myPluginTools = defineAgentToolDescriptors(
  MY_PLUGIN_ID,
  myToolDefinitions,
);

export type { EchoArgs, EchoResult } from './src/shared/agent-tools.js';

Recommended conventions:

  • Use undefined for tools that take no arguments, so SDK users can write session.tools.call(myPluginTools.ping) without passing {}.
  • Export any shared value/model types referenced by your public results from sdk.ts, not just the descriptor object.
  • Keep the tool name, description, and inputSchema in the shared contract module so your runtime registration and published SDK surface cannot drift.
Tip

You do not need to hand-maintain the ./sdk export in package.json. If your plugin has sdk.ts, rozenite build will detect it and publish the ./sdk subpath automatically.

Optional: enable/disable

You can conditionally register tools with the enabled option:

useRozenitePluginAgentTool({
  pluginId: PLUGIN_ID,
  tool: echoTool,
  handler: ({ value }) => ({ value }),
  enabled: isPluginReady,
});

When enabled is false, the tool is not registered and will not appear under your plugin’s domain.

How agents see your plugin tools

Agents discover domains and tools at runtime:

rozenite agent domains --session <sessionId> --json
rozenite agent @my-org/my-rozenite-plugin tools --session <sessionId> --json
rozenite agent @my-org/my-rozenite-plugin call --tool echo --args '{"value":"hello"}' --session <sessionId> --json

The domain slug is derived from your pluginId. Document your plugin’s agent tools (names, arguments, and return shapes) so agent users know what they can call.

Summary

  1. Add @rozenite/agent-bridge to your plugin.
  2. In your plugin’s React Native code, call useRozenitePluginAgentTool with a stable pluginId, a tool (name, description, inputSchema), and a handler.
  3. Mount the component that registers these tools so it runs when the plugin is active.
  4. Agents will see your plugin as a domain and can list and call your tools via rozenite agent <pluginId> tools|call ....

Next steps

Need React or React Native expertise you can count on?