Plugin Architecture
Monday Morning uses a plugin system to extend the MCP server with new tools, settings, and UI contributions. Plugins are loaded at server startup and their tools become available to any AI agent connected to the MCP server.
How Plugins Work
Section titled “How Plugins Work”The plugin system has three layers:
- Plugin Registry — Discovers plugin directories, validates exports, and calls
register(). - Plugin Interface — The
MondayMorningPlugincontract that every plugin implements. - MCP Server — Merges plugin tools into the server’s tool list so agents can call them.
Plugin Locations
Section titled “Plugin Locations”There are two kinds of plugins:
| Type | Location | Loaded By |
|---|---|---|
| Bundled | Ships with the app in mcp-servers/monday-morning/plugins/ | Always loaded |
| Community | Installed to ~/.monday-morning/plugins/{id}/ | Loaded if present |
Bundled plugins are compiled alongside the MCP server. Community plugins are standalone pre-compiled JavaScript packages installed from the Plugin Marketplace or manually.
Both types implement the same MondayMorningPlugin interface and go through the same loading pipeline. Bundled plugins take precedence on ID conflicts.
The MondayMorningPlugin Interface
Section titled “The MondayMorningPlugin Interface”Every plugin must default-export an object implementing MondayMorningPlugin:
interface MondayMorningPlugin { /** Unique plugin identifier, e.g., "github" */ id: string; /** Human-readable name, e.g., "GitHub Integration" */ name: string; /** Brief description */ description: string; /** Semver version string */ version: string; /** Classification: "integration" | "migration" | "export" */ category: PluginCategory; /** Tier for future entitlement gating (defaults to "free") */ tier?: PluginTier; /** MCP tools this plugin provides */ tools: PluginToolDefinition[]; /** Called during registration to initialize the plugin */ register(context: PluginContext): Promise<void>; /** Declarative settings schema for auto-rendered settings UI */ settings?: PluginSettingsSchema; /** UI category for desktop sidebar grouping */ uiCategory?: PluginUICategory; /** Declarative UI contributions (slots, sidebar items, widgets, actions) */ ui?: PluginUIRegistration; /** Called when the plugin is enabled by the user */ onEnable?(): Promise<void>; /** Called when the plugin is disabled by the user */ onDisable?(): Promise<void>; /** Called when the user switches to a different project */ onProjectSwitch?(projectPath: string): Promise<void>;}Required Fields
Section titled “Required Fields”| Field | Type | Purpose |
|---|---|---|
id | string | Unique identifier used internally |
name | string | Display name in the desktop app |
description | string | Shown in plugin listings |
version | string | Semver version for the plugin |
category | PluginCategory | One of "integration", "migration", "export" |
tools | PluginToolDefinition[] | Array of MCP tools the plugin provides |
register | function | Async init function called at load time |
Optional Fields
Section titled “Optional Fields”| Field | Type | Purpose |
|---|---|---|
tier | "free" | "pro" | Future entitlement gating (default: "free") |
settings | PluginSettingsSchema | Declarative settings for auto-rendered UI |
uiCategory | PluginUICategory | Sidebar grouping in desktop app |
ui | PluginUIRegistration | Slots, sidebar items, widgets, actions |
onEnable | function | Called when user enables the plugin |
onDisable | function | Called when user disables the plugin |
onProjectSwitch | function | Called when user switches projects |
Plugin Lifecycle
Section titled “Plugin Lifecycle”1. Discovery
Section titled “1. Discovery”On server startup, the PluginRegistry scans two directories for subdirectories containing an index.ts or index.js file:
- Bundled plugins — compiled output in the MCP server’s
plugins/directory - Community plugins —
~/.monday-morning/plugins/for user-installed plugins
const registry = new PluginRegistry(pluginsDir);await registry.discoverAndLoad();Bundled plugins are discovered first. Community plugins are discovered second — if a community plugin has the same id as a bundled plugin, it is skipped.
2. Validation
Section titled “2. Validation”After importing, the registry validates the default export:
idmust be a non-empty stringnamemust be a non-empty stringdescription,versionmust be stringscategorymust be"integration","migration", or"export"toolsmust be an arrayregistermust be a function- No duplicate
idvalues across plugins
If validation fails, the plugin is skipped with a warning.
3. Entitlement Check
Section titled “3. Entitlement Check”The registry calls checkEntitlement(plugin) before loading. Currently all plugins pass (both "free" and "pro" tiers). When a licensing backend is added, "pro" plugins will require a valid entitlement.
4. Registration
Section titled “4. Registration”The registry calls register() with a PluginContext:
interface PluginContext { /** Absolute path to the project root */ projectPath: string; /** Path utility functions for resolving .mm/ directory structure */ paths: typeof paths; /** Logger for plugin output */ logger: PluginLogger;}The logger writes to stderr (appropriate for MCP servers). Use it instead of console.log:
register: async (context) => { context.logger.info("My plugin registered");}5. Tool Merging
Section titled “5. Tool Merging”After all plugins load, the server calls registry.getTools() to collect every PluginToolDefinition and merges them into the MCP server’s tool list. Agents see plugin tools alongside built-in tools with no distinction.
6. Global Manifest
Section titled “6. Global Manifest”After loading, the registry writes a manifest.json to ~/.monday-morning/. The desktop app reads this manifest to populate the Plugins settings tab without needing a running MCP server:
{ "version": 2, "generatedAt": "2026-03-31T10:00:00.000Z", "plugins": [ { "id": "github", "name": "GitHub Integration", "description": "Sync issues and link pull requests", "version": "1.0.0", "category": "integration", "tier": "free", "tools": [ { "name": "mm_sync_github", "description": "..." }, { "name": "mm_link_github_pr", "description": "..." } ], "settings": { "credentials": ["..."] }, "uiCategory": "development", "ui": { "icon": "...", "slots": ["..."] } } ]}Plugin Categories
Section titled “Plugin Categories”Classification Categories
Section titled “Classification Categories”| Category | Purpose |
|---|---|
integration | Connect external services (GitHub, Slack, etc.) |
migration | Import data from other tools (Obsidian, Jira) |
export | Export Monday Morning data to other formats |
UI Categories
Section titled “UI Categories”UI categories determine how plugins are grouped in the desktop sidebar:
| UI Category | Examples |
|---|---|
communications | Slack, email integrations |
development | GitHub, GitLab |
project-management | Jira, Trello, Linear |
financial | Harvest, invoicing |
data-storage | Obsidian, Notion |
export-reporting | PDF export, dashboards |
Plugin Tiers
Section titled “Plugin Tiers”| Tier | Behavior |
|---|---|
free | Always loads, no restrictions |
pro | Reserved for future entitlement gating |
All plugins currently load regardless of tier. When a licensing system is added, pro plugins will require a valid subscription.
Next Steps
Section titled “Next Steps”- Creating a Plugin — Build a plugin from scratch with a step-by-step tutorial.
- Publishing & Marketplace — Submit your plugin to the marketplace.