FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

feat(mcp): add Model Context Protocol server implementation by escapeboy · Pull Request #202 · tweakphp/tweakphp · GitHub

Repository navigation

feat(mcp): add Model Context Protocol server implementation - #202

Merged
ILDaviz merged 22 commits into
tweakphp:mainfrom
escapeboy:feature/mcp-server
Jul 30, 2026
Merged

ILDaviz merged 22 commits into
tweakphp:mainfrom
escapeboy:feature/mcp-server

Conversation

escapeboy commented Nov 19, 2025 •
edited
Loading

Copy link
Copy Markdown
Contributor
  • Add MCP server with connection manager and router for handling protocol requests
  • Implement execution history tracking with SQLite database migration
  • Add MCP tools: execute-php, execute-with-loader, get-execution-history, get-php-info, switch-connection
  • Create comprehensive MCP documentation: API, configuration, setup guide, troubleshooting, and implementation summary
  • Add error handling and logging infrastructure for MCP operations
  • Integrate MCP settings UI component in SettingsView
  • Add @modelcontextprotocol/sdk dependency (^1.22.0)
  • Update build configuration to externalize cpu-features dependency
  • Add .kiro directory to .gitignore
  • Include test-mcp-connection.js for connection validation
  • Enable AI assistants to execute PHP code and manage database connections through standardized protocol

Documentation PR: tweakphp/docs#7

ILDaviz commented Nov 19, 2025

Copy link
Copy Markdown
Member

Copy link
Copy Markdown
Member

Thanks for the PR.

I believe the MD files can be on the documentation instead of the code

Copy link
Copy Markdown
Contributor Author

Thanks for the PR.

I believe the MD files can be on the documentation instead of the code

Documentation PR: tweakphp/docs#7

ILDaviz commented Dec 1, 2025

Copy link
Copy Markdown
Member

Thank you very much for your work. As soon as I have a free moment, I'll get started on the review!

Copy link
Copy Markdown
Member

I can't get it working

2025-12-06T11:51:14.084Z [tweakphp] [info] Initializing server... { metadata: undefined }
2025-12-06T11:51:14.090Z [tweakphp] [info] Using MCP server command: node with args and path: {
  metadata: {
    args: [
      '-e',
      "require('http').request({host:'127.0.0.1',port:3000,method:'POST',path:'/mcp'},r=>{let d='';r.on('data',c=>d+=c);r.on('end',()=>console.log(d))}).end(JSON.stringify(process.argv[2]))",
      [length]: 2
    ],
    paths: [
      '/usr/local/bin',
      '/opt/homebrew/bin',
      '/usr/bin',
      '/bin',
      '/usr/sbin',
      '/sbin',
      [length]: 6
    ]
  }
} %o
2025-12-06T11:51:14.091Z [tweakphp] [info] Server started and connected successfully { metadata: undefined }
Failed to spawn process: No such file or directory
2025-12-06T11:51:14.094Z [tweakphp] [info] Server transport closed { metadata: undefined }
2025-12-06T11:51:14.094Z [tweakphp] [info] Client transport closed { metadata: undefined }
2025-12-06T11:51:14.095Z [tweakphp] [info] Server transport closed unexpectedly, this is likely due to the process exiting early. If you are developing this MCP server you can add output to stderr (i.e. `console.error('...')` in JavaScript, `print('...', file=sys.stderr)` in python) and it will appear in this log. { metadata: undefined }
2025-12-06T11:51:14.095Z [tweakphp] [error] Server disconnected. For troubleshooting guidance, please visit our [debugging documentation](https://modelcontextprotocol.io/docs/tools/debugging) { metadata: { context: 'connection', stack: undefined } }
2025-12-06T11:51:14.095Z [tweakphp] [info] Client transport closed { metadata: undefined }

Copy link
Copy Markdown
Member
{
  "mcpServers": {
    "tweakphp": {
      "url": "http://127.0.0.1:3001",
      "type": "http"
    }
  }
}
  • I can get work after toggling tweakphp on the cursor.
  • When the cursor is initialized, the MCP starts in an error state.

ILDaviz removed their request for review February 3, 2026 08:08
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Copy link
Copy Markdown
Contributor Author

Root cause analysis & fixes

After testing on a machine with PHP 8.5, three bugs were found and fixed in feature/mcp-server:


Bug 1 — PHP version / phar mismatch (src/main/client/local.ts)

getLocalPharClient() was constructing the phar path as client-{phpVersion}.phar and returning it unconditionally. On PHP 8.5 client-8.5.phar doesn't exist (only client-8.4.phar ships), so execution failed silently.

Fix: check whether the exact-version phar exists first; if not, fall back to the highest-versioned phar available in the public/ directory.


Bug 2 — Standard loader name passed as --loader arg (src/main/mcp/tools/execute-with-loader.ts)

execute_with_loader was calling client.execute(params.code, params.loader) which forwarded 'laravel' or 'symfony' as --loader=bGFyYXZlbA== (base64) to the phar. The phar's --loader flag expects a base64-encoded PHP class for custom loaders — it decodes and evals the value. Evaluating the string "laravel" as PHP caused a parse error.

The phar already auto-detects Laravel/Symfony by checking for artisan / bin/console in the project path, so --loader should not be passed for standard frameworks.

Fix: only forward the loader arg for non-standard (custom) loaders:

const standardLoaders = ['laravel', 'symfony']
const loaderArg = standardLoaders.includes(params.loader) ? undefined : params.loader
client.execute(params.code, loaderArg)

Bug 3 — SQLite object binding (execute-php.ts and execute-with-loader.ts)

Phar output format is TWEAKPHP_RESULT:{...json...}. After JSON.parse() the output variable becomes a JavaScript object. better-sqlite3 only accepts null / number / string / Buffer as bound parameters — passing a JS object throws:

RangeError: Too few parameter values were provided

This surfaced as execute_php always returning {"error":"Too few parameter values were provided"}.

Fix: stringify before the DB insert:

output: typeof output === 'string' ? output : JSON.stringify(output)

Testing

After all three fixes, end-to-end MCP tests pass on PHP 8.5 (local connection):

Tool Result
GET /health ✅ 200 ok
tools/list ✅ 5 tools
switch_connection ✅ switched to local
execute_php (<?php echo phpversion();) ✅ "8.5.1"
execute_with_loader (laravel) ✅ Laravel 12.55.1 user count
get_php_info ✅ ok
get_execution_history ✅ 16 records returned

escapeboy and others added 7 commits March 28, 2026 18:11
- Add MCP server with connection manager and router for handling protocol requests
- Implement execution history tracking with SQLite database migration
- Add MCP tools: execute-php, execute-with-loader, get-execution-history, get-php-info, switch-connection
- Create comprehensive MCP documentation: API, configuration, setup guide, troubleshooting, and implementation summary
- Add error handling and logging infrastructure for MCP operations
- Integrate MCP settings UI component in SettingsView
- Add @modelcontextprotocol/sdk dependency (^1.22.0)
- Update build configuration to externalize cpu-features dependency
- Add .kiro directory to .gitignore
- Include test-mcp-connection.js for connection validation
- Enable AI assistants to execute PHP code and manage database connections through standardized protocol
- Delete API.md documentation file
- Delete CONFIGURATION.md documentation file
- Delete ERROR_HANDLING.md documentation file
- Delete IMPLEMENTATION_SUMMARY.md documentation file
- Delete INDEX.md documentation file
- Delete README.md documentation file
- Delete SETUP_GUIDE.md documentation file
- Delete TROUBLESHOOTING.md documentation file
- Update test-mcp-connection.js with latest changes
- Consolidate MCP documentation into main project documentation
The previous implementation used a custom {tool, parameters} HTTP
protocol incompatible with MCP JSON-RPC 2.0. Clients were also
instructed to connect via `command: node -e <script>`, which fails
on machines without system Node.js installed.

- Replace server.ts with McpServer + StreamableHTTPServerTransport
  from @modelcontextprotocol/sdk (already in dependencies)
- Register all 5 tools using mcpServer.tool() with Zod schemas
- Use stateless transport mode (no session management needed)
- Delete router.ts (replaced by SDK tool registration)
- Delete example-usage.ts (unused)
- Trim MCPToolRequest/MCPToolResponse from types.ts (no longer needed)
- Update MCPSettings.vue to show correct HTTP URL config snippet

Clients now connect with no system Node required:
  { "url": "http://127.0.0.1:3000/mcp", "type": "http" }

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Create fresh McpServer + StreamableHTTPServerTransport per POST request
  (matches the SDK stateless example - per-request lifecycle)
- Pre-parse request body and pass as third arg to transport.handleRequest
- GET/DELETE /mcp now return 405 (no SSE stream in stateless mode)
- Shared state (ConnectionManager, ExecutionHistoryDB, handlers) stays on
  MCPServerImpl and is accessed via closure from each per-request server
- Call closeAllConnections() on stop() to prevent EADDRINUSE on restart
- Clean up transport + server on res.close

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1. PHP version/phar mismatch: getLocalPharClient() now falls back to the
   highest available phar (client-8.4.phar) when the exact version for
   the installed PHP (e.g. 8.5) doesn't exist.

2. Loader arg sent to phar: standard loader names ('laravel', 'symfony')
   were passed as --loader=<base64> to the phar, causing it to eval the
   decoded string as PHP and fail with a parse error. The phar auto-detects
   laravel/symfony from the project path — only custom loaders need --loader.

3. SQLite object binding: phar output is TWEAKPHP_RESULT:{...json...}.
   After JSON.parse() the result becomes a JS object, which better-sqlite3
   cannot bind (RangeError: Too few parameter values were provided). Fixed
   by stringifying the output before the DB insert in both handlers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
escapeboy force-pushed the feature/mcp-server branch from a00a648 to 2e103df Compare March 28, 2026 16:15

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Pull request overview

Adds a Model Context Protocol (MCP) HTTP server inside the Electron main process, plus a renderer settings UI to enable/monitor it, and an execution-history SQLite table to persist tool runs.

Changes:

  • Implement MCP server + IPC wiring, including tools for executing PHP, switching connections, querying execution history, and retrieving PHP info.
  • Add execution history persistence via SQLite migration and a DB helper.
  • Add renderer UI/settings plumbing for enabling MCP and configuring the server port; add MCP SDK dependency and build adjustments.

Reviewed changes

Copilot reviewed 26 out of 27 changed files in this pull request and generated 19 comments.

Show a summary per file
File Description
test-mcp-connection.js Adds a local script intended to validate MCP connectivity.
src/types/settings.type.ts Extends app settings with MCP enablement + port fields.
src/renderer/views/settings/MCPSettings.vue Adds settings UI for enabling MCP server, configuring port, and displaying status.
src/renderer/views/SettingsView.vue Wires MCP settings tab into Settings view.
src/renderer/stores/settings.ts Adds defaults for MCP settings in renderer store.
src/renderer/components/Container.vue Adjusts container scrolling behavior.
src/renderer/assets/main.css Adds global scrollbar styling and some react-flow control styles.
src/preload/preload.ts Extends exposed IPC bridge with invoke.
src/main/settings.ts Adds MCP fields to main-process settings defaults and persistence.
src/main/mcp/types.ts Introduces MCP types (config/status/errors).
src/main/mcp/tools/schemas.ts Defines parameter types for MCP tools.
src/main/mcp/tools/index.ts Exports MCP tool handlers/schemas.
src/main/mcp/tools/execute-php.ts Implements PHP execution tool with history tracking and error handling.
src/main/mcp/tools/execute-with-loader.ts Implements framework-loader execution tool with history tracking and error handling.
src/main/mcp/tools/get-execution-history.ts Implements querying execution history via MCP.
src/main/mcp/tools/get-php-info.ts Implements PHP info retrieval via MCP.
src/main/mcp/tools/switch-connection.ts Implements switching/creating execution connections via MCP.
src/main/mcp/server.ts Implements HTTP MCP server endpoint + per-request MCP server instances.
src/main/mcp/index.ts Adds Electron IPC handlers and auto-start/stop behavior driven by settings.
src/main/mcp/execution-history-db.ts Adds SQLite helper for execution history CRUD/querying.
src/main/mcp/error-logger.ts Adds log file writer/rotation for MCP errors/info/warnings.
src/main/mcp/error-handler.ts Adds MCP error classification, retry, timeout, and troubleshooting helpers.
src/main/mcp/connection-manager.ts Adds MCP-focused connection manager mapping to existing clients.
src/main/main.ts Initializes MCP module and stops MCP server during app shutdown.
src/main/client/local.ts Updates local client phar resolution and fallback logic.
package.json Adds @modelcontextprotocol/sdk dependency; bumps glob.
package-lock.json Locks new dependency graph for MCP SDK + updated packages.
migrations/002_create_execution_history_table.sql Adds execution history schema + indexes.
build.js Externalizes cpu-features in the build bundle.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/main/mcp/tools/switch-connection.ts Outdated
Comment thread src/preload/preload.ts Outdated
Comment thread test-mcp-connection.js Outdated
Comment thread src/main/mcp/server.ts Outdated
Comment thread src/main/mcp/server.ts Outdated
Comment thread src/main/mcp/tools/execute-php.ts Outdated
Comment thread src/main/mcp/tools/switch-connection.ts Outdated
escapeboy and others added 5 commits March 28, 2026 18:26
- CORS: restrict Access-Control-Allow-Origin to /health only; /mcp
  should not be callable from arbitrary websites (CSRF risk)
- switch-connection: align kubectl validation with actual type (pod,
  not pod_name/deployment_name); require container_name for Docker
- server: register res.on('close') cleanup before handleRequest to
  avoid missing early close events
- server: clear timeout flag on clean shutdown to prevent spurious
  'Forcing shutdown' log on normal stop
- error-handler: only treat objects as MCPError when code is a known
  MCPErrorCode value (prevents Node EADDRINUSE etc. from bypassing
  error classification)
- MCPSettings: coerce mcpPort to number with bounds check; send
  mcp.settings-changed on port change so server restarts with new port
- index: restart running server on settings-changed to pick up config
  changes (e.g. port)
- local.ts: use path.join for baseDir (cross-platform path separators)
- test-mcp-connection.js: fix payload to use JSON-RPC tools/call format

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- execute-php/execute-with-loader: type output as unknown in result
  interfaces and local variable — JSON.parse can return any type
- execute-with-loader: detectFramework and validateFrameworkPath now
  skip local fs.existsSync checks for non-local connections (docker,
  ssh, kubectl, vapor) where the path refers to a remote filesystem
- execute-with-loader: pass params.projectPath to client.execute() so
  an explicit project path is honoured rather than silently ignored
- client.base/local/docker/ssh/kubectl/vapor: add optional projectPath
  param to execute() — each client uses it to override the connection's
  default project path when provided
- server: enforce 1 MB body size limit on POST /mcp requests (413
  response + req.destroy() on overflow)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Keeps the interface in sync with the updated execute() signature in
client.base.ts and all concrete implementations.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
preload.ts:
- Store IPC listener wrappers in a Map keyed by original callback so
  that removeListener(channel, callback) can find and remove the
  correct wrapper function — previously the wrapper was a fresh
  anonymous function each time so removeListener was a no-op
- Remove duplicate invoke property from IpcRenderer interface and
  implementation (caused TS2300 duplicate identifier errors)

settings.ts / MCPSettings.vue:
- Add ipcMain.handle('settings.save') — awaitable counterpart to the
  fire-and-forget 'settings.store' channel
- mcpEnabled setter now awaits invoke('settings.save') before sending
  'mcp.settings-changed', guaranteeing settings are written to disk
  before the main process reads them to start/stop the MCP server

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… header

server.ts: req.destroy() was called before res.end(), so the 413
response never reached the client. Now the response is flushed first
and req.destroy() is called in the res.end() callback.

test-mcp-connection.js: add Accept: application/json, text/event-stream
header — required by StreamableHTTPServerTransport (SDK enforces this
on POST /mcp requests).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copy link
Copy Markdown
Member

@escapeboy I see you're boosting a server for the MCP which means the app needs to be open to work. I was thinking to make it a command based MCP so user doesn't need to have tweakphp open to interact with the mcp.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copy link
Copy Markdown
Contributor Author

@escapeboy I see you're boosting a server for the MCP which means the app needs to be open to work. I was thinking to make it a command based MCP so user doesn't need to have tweakphp open to interact with the mcp.

Probably it is solution but I had some issues with Cursor and Kiro with their command way but using the MCP server was working perfectly without crashing. I had moved to Claude and don't have such issues anymore, but still decided to implement it for the community.

ILDaviz commented Jul 29, 2026 •
edited
Loading

Copy link
Copy Markdown
Member

@luanfreitasdev Hi! I've updated this branch with the latest changes from the main branch. I've also added a new tool called list-connections. I've included some additional fixes and moved the logging logic. Do you have the time and opportunity to give it a test run? I tried connecting it to Antigravity, and it works without any problems. Tks! A big thank you also to @escapeboy.

luanfreitasdev left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Hello @ILDaviz, @escapeboy, it's working now! I tested it in OpenCode on macOS.

ILDaviz commented Jul 30, 2026

Copy link
Copy Markdown
Member

Okay, great! So I'll try building this branch directly from the workflow. Then I'll push it to main. Thanks, everyone!

ILDaviz merged commit d79d863 into tweakphp:main Jul 30, 2026
4 checks passed
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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants


Back | FazBrowse Home | New Git URL