Skip to main content

Basic Usage

import { Plugin, Provider, Token } from '@frontmcp/sdk';

// Provider for the plugin
@Provider()
class CacheService {
  private cache = new Map<string, string>();

  async get(key: string) {
    return this.cache.get(key);
  }

  async set(key: string, value: string) {
    this.cache.set(key, value);
  }
}

export const CacheServiceToken = new Token<CacheService>('CacheService');

// Plugin definition
@Plugin({
  name: 'cache',
  description: 'In-memory caching for tools',
  providers: [
    { provide: CacheServiceToken, useClass: CacheService },
  ],
})
export class CachePlugin {}

Signature

function Plugin(providedMetadata: PluginMetadata): ClassDecorator

Configuration Options

Required Properties

PropertyTypeDescription
namestringUnique plugin identifier

Optional Properties

PropertyTypeDescription
idstringStable identifier
descriptionstringPlugin description
scope'app' | 'server'Plugin scope level

Components

PropertyTypeDescription
providersProviderType[]Plugin providers
toolsToolType[]Plugin tools
resourcesResourceType[]Plugin resources
promptsPromptType[]Plugin prompts
skillsSkillType[]Plugin skills
adaptersAdapterType[]Framework adapters
pluginsPluginType[]Nested plugins

Extensions

PropertyTypeDescription
contextExtensionsContextExtension[]Add properties to context classes

Context Extensions

Extend all context classes with custom properties:
import { Plugin, Provider, Token, ExecutionContextBase } from '@frontmcp/sdk';

// 1. Define token
export const RememberAccessorToken = new Token<RememberAccessor>('RememberAccessor');

// 2. Define accessor
@Provider()
class RememberAccessor {
  async get(key: string): Promise<string | undefined> {
    // Implementation
  }

  async set(key: string, value: string): Promise<void> {
    // Implementation
  }
}

// 3. Augment types (in .d.ts or separate file)
declare module '@frontmcp/sdk' {
  interface ExecutionContextBase {
    readonly remember: RememberAccessor;
  }
}

// 4. Define plugin with context extension
@Plugin({
  name: 'remember',
  description: 'Session memory for tools',
  providers: [
    { provide: RememberAccessorToken, useClass: RememberAccessor },
  ],
  contextExtensions: [
    {
      property: 'remember',
      token: RememberAccessorToken,
      errorMessage: 'Remember plugin not installed',
    },
  ],
})
export class RememberPlugin {}
Now in tools:
@Tool({ name: 'my_tool', inputSchema: {} })
class MyTool extends ToolContext {
  async execute() {
    // Access extended property
    await this.remember.set('lastRun', new Date().toISOString());
    const lastRun = await this.remember.get('lastRun');
  }
}

Plugin Scopes

App Scope (Default)

Hooks register at app level:
@Plugin({
  name: 'app-plugin',
  scope: 'app', // Default
})
class AppPlugin {}

Server Scope

Hooks register at gateway/server level:
@Plugin({
  name: 'server-plugin',
  scope: 'server',
})
class ServerPlugin {}
Server-scoped plugins cannot be used in standalone apps. They require a gateway configuration.

Nested Plugins

Plugins can include other plugins:
@Plugin({
  name: 'analytics',
  plugins: [MetricsPlugin, LoggingPlugin],
  providers: [AnalyticsService],
})
class AnalyticsPlugin {}

Using Plugins

In Apps

@App({
  name: 'my-app',
  plugins: [CachePlugin, RememberPlugin],
  tools: [MyTool],
})
class MyApp {}

In Server

@FrontMcp({
  info: { name: 'Server', version: '1.0.0' },
  apps: [MyApp],
  plugins: [GlobalLoggingPlugin], // Server-level plugins
})
class Server {}

Plugin with Tools

import { Plugin, Tool, ToolContext } from '@frontmcp/sdk';
import { z } from 'zod';

@Tool({
  name: 'send_elicitation_result',
  description: 'Send user response to pending elicitation',
  inputSchema: {
    elicitId: z.string(),
    response: z.any(),
  },
})
class SendElicitationResultTool extends ToolContext {
  async execute(input) {
    // Plugin-provided tool implementation
  }
}

@Plugin({
  name: 'elicitation',
  tools: [SendElicitationResultTool],
  providers: [ElicitationService],
})
export class ElicitationPlugin {}

Full Example

import { Plugin, Provider, Token, Tool, ToolContext, App, FrontMcp } from '@frontmcp/sdk';
import { z } from 'zod';

// Tokens
export const ApprovalServiceToken = new Token<ApprovalService>('ApprovalService');
export const ApprovalAccessorToken = new Token<ApprovalAccessor>('ApprovalAccessor');

// Services
@Provider()
class ApprovalService {
  private approvals = new Map<string, boolean>();

  approve(toolName: string) {
    this.approvals.set(toolName, true);
  }

  isApproved(toolName: string): boolean {
    return this.approvals.get(toolName) === true;
  }

  revoke(toolName: string) {
    this.approvals.delete(toolName);
  }
}

@Provider()
class ApprovalAccessor {
  constructor(private service: ApprovalService) {}

  async isApproved(toolName: string): Promise<boolean> {
    return this.service.isApproved(toolName);
  }

  async approve(toolName: string): Promise<void> {
    this.service.approve(toolName);
  }
}

// Module augmentation
declare module '@frontmcp/sdk' {
  interface ExecutionContextBase {
    readonly approval: ApprovalAccessor;
  }
}

// Plugin tool
@Tool({
  name: 'approve_tool',
  description: 'Approve a tool for execution',
  inputSchema: { toolName: z.string() },
})
class ApproveToolTool extends ToolContext {
  async execute(input: { toolName: string }) {
    const accessor = this.get(ApprovalAccessorToken);
    await accessor.approve(input.toolName);
    return { approved: input.toolName };
  }
}

// Plugin definition
@Plugin({
  name: 'approval',
  description: 'Tool approval system',
  providers: [
    { provide: ApprovalServiceToken, useClass: ApprovalService },
    { provide: ApprovalAccessorToken, useClass: ApprovalAccessor },
  ],
  tools: [ApproveToolTool],
  contextExtensions: [
    {
      property: 'approval',
      token: ApprovalAccessorToken,
      errorMessage: 'Approval plugin not installed',
    },
  ],
})
export class ApprovalPlugin {}

// Using the plugin
@Tool({
  name: 'dangerous_operation',
  inputSchema: { data: z.string() },
})
class DangerousOperationTool extends ToolContext {
  async execute(input: { data: string }) {
    // Check approval via context extension
    const isApproved = await this.approval.isApproved('dangerous_operation');

    if (!isApproved) {
      return { error: 'This tool requires approval. Please run approve_tool first.' };
    }

    // Proceed with operation
    return { result: 'Operation completed' };
  }
}

@App({
  name: 'secure-app',
  plugins: [ApprovalPlugin],
  tools: [DangerousOperationTool],
})
class SecureApp {}

@FrontMcp({
  info: { name: 'Secure Server', version: '1.0.0' },
  apps: [SecureApp],
})
export default class SecureServer {}

Official Plugins

FrontMCP provides several official plugins:

RememberPlugin

Session memory for tools

CachePlugin

Response caching

CodeCallPlugin

Dynamic code execution

PluginRegistry

Plugin registry API

@Provider

Dependency providers

Creating Plugins

Plugin development guide

@App

Application modules