-
Notifications
You must be signed in to change notification settings - Fork 0
[WIP] Update log analysis tools for MCP server based on feedback #10
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
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 <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Brian Horakh <35611074+elasticdotventures@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Brian Horakh <35611074+elasticdotventures@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Brian Horakh <35611074+elasticdotventures@users.noreply.github.com>
Signed-off-by: Brian Horakh <35611074+elasticdotventures@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
* Initial plan * Add explanation of Codex CLI tool in README Co-authored-by: elasticdotventures <35611074+elasticdotventures@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: elasticdotventures <35611074+elasticdotventures@users.noreply.github.com>
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 extends the MCP server with semantic process state and log-analysis capabilities, adds HTTP hardening timeouts, and updates documentation for platform support and Codex usage. It builds on PR #6 by enriching process descriptions, adding new tools/resources for log inspection and safe descriptions, and tightening server behavior.
Changes:
- Add connection coalescing for
ensureConnected, HTTP server timeouts, and a suite of helpers for log-tail reading, log pattern analysis, and semantic state inference. - Enrich existing tools/resources (
pm2_list_processes,pm2_describe_process, and process resources) with asemantic_statefield and introduce new toolspm2_analyze_logsandpm2_describe_process_safefor structured log analysis and privacy-safe process descriptions. - Update README platform support wording and add a brief section describing how to use the MCP server with OpenAI Codex.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
lib/mcp/server.js |
Adds connectionPromise-based connection handling, HTTP timeouts (with currently undefined timeout constants), environment redaction helpers, log pattern/progress analysis, semantic-state heuristics, new MCP tools for log analysis and safe describe, and semantic enrichment of process tools/resources. |
README.md |
Clarifies supported Node.js/Bun versions and briefly documents using the MCP server with OpenAI’s Codex CLI. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| wrapTool('pm2_analyze_logs', async ({ process, timeframe_minutes, lines }) => { | ||
| try { | ||
| await ensureConnected(); | ||
| const description = await pm2Describe(process); | ||
| if (!description || description.length === 0) { | ||
| throw new Error(`No process found for "${process}"`); | ||
| } | ||
| const env = description[0].pm2_env || {}; | ||
| const { lines: logLines, logPath } = await readRecentLogLines(env, lines); | ||
| const logAnalysis = analyzeLogPatterns(logLines); | ||
| const semantic = buildSemanticStateFromHeuristics({ | ||
| env, | ||
| monit: description[0].monit, | ||
| logAnalysis, | ||
| logInfo: { lastModified: null } | ||
| }); | ||
|
|
||
| let suggested_action = 'none'; | ||
| if (logAnalysis.errorsFound.length > 0) suggested_action = 'investigate'; | ||
| else if (logAnalysis.topPattern?.semanticStatus === 'downloading') suggested_action = 'wait_for_completion'; | ||
| else if (logAnalysis.topPattern?.semanticStatus === 'degraded') suggested_action = 'investigate'; | ||
|
|
||
| const payload = { | ||
| process, | ||
| timeframe_minutes, | ||
| analysis: { |
Copilot
AI
Jan 25, 2026
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 timeframe_minutes input parameter is accepted and returned in the pm2_analyze_logs tool, but it is not actually used to limit or filter the logs read from disk, which may confuse clients expecting time-bounded analysis. Either apply this parameter when selecting log lines (for example, by comparing mtime or parsing timestamps) or clarify/remove it from the schema and response.
| function sanitizeDescriptionEntry(entry, options = {}) { | ||
| const cloned = JSON.parse(JSON.stringify(entry || {})); | ||
| const env = cloned.pm2_env || {}; | ||
|
|
||
| if (!options.includeEnvironment) { | ||
| delete env.env; | ||
| delete env.env_pm2; | ||
| } else { | ||
| env.env = filterEnvironment(env.env, options.environmentFilter, options.redactSecrets); | ||
| env.env_pm2 = filterEnvironment(env.env_pm2, options.environmentFilter, options.redactSecrets); | ||
| } | ||
|
|
||
| cloned.pm2_env = env; | ||
| return cloned; |
Copilot
AI
Jan 25, 2026
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.
sanitizeDescriptionEntry clones the entire PM2 description via JSON.parse(JSON.stringify(entry || {})), which will drop non-JSON-safe data such as Dates, Buffers, or functions that might exist in pm2_env and could be useful for debugging. If you need a defensive copy, consider a more targeted shallow/deep clone of the properties you care about so structure is preserved without unintentionally altering types.
| server.registerTool( | ||
| 'pm2_describe_process_safe', | ||
| { | ||
| title: 'Describe a PM2 process (privacy-safe)', | ||
| description: 'Describe a process with optional environment filtering and secret redaction.', | ||
| inputSchema: describeSafeSchema | ||
| }, | ||
| wrapTool('pm2_describe_process_safe', async ({ process, include_environment, environment_filter, redact_secrets }) => { | ||
| try { | ||
| await ensureConnected(); | ||
| const description = await pm2Describe(process); | ||
| if (!description || description.length === 0) { | ||
| throw new Error(`No process found for "${process}"`); | ||
| } | ||
|
|
||
| const sanitized = await Promise.all( | ||
| description.map(async item => ({ | ||
| ...sanitizeDescriptionEntry(item, { | ||
| includeEnvironment: include_environment, | ||
| environmentFilter: environment_filter, | ||
| redactSecrets: redact_secrets | ||
| }), | ||
| semantic_state: await buildSemanticState(item) | ||
| })) | ||
| ); | ||
|
|
||
| return { | ||
| content: textContent(sanitized), | ||
| structuredContent: { description: sanitized } | ||
| }; |
Copilot
AI
Jan 25, 2026
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.
pm2_describe_process_safe introduces new behavior around environment filtering, secret redaction, and inclusion of semantic_state, but there are no tests covering this tool in the existing MCP server test suite. To avoid regressions and to validate that redaction and filtering work as intended (including default vs. overridden options), consider adding targeted tests that exercise this tool and inspect the returned description objects.
| async function buildSemanticState(procLike) { | ||
| const env = procLike.pm2_env || {}; | ||
| const monit = procLike.monit || procLike.pm2_env?.monit || {}; | ||
| const logInfo = await readRecentLogLines(env, 200); | ||
| const logAnalysis = analyzeLogPatterns(logInfo.lines); | ||
| return buildSemanticStateFromHeuristics({ env, monit, logAnalysis, logInfo }); | ||
| } | ||
|
|
||
| async function enrichProcess(proc) { | ||
| const base = formatProcess(proc); | ||
| base.semantic_state = await buildSemanticState(proc); | ||
| return base; | ||
| } |
Copilot
AI
Jan 25, 2026
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 new semantic state helpers (buildSemanticStateFromHeuristics / buildSemanticState / enrichProcess) add non-trivial heuristics for inferring status and progress from logs and monit data, but there are currently no tests exercising this behavior or asserting the semantic_state shape on tools/resources. Given that this file already has MCP integration tests, it would be valuable to add tests that cover at least a few representative scenarios (e.g., online vs degraded vs stuck, and presence of progress indicators) to prevent regressions.
| // Set timeouts to protect against slow HTTP attacks | ||
| httpServer.timeout = HTTP_SERVER_TIMEOUT_MS; | ||
| httpServer.headersTimeout = HTTP_HEADERS_TIMEOUT_MS; | ||
| httpServer.requestTimeout = HTTP_REQUEST_TIMEOUT_MS; |
Copilot
AI
Jan 25, 2026
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 new HTTP timeout properties reference HTTP_SERVER_TIMEOUT_MS, HTTP_HEADERS_TIMEOUT_MS, and HTTP_REQUEST_TIMEOUT_MS, but these constants are not defined in this file or elsewhere in the repo, which will throw a ReferenceError when createTransport runs for HTTP. These values should either be defined (e.g., with sensible defaults or environment overrides) or the assignments guarded/removed.
| server.registerTool( | ||
| 'pm2_analyze_logs', | ||
| { | ||
| title: 'Analyze PM2 logs', | ||
| description: 'Parse recent logs for activity, patterns, and errors.', | ||
| inputSchema: analyzeLogsSchema | ||
| }, | ||
| wrapTool('pm2_analyze_logs', async ({ process, timeframe_minutes, lines }) => { | ||
| try { | ||
| await ensureConnected(); | ||
| const description = await pm2Describe(process); | ||
| if (!description || description.length === 0) { | ||
| throw new Error(`No process found for "${process}"`); | ||
| } | ||
| const env = description[0].pm2_env || {}; | ||
| const { lines: logLines, logPath } = await readRecentLogLines(env, lines); | ||
| const logAnalysis = analyzeLogPatterns(logLines); | ||
| const semantic = buildSemanticStateFromHeuristics({ | ||
| env, | ||
| monit: description[0].monit, | ||
| logAnalysis, | ||
| logInfo: { lastModified: null } | ||
| }); | ||
|
|
||
| let suggested_action = 'none'; | ||
| if (logAnalysis.errorsFound.length > 0) suggested_action = 'investigate'; | ||
| else if (logAnalysis.topPattern?.semanticStatus === 'downloading') suggested_action = 'wait_for_completion'; | ||
| else if (logAnalysis.topPattern?.semanticStatus === 'degraded') suggested_action = 'investigate'; | ||
|
|
||
| const payload = { | ||
| process, | ||
| timeframe_minutes, | ||
| analysis: { | ||
| current_activity: logAnalysis.topPattern?.pattern || semantic.status, | ||
| detected_patterns: logAnalysis.detectedPatterns, | ||
| errors_found: logAnalysis.errorsFound, | ||
| warnings_found: logAnalysis.warningsFound, | ||
| progress_indicators: logAnalysis.progressIndicators, | ||
| anomalies: [], | ||
| suggested_action | ||
| }, | ||
| meta: { | ||
| log_path: logPath, | ||
| semantic_state: semantic | ||
| } | ||
| }; | ||
|
|
||
| return { | ||
| content: textContent(payload), | ||
| structuredContent: payload | ||
| }; |
Copilot
AI
Jan 25, 2026
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_analyze_logs tool is newly introduced with custom log pattern and progress analysis, but there are no tests in test/mcp/server.mocha.js (or elsewhere under test/) that call this tool or validate its structured response shape. Adding at least a basic happy-path test that starts a process, invokes pm2_analyze_logs, and asserts key fields (patterns, errors/warnings arrays, semantic_state, etc.) would improve confidence and catch future changes to the analysis heuristics.
Thanks for the feedback on #6. I've created this new PR, which merges into #6, to address your comment. I will work on the changes and keep this PR's description up to date as I make progress.
Original PR: #6
Triggering review: #6 (comment)
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.