Plugin Class
Every OpenTabs plugin is a subclass of OpenTabsPlugin. This is the one class you need to extend — it holds everything the platform needs to discover, inject, and run your plugin: identity, URL patterns, readiness probe, and tool list. All in one place.
Import
import { OpenTabsPlugin } from '@opentabs-dev/plugin-sdk';Class Signature
abstract class OpenTabsPlugin {
abstract readonly name: string;
abstract readonly displayName: string;
abstract readonly description: string;
abstract readonly urlPatterns: string[];
readonly excludePatterns?: string[];
readonly homepage?: string;
abstract readonly tools: ToolDefinition[];
abstract isReady(): Promise<boolean>;
teardown?(): void;
// Lifecycle hooks (optional)
onActivate?(): void;
onDeactivate?(): void;
onNavigate?(url: string): void;
onToolInvocationStart?(toolName: string): void;
onToolInvocationEnd?(toolName: string, success: boolean, durationMs: number): void;
}Abstract Properties
These properties are required — your subclass must define them.
| Property | Type | Description |
|---|---|---|
name | string | Unique identifier. Lowercase alphanumeric with hyphens (e.g., 'my-app'). Used as the tool name prefix and config key. |
description | string | Brief description of the plugin's purpose. Shown to MCP clients. |
displayName | string | Human-readable name shown in the side panel and health endpoint (e.g., 'My App'). |
urlPatterns | string[] | Chrome match patterns. Determines which tabs receive the adapter injection. Example: ['*://*.slack.com/*']. |
tools | ToolDefinition[] | Array of tool definitions. See Tools. |
Optional Properties
These properties are optional — override them when your plugin needs URL exclusions or a homepage link.
| Property | Type | Description |
|---|---|---|
excludePatterns | string[] | Chrome match patterns for URLs that should not match this plugin. Tabs matching both urlPatterns and excludePatterns are excluded. Useful when a broad urlPattern overlaps with another plugin's domain. |
homepage | string | URL to open when no matching tab exists and the user triggers an "open tab" action from the side panel. Should be a concrete URL (e.g., 'https://github.com'), not a match pattern. |
Example — excluding URLs:
class MyPlugin extends OpenTabsPlugin {
readonly urlPatterns = ['*://*.example.com/*'];
readonly excludePatterns = ['*://docs.example.com/*'];
// ...
}Example — setting a homepage:
class MyPlugin extends OpenTabsPlugin {
readonly urlPatterns = ['*://*.github.com/*'];
readonly homepage = 'https://github.com';
// ...
}Abstract Methods
isReady()
Readiness probe called by the extension to determine if the current tab is ready to accept tool requests. Runs in the page context.
abstract isReady(): Promise<boolean>;Return true when the user is authenticated and the page is operational. Return false when the page is loading, logged out, or in an error state.
The return value maps to the tab state machine:
| Condition | Tab State |
|---|---|
| No matching tab exists | closed |
Tab exists, isReady() returns false | unavailable |
Tab exists, isReady() returns true | ready |
Example:
async isReady(): Promise<boolean> {
// Check for an auth token that the app stores on login
const token = getPageGlobal('__APP__.authToken') as string | undefined;
return token !== undefined;
}teardown()
Optional cleanup method called before re-injection on plugin updates. Remove any event listeners, timers, or global state set up by the previous adapter version.
teardown?(): void;Plugins that don't set up persistent side effects can omit this.
Lifecycle Hooks
All lifecycle hooks are optional. They run in the page context and are wired automatically by the build process. Errors in hooks are caught and logged — they never affect adapter registration or tool execution.
See Lifecycle Hooks for the complete reference.
Example
import { OpenTabsPlugin, getPageGlobal } from '@opentabs-dev/plugin-sdk';
import type { ToolDefinition } from '@opentabs-dev/plugin-sdk';
import { listChannels } from './tools/list-channels.js';
import { sendMessage } from './tools/send-message.js';
class SlackPlugin extends OpenTabsPlugin {
readonly name = 'slack';
readonly description = 'Interact with Slack through the browser';
override readonly displayName = 'Slack';
readonly urlPatterns = ['*://*.slack.com/*'];
readonly tools: ToolDefinition[] = [listChannels, sendMessage];
async isReady(): Promise<boolean> {
const token = getPageGlobal('TS.boot_data.api_token') as string | undefined;
return token !== undefined;
}
}
export default new SlackPlugin();The plugin file exports a class instance (export default new SlackPlugin()), not the class itself. The build process wraps this instance into an adapter IIFE.
Last Updated: 10 Mar, 2026