Files
yeachan-heo--oh-my-claudecode/docs/COMPATIBILITY.md
T
wehub-resource-sync adc62957a7
CI / Multi-repo Path Gate (AST-grep) (windows-latest) (push) Has been cancelled
CI / Version Consistency Check (push) Has been cancelled
CI / npm pack + install test (push) Has been cancelled
CI / Lint & Type Check (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Test (Windows path suite) (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / No Committed Build Artifacts (push) Has been cancelled
CI / Multi-repo Path Gate (AST-grep) (ubuntu-latest) (push) Has been cancelled
Upgrade Test / omc update + session-start hook (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:36:54 +08:00

28 KiB

MCP/Plugin Compatibility Layer

The Compatibility Layer enables oh-my-claudecode to discover, register, and use external plugins, MCP servers, and tools. It provides a unified interface for managing external tools while maintaining security through an integrated permission system.

Table of Contents

Overview

The Compatibility Layer consists of four integrated systems working together:

  1. Discovery System - Automatically finds plugins and MCP servers from user directories
  2. Tool Registry - Central hub that registers and manages all external tools with conflict resolution
  3. Permission Adapter - Integrates with OMC's permission system for safe tool execution
  4. MCP Bridge - Connects to MCP servers and exposes their tools for use
Plugins              MCP Configs          OMC Tools
   ↓                      ↓                    ↓
 Discovery System ────────────────────────────┐
                                              ↓
                           Tool Registry ← ← ←┘
                              ↓
                         Permission Adapter
                              ↓
                           MCP Bridge

Architecture

Discovery System (discovery.ts)

Scans for external plugins and MCP servers from:

  • ~/.claude/plugins/ - OMC/Claude Code plugins directory
  • ~/.claude/installed-plugins/ - Alternative plugins location
  • ~/.claude/settings.json - Claude Code MCP server configs
  • ~/.claude/claude_desktop_config.json - Claude Desktop MCP server configs
  • Plugin manifests (plugin.json) for embedded MCP servers

Discovers:

  • Plugin skills and agents (from SKILL.md and agent .md files)
  • MCP server configurations
  • Tool definitions from plugin manifests

Tool Registry (registry.ts)

Central hub for tool management:

  • Registers tools from discovered plugins and MCP servers
  • Handles tool name conflicts using priority-based resolution
  • Routes commands to appropriate handlers
  • Provides search and filtering capabilities
  • Emits events for registration and connection status

Key features:

  • Tools are namespaced (e.g., plugin-name:tool-name)
  • Priority system for conflict resolution (higher priority wins)
  • Short name lookup (finds tool-name even with namespace)
  • Event listeners for monitoring registry state

Permission Adapter (permission-adapter.ts)

Integrates external tools with OMC's permission system:

  • Maintains safe patterns for read-only tools
  • Auto-approves known-safe operations
  • Prompts user for dangerous operations (write, execute)
  • Caches permission decisions
  • Determines delegation targets for tool execution

Safe patterns:

  • Built-in patterns for common MCP tools (filesystem read, context7 queries)
  • Plugin-contributed patterns from manifests
  • Custom patterns can be registered at runtime

MCP Bridge (mcp-bridge.ts)

Manages MCP server connections:

  • Spawns server processes
  • Sends JSON-RPC requests and handles responses
  • Discovers tools and resources from servers
  • Routes tool invocations to servers
  • Handles connection lifecycle (connect, disconnect, reconnect)

Protocol: JSON-RPC 2.0 over process stdio with newline-delimited messages

Plugin Discovery

Directory Structure

Plugins are discovered from ~/.claude/plugins/ and ~/.claude/installed-plugins/:

~/.claude/plugins/
├── my-plugin/
│   ├── plugin.json          (required)
│   ├── skills/              (optional)
│   │   ├── skill-1/
│   │   │   └── SKILL.md
│   │   └── skill-2/
│   │       └── SKILL.md
│   ├── agents/              (optional)
│   │   ├── agent-1.md
│   │   └── agent-2.md
│   └── commands/            (optional)
└── another-plugin/
    └── plugin.json

Plugin Manifest Structure

The plugin.json defines the plugin's metadata and tools:

{
  "name": "my-plugin",
  "version": "1.0.0",
  "description": "My awesome plugin",
  "namespace": "my-plugin",
  "skills": "./skills/",
  "agents": "./agents/",
  "commands": "./commands/",
  "mcpServers": {
    "server-name": {
      "command": "node",
      "args": ["server.js"],
      "env": {},
      "enabled": true,
      "description": "My MCP server"
    }
  },
  "permissions": [
    {
      "tool": "my-plugin:search",
      "scope": "read",
      "patterns": [".*"],
      "reason": "Search is read-only"
    }
  ],
  "tools": [
    {
      "name": "my-tool",
      "description": "Does something useful",
      "handler": "tools/my-tool.js",
      "inputSchema": {
        "type": "object",
        "properties": {
          "query": { "type": "string" }
        }
      }
    }
  ]
}

Skill and Agent Discovery

Skills are discovered from SKILL.md files in supported skills directories. OMC's canonical project-local write target remains .omc/skills/, and it also reads Claude Code project skills from .claude/skills/ plus project-local compatibility skills from .agents/skills/. Each skill directory must contain a SKILL.md with frontmatter:

---
name: my-skill
description: Describes what this skill does
tags: tag1, tag2
---

Skill documentation here...

Agents are discovered from .md files in the agents directory with similar frontmatter structure. Supported runtime fields depend on Claude Code; OMC's bundled agent files currently rely on name, description, model, optional tool restrictions, and prompt body guidance. They do not currently ship an effort: frontmatter override, so effort inherits from the parent Claude Code session unless a custom agent explicitly adds one.

MCP Server Discovery

Claude Desktop Config

Located at ~/.claude/claude_desktop_config.json:

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/"],
      "enabled": true
    },
    "web": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-web"],
      "enabled": true
    }
  }
}

