Draft: Share JSON-RPC request handling between MCP endpoints

What this does

This merge request extracts shared JSON-RPC handling logic from two GitLab MCP server endpoints. The CE endpoint in lib/api/mcp/base.rb and the EE Orbit endpoint in ee/lib/api/orbit/mcp.rb each had duplicate copies of the same JSON-RPC code. This refactor moves the shared parts into the module at lib/api/helpers/mcp/json_rpc.rb. There is no behaviour change, and no spec assertion needed a change.

This is merge request 1 of 5 planned merge requests for issue #627825. It targets branch 611372-mcp-error-response-request-id, which belongs to a separate merge request, !253329 (merged), that creates the shared module file.

The extracted code is:

  • the JSON-RPC version constant JSONRPC_VERSION
  • the error table JSONRPC_ERRORS
  • method_not_found!
  • format_jsonrpc_response

Notable decisions

  • The dead version_mismatch error entry with code -32001 was removed. Nothing in the codebase called it. An unsupported protocol version raises ArgumentError, which the endpoints answer with -32602. The MCP spec from 2026-07-28 marks the -32000 to -32019 range as legacy and tells new implementations to stay out of it.
  • The -32603 internal error code stays only in ee/lib/api/orbit/mcp.rb, because only that endpoint raises it. The file merges this one extra entry onto the shared error table.
  • The -32700 parse error is still absent on purpose. Workhorse answers with HTTP 400 before a parse error reaches this code.
  • The two endpoints answer JSON-RPC notifications differently. The CE endpoint returns HTTP 202 Accepted with an empty body. The EE Orbit endpoint returns HTTP 204. Specs pin both, so the difference is preserved through a small overridable method, jsonrpc_notification_response, which the EE endpoint overrides.
  • Both endpoints use include to bring in the shared constants and helpers to bring in the shared methods. Grape helpers only shares methods, not constants, and the class bodies need the constants in their params and rescue_from blocks.

How to test locally

bundle exec rspec spec/requests/api/mcp/ spec/lib/api/helpers/mcp/json_rpc_spec.rb

result: 174 examples, 0 failures, 4 pending

bundle exec rspec ee/spec/requests/api/orbit/mcp_spec.rb

result: 78 examples, 0 failures, 4 pending

bundle exec rubocop lib/api/helpers/mcp/json_rpc.rb lib/api/mcp/base.rb ee/lib/api/orbit/mcp.rb

result: 3 files inspected, no offenses detected

The 4 pending examples in each rspec run are pre-existing skips in a shared granular token example group. They are not related to this change.

Closes #628249 Related to #627825

🤖 Generated with Claude Code

Edited by Amr Taha

Merge request reports

Loading
Loading