feat: new MCP tools — run_command, LogTools, CacheTools, ModelTools - #7
Merged
Merged
Conversation
…nkerTools - Extract SubprocessRunner from TinkerTools for shared subprocess execution - Add run_command MCP tool to CommandTools with collection-based allowlist - Refactor TinkerTools to delegate proc_open logic to SubprocessRunner - Add tests for SubprocessRunner (13 tests) and run_command (6 tests)
- log_list: lists .log files in LOGS dir with name/size/modified/path - log_read: tails N lines from a log file with optional level filter - Path traversal prevention via realpath validation inside LOGS - 13 tests covering listing, reading, filtering, and security boundaries
On PHP 8.2 (Linux), proc_get_status() internally reaps the child process zombie via waitpid() on the first call where running=false. A subsequent proc_close() then returns -1 because the process has already been reaped. Fix: capture $status['exitcode'] immediately when running=false, and use proc_close() in the finally block only for resource cleanup. Also: - Replace glob() with Symfony Finder in LogTools (cleaner, handles edge cases) - Extend log_list description to note non-filesystem logging returns [] - Fix PHPCS: change inline doc comment to multi-line on LogToolsTest property
CakePHP 5.2.0 (prefer-lowest) exits with code 1 for --help, while newer versions exit 0. The integration test's goal is to verify the subprocess mechanism works end-to-end, not to audit CakePHP's --help behaviour, so assert output is non-empty and exit_code != -1 instead.
CakePHP 5.2.0 writes --help output to stderr; newer versions write to stdout. Check both to avoid version-specific failures.
…ompat stream_set_blocking() is unreliable for proc_open pipes on Windows — fread() on a 'non-blocking' pipe still blocks there, causing the timeout check to never fire (the process exits naturally after its full duration and success=true is returned instead of timed_out=true/success=false). Fix: poll only via proc_get_status() in a usleep(10ms) loop and do a single stream_get_contents() read once the process has exited (or been terminated). Output is intentionally limited to the OS pipe buffer (~64 KB on Linux); this is acceptable for the commands this plugin runs.
…e, cache_delete, cache_clear)
There was a problem hiding this comment.
Pull request overview
This PR expands the Synapse CakePHP MCP server plugin with new tool groups for subprocess-driven command execution, log inspection, cache inspection/management, and ORM introspection, while extracting shared subprocess logic into a reusable utility.
Changes:
- Add
Synapse\Utility\SubprocessRunnerand refactorTinkerToolsto delegate subprocess execution to it. - Add new MCP tool groups:
run_command(CommandTools),LogTools(log_list,log_read),CacheTools(cache_*), andModelTools(orm_describe,orm_find). - Add/extend PHPUnit coverage for the new utilities and tools.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
src/Utility/SubprocessRunner.php |
New proc_open wrapper for stdin piping, timeout handling, and output capture. |
src/Tools/TinkerTools.php |
Refactors execution to use SubprocessRunner. |
src/Tools/CommandTools.php |
Adds run_command MCP tool that shells out to bin/cake.php for allowlisted commands. |
src/Tools/LogTools.php |
Adds log listing and tail-like reading with LOGS path validation. |
src/Tools/CacheTools.php |
Adds cache configuration listing + CRUD-like cache entry tools. |
src/Tools/ModelTools.php |
Adds ORM describe + basic find/query tooling. |
tests/TestCase/Utility/SubprocessRunnerTest.php |
Unit tests for SubprocessRunner. |
tests/TestCase/Tools/CommandToolsTest.php |
Adds unit + integration tests for run_command. |
tests/TestCase/Tools/LogToolsTest.php |
Tests for log listing/reading + traversal protections. |
tests/TestCase/Tools/CacheToolsTest.php |
Tests for cache config listing and cache read/write/delete/clear. |
tests/TestCase/Tools/ModelToolsTest.php |
Tests for ORM describe/find behavior using fixtures. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…cessRunner caching, LogTools memory efficiency
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
Adds four new MCP tool groups developed in TDD red/green cycles, one feature at a time.
Completed
Feature 1 —
SubprocessRunner+run_commandsrc/Utility/SubprocessRunner.php(new)Shared subprocess execution utility extracted from
TinkerTools. Wrapsproc_openwith stdin piping, timeout + SIGKILL, and a consistent return shape{success, output, stderr, exit_code, [timed_out]}.src/Tools/CommandTools—run_commandMCP toolRuns any registered CakePHP console command in a subprocess. Command names are validated against the application's
CommandCollectionbefore execution (allowlist), preventing arbitrary shell invocation. Timeout clamped to [1, 300] seconds. Each argument token in$argsis individually escaped withescapeshellargto prevent shell injection.src/Tools/TinkerTools— refactoredDelegates all
proc_openlogic toSubprocessRunner; public API unchanged.Tests: 13 for
SubprocessRunner, 6 forrun_command.Feature 2 —
LogToolsMCP tools for reading application logs:
log_list— list available log files with size/mtime metadatalog_read(file, lines, level)— tail lines from a log file, optionally filtered by level; path validated insideLOGSdirectory. Uses reverse-seek for plain reads (bounded memory) and a rolling-buffer for level-filtered reads. Thelinesparameter is clamped to [1, 5000].Feature 3 —
CacheToolsMCP tools for cache inspection and management:
cache_configs— list configured cache engines and their settingscache_read(config, key)— read a cache entrycache_write(config, key, value)— write a cache entrycache_delete(config, key)— delete a cache entrycache_clear(config)— clear a cache configAll write/read/delete/clear tools validate the config name against
Cache::configured()and throw aToolCallExceptionfor unknown configs.Feature 4 —
ModelToolsMCP tools for ORM introspection and querying:
orm_describe(alias, connection)— describe a Table's alias, table name, connection, primary key, display field, entity class, associations, and behaviorsorm_find(alias, connection, type, limit)— run aall/first/countfind on a Table and return plain arrays; limit clamped to [1, 100]