DEV Community

Pavan Kumar Sadashiv
Pavan Kumar Sadashiv

Posted on

Connecting AI Agents to GitLab: How I Implemented MCP Resources in GitLab Core

Connecting AI Agents to GitLab: How I Implemented MCP Resources in GitLab Core

As autonomous AI coding assistants (like Cline, Claude Desktop, Cursor, and Antigravity) rapidly evolve from simple chat interfaces to agentic workflows, the Model Context Protocol (MCP) has emerged as the open industry standard for letting LLMs interact with external systems.

Instead of writing fragile bespoke plugins for every tool, MCP allows an application like GitLab to expose its projects, issues, merge requests, and telemetry directly to any AI client via a clean JSON-RPC interface.

Recently, I contributed two key merge requests to GitLab Core:

  1. !260212 — Add MCP resources and resource templates list endpoints (Resolves Issue #584258, tagged community-bonus::100).
  2. !259119 — Add enum constraint to search scope parameter in MCP Server JSON schema (Resolves Issue #595159).

In this article, I will walk you step-by-step through:

  • Why the connection was failing (404 Method not found).
  • How MCP handshakes and JSON-RPC capabilities work under the hood.
  • How we implemented and verified the handlers in GitLab’s monolithic Ruby on Rails codebase.
  • Lessons learned navigating GitLab’s enterprise-grade CI pipelines (Danger Bot, RuboCop, and Predictive RSpec).

1. The Problem: The Handshake Disconnect

When an MCP client initializes a session with an MCP server, it starts with an initialize JSON-RPC handshake. Both sides declare their supported capabilities.

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "capabilities": {},
    "clientInfo": { "name": "cline", "version": "3.0.0" }
  }
}
Enter fullscreen mode Exit fullscreen mode

The server responds with its capabilities: tools, prompts, or resources.

The Bug

When modern MCP clients like Cline connected to GitLab's MCP server, they immediately probed for available context by sending requests to:

  • resources/list
  • resources/templates/list

Because GitLab's server had not implemented or advertised these endpoints, the server responded with:

{
  "jsonrpc": "2.0",
  "id": 2,
  "error": {
    "code": -32601,
    "message": "Method not found: resources/list"
  }
}
Enter fullscreen mode Exit fullscreen mode

This error caused AI clients to either fail to load or disable context-awareness entirely when browsing GitLab repositories.


2. Architectural Deep-Dive: How MCP Works in GitLab Core

GitLab's MCP server implementation runs within GitLab Core (gitlab-org/gitlab). Incoming JSON-RPC requests are parsed and routed to dedicated service handlers.

To fix the issue properly, two things had to happen:

  1. Advertise the Capability: Tell the client during the initialize handshake that resources and resource templates are supported.
  2. Implement the RPC Handlers: Route resources/list and resources/templates/list to return structured schemas and URIs that agents can inspect.

Step A: Updating the Initialization Handshake

In the server's handshake responder, we registered the resources capability:

def server_capabilities
  {
    tools: { listChanged: false },
    prompts: { listChanged: false },
    resources: {
      subscribe: false,
      listChanged: false
    }
  }
end
Enter fullscreen mode Exit fullscreen mode

By advertising resources: { ... }, the connecting agent knows that GitLab can supply contextual resources (such as project files, issues, or pipeline logs).

Step B: Implementing resources/list and resources/templates/list

Next, we added request dispatchers for the resource endpoints:

module Mcp
  module Handlers
    class ResourcesListHandler < BaseHandler
      def call
        resources = [
          # Context resources exposed to AI agents
          {
            uri: "gitlab://projects/{project_id}/issues",
            name: "GitLab Project Issues",
            description: "Contextual issue tracker for the target repository",
            mimeType: "application/json"
          }
        ]

        success_response(resources: resources)
      end
    end

    class ResourceTemplatesListHandler < BaseHandler
      def call
        templates = [
          {
            uriTemplate: "gitlab://projects/{project_id}/repository/files/{file_path}",
            name: "Repository File Viewer",
            description: "Allows AI models to inspect files via URI parameter matching",
            mimeType: "text/plain"
          }
        ]

        success_response(resourceTemplates: templates)
      end
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Once dispatched, any standard MCP-compliant AI agent can list and subscribe to these resources without breaking.


3. Hardening the Schema: Preventing Agent Hallucinations (!259119)

While testing agent interactions, another critical issue arose with the search tool.

AI agents rely heavily on JSON schemas to generate tool-call arguments. In the MCP schema for GitLab search, the scope parameter was defined simply as type: string.

The Vulnerability

LLMs would frequently hallucinate invalid scopes like:

  • "scope": "all"
  • "scope": "code_and_issues"
  • "scope": "mr"

These invalid inputs resulted in server-side 500 or 400 errors.

The Fix

In Merge Request !259119, we introduced explicit enum constraints to the JSON Schema:

{
  "scope": {
    "type": "string",
    "description": "The search scope within GitLab",
    "enum": [
      "blobs",
      "issues",
      "merge_requests",
      "milestones",
      "notes",
      "projects",
      "users",
      "wiki_blobs"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

By constraining the schema, modern LLMs with structured function calling (Claude 3.7 / 3.5 Sonnet, Gemini 2.0 / 1.5 Pro, GPT-4o) are guaranteed to generate syntactically valid search parameters 100% of the time.


4. Navigating GitLab’s Enterprise CI & Code Review

Contributing to GitLab Core isn't just about writing code; it's about passing one of the most rigorous CI/CD test pipelines in the open-source world.

When you submit a Merge Request to gitlab-org/gitlab, the pipeline runs:

  1. Danger Bot: Checks for changelog entries, commit message formatting, documentation updates, and license compliance.
  2. RuboCop: Enforces GitLab's strict Ruby style guide and AST rules.
  3. Predictive RSpec: Dynamically selects and runs only the relevant test suites out of tens of thousands of unit tests.

Writing the RSpec Tests

To ensure zero regressions, we added comprehensive unit specs covering both success cases and edge cases:

# spec/lib/mcp/handlers/resources_list_handler_spec.rb
RSpec.describe Mcp::Handlers::ResourcesListHandler, feature_category: :ai_abstraction do
  subject(:handler) { described_class.new(params) }

  let(:params) { {} }

  describe '#call' do
    it 'returns the list of available MCP resources' do
      response = handler.call

      expect(response).to be_success
      expect(response.payload[:resources]).to be_an(Array)
      expect(response.payload[:resources].first).to include(
        uri: a_string_matching(%r{^gitlab://}),
        name: be_a(String)
      )
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Both Merge Requests passed:

  • danger-review
  • rubocop
  • rspec:predictive
  • rspec-ee:predictive

Both MRs were approved by GitLab maintainers and merged into the main release!


5. End-to-End Verification: Testing with AI Agents

With these changes merged, configuring an AI agent (such as Cline in VS Code or Claude Desktop) is effortless.

claude_desktop_config.json Configuration:

{
  "mcpServers": {
    "gitlab": {
      "command": "gitlab-mcp-server",
      "args": ["--url", "https://gitlab.com", "--token", "YOUR_GITLAB_PAT"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, when you prompt:

"List the open security issues in project #123456 and suggest a patch based on our current branch."

The AI agent:

  1. Connects to the GitLab MCP Server.
  2. Discovers the resources and tools without handshake failures.
  3. Pulls context directly into its prompt window.
  4. Executes searches using strict, verified enum scopes.

6. How You Can Start Contributing to GitLab

If you are an engineer looking to break into open-source or contribute to enterprise developer tooling:

  1. Check GitLab’s Issue Board: Filter issues with community-bonus or quick win. GitLab actively rewards contributors through their GitLab Contributor Platform.
  2. Leverage GitLab Development Kit (GDK): Test your changes locally before submitting.
  3. Document Your Work: Share what you build! The developer community thrives on real-world engineering walkthroughs.

Have questions about MCP or contributing to GitLab Core? Drop a comment below!

Top comments (0)