Integrate

Custom Plugins

Create your own plugins for any service

Creating Custom Plugins

The Integrate SDK's plugin system is extensible, allowing you to create custom plugins for any service supported by the MCP server.

Using the Generic OAuth Plugin

The easiest way to create a plugin is using the genericOAuthPlugin helper:

import { createMCPClient, genericOAuthPlugin } from "integrate-sdk";

const slackPlugin = genericOAuthPlugin({
  id: "slack",
  provider: "slack",
  clientId: process.env.SLACK_CLIENT_ID!,
  clientSecret: process.env.SLACK_CLIENT_SECRET!,
  scopes: ["chat:write", "channels:read", "users:read"],
  tools: [
    "slack_send_message",
    "slack_list_channels",
    "slack_get_channel",
    "slack_invite_user",
  ],
  redirectUri: "https://your-app.com/callback",
});

const client = createMCPClient({
  plugins: [slackPlugin],
});

Generic OAuth Plugin Options

OptionTypeRequiredDescription
idstringYesUnique plugin identifier
providerstringYesOAuth provider name (e.g., 'slack', 'notion')
clientIdstringYesOAuth client ID
clientSecretstringYesOAuth client secret
scopesstring[]YesOAuth scopes
toolsstring[]YesTool names to enable
redirectUristringNoOAuth redirect URI

Creating a Simple Plugin (No OAuth)

For tools that don't require OAuth, use createSimplePlugin:

import { createSimplePlugin } from "integrate-sdk";

const mathPlugin = createSimplePlugin({
  id: "math",
  tools: ["math_add", "math_subtract", "math_multiply", "math_divide"],
  onInit: async (client) => {
    console.log("Math plugin initialized");
  },
});

const client = createMCPClient({
  plugins: [mathPlugin],
});

Creating a Custom Plugin from Scratch

For more control, implement the MCPPlugin interface:

import type { MCPPlugin } from "integrate-sdk";

interface NotionPluginConfig {
  clientId: string;
  clientSecret: string;
  scopes?: string[];
}

export function notionPlugin(config: NotionPluginConfig): MCPPlugin {
  return {
    id: "notion",
    tools: [
      "notion_search",
      "notion_create_page",
      "notion_update_page",
      "notion_query_database",
    ],
    oauth: {
      provider: "notion",
      clientId: config.clientId,
      clientSecret: config.clientSecret,
      scopes: config.scopes || ["read_content", "update_content"],
    },

    async onInit(client) {
      console.log("Notion plugin initialized");
    },

    async onBeforeConnect(client) {
      console.log("Notion plugin: preparing to connect");
    },

    async onAfterConnect(client) {
      console.log("Notion plugin: connected successfully");

      // You can call tools here if needed
      const tools = client.getEnabledTools();
      console.log("Notion tools available:", tools.length);
    },

    async onDisconnect(client) {
      console.log("Notion plugin: disconnected");
    },
  };
}

Plugin Interface

interface MCPPlugin {
  id: string; // Unique identifier
  tools: string[]; // Tool names to enable
  oauth?: OAuthConfig; // OAuth configuration (optional)
  onInit?: PluginHook; // Called when plugin is created
  onBeforeConnect?: PluginHook; // Called before connecting
  onAfterConnect?: PluginHook; // Called after connecting
  onDisconnect?: PluginHook; // Called on disconnect
}

interface OAuthConfig {
  provider: string; // OAuth provider name
  clientId: string; // Client ID
  clientSecret: string; // Client secret
  scopes: string[]; // OAuth scopes
  redirectUri?: string; // Redirect URI (optional)
}

type PluginHook = (client: MCPClient) => Promise<void> | void;

Lifecycle Hooks

Plugins support four lifecycle hooks:

onInit

Called when the plugin is created (during createMCPClient):

async onInit(client) {
  // Initialize plugin state
  console.log('Plugin initialized');
}

onBeforeConnect

Called before establishing the connection to the MCP server:

async onBeforeConnect(client) {
  // Prepare for connection
  console.log('About to connect');
}

onAfterConnect

Called after successfully connecting to the MCP server:

async onAfterConnect(client) {
  // Verify tools are available
  const tools = client.getEnabledTools();
  console.log('Tools available:', tools.length);
}

onDisconnect

Called when disconnecting from the server:

async onDisconnect(client) {
  // Cleanup
  console.log('Disconnected');
}

Example: Slack Plugin

Here's a complete example of a Slack plugin:

import { genericOAuthPlugin } from "integrate-sdk";

export const slackPlugin = (config: {
  clientId: string;
  clientSecret: string;
  scopes?: string[];
}) =>
  genericOAuthPlugin({
    id: "slack",
    provider: "slack",
    clientId: config.clientId,
    clientSecret: config.clientSecret,
    scopes: config.scopes || [
      "chat:write",
      "channels:read",
      "channels:manage",
      "users:read",
    ],
    tools: [
      "slack_send_message",
      "slack_list_channels",
      "slack_get_channel",
      "slack_create_channel",
      "slack_invite_user",
      "slack_list_users",
    ],
  });

// Usage
const client = createMCPClient({
  plugins: [
    slackPlugin({
      clientId: process.env.SLACK_CLIENT_ID!,
      clientSecret: process.env.SLACK_CLIENT_SECRET!,
    }),
  ],
});

Example: Notion Plugin

import type { MCPPlugin } from "integrate-sdk";

interface NotionConfig {
  clientId: string;
  clientSecret: string;
  scopes?: string[];
}

export function notionPlugin(config: NotionConfig): MCPPlugin {
  return {
    id: "notion",
    tools: [
      "notion_search",
      "notion_create_page",
      "notion_update_page",
      "notion_get_page",
      "notion_query_database",
      "notion_create_database",
    ],
    oauth: {
      provider: "notion",
      clientId: config.clientId,
      clientSecret: config.clientSecret,
      scopes: config.scopes || ["read_content", "update_content"],
    },
  };
}

// Usage
const client = createMCPClient({
  plugins: [
    notionPlugin({
      clientId: process.env.NOTION_CLIENT_ID!,
      clientSecret: process.env.NOTION_CLIENT_SECRET!,
    }),
  ],
});

Discovering Available Tools

Before creating a plugin, you might want to see what tools are available on the MCP server:

// Create client without filters to see all tools
const client = createMCPClient({
  plugins: [],
});

await client.connect();

const allTools = client.getAvailableTools();
console.log("All available tools:");
allTools.forEach((tool) => {
  console.log(`- ${tool.name}: ${tool.description}`);
});

Best Practices

  1. Use meaningful IDs - Plugin IDs should be unique and descriptive
  2. Provide sensible defaults - Set default scopes for common use cases
  3. Document required scopes - Let users know what permissions are needed
  4. Handle errors gracefully - Use lifecycle hooks to validate configuration
  5. List all tools - Include all relevant tools in the tools array
  6. Test thoroughly - Verify tools work as expected

Next Steps