-
Notifications
You must be signed in to change notification settings - Fork 2.7k
feat: add Model Context Protocol (MCP) server #6066
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
feat: add Model Context Protocol (MCP) server #6066
Conversation
Add MCP server support to PM2 for process management through MCP-compatible clients.
Features:
- New pm2-mcp binary that exposes PM2 process management via MCP
- 12 MCP tools for process lifecycle, logging, and monitoring:
- pm2_list_processes, pm2_describe_process
- pm2_start_process, pm2_restart_process, pm2_reload_process
- pm2_stop_process, pm2_delete_process
- pm2_flush_logs, pm2_reload_logs, pm2_tail_logs
- pm2_dump, pm2_kill_daemon
- 2 MCP resources for real-time process information:
- pm2://processes (list)
- pm2://process/{id} (detail)
- Automatic sandbox environment detection and adaptation
- Support for stdio and HTTP (Streamable) transports
- Client notifications for sandbox status and recommendations
- Compatible with Claude Code, Codex, and other MCP clients
Implementation:
- New lib/mcp/server.js with full MCP server implementation
- Uses @modelcontextprotocol/sdk for MCP protocol
- Sandbox detection checks home directory writability and environment
- Auto-selects writable PM2_HOME in sandboxed environments
- No-daemon mode by default for MCP client compatibility
- Comprehensive environment variable configuration
Documentation:
- README with MCP server quickstart and setup commands
- Environment variables table (PM2_MCP_*, PM2_HOME, etc.)
- Sandbox detection explanation
- Tool and resource documentation
- Justfile recipes for easy registration with MCP clients
Related:
- Enables pkgx packaging: pkgxdev/pantry#11219
- Development fork: https://github.com/PromptExecution/pm2-mcp
- MCP Specification: https://modelcontextprotocol.io/
Co-authored-by: Claude <[email protected]>
📦 pkgx Package IntegrationA pkgx package has been prepared to make PM2 with MCP server support easily installable via pkgx: pkgx Pantry PR: pkgxdev/pantry#11219 Installation (after this merges)# Install with pkgx
pkgx install pm2
# Use pm2-mcp
pkgx pm2-mcp --help
# Register with Claude Code
claude mcp add pm2-mcp -- pkgx pm2-mcp
# Register with Codex
codex mcp add pm2-mcp -- pkgx pm2-mcpCurrent TestingThe pkgx package currently points to the development fork ( pkgx install github.com/PromptExecution/pm2-mcpOnce this PR is merged, the pkgx package will be updated to point to the official Cross-References
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds Model Context Protocol (MCP) server support to PM2, enabling AI assistants like Claude Code and Codex to manage PM2 processes through a standardized protocol. The implementation introduces a new pm2-mcp binary, 12 MCP tools for process management operations, 2 MCP resources for process state access, and comprehensive sandbox detection for restricted environments.
Key Changes:
- New MCP server implementation with stdio and HTTP transport support
- 12 process management tools (list, start, stop, restart, reload, delete, logs, etc.)
- Automatic sandbox environment detection and adaptation
- Dual transport modes: stdio (for MCP clients) and HTTP (for long-lived usage)
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| package.json | Added MCP SDK dependency, updated Node.js requirement to 22.0.0, added pm2-mcp binary, reorganized dependencies alphabetically |
| lib/mcp/server.js | Complete MCP server implementation with tools, resources, sandbox detection, and dual transport support (891 lines) |
| bin/pm2-mcp | CLI entry point with argument parsing for transport configuration and PM2 self-management |
| README.md | Comprehensive MCP documentation including setup, environment variables, sandbox detection, and available tools/resources |
| Justfile | Development recipes for registering with MCP clients and testing sandbox detection |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| setTimeout(() => { | ||
| server.sendLoggingMessage({ | ||
| level: 'warning', | ||
| logger: 'pm2-mcp', | ||
| data: { | ||
| message: `PM2 MCP server running in sandboxed environment`, | ||
| reasons: sandboxInfo.reasons, | ||
| pm2_home: process.env.PM2_HOME, | ||
| recommendations: [ | ||
| 'Process management features are available but may have limited access', | ||
| 'PM2 daemon is running in no-daemon mode by default', | ||
| 'Set PM2_MCP_NO_DAEMON=false to connect to an existing daemon', | ||
| 'Set PM2_HOME or PM2_MCP_HOME to specify a writable location' | ||
| ] | ||
| } | ||
| }).catch(err => { | ||
| if (process.env.PM2_MCP_DEBUG === 'true') { | ||
| console.error('[pm2-mcp][debug] failed to send sandbox notification', err); | ||
| } | ||
| }); | ||
| }, 100); |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The magic number 100 (milliseconds) for the setTimeout delay should be extracted to a named constant for better maintainability. For example: const SANDBOX_NOTIFICATION_DELAY_MS = 100;. This makes the code more self-documenting and easier to adjust if the timing needs to change.
| server.registerTool( | ||
| 'pm2_kill_daemon', | ||
| { | ||
| title: 'Kill PM2 daemon', | ||
| description: 'Stops the PM2 daemon and all managed processes.' | ||
| }, | ||
| wrapTool('pm2_kill_daemon', async () => { | ||
| try { | ||
| await ensureConnected(); | ||
| await pm2KillDaemon(); | ||
| isConnected = false; | ||
| return { | ||
| content: textContent({ action: 'killDaemon' }), | ||
| structuredContent: { action: 'killDaemon' } | ||
| }; | ||
| } catch (err) { | ||
| return errorResult(err); | ||
| } | ||
| }) | ||
| ); |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pm2_kill_daemon tool allows external MCP clients to kill the PM2 daemon and all managed processes without any authentication or authorization checks. This is a critical operation that should require explicit permission or at least a confirmation mechanism. Consider adding:
- An environment variable flag to enable/disable this dangerous operation
- Additional confirmation requirements
- Warning documentation about the security implications
This tool gives any MCP client complete control to shut down all PM2-managed services.
| const httpServer = http.createServer((req, res) => { | ||
| logRequests('http %s %s', req.method, req.url); | ||
| try { | ||
| const base = `http://${req.headers.host || `${host}:${port}`}`; | ||
| const url = new URL(req.url || '', base); | ||
| if (url.pathname !== pathPart) { | ||
| res.writeHead(404).end('Not Found'); | ||
| return; | ||
| } | ||
| transport.handleRequest(req, res).catch(err => { | ||
| console.error('[pm2-mcp] transport request failed', err); | ||
| if (!res.headersSent) res.writeHead(500); | ||
| res.end('Internal Server Error'); | ||
| }); | ||
| } catch (err) { | ||
| console.error('[pm2-mcp] transport request failed', err); | ||
| if (!res.headersSent) res.writeHead(500); | ||
| res.end('Internal Server Error'); | ||
| } | ||
| }); | ||
|
|
||
| httpServer.listen(port, host, () => { | ||
| if (process.env.PM2_MCP_DEBUG === 'true') { | ||
| console.error('[pm2-mcp][debug] HTTP transport listening', `${host}:${port}${pathPart}`); | ||
| } | ||
| }); | ||
|
|
||
| return { transport, httpServer, address: `http://${host}:${port}${pathPart}` }; |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The HTTP server doesn't implement authentication or authorization. When running in HTTP mode, anyone who can reach the host:port can execute PM2 operations including starting arbitrary processes, stopping processes, and killing the daemon. The documentation mentions PM2_MCP_ALLOWED_HOSTS and PM2_MCP_ALLOWED_ORIGINS for DNS rebinding protection, but these don't provide actual authentication. Consider:
- Adding authentication mechanisms (API keys, tokens, etc.)
- Documenting that HTTP mode should only be used on localhost or within trusted networks
- Making authentication mandatory or at least strongly recommended for non-localhost bindings
| server.registerTool( | ||
| 'pm2_start_process', | ||
| { | ||
| title: 'Start a process with PM2', | ||
| description: 'Start a script or JSON ecosystem file.', | ||
| inputSchema: startSchema | ||
| }, | ||
| wrapTool('pm2_start_process', async args => { | ||
| try { | ||
| await ensureConnected(); | ||
| const target = args.jsonConfigFile || args.script; | ||
| const options = cleanOptions({ | ||
| name: args.name, | ||
| args: args.args, | ||
| cwd: args.cwd, | ||
| watch: args.watch, | ||
| instances: args.instances, | ||
| env: args.env, | ||
| interpreter: args.interpreter | ||
| }); | ||
|
|
||
| if (process.env.PM2_MCP_DEBUG === 'true') { | ||
| console.error('[pm2-mcp][debug] starting process', target, options); | ||
| } | ||
|
|
||
| await pm2Start(target, options); |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pm2_start_process tool accepts file paths (script and jsonConfigFile) and other parameters like cwd, interpreter, and env from external MCP clients without validation. This could allow an attacker to:
- Start arbitrary executables on the system
- Set malicious environment variables
- Specify arbitrary working directories
While PM2 itself should have some safeguards, the MCP server should implement input validation to ensure that:
- File paths are validated and/or within allowed directories
- Interpreter paths are validated
- Environment variables don't contain sensitive overrides
- Working directory is within expected bounds
Consider adding a whitelist/configuration for allowed directories or requiring explicit authorization for process starts.
| async function ensureConnected() { | ||
| if (isConnected) return; | ||
| log('connecting to PM2 (noDaemon default true, override with PM2_MCP_NO_DAEMON)'); | ||
| await new Promise((resolve, reject) => { | ||
| // Default to no-daemon mode so the MCP server can start without needing an existing PM2 daemon. | ||
| const noDaemon = | ||
| process.env.PM2_MCP_NO_DAEMON === undefined | ||
| ? true | ||
| : process.env.PM2_MCP_NO_DAEMON === 'true'; | ||
| log('pm2.connect noDaemon=%s', noDaemon); | ||
| pm2.connect(noDaemon, err => { | ||
| if (err) return reject(err); | ||
| isConnected = true; | ||
| log('connected to PM2'); | ||
| return resolve(); | ||
| }); | ||
| }); | ||
| } |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The isConnected flag is a simple boolean without any locking mechanism. If multiple requests call ensureConnected() concurrently before PM2 connects, they will all attempt to call pm2.connect() simultaneously. This could lead to race conditions or multiple connection attempts. Consider implementing a proper connection state management with a promise that's reused across concurrent calls, or using a mutex/lock pattern to ensure only one connection attempt occurs at a time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot open a new pull request to apply changes based on this feedback
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
Add MCP Server Support to PM2
This PR adds Model Context Protocol (MCP) server support to PM2, enabling process management through MCP-compatible clients like Claude Code and Codex.
🎯 Overview
The MCP server exposes PM2's core process management capabilities through a standardized protocol, making it easy to integrate PM2 with AI assistants and other MCP clients.
✨ Features
New Binary
pm2-mcp- Standalone MCP server binary12 MCP Tools
pm2_list_processes- List all PM2 processes with metricspm2_describe_process- Get detailed process informationpm2_start_process- Start a new process or ecosystem filepm2_restart_process- Restart a process by id/namepm2_reload_process- Zero-downtime reload (cluster mode)pm2_stop_process- Stop a processpm2_delete_process- Delete a process from PM2pm2_flush_logs- Flush log filespm2_reload_logs- Rotate and reopen logspm2_dump- Save process list to diskpm2_tail_logs- Read last N lines from logspm2_kill_daemon- Stop PM2 daemon2 MCP Resources
pm2://processes- Current PM2 process list as JSONpm2://process/{id}- Detailed process information as JSONSandbox Detection
/tmp/pm2-mcp,./.pm2-mcpMultiple Transports
📦 Implementation
New Files
lib/mcp/server.js(891 lines) - Full MCP server implementationbin/pm2-mcp- MCP server entry point with argument parsingJustfile- Setup recipes for Claude Code and CodexREADME.md- Comprehensive MCP documentationDependencies
@modelcontextprotocol/sdk@^1.23.0- MCP protocol implementationConfiguration
Extensive environment variable support:
PM2_MCP_TRANSPORT- Transport type (stdio/http)PM2_MCP_PORT,PM2_MCP_HOST,PM2_MCP_PATH- HTTP configPM2_MCP_NO_DAEMON- No-daemon mode (default: true)PM2_MCP_DEBUG- Debug loggingPM2_HOME,PM2_MCP_HOME- PM2 home directory🚀 Usage
Quick Start with Claude Code
Quick Start with Codex
HTTP Transport
Run under PM2
🔗 Related
🧪 Testing
The MCP server has been tested with:
📝 Documentation
Complete documentation added to README.md including:
💡 Design Decisions
No-daemon mode by default - Ensures compatibility with sandboxed MCP clients that may not have persistent daemon access
Sandbox detection - Automatically adapts to restricted environments by selecting writable locations for PM2_HOME
Client notifications - Uses MCP logging protocol to inform clients about sandbox status and provide helpful recommendations
Dual transport support - stdio for MCP clients, HTTP for long-lived/remote access
Minimal API surface - Focuses on core PM2 operations most useful for programmatic access
✅ Checklist
lib/mcp/server.jspm2-mcpbinary with CLI argument parsing@modelcontextprotocol/sdkdependencypackage.jsonwith new binary and dependency🙏 Acknowledgments
This implementation enables PM2 to be easily integrated into the growing MCP ecosystem, making process management accessible to AI assistants and other MCP-compatible tools.
Co-authored-by: Claude [email protected]