Define an MCP tool via API setting
What does this MR do and why?
Define an MCP tool via API setting
We can get MCP definition from the OpenAPI route description. It'll allow us to avoid duplication. Also, calling a route directly as a middleware would allow us to avoid generating an oauth token every time.
An MCP tool definition will come down to adding the following line to a route:
params do
requires :issue_iid, type: Integer, desc: 'The internal ID of a project issue'
end
+ route_setting :mcp, name: :get_issue
get ":id/issues/:issue_iid", as: :api_v4_project_issue do
Examples
POST http://gdk.test:3000/api/v4/mcp_server
jsonrpc=2.0 method=tools/list id=1
{
"id": "1",
"jsonrpc": "2.0",
"result": {
"tools": [
{
"description": "Get a single project issue",
"inputSchema": {
"additionalProperties": false,
"properties": {
"id": {
"description": "The ID or URL-encoded path of the project",
"type": "string"
},
"issue_iid": {
"description": "The internal ID of a project issue",
"type": "integer"
}
},
"required": [
"id",
"issue_iid"
],
"type": "object"
},
"name": "get_issue"
},
{
"description": "Get single merge request",
"inputSchema": {
"additionalProperties": false,
"properties": {
"id": {
"description": "The ID or URL-encoded path of the project.",
"type": "string"
},
"merge_request_iid": {
"description": "The internal ID of the merge request.",
"type": "integer"
}
},
"required": [
"id",
"merge_request_iid"
],
"type": "object"
},
"name": "get_merge_request"
}
]
}
}
http POST http://gdk.test:3000/api/v4/mcp_server
{
"jsonrpc": 2.0,
"method": "tools/call",
"id": 1,
"params": {
"name":"get_issue",
"arguments": {
"id": 25,
"issue_iid": 1
}
}
}
{
"id": 1,
"jsonrpc": "2.0",
"result": [
"{\"id\":655,\"iid\":1,\"project_id\":25,\"title\":\"Rename setupTest to setup in internal/services/runner/runner_test.go\",\"description\":\"## Summary\\nThe function `setupTest` in `internal/services/runner/runner_test.go` should be renamed to `setup` for consistency and brevity.\\n\\n## Current State\\n- Function is currently named `setupTest`\\n- Located in `internal/services/runner/runner_test.go`\\n\\n## Proposed Change\\n- Rename function from `setupTest` to `setup`\\n- Update all references to use the new name\\n\\n## Benefits\\n- Shorter, cleaner function name\\n- Improved code consistency\\n- Better readability\\n\\n## Files to Update\\n- `internal/services/runner/runner_test.go`\",\"state\":\"opened\",\"created_at\":\"2025-08-04T21:16:34.230Z\",\"updated_at\":\"2025-08-15T06:08:01.610Z\",\"closed_at\":null,\"closed_by\":null,\"labels\":[\"refactor\",\"test\"],\"milestone\":null,\"assignees\":[{\"id\":1,\"username\":\"root\",\"public_email\":null,\"name\":\"Administrator\",\"state\":\"active\",\"locked\":false,\"avatar_url\":\"https://www.gravatar.com/avatar/8bf0d08f75ff3ca6381d96b1d1035f8eed7a600904fa0e44ef0553192bafd567?s=80\\u0026d=identicon\",\"web_url\":\"http://host.docker.internal:3000/root\"}],\"author\":{\"id\":1,\"username\":\"root\",\"public_email\":null,\"name\":\"Administrator\",\"state\":\"active\",\"locked\":false,\"avatar_url\":\"https://www.gravatar.com/avatar/8bf0d08f75ff3ca6381d96b1d1035f8eed7a600904fa0e44ef0553192bafd567?s=80\\u0026d=identicon\",\"web_url\":\"http://host.docker.internal:3000/root\"},\"type\":\"ISSUE\",\"assignee\":{\"id\":1,\"username\":\"root\",\"public_email\":null,\"name\":\"Administrator\",\"state\":\"active\",\"locked\":false,\"avatar_url\":\"https://www.gravatar.com/avatar/8bf0d08f75ff3ca6381d96b1d1035f8eed7a600904fa0e44ef0553192bafd567?s=80\\u0026d=identicon\",\"web_url\":\"http://host.docker.internal:3000/root\"},\"user_notes_count\":2,\"merge_requests_count\":0,\"upvotes\":0,\"downvotes\":0,\"due_date\":null,\"confidential\":false,\"discussion_locked\":null,\"issue_type\":\"issue\",\"web_url\":\"http://host.docker.internal:3000/gitlab-org/duo-workflow-executor/-/issues/1\",\"time_stats\":{\"time_estimate\":0,\"total_time_spent\":0,\"human_time_estimate\":null,\"human_total_time_spent\":null},\"task_completion_status\":{\"count\":0,\"completed_count\":0},\"weight\":null,\"blocking_issues_count\":0,\"has_tasks\":true,\"task_status\":\"0 of 0 checklist items completed\",\"_links\":{\"self\":\"http://host.docker.internal:3000/api/v4/projects/25/issues/1\",\"notes\":\"http://host.docker.internal:3000/api/v4/projects/25/issues/1/notes\",\"award_emoji\":\"http://host.docker.internal:3000/api/v4/projects/25/issues/1/award_emoji\",\"project\":\"http://host.docker.internal:3000/api/v4/projects/25\",\"closed_as_duplicate_of\":null},\"references\":{\"short\":\"#1\",\"relative\":\"#1\",\"full\":\"gitlab-org/duo-workflow-executor#1\"},\"severity\":\"UNKNOWN\",\"subscribed\":false,\"moved_to_id\":null,\"imported\":false,\"imported_from\":\"none\",\"service_desk_reply_to\":null,\"epic_iid\":null,\"epic\":null,\"iteration\":null}"
]
}
Edited by Igor Drozdov