Claude Code Settings

Located at ~/.claude/settings.json:

{
  "mcpServers": {
    "my-server": {
      "command": "node",
      "args": ["server.js"],
      "env": {
        "API_KEY": "secret"
      }
    }
  }
}

Remote MCP / Remote OMC Shape

OMC can sync and preserve remote MCP entries in the unified registry. That is the supported narrow answer to "connect to a remote OMC":

{
  "mcpServers": {
    "remoteOmc": {
      "url": "https://lab.example.com/mcp",
      "timeout": 30
    }
  }
}

This supports remote MCP endpoints. It does not create a general multi-host OMC cluster or a transparent shared remote filesystem view.

Plugin-Embedded MCP Servers

Plugins can define MCP servers in their manifest:

{
  "name": "plugin-with-server",
  "mcpServers": {
    "my-mcp": {
      "command": "node",
      "args": ["./mcp/server.js"]
    }
  }
}

Plugin Manifest Format

Complete Schema

Field Type Required Description
name string Yes Plugin name (alphanumeric, hyphens, underscores)
version string Yes Semantic version (e.g., "1.0.0")
description string No Human-readable description
namespace string No Prefix for tool names (defaults to plugin name)
skills string|string[] No Path(s) to skills directory
agents string|string[] No Path(s) to agents directory
commands string|string[] No Path(s) to commands directory
mcpServers object No MCP server configurations (name → McpServerEntry)
permissions PluginPermission[] No Permissions needed for plugin tools
tools PluginToolDefinition[] No Tool definitions

McpServerEntry

Field Type Required Description
command string Yes Command to run server (e.g., "node", "npx")
args string[] No Command arguments
env object No Environment variables to pass to server
enabled boolean No Whether server connects on init (default: true)
description string No Human-readable description

PluginPermission

Field Type Description
tool string Tool name requiring permission
scope "read"|"write"|"execute"|"all" Permission scope
patterns string[] Regex patterns for allowed paths/commands
reason string Why this permission is needed

PluginToolDefinition

Field Type Description
name string Tool name (becomes namespace:name)
description string Human-readable description
handler string Path to handler function or command
inputSchema object JSON Schema for tool input

Tool Registration

Registration Process

Tools are registered in this order:

  1. Plugin discovery - Plugins found in configured paths
  2. Tool extraction - Skills, agents, and tool definitions extracted from plugins
  3. MCP server discovery - MCP servers found from config files
  4. Tool conversion - MCP tools converted to ExternalTool format
  5. Conflict resolution - Tools with same name resolved by priority

Tool Naming

Tools use a namespaced format:

{namespace}:{tool-name}

Examples:
- my-plugin:search
- filesystem:read_file
- context7:query-docs

Short names also work:

getRegistry().getTool('search')     // Finds 'my-plugin:search'
getRegistry().getTool('my-plugin:search')  // Exact match

