Skip to content

Latest commit

 

History

History
245 lines (191 loc) · 8.26 KB

File metadata and controls

245 lines (191 loc) · 8.26 KB

Model Context Protocol (MCP)

Codeward integrates with the Model Context Protocol in two complementary ways:

  1. Codeward as an MCP server — exposes GitLab-aware tools, resources, and prompts to any MCP client (Claude Desktop, Cursor, an Agno agent in another project, ...).
  2. Codeward as an MCP client — agents can call tools from any external MCP server you declare in codeward.yml.

This page covers both directions.


1. Codeward as an MCP server

Codeward mounts a FastMCP server at /mcp. The mount is wired up in codeward/main.py and the server itself is built in codeward/mcp_server/server.py.

  • URL: http://<host>:8420/mcp
  • Transport: streamable HTTP (FastMCP streamable_http_app)
  • Auth: optional bearer token via CODEWARD_MCP_KEY
  • Source: codeward/mcp_server/{server,tools,resources,prompts}.py

Authentication

Set CODEWARD_MCP_KEY in .env to require a bearer token on every request to /mcp:

CODEWARD_MCP_KEY=replace-with-a-long-random-string

When the key is set, MCPAuthMiddleware rejects requests whose Authorization header is missing or does not match Bearer <CODEWARD_MCP_KEY>. If CODEWARD_MCP_KEY is unset, the mount is exposed without authentication — useful for local development, but not recommended for any deployment that GitLab can reach over a non-trusted network.

Tools

Registered in codeward/mcp_server/tools.py:

Tool Purpose
list_merge_requests(project, state, max_results) List MRs in a given state
get_merge_request(project, mr_iid) Fetch a single MR's metadata
get_merge_request_diff(project, mr_iid) Fetch the changes for a single MR
post_merge_request_comment(project, mr_iid, body) Post a note on an MR
list_issues(project, ...) List issues with filters
get_issue(project, issue_iid) Fetch a single issue
create_issue(project, ...) Open a new issue
get_file_content(project, file_path, ref) Fetch a file from the repository
get_repository_tree(project, ...) List files/directories in the repository
search_code(project, query) Search project code
get_pipeline_status(project, ref) Pipeline status for a branch
get_failed_pipeline_logs(project, pipeline_id) Logs for failed jobs in a pipeline
list_project_labels(project) List labels available on the project

The project argument accepts either a numeric ID or a group/project path. Path lookups are cached in Redis for one hour when REDIS_URL is configured (resolve_project_id in codeward/mcp_server/server.py).

Resources

Registered in codeward/mcp_server/resources.py:

URI Returns
gitlab://project/{path}/readme The project's README.md
gitlab://project/{path}/ci-config The project's .gitlab-ci.yml
gitlab://project/{path}/merge-requests/open Open merge requests
gitlab://project/{path}/issues/open Open issues
gitlab://project/{path}/pipelines/latest Latest pipeline status

Prompts

Registered in codeward/mcp_server/prompts.py:

Prompt Arguments
review_merge_request project, mr_iid
explain_pipeline_failure project, pipeline_id
summarize_issue project, issue_iid

Connecting from Claude Desktop

Add an entry to your Claude Desktop config (e.g. ~/Library/Application Support/Claude/claude_desktop_config.json on macOS, or %APPDATA%\Claude\claude_desktop_config.json on Windows):

{
  "mcpServers": {
    "codeward": {
      "transport": "http",
      "url": "http://localhost:8420/mcp",
      "headers": {
        "Authorization": "Bearer replace-with-a-long-random-string"
      }
    }
  }
}

Drop the headers block if CODEWARD_MCP_KEY is unset. Restart Claude Desktop and you should see the Codeward tools, resources, and prompts in the MCP picker.

Connecting from Cursor / other clients

Any client that speaks streamable-HTTP MCP can talk to Codeward. Point it at http://<host>:8420/mcp and (if applicable) add a Authorization: Bearer <CODEWARD_MCP_KEY> header.

Smoke testing

# With auth
curl -i \
  -H "Accept: text/event-stream" \
  -H "Authorization: Bearer $CODEWARD_MCP_KEY" \
  http://localhost:8420/mcp

# Without auth (CODEWARD_MCP_KEY unset)
curl -i \
  -H "Accept: text/event-stream" \
  http://localhost:8420/mcp

A 401 Unauthorized JSON response indicates the bearer token did not match. A successful response is an SSE stream — kill it with Ctrl-C.


2. Agents calling external MCP servers

Codeward agents can be granted tools from any external MCP server without writing Python. The connection is opened lazily on first use, cached, health-checked between calls, and merged into the agent's toolset alongside GitLabToolkit. The implementation lives in codeward/mcp_client/{registry,manager}.py.

Step 1 — declare the server in codeward.yml

codeward:
  mcp_servers:
    context7:
      url: https://mcp.context7.com/mcp
      transport: streamable-http     # streamable-http | sse
      auth_type: bearer              # bearer | header | none
      auth_token_env: CONTEXT7_TOKEN # env var that holds the token
      enabled: true
      description: Context7 documentation lookups

    sentry:
      url: https://mcp.sentry.dev/mcp
      transport: streamable-http
      auth_type: header
      headers:
        Authorization: Bearer ${SENTRY_TOKEN}  # ${VAR} expansion is supported

The schema is defined by MCPServerConfig in codeward/mcp_client/registry.py:

Field Type Notes
url string The MCP endpoint. ${ENV_VAR} references are expanded from the process environment.
transport string streamable-http (default) or sse.
auth_type string bearer, header, or none.
auth_token_env string For auth_type: bearer — the env var holding the token. The header Authorization: Bearer <token> is added automatically.
headers mapping For auth_type: header — literal headers to send. ${ENV_VAR} is expanded.
enabled bool Disable a server without removing the entry. Defaults to true.
description string Free-form, surfaced in logs.

Step 2 — opt the agent in

Reference the server name from the agent YAML's mcp_servers: list:

# agents/chat.yml
name: chat
type: chat
# ...
mcp_servers:
  - context7
  - sentry

At run time, MCPConnectionManager.get_tools_for_agent (in codeward/mcp_client/manager.py) collects the configured MCPTools instances and Agno merges them into the agent's toolset alongside GitLabToolkit.

Step 3 — per-project overrides (optional)

A project can replace the MCP server list per agent in its .codeward.yml:

codeward:
  mcp_servers:
    chat:                       # agent name
      - context7                # only context7 in this repo, drop sentry

If mcp_servers is set for an agent in a project's .codeward.yml, that list completely replaces the agent-level list — it is not merged with it.

Behaviour at run time

  • Lazy connect. Connections are opened on first use and cached for the lifetime of the process.
  • Health checks. Before reusing a cached connection, the manager calls is_alive() and reconnects if the connection has gone stale.
  • Failure isolation. If an MCP server is unreachable, the connection attempt is logged and the agent runs without that server's tools — it does not abort the run.
  • Tool prefixing. Tool names are prefixed with the server name (tool_name_prefix=config.name) to avoid collisions across multiple MCP servers.

Listing configured servers

The manager exposes a health_check() helper used by the /health endpoint to report per-server connectivity. Failing servers will show up there with false so you can spot misconfigured tokens quickly.


Related reading

  • AGENTS.md — agent architecture, YAML schema, and the MCP-related sections cross-referenced from here
  • Configuration — env var reference and the codeward.mcp_servers section
  • Multi-Agent Flows — flows can also call agents that have external MCP tools attached