Problem/Motivation
This is kind of a heisenbug and took some time to track down, but I have some understanding what is happening now. The problem is the following - when you try to save a mcp server with "fetch tools" on, you get the following error:
The website encountered an unexpected error. Try again later.
TypeError: Swis\McpClient\Client::listTools(): Return value must be of type Swis\McpClient\Results\ListToolsResult|Swis\McpClient\Results\JsonRpcError, null returned in Swis\McpClient\Client->listTools() (line 156 of /var/www/html/vendor/swisnl/mcp-client/src/ClientRequestTrait.php).
Independent on if its an update or a creation this happens.
If you add the data and click "Test Connection", the same problem does not occur. I have looked into MCPClient.php and where it's using listTools() method and I can not see any differences in how the client is loaded before it uses the listTools.
I have been able to track it down to only happening in Drupal 11.3.0, but if I revert to Drupal 11.2.0 its all working again.
Steps to reproduce
1. Install Drupal 11.3.0
2. Install the latest MCP Client module (or any)
3. Add a MCP Server and click save.
Proposed resolution
Figure out if there is some secondary dependency that got updated in Drupal 11.3.0 that swisnl/mcp-client or some other library is dependent on.
| Comment | File | Size | Author |
|---|---|---|---|
| #13 | Screenshot 2026-03-20 at 9.46.55 PM.png | 153.4 KB | harivansh |
| #2 | Screenshot from 2026-01-07 12-56-14.png | 49 KB | marcus_johansson |
Issue fork mcp_client-3565919
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
marcus_johansson commentedBoth goes into McpClientFactory.php and run createFromEntity. The debug point there points to the exact same input, yet one fails and not the other.
Both runs listTools() directly after instantiation where one works and the other doesn't.
I can see also that the working swisnl/clients, whether that is the ajax call or Drupal 11.2 backend call, has two requestMaps, while the broken one only has one. The second one missing is of InitializedNotificationRequest. See screenshot from xdebug.
Comment #3
marcus_johansson commentedSo in Drupal 11.2 afterInitialization inside SseTransporter is not being blocked, before the listTools is run in Drupal 11.3 on the backend save.
So you see the error render out on the page before the actual afterInitialization is run.
Comment #4
robertoperuzzoI noticed this behaviour, I investigated without finding a real solution! For now, I suggest the workaround described in my comment #3565713-7: Support persistent sessions.
But I suspect there is something weird in using the SwissNL MCP client, we must go deeper on that and/or test other MCP client libraries like this one https://github.com/modelcontextprotocol/php-sdk
More details:
Comment #5
robertoperuzzoI'm not sure but probably the Root Cause is the incompatibility between PHP Fibers (introduced in Drupal 11.3) and ReactPHP Event Loop (used by swissnl/mcp_client).
Drupal 11.3.0 introduced PHP Fibers for improved async performance. The problem is that the MCP Client module uses ReactPHP's await() function, which relies on PHP Fibers to handle promises. There's a timing/race condition between Drupal's Fiber management and ReactPHP's event loop.
Comment #7
robertoperuzzoI implemented a roughly solution (see the MR) to fix the Fiber PHP breaking changes introduced with D11.3. I'm not sure that is the best solution but it seems to work.
I detailed the implementation in the FINAL_COMPLETE_SOLUTION.md file that can help for further improvements (we will remove it when the work will be completed).
Now I need some help by the community to understand if this is a correct path for the final implementation.
DISCLAIMER: I used AI assistant to help me in investiation and to write the documentation.
Comment #8
robertoperuzzoComment #9
robertoperuzzoSince Drupal 11.3 use Fibers and there is an open discussion in [Client] Implement PHP MCP Client Component in php-sdk and in the PR [Client] Feat: Implement MCP client component to use Fibers instead of external lib like ReactPHP, I was wondering if we can implement the event loop using Fibers.
I implemented, just for fun, a Fiber rough solution here. I tested using Slack MCP server (HTTP and STTDIO) and the communication seems to work, and it could be a starting point.
Some Benefits- ✅ Works seamlessly in Drupal 11.3+ Fiber context
- ✅ No more "Connection aborted early" errors
- ✅ No external dependencies to manage
- ✅ Same API - no code changes needed
- ✅ Pure PHP - easier to debug
- ✅ No ReactPHP learning curve
- ✅ Simpler stack traces
- ✅ Direct control over I/O
- ✅ Fewer dependencies in composer.json
- ✅ Smaller vendor/ directory
- ✅ Less attack surface
For Users
For Developers
For Operations
Comment #10
harivansh commentedComment #12
harivansh commentedComment #13
harivansh commentedTest Preview:

Comment #14
harivansh commentedComment #15
marcus_johansson commentedComment #16
marcus_johansson commentedI've pushed a small change and now it works with 0.5.0 of the swisnl/mcp-client, but not with the 0.7.0 or 0.6.0 release.
Changes in the library: https://github.com/swisnl/mcp-client/compare/0.5.0...0.6.0.
Comment #17
robertoperuzzoSorry for my latency!
Thank you so much @harivansh for your contribution, I've reviewed it and it sounds good for me.
About the breaking change in swisnl/mcp-client from 0.5 to 0.6 reported by @marcus, I proposed a fix in the commit above. Check it out and tell me if it is fine for you both.
Comment #18
robertoperuzzo