Conflict Resolution

When two plugins provide a tool with the same name:

  1. Priority value - Tool with higher priority wins (default: 50)
  2. Namespace - Use full namespaced name to disambiguate
  3. Manual - Check conflicts and re-register with different priority
// Check for conflicts
const conflicts = registry.getConflicts();

// Get winner for conflict
const winner = conflicts[0].winner;
console.log(`${winner.source} won with priority ${winner.priority}`);

Permission System

Safe Patterns

Read-only tools are auto-approved without user prompting:

// Check if tool is safe
const result = checkPermission('mcp__filesystem__read_file');
// { allowed: true, reason: "Filesystem read (read-only)" }

Built-in safe patterns cover:

  • Context7 - Documentation queries (read-only)
  • Filesystem - Read operations only
  • Exa - Web search (read-only, external)

Permission Check Flow

Tool invocation
    ↓
Check safe patterns → Allowed (no prompt needed)
    ↓ (not in safe patterns)
Check dangerous patterns → Ask user
    ↓ (not dangerous)
Check tool capabilities → Safe caps (auto-approve) or Dangerous (ask user)
    ↓
Execute or Deny

Auto-Approval Examples

// Read-only tools are safe
checkPermission('my-plugin:search')
// { allowed: true, reason: "Tool has safe capabilities: search" }

// Write/execute requires user confirmation
checkPermission('filesystem:write_file', { path: '/etc/passwd' })
// { allowed: false, askUser: true, reason: "Tool requires explicit permission" }

Caching Permissions

Permission decisions are cached. Users can grant or deny persistently:

// User grants permission
grantPermission('custom:dangerous-tool', { mode: 'aggressive' });

// Later calls use cached decision
checkPermission('custom:dangerous-tool', { mode: 'aggressive' });
// { allowed: true, reason: "User granted permission" }

// Clear cache when needed
clearPermissionCache();

Registering Safe Patterns

Plugins can register safe patterns in manifest:

{
  "name": "my-plugin",
  "permissions": [
    {
      "tool": "my-plugin:query-docs",
      "scope": "read",
      "patterns": [".*"],
      "reason": "Documentation lookup is read-only"
    }
  ]
}

These are automatically integrated when the plugin is initialized.

MCP Bridge

Connecting to Servers

import { getMcpBridge } from './compatibility';

const bridge = getMcpBridge();

// Connect to a single server
const tools = await bridge.connect('filesystem');
console.log(`Connected. Available tools: ${tools.map(t => t.name).join(', ')}`);

// Auto-connect all enabled servers
const results = await bridge.autoConnect();
for (const [serverName, tools] of results) {
  console.log(`${serverName}: ${tools.length} tools`);
}

Invoking Tools

// Invoke a tool on an MCP server
const result = await bridge.invokeTool('filesystem', 'read_file', {
  path: '/home/user/.bashrc'
});

if (result.success) {
  console.log('File contents:', result.data);
  console.log('Time:', result.executionTime, 'ms');
} else {
  console.error('Error:', result.error);
}

Reading Resources

Some MCP servers provide resources (documents, APIs, etc.):

// Read a resource
const result = await bridge.readResource('web', 'https://example.com');

if (result.success) {
  console.log(result.data);
}

Connection Management

// Check connection status
if (bridge.isConnected('filesystem')) {
  console.log('Connected to filesystem server');
}

// Get all server tools and resources
const tools = bridge.getServerTools('filesystem');
const resources = bridge.getServerResources('web');

// Disconnect from server
bridge.disconnect('filesystem');

// Disconnect from all servers
bridge.disconnectAll();

Events

Monitor bridge activity:

const bridge = getMcpBridge();

bridge.on('server-connected', ({ server, toolCount }) => {
  console.log(`Connected to ${server} with ${toolCount} tools`);
});

bridge.on('server-disconnected', ({ server, code }) => {
  console.log(`Disconnected from ${server}`);
});

bridge.on('server-error', ({ server, error }) => {
  console.error(`Error from ${server}:`, error);
});

API Reference

Initialization

import {
  initializeCompatibility,
  getRegistry,
  getMcpBridge
} from './compatibility';

// Initialize everything
const result = await initializeCompatibility({
  pluginPaths: ['~/.claude/plugins'],
  mcpConfigPath: '~/.claude/claude_desktop_config.json',
  autoConnect: true  // Auto-connect to MCP servers
});

console.log(`Plugins: ${result.pluginCount}`);
console.log(`MCP servers: ${result.mcpServerCount}`);
console.log(`Tools: ${result.toolCount}`);
console.log(`Connected: ${result.connectedServers.join(', ')}`);

Discovery Functions

import {
  discoverPlugins,
  discoverMcpServers,
  discoverAll,
  isPluginInstalled,
  getPluginInfo,
  getPluginPaths,
  getMcpConfigPath
} from './compatibility';

// Discover plugins from custom paths
const plugins = discoverPlugins({
  pluginPaths: ['/custom/plugins/path']
});

// Discover MCP servers
const servers = discoverMcpServers({
  mcpConfigPath: '~/.claude/claude_desktop_config.json',
  settingsPath: '~/.claude/settings.json'
});

// Discover everything at once
const result = discoverAll({
  force: true  // Force re-discovery even if cached
});

// Check plugin installation
if (isPluginInstalled('my-plugin')) {
  const info = getPluginInfo('my-plugin');
  console.log(`${info.name} v${info.version}`);
}

// Get configured paths
const pluginPaths = getPluginPaths();
const mcpPath = getMcpConfigPath();

Registry Functions

import {
  getRegistry,
  initializeRegistry,
  routeCommand,
  getExternalTool,
  listExternalTools,
  hasExternalPlugins,
  hasMcpServers
} from './compatibility';

const registry = getRegistry();

// Register discovery and tools
await initializeRegistry({ force: true });

// Access tools
const allTools = listExternalTools();
const tool = getExternalTool('my-plugin:search');

// Route command
const route = routeCommand('search');
if (route) {
  console.log(`Handler: ${route.handler}`);
  console.log(`Requires permission: ${route.requiresPermission}`);
}

// Check what's available
if (hasExternalPlugins()) {
  console.log('External plugins available');
}
if (hasMcpServers()) {
  console.log('MCP servers available');
}

// Get all plugins and servers
const plugins = registry.getAllPlugins();
const servers = registry.getAllMcpServers();

// Search tools
const results = registry.searchTools('filesystem');

// Listen to events
registry.addEventListener(event => {
  if (event.type === 'tool-registered') {
    console.log(`Registered: ${event.data.tool}`);
  }
});

Permission Functions

import {
  checkPermission,
  grantPermission,
  denyPermission,
  clearPermissionCache,
  addSafePattern,
  getSafePatterns,
  shouldDelegate,
  getDelegationTarget,
  integrateWithPermissionSystem,
  processExternalToolPermission
} from './compatibility';

// Check if tool is allowed
const check = checkPermission('my-tool:dangerous-op');
if (check.allowed) {
  console.log('Allowed:', check.reason);
} else if (check.askUser) {
  console.log('Ask user:', check.reason);
}

// Cache user decisions
grantPermission('custom:tool', { mode: 'aggressive' });
denyPermission('risky:tool');
clearPermissionCache();

// Manage safe patterns
const patterns = getSafePatterns();
addSafePattern({
  tool: 'my-safe-tool',
  pattern: /^\/safe\/path/,
  description: 'Only allows /safe/path',
  source: 'myapp'
});

// Check if tool should be delegated
if (shouldDelegate('external:tool')) {
  const target = getDelegationTarget('external:tool');
  console.log(`Delegate to: ${target.type}/${target.target}`);
}

// Integrate with permission system at startup
integrateWithPermissionSystem();

MCP Bridge Functions

import {
  getMcpBridge,
  resetMcpBridge,
  invokeMcpTool,
  readMcpResource
} from './compatibility';

const bridge = getMcpBridge();

// Connect to server
const tools = await bridge.connect('filesystem');

// Invoke tool
const result = await invokeMcpTool('filesystem', 'read_file', {
  path: '/etc/hosts'
});

// Read resource
const resourceResult = await readMcpResource('web', 'https://api.example.com');

// Check connections
const status = bridge.getConnectionStatus();

// Clean up
bridge.disconnectAll();
resetMcpBridge();

Examples

Example 1: Initialize and List Tools

import { initializeCompatibility, getRegistry } from './compatibility';

async function listAvailableTools() {
  // Initialize the compatibility layer
  const result = await initializeCompatibility({
    autoConnect: true
  });

  console.log(`Discovered ${result.pluginCount} plugins`);
  console.log(`Connected to ${result.connectedServers.length} MCP servers`);

  // List all available tools
  const registry = getRegistry();
  const tools = registry.getAllTools();

  console.log('\nAvailable tools:');
  for (const tool of tools) {
    console.log(`  ${tool.name} (${tool.type})`);
    console.log(`    Description: ${tool.description}`);
    console.log(`    Capabilities: ${tool.capabilities?.join(', ')}`);
  }
}

listAvailableTools().catch(console.error);

Example 2: Search and Use a Tool

import {
  initializeCompatibility,
  getRegistry,
  checkPermission,
  getMcpBridge
} from './compatibility';

async function searchAndRead() {
  await initializeCompatibility();

  const registry = getRegistry();

  // Search for filesystem tools
  const fileTools = registry.searchTools('filesystem');
  console.log(`Found ${fileTools.length} filesystem tools`);

  // Find read_file tool
  const readTool = fileTools.find(t => t.name.includes('read'));

  if (readTool) {
    // Check permission
    const perm = checkPermission(readTool.name);

    if (perm.allowed) {
      const bridge = getMcpBridge();
      const result = await bridge.invokeTool(
        readTool.source,
        'read_file',
        { path: '/etc/hosts' }
      );

      if (result.success) {
        console.log('File contents:', result.data);
      }
    }
  }
}

searchAndRead().catch(console.error);

Example 3: Handle Plugin with MCP Server

import {
  discoverPlugins,
  initializeRegistry,
  getMcpBridge
} from './compatibility';

async function setupPluginMcp() {
  // Discover plugins (includes MCP servers defined in manifests)
  const plugins = discoverPlugins();
  const pluginWithMcp = plugins.find(p => p.manifest.mcpServers);

  if (pluginWithMcp) {
    console.log(`Plugin ${pluginWithMcp.name} has embedded MCP servers:`);
    for (const serverName of Object.keys(pluginWithMcp.manifest.mcpServers || {})) {
      console.log(`  - ${serverName}`);
    }

    // Initialize registry (registers MCP servers from plugins)
    await initializeRegistry();

    // Connect to plugin's MCP server
    const bridge = getMcpBridge();
    const fullServerName = `${pluginWithMcp.name}:${serverName}`;

    try {
      const tools = await bridge.connect(fullServerName);
      console.log(`Connected to ${fullServerName} with ${tools.length} tools`);
    } catch (err) {
      console.error('Failed to connect:', err.message);
    }
  }
}

setupPluginMcp().catch(console.error);

Example 4: Conflict Resolution

import { getRegistry } from './compatibility';

function showConflicts() {
  const registry = getRegistry();
  const conflicts = registry.getConflicts();

  if (conflicts.length === 0) {
    console.log('No tool conflicts');
    return;
  }

  console.log(`Found ${conflicts.length} conflicts:\n`);

  for (const conflict of conflicts) {
    console.log(`Tool: ${conflict.name}`);
    console.log(`  Winner: ${conflict.winner.source} (priority: ${conflict.winner.priority})`);
    console.log('  Alternatives:');
    for (const tool of conflict.tools) {
      if (tool !== conflict.winner) {
        console.log(`    - ${tool.source} (priority: ${tool.priority})`);
      }
    }
    console.log();
  }
}

showConflicts();

Example 5: Custom Permission Pattern

import {
  addSafePattern,
  checkPermission,
  getSafePatterns
} from './compatibility';

function registerCustomPatterns() {
  // Register a safe pattern for a plugin tool
  addSafePattern({
    tool: 'analytics:track',
    pattern: /^(page_view|event|error)$/,
    description: 'Only allows tracking specific event types',
    source: 'myapp'
  });

  // Now check permission with valid input
  let result = checkPermission('analytics:track');
  console.log('Safe:', result.allowed);  // true

  // View all patterns
  const patterns = getSafePatterns();
  const myPatterns = patterns.filter(p => p.source === 'myapp');
  console.log('My patterns:', myPatterns.length);
}

registerCustomPatterns();

Troubleshooting

Plugins Not Discovered

Problem: discoverPlugins() returns empty array.

Checklist:

  • Plugins are in ~/.claude/plugins/ or ~/.claude/installed-plugins/
  • Each plugin has a plugin.json in the root or .claude-plugin/ subdirectory
  • Plugin name doesn't conflict with reserved names (e.g., 'oh-my-claudecode')
  • File permissions allow reading the directory

Debug:

import { getPluginPaths } from './compatibility';

const paths = getPluginPaths();
console.log('Scanning paths:', paths);

// Check if directory exists
import { existsSync } from 'fs';
for (const path of paths) {
  console.log(`${path}: ${existsSync(path) ? 'exists' : 'missing'}`);
}

MCP Server Won't Connect

Problem: bridge.connect() times out.

Checklist:

  • Server command is correct (e.g., npx, node)
  • Command is executable and in PATH
  • Arguments are valid
  • Server implements MCP protocol (JSON-RPC 2.0)
  • Check stderr output for errors

Debug:

import { getMcpBridge } from './compatibility';

const bridge = getMcpBridge();

bridge.on('server-error', ({ server, error }) => {
  console.error(`Server error from ${server}:`, error);
});

bridge.on('connect-error', ({ server, error }) => {
  console.error(`Failed to connect to ${server}:`, error);
});

Tools Not Showing Up

Problem: Registered tools don't appear in getRegistry().getAllTools().

Causes and solutions:

  • Plugin not discovered - Check plugin discovery first
  • Tools not extracted - Ensure SKILL.md files exist in skills directory
  • Namespace conflict - Two plugins with same namespace
  • Tool registration failed - Check registry events for errors

Debug:

import { getRegistry, discoverPlugins } from './compatibility';

const plugins = discoverPlugins();
for (const plugin of plugins) {
  console.log(`${plugin.name}: ${plugin.tools.length} tools`);
  for (const tool of plugin.tools) {
    console.log(`  - ${tool.name}`);
  }
}

// Check what's actually registered
const registry = getRegistry();
const registered = registry.getAllTools();
console.log(`Registry has ${registered.length} tools`);

// Listen for registration events
registry.addEventListener(event => {
  if (event.type === 'tool-registered') {
    console.log('Registered:', event.data.tool);
  } else if (event.type === 'tool-conflict') {
    console.log('Conflict:', event.data.name, '→', event.data.winner);
  }
});

Permission Always Denied

Problem: Tools requiring permission always get denied even after user approval.

Solutions:

  • Clear permission cache: clearPermissionCache()
  • Ensure you're using same tool name/input for cached decision
  • Check if tool matches a dangerous pattern that overrides caching

Debug:

import {
  checkPermission,
  grantPermission,
  getSafePatterns
} from './compatibility';

// Check if tool is in dangerous patterns
const patterns = getSafePatterns();
console.log('Safe patterns:', patterns.length);

// Manually grant
grantPermission('my-tool');

// Verify it's cached
const result = checkPermission('my-tool');
console.log('Allowed:', result.allowed);
console.log('Reason:', result.reason);

Manifest Parse Errors

Problem: Plugin loads but manifest parsing fails.

Checklist:

  • plugin.json is valid JSON (use npm install -g jsonlint to validate)
  • Required fields present: name, version
  • No syntax errors in paths or configs
  • File encoding is UTF-8

Debug:

import { getPluginInfo } from './compatibility';

const plugin = getPluginInfo('my-plugin');
if (plugin && !plugin.loaded) {
  console.error('Failed to load:', plugin.error);
  console.log('Manifest:', plugin.manifest);
}

MCP Tool Invocation Fails

Problem: Tool invocation returns error.

Debug:

import { getMcpBridge } from './compatibility';

const bridge = getMcpBridge();

// Check connection
console.log('Connected:', bridge.isConnected('myserver'));

// Get available tools
const tools = bridge.getServerTools('myserver');
console.log('Available tools:', tools.map(t => t.name));

// Try invocation with error details
const result = await bridge.invokeTool('myserver', 'tool-name', {});
if (!result.success) {
  console.error('Error:', result.error);
  console.error('Time:', result.executionTime, 'ms');
}

Best Practices

  1. Initialize early - Call initializeCompatibility() on startup
  2. Cache registry - Reuse getRegistry() instance, don't repeatedly initialize
  3. Handle permissions gracefully - Always check checkPermission() before invoking dangerous tools
  4. Monitor events - Use event listeners to track plugin/server status changes
  5. Version check - Include version constraints in plugin manifests for compatibility
  6. Test plugins locally - Before publishing, test with local discovery paths
  7. Use namespaces - Set namespace in manifest to avoid conflicts
  8. Document permissions - Clearly explain why plugins need specific scopes
  9. Handle errors - MCP connections can fail; implement retry logic
  10. Clean up - Call disconnectAll() and resetMcpBridge() on shutdown