Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Always-on process isolation: eliminate conditional complexity #184

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

Merged
ashleyhindle merged 4 commits into laravel:main from andreilungeanu:main
Sep 4, 2025

Conversation

Copy link
Contributor

@andreilungeanu andreilungeanu commented Aug 21, 2025
edited
Loading

This PR simplifies the MCP tool execution architecture by removing all conditional logic around process isolation. Instead of having dual execution paths (executeInline() vs executeInProcess()), we now always use subprocess execution with unified timeout handling. This eliminates the dangerous Windows crash scenario where set_time_limit() combined with infinite loops would cause uncatchable fatal errors that kill the entire MCP server process.

All timeout management is now centralized in the ToolExecutor with per-call timeout arguments (clamped between 1-600 seconds) instead of global configuration. This provides consistent, safe behavior across all platforms while making the codebase significantly more maintainable and testable. Users get the same reliable subprocess isolation that was already working well, but now it's the only execution path with no configuration complexity.

(削除) A new PR on the same subject after further brainstorming. The issue is not related to the timeout value.
Note: in some cases 30s might still be short, but that’s a separate concern. (削除ここまで)

(削除) ## Problem
The Tinker tool was crashing the entire MCP server when user code exceeded set_time_limit() timeouts, causing uncatchable fatal errors. Windows systems were particularly vulnerable due to lack of PCNTL signal alternatives for timeout handling. (削除ここまで)

(削除) ## Root Cause
- Windows limitation: set_time_limit() + infinite loops = fatal error crash (cannot be caught)
- PCNTL unavailable: Windows doesn't support PCNTL signals for timeout handling
- No graceful recovery: Server process dies completely, affecting all connected clients (削除ここまで)

(削除) ## Solution
Implemented process isolation by default with platform-aware fallbacks:
Fix for #129 // also, isolation will Fix #194 (削除ここまで)

(削除) ### Changes (削除ここまで)

(削除) 1. Process Isolation Config (config/boost.php) (削除ここまで)

 'process_isolation' => [
 'enabled' => env('BOOST_PROCESS_ISOLATION', true), // ON by default
 'timeout' => env('BOOST_PROCESS_TIMEOUT', 180),
 ]
(削除) 2. **Enhanced Tinker Tool** (`src/Mcp/Tools/Tinker.php`) - **Primary**: Relies on ToolExecutor process isolation (Windows-safe) - **Fallback**: Uses `set_time_limit()` only when isolation disabled (Unix systems) - **PCNTL support**: Additional protection on Unix systems - **Proper cleanup**: Finally blocks ensure resource cleanup 3. **Platform-Aware Tests** (`tests/Feature/Mcp/Tools/TinkerTest.php`) - Automatically skips timeout tests on Windows (PCNTL unavailable) - Tests both isolation enabled/disabled scenarios

Configuration

# Default (recommended for Windows)
BOOST_PROCESS_ISOLATION=true
# Performance mode (Unix only - risky on Windows)
BOOST_PROCESS_ISOLATION=false

## 📊 Platform Behavior (削除ここまで)

(削除) | Platform | Process Isolation | Timeout Method | Risk Level |
|----------|------------------|----------------|------------|
| Windows | ✅ Enabled (required) | ToolExecutor isolation | ✅ Safe |
| Windows | ❌ Disabled | set_time_limit() | ⚠️ CRASHES |
| Unix/Linux | ✅ Enabled | ToolExecutor isolation | ✅ Safe |
| Unix/Linux | ❌ Disabled | set_time_limit() + PCNTL | ✅ Safe | (削除ここまで)

Bottom line: Windows users can now use Tinker without fear of crashing the MCP server.

ahosker reacted with thumbs up emoji
Copy link

Thanks for submitting a PR!

Note that draft PR's are not reviewed. If you would like a review, please mark your pull request as ready for review in the GitHub user interface.

Pull requests that are abandoned in draft may be closed due to inactivity.

Copy link

ahosker commented Aug 25, 2025

Thanks for this, it realy does keep crashing for me on windows with timeout messages.

Copy link
Contributor Author

andreilungeanu commented Aug 25, 2025
edited
Loading

Thanks for this, it realy does keep crashing for me on windows with timeout messages.

After applying this PR, turn on process isolation in boost.php, ever since I did that, no more crashes from the timeout issue on Windows in VSCode. (if you had the boost.php config published before this change, remove it or add manually new options)

More recently, I also set isolation to be enabled by default, even if your old config didn’t include that option. 75d215f

Copy link
Collaborator

@ashleyhindle ashleyhindle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems sensible to me 👌

If you could remove the config changes, and update the branch, we're good to go 👍

andreilungeanu reacted with hooray emoji
The MCP server was crashing when Tinker code ran too long or got stuck in infinite loops. This adds process isolation by default to prevent crashes, but also makes it work differently on Windows vs Linux/Mac since they handle timeouts differently.
What changed:
- Added process isolation config (on by default for safety)
- Windows: Uses process isolation to avoid fatal crashes
- Linux/Mac: Can disable isolation for speed since PCNTL handles timeouts gracefully
- Added tests that skip appropriately on each platform
Copy link
Contributor Author

Since parts of this PR were already implemented in feat: default process isolation to true, things are much simpler now. With the config section removed as requested and the fallback changed from false to true, the code relies on config('boost.process_isolation.enabled', true) which always returns true.

Windows users get safe process isolation by default, and this PR now simply focuses on platform-aware timeout handling and test coverage.

Copy link
Collaborator

Since parts of this PR were already implemented in feat: default process isolation to true, things are much simpler now. With the config section removed as requested and the fallback changed from false to true, the code relies on config('boost.process_isolation.enabled', true) which always returns true.

Windows users get safe process isolation by default, and this PR now simply focuses on platform-aware timeout handling and test coverage.

🤔 What do you think about us removing the process isolation configuration entirely, so we can always rely on the subprocess timeout management?

Then in ToolExecutor use $arguments['timeout'] ?? 180 as the timeout?

Thinking
Originally the config was there in case the process isolation had issues, but it's been working really well for me and I don't see why it should ever be disabled.

Right now, as you documented, if somebody disables process isolation, then we use set_time_limit in Tinker and their code takes too long, the entire MCP process dies.

Two options
Fundamentally the Tinker tool should always run the eval'ed code in a fork or subprocess to protected against that, so we could add that into the Tinker tool additionally, or just only work with process isolation because it's great and works?

I recognise this is a departure from your original plan, but it solves the same issue and simplifies the Tinker tool a lot.

... handling; update Tinker tool to reflect new timeout defaults and remove inline execution tests.
@andreilungeanu andreilungeanu changed the title (削除) Fix MCP crashes on Windows with process isolation (削除ここまで) (追記) Always-on process isolation: eliminate conditional complexity (追記ここまで) Sep 3, 2025
Copy link
Contributor Author

andreilungeanu commented Sep 3, 2025
edited
Loading

@ashleyhindle you're right - that was actually my first instinct too, but I second-guessed myself thinking the changes might be too brutal for the initial PR. The current approach of always using subprocess with getTimeout() method that extracts $arguments['timeout'] ?? 180 and clamps it between 1-600 seconds eliminates the crash risk completely while dramatically simplifying the architecture.

One thing I'd also suggest is reconsidering the hardcoded ini_set('memory_limit', '128M') in Tinker - for large projects this might be too restrictive. It should probably be configurable or at least doubled to 256M to ensure it works properly across different project sizes without running into memory issues during code execution.

Copy link
Collaborator

@ashleyhindle ashleyhindle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great change, thank you @andreilungeanu, and appreciate you going the extra mile with the latest refactor 🙌

pushpak1300 reacted with heart emoji andreilungeanu reacted with rocket emoji
@ashleyhindle ashleyhindle merged commit 8aae29e into laravel:main Sep 4, 2025
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Reviewers

@ashleyhindle ashleyhindle ashleyhindle approved these changes

Labels
None yet
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

Laravel Boost MCP Tinker tool caches class definitions and doesn't reflect code changes

AltStyle によって変換されたページ (->オリジナル) /