diff --git a/app/controllers/oauth/tokens_controller.rb b/app/controllers/oauth/tokens_controller.rb index 5133b9fc32e6db0414a496226d936f7fa3f2c2a3..8a8080c3893777ef3464807ab4e80e14567034a9 100644 --- a/app/controllers/oauth/tokens_controller.rb +++ b/app/controllers/oauth/tokens_controller.rb @@ -48,6 +48,8 @@ def append_info_to_payload(payload) def validate_pkce_for_dynamic_applications return unless server.client&.application&.dynamic? + # PKCE is only required for authorization_code grant, not refresh_token + return if params[:grant_type] == 'refresh_token' # rubocop:disable Rails/StrongParams -- Only accessing a single named param return unless params[:code_verifier].blank? # rubocop:disable Rails/StrongParams -- Only accessing a single named param render json: { diff --git a/doc/user/gitlab_duo/model_context_protocol/mcp_server.md b/doc/user/gitlab_duo/model_context_protocol/mcp_server.md index cbe9abd7fbf5aba0b80540d39397df0c1f003292..4a8c3bd748f0051c8c946363c85ae965a09d1e40 100644 --- a/doc/user/gitlab_duo/model_context_protocol/mcp_server.md +++ b/doc/user/gitlab_duo/model_context_protocol/mcp_server.md @@ -59,20 +59,57 @@ For a click-through demo, see [Duo Agent Platform - MCP server](https://gitlab.n ## Connect Cursor to a GitLab MCP server +Cursor supports two connection methods: + +- **HTTP transport (Recommended)**: Direct connection without additional dependencies. +- **stdio transport with mcp-remote**: Connection through a proxy (requires Node.js). + +### HTTP transport (Recommended) + +To configure the GitLab MCP server using HTTP transport: + +1. Open Cursor. +1. In Cursor, go to **Settings** > **Cursor Settings** > **Tools & MCP**. +1. Under **Installed MCP Servers**, select **New MCP Server**. +1. Add this definition to the `mcpServers` key in the opened `mcp.json` file: + - Replace `` with: + - On GitLab Self-Managed, your GitLab instance URL. + - On GitLab.com, `gitlab.com`. + + ```json + { + "mcpServers": { + "GitLab": { + "type": "http", + "url": "https:///api/v4/mcp" + } + } + } + ``` + +1. Save the file and restart Cursor. +1. In Cursor, go to **Settings** > **Cursor Settings** > **Tools & MCP**. +1. Under **Installed MCP Servers**, find your GitLab server and click **Connect**. +1. Your browser opens for OAuth authorization. Review and approve the authorization request. + +You can now start a new chat and ask a question depending on the available tools. + +### stdio transport with mcp-remote + Prerequisites: - Install Node.js version 20 or later. -To configure the GitLab MCP server in Cursor: +To configure the GitLab MCP server using stdio transport: 1. Open Cursor. -1. In Cursor, go to **Settings** > **Cursor Settings** > **Tools & Integrations**. -1. Under **MCP Tools**, select `New MCP Server`. +1. In Cursor, go to **Settings** > **Cursor Settings** > **Tools & MCP**. +1. Under **Installed MCP Servers**, select **New MCP Server**. 1. Add this definition to the `mcpServers` key in the opened `mcp.json` file, editing as needed: - For the `"command":` parameter, if `npx` is installed locally instead of globally, provide the full path to `npx`. - Replace `` with: - On GitLab Self-Managed, your GitLab instance URL. - - On GitLab.com, `GitLab.com`. + - On GitLab.com, `gitlab.com`. ```json { diff --git a/spec/requests/oauth/tokens_controller_spec.rb b/spec/requests/oauth/tokens_controller_spec.rb index 520025827c8286dc9768a2744ed19d9226d7d564..8796ce34d00f66fdb648e71bf1558e67ef7e055e 100644 --- a/spec/requests/oauth/tokens_controller_spec.rb +++ b/spec/requests/oauth/tokens_controller_spec.rb @@ -312,6 +312,35 @@ def authenticate(with_password) expect(response.parsed_body['error_description']).not_to include('PKCE code_verifier is required') end end + + context 'when using refresh token grant' do + let_it_be(:dynamic_oauth_application) do + create(:oauth_application, dynamic: true, confidential: false, scopes: 'mcp') + end + + let_it_be(:oauth_token) do + create( + :oauth_access_token, + application: dynamic_oauth_application, + resource_owner_id: user.id, + use_refresh_token: true, + scopes: 'mcp' + ) + end + + it 'does not require PKCE code_verifier for refresh token flow' do + post('/oauth/token', params: { + grant_type: 'refresh_token', + refresh_token: oauth_token.plaintext_refresh_token, + client_id: dynamic_oauth_application.uid, + scopes: 'mcp' + }) + + expect(response).to have_gitlab_http_status(:ok) + expect(response.parsed_body).to have_key('access_token') + expect(response.parsed_body['error']).to be_nil + end + end end context 'with non-dynamic OAuth application' do