From ef8dbf01243e64f97b68b45f98f57db62ad4b98a Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Tue, 11 Aug 2026 11:50:14 +0000 Subject: [PATCH 1/3] docs: complete swagger annotations for organization-scoped MCP routes The moved and newly introduced MCP endpoints carried only @Summary and the apidocgen skip marker. Add the full annotation set (@ID, @Security, @Tags, params, responses, @Router) matching the ACL endpoints, and regenerate the API reference. --- coderd/apidoc/docs.go | 638 ++++++++++++++++++++++++++++++++++ coderd/apidoc/swagger.json | 580 +++++++++++++++++++++++++++++++ coderd/mcp.go | 58 ++++ docs/manifest.json | 4 + docs/reference/api/mcp.md | 6 + docs/reference/api/schemas.md | 236 +++++++++++++ 6 files changed, 1522 insertions(+) create mode 100644 docs/reference/api/mcp.md diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index e9a3a9b02c5..e7afec61b78 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1277,6 +1277,316 @@ const docTemplate = `{ ] } }, + "/api/experimental/mcp-servers/{mcpserverconfig}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "MCP" + ], + "summary": "Get MCP server config", + "operationId": "get-mcp-server-config", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "delete": { + "tags": [ + "MCP" + ], + "summary": "Delete MCP server config", + "operationId": "delete-mcp-server-config", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "patch": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "MCP" + ], + "summary": "Update MCP server config", + "operationId": "update-mcp-server-config", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + }, + { + "description": "Update MCP server config request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateMCPServerConfigRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/experimental/mcp-servers/{mcpserverconfig}/oauth2/connect": { + "get": { + "tags": [ + "MCP" + ], + "summary": "Initiate MCP server OAuth2 connect", + "operationId": "initiate-mcp-server-oauth2-connect", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + } + ], + "responses": { + "307": { + "description": "Temporary Redirect" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/experimental/mcp-servers/{mcpserverconfig}/oauth2/disconnect": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "MCP" + ], + "summary": "Disconnect MCP server OAuth2 token", + "operationId": "disconnect-mcp-server-oauth2-token", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerOAuth2DisconnectResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/experimental/mcp/servers/{mcpServer}/oauth2/callback": { + "get": { + "produces": [ + "text/html" + ], + "tags": [ + "MCP" + ], + "summary": "Handle MCP server OAuth2 callback", + "operationId": "handle-mcp-server-oauth2-callback", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpServer", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/experimental/organizations/{organization}/mcp-servers": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "MCP" + ], + "summary": "List MCP server configs", + "operationId": "list-mcp-server-configs", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "MCP" + ], + "summary": "Create MCP server config", + "operationId": "create-mcp-server-config", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "description": "Create MCP server config request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateMCPServerConfigRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, "/api/experimental/users/{user}/skills": { "get": { "produces": [ @@ -19354,6 +19664,115 @@ const docTemplate = `{ } } }, + "codersdk.CreateMCPServerConfigRequest": { + "type": "object", + "required": [ + "auth_type", + "availability", + "display_name", + "slug", + "transport", + "url" + ], + "properties": { + "allow_in_plan_mode": { + "type": "boolean" + }, + "api_key_header": { + "type": "string" + }, + "api_key_value": { + "type": "string" + }, + "auth_type": { + "type": "string", + "enum": [ + "none", + "oauth2", + "api_key", + "custom_headers", + "user_oidc" + ] + }, + "availability": { + "type": "string", + "enum": [ + "force_on", + "default_on", + "default_off" + ] + }, + "custom_headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "description": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "forward_coder_headers": { + "description": "ForwardCoderHeaders, when true, forwards Coder identity\nheaders on every outgoing MCP request. See MCPServerConfig.", + "type": "boolean" + }, + "icon_url": { + "type": "string" + }, + "model_intent": { + "type": "boolean" + }, + "oauth2_auth_url": { + "type": "string" + }, + "oauth2_client_id": { + "type": "string" + }, + "oauth2_client_secret": { + "type": "string" + }, + "oauth2_revocation_url": { + "description": "OAuth2RevocationURL is the provider's RFC 7009 revocation\nendpoint; auto-populated by OAuth2 discovery when omitted.", + "type": "string" + }, + "oauth2_scopes": { + "type": "string" + }, + "oauth2_token_url": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "tool_allow_list": { + "type": "array", + "items": { + "type": "string" + } + }, + "tool_deny_list": { + "type": "array", + "items": { + "type": "string" + } + }, + "transport": { + "type": "string", + "enum": [ + "streamable_http", + "sse" + ] + }, + "url": { + "type": "string" + } + } + }, "codersdk.CreateOrganizationRequest": { "type": "object", "required": [ @@ -21683,6 +22102,124 @@ const docTemplate = `{ } } }, + "codersdk.MCPServerConfig": { + "type": "object", + "properties": { + "allow_in_plan_mode": { + "type": "boolean" + }, + "api_key_header": { + "description": "API key fields (only populated for admins).", + "type": "string" + }, + "auth_connected": { + "description": "Per-user state (populated for non-admin requests).", + "type": "boolean" + }, + "auth_type": { + "description": "\"none\", \"oauth2\", \"api_key\", \"custom_headers\", \"user_oidc\"", + "type": "string" + }, + "availability": { + "description": "Availability policy set by admin.", + "type": "string" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "description": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "forward_coder_headers": { + "description": "ForwardCoderHeaders forwards the same Coder identity headers we\nsend to LLM providers (X-Coder-Owner-Id, X-Coder-Chat-Id, and the\noptional X-Coder-Subchat-Id and X-Coder-Workspace-Id) to this\nMCP server on every request. Off by default to avoid leaking\nchat identity to third-party servers.", + "type": "boolean" + }, + "has_api_key": { + "type": "boolean" + }, + "has_custom_headers": { + "type": "boolean" + }, + "has_oauth2_secret": { + "type": "boolean" + }, + "icon_url": { + "type": "string" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "model_intent": { + "type": "boolean" + }, + "oauth2_auth_url": { + "type": "string" + }, + "oauth2_client_id": { + "description": "OAuth2 fields (only populated for admins).", + "type": "string" + }, + "oauth2_revocation_url": { + "type": "string" + }, + "oauth2_scopes": { + "type": "string" + }, + "oauth2_token_url": { + "type": "string" + }, + "organization_id": { + "type": "string", + "format": "uuid" + }, + "slug": { + "type": "string" + }, + "tool_allow_list": { + "description": "Tool governance.", + "type": "array", + "items": { + "type": "string" + } + }, + "tool_deny_list": { + "type": "array", + "items": { + "type": "string" + } + }, + "transport": { + "description": "\"streamable_http\" or \"sse\"", + "type": "string" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "url": { + "type": "string" + } + } + }, + "codersdk.MCPServerOAuth2DisconnectResponse": { + "type": "object", + "properties": { + "token_revocation_error": { + "type": "string" + }, + "token_revoked": { + "type": "boolean" + } + } + }, "codersdk.MatchedProvisioners": { "type": "object", "properties": { @@ -26353,6 +26890,107 @@ const docTemplate = `{ } } }, + "codersdk.UpdateMCPServerConfigRequest": { + "type": "object", + "properties": { + "allow_in_plan_mode": { + "type": "boolean" + }, + "api_key_header": { + "type": "string" + }, + "api_key_value": { + "type": "string" + }, + "auth_type": { + "type": "string", + "enum": [ + "none", + "oauth2", + "api_key", + "custom_headers", + "user_oidc" + ] + }, + "availability": { + "type": "string", + "enum": [ + "force_on", + "default_on", + "default_off" + ] + }, + "custom_headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "description": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "forward_coder_headers": { + "description": "ForwardCoderHeaders, when set, updates whether Coder identity\nheaders are forwarded on every outgoing MCP request.", + "type": "boolean" + }, + "icon_url": { + "type": "string" + }, + "model_intent": { + "type": "boolean" + }, + "oauth2_auth_url": { + "type": "string" + }, + "oauth2_client_id": { + "type": "string" + }, + "oauth2_client_secret": { + "type": "string" + }, + "oauth2_revocation_url": { + "description": "OAuth2RevocationURL is validated in the handler because a\nvalidate tag would reject the pointer to \"\" that clears it.", + "type": "string" + }, + "oauth2_scopes": { + "type": "string" + }, + "oauth2_token_url": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "tool_allow_list": { + "type": "array", + "items": { + "type": "string" + } + }, + "tool_deny_list": { + "type": "array", + "items": { + "type": "string" + } + }, + "transport": { + "type": "string", + "enum": [ + "streamable_http", + "sse" + ] + }, + "url": { + "type": "string" + } + } + }, "codersdk.UpdateOrganizationRequest": { "type": "object", "properties": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index bef2266b25d..77c160c2514 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1136,6 +1136,284 @@ ] } }, + "/api/experimental/mcp-servers/{mcpserverconfig}": { + "get": { + "produces": ["application/json"], + "tags": ["MCP"], + "summary": "Get MCP server config", + "operationId": "get-mcp-server-config", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "delete": { + "tags": ["MCP"], + "summary": "Delete MCP server config", + "operationId": "delete-mcp-server-config", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "patch": { + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["MCP"], + "summary": "Update MCP server config", + "operationId": "update-mcp-server-config", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + }, + { + "description": "Update MCP server config request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateMCPServerConfigRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/experimental/mcp-servers/{mcpserverconfig}/oauth2/connect": { + "get": { + "tags": ["MCP"], + "summary": "Initiate MCP server OAuth2 connect", + "operationId": "initiate-mcp-server-oauth2-connect", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + } + ], + "responses": { + "307": { + "description": "Temporary Redirect" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/experimental/mcp-servers/{mcpserverconfig}/oauth2/disconnect": { + "delete": { + "produces": ["application/json"], + "tags": ["MCP"], + "summary": "Disconnect MCP server OAuth2 token", + "operationId": "disconnect-mcp-server-oauth2-token", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerOAuth2DisconnectResponse" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/experimental/mcp/servers/{mcpServer}/oauth2/callback": { + "get": { + "produces": ["text/html"], + "tags": ["MCP"], + "summary": "Handle MCP server OAuth2 callback", + "operationId": "handle-mcp-server-oauth2-callback", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpServer", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, + "/api/experimental/organizations/{organization}/mcp-servers": { + "get": { + "produces": ["application/json"], + "tags": ["MCP"], + "summary": "List MCP server configs", + "operationId": "list-mcp-server-configs", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + }, + "post": { + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["MCP"], + "summary": "Create MCP server config", + "operationId": "create-mcp-server-config", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + }, + { + "description": "Create MCP server config request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateMCPServerConfigRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } + } + }, + "security": [ + { + "CoderSessionToken": [] + } + ], + "x-apidocgen": { + "skip": true + } + } + }, "/api/experimental/users/{user}/skills": { "get": { "produces": ["application/json"], @@ -17518,6 +17796,102 @@ } } }, + "codersdk.CreateMCPServerConfigRequest": { + "type": "object", + "required": [ + "auth_type", + "availability", + "display_name", + "slug", + "transport", + "url" + ], + "properties": { + "allow_in_plan_mode": { + "type": "boolean" + }, + "api_key_header": { + "type": "string" + }, + "api_key_value": { + "type": "string" + }, + "auth_type": { + "type": "string", + "enum": ["none", "oauth2", "api_key", "custom_headers", "user_oidc"] + }, + "availability": { + "type": "string", + "enum": ["force_on", "default_on", "default_off"] + }, + "custom_headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "description": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "forward_coder_headers": { + "description": "ForwardCoderHeaders, when true, forwards Coder identity\nheaders on every outgoing MCP request. See MCPServerConfig.", + "type": "boolean" + }, + "icon_url": { + "type": "string" + }, + "model_intent": { + "type": "boolean" + }, + "oauth2_auth_url": { + "type": "string" + }, + "oauth2_client_id": { + "type": "string" + }, + "oauth2_client_secret": { + "type": "string" + }, + "oauth2_revocation_url": { + "description": "OAuth2RevocationURL is the provider's RFC 7009 revocation\nendpoint; auto-populated by OAuth2 discovery when omitted.", + "type": "string" + }, + "oauth2_scopes": { + "type": "string" + }, + "oauth2_token_url": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "tool_allow_list": { + "type": "array", + "items": { + "type": "string" + } + }, + "tool_deny_list": { + "type": "array", + "items": { + "type": "string" + } + }, + "transport": { + "type": "string", + "enum": ["streamable_http", "sse"] + }, + "url": { + "type": "string" + } + } + }, "codersdk.CreateOrganizationRequest": { "type": "object", "required": ["name"], @@ -19746,6 +20120,124 @@ } } }, + "codersdk.MCPServerConfig": { + "type": "object", + "properties": { + "allow_in_plan_mode": { + "type": "boolean" + }, + "api_key_header": { + "description": "API key fields (only populated for admins).", + "type": "string" + }, + "auth_connected": { + "description": "Per-user state (populated for non-admin requests).", + "type": "boolean" + }, + "auth_type": { + "description": "\"none\", \"oauth2\", \"api_key\", \"custom_headers\", \"user_oidc\"", + "type": "string" + }, + "availability": { + "description": "Availability policy set by admin.", + "type": "string" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "description": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "forward_coder_headers": { + "description": "ForwardCoderHeaders forwards the same Coder identity headers we\nsend to LLM providers (X-Coder-Owner-Id, X-Coder-Chat-Id, and the\noptional X-Coder-Subchat-Id and X-Coder-Workspace-Id) to this\nMCP server on every request. Off by default to avoid leaking\nchat identity to third-party servers.", + "type": "boolean" + }, + "has_api_key": { + "type": "boolean" + }, + "has_custom_headers": { + "type": "boolean" + }, + "has_oauth2_secret": { + "type": "boolean" + }, + "icon_url": { + "type": "string" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "model_intent": { + "type": "boolean" + }, + "oauth2_auth_url": { + "type": "string" + }, + "oauth2_client_id": { + "description": "OAuth2 fields (only populated for admins).", + "type": "string" + }, + "oauth2_revocation_url": { + "type": "string" + }, + "oauth2_scopes": { + "type": "string" + }, + "oauth2_token_url": { + "type": "string" + }, + "organization_id": { + "type": "string", + "format": "uuid" + }, + "slug": { + "type": "string" + }, + "tool_allow_list": { + "description": "Tool governance.", + "type": "array", + "items": { + "type": "string" + } + }, + "tool_deny_list": { + "type": "array", + "items": { + "type": "string" + } + }, + "transport": { + "description": "\"streamable_http\" or \"sse\"", + "type": "string" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "url": { + "type": "string" + } + } + }, + "codersdk.MCPServerOAuth2DisconnectResponse": { + "type": "object", + "properties": { + "token_revocation_error": { + "type": "string" + }, + "token_revoked": { + "type": "boolean" + } + } + }, "codersdk.MatchedProvisioners": { "type": "object", "properties": { @@ -24231,6 +24723,94 @@ } } }, + "codersdk.UpdateMCPServerConfigRequest": { + "type": "object", + "properties": { + "allow_in_plan_mode": { + "type": "boolean" + }, + "api_key_header": { + "type": "string" + }, + "api_key_value": { + "type": "string" + }, + "auth_type": { + "type": "string", + "enum": ["none", "oauth2", "api_key", "custom_headers", "user_oidc"] + }, + "availability": { + "type": "string", + "enum": ["force_on", "default_on", "default_off"] + }, + "custom_headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "description": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "forward_coder_headers": { + "description": "ForwardCoderHeaders, when set, updates whether Coder identity\nheaders are forwarded on every outgoing MCP request.", + "type": "boolean" + }, + "icon_url": { + "type": "string" + }, + "model_intent": { + "type": "boolean" + }, + "oauth2_auth_url": { + "type": "string" + }, + "oauth2_client_id": { + "type": "string" + }, + "oauth2_client_secret": { + "type": "string" + }, + "oauth2_revocation_url": { + "description": "OAuth2RevocationURL is validated in the handler because a\nvalidate tag would reject the pointer to \"\" that clears it.", + "type": "string" + }, + "oauth2_scopes": { + "type": "string" + }, + "oauth2_token_url": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "tool_allow_list": { + "type": "array", + "items": { + "type": "string" + } + }, + "tool_deny_list": { + "type": "array", + "items": { + "type": "string" + } + }, + "transport": { + "type": "string", + "enum": ["streamable_http", "sse"] + }, + "url": { + "type": "string" + } + } + }, "codersdk.UpdateOrganizationRequest": { "type": "object", "properties": { diff --git a/coderd/mcp.go b/coderd/mcp.go index 1e653dc16a9..429acf4cbed 100644 --- a/coderd/mcp.go +++ b/coderd/mcp.go @@ -144,6 +144,13 @@ func shouldRefreshOIDCToken(link database.UserLink) (bool, time.Time) { } // @Summary List MCP server configs +// @ID list-mcp-server-configs +// @Security CoderSessionToken +// @Tags MCP +// @Produce json +// @Param organization path string true "Organization ID" format(uuid) +// @Success 200 {array} codersdk.MCPServerConfig +// @Router /api/experimental/organizations/{organization}/mcp-servers [get] // @x-apidocgen {"skip": true} // EXPERIMENTAL: this endpoint is experimental and is subject to change. // @@ -224,6 +231,15 @@ func (api *API) listMCPServerConfigs(rw http.ResponseWriter, r *http.Request) { } // @Summary Create MCP server config +// @ID create-mcp-server-config +// @Security CoderSessionToken +// @Tags MCP +// @Accept json +// @Produce json +// @Param organization path string true "Organization ID" format(uuid) +// @Param request body codersdk.CreateMCPServerConfigRequest true "Create MCP server config request" +// @Success 201 {object} codersdk.MCPServerConfig +// @Router /api/experimental/organizations/{organization}/mcp-servers [post] // @x-apidocgen {"skip": true} // EXPERIMENTAL: this endpoint is experimental and is subject to change. // @@ -429,6 +445,13 @@ func (api *API) createMCPServerConfig(rw http.ResponseWriter, r *http.Request) { } // @Summary Get MCP server config +// @ID get-mcp-server-config +// @Security CoderSessionToken +// @Tags MCP +// @Produce json +// @Param mcpserverconfig path string true "MCP server config ID" format(uuid) +// @Success 200 {object} codersdk.MCPServerConfig +// @Router /api/experimental/mcp-servers/{mcpserverconfig} [get] // @x-apidocgen {"skip": true} // EXPERIMENTAL: this endpoint is experimental and is subject to change. // @@ -503,6 +526,15 @@ func (api *API) getMCPServerConfigForMutation(rw http.ResponseWriter, r *http.Re } // @Summary Update MCP server config +// @ID update-mcp-server-config +// @Security CoderSessionToken +// @Tags MCP +// @Accept json +// @Produce json +// @Param mcpserverconfig path string true "MCP server config ID" format(uuid) +// @Param request body codersdk.UpdateMCPServerConfigRequest true "Update MCP server config request" +// @Success 200 {object} codersdk.MCPServerConfig +// @Router /api/experimental/mcp-servers/{mcpserverconfig} [patch] // @x-apidocgen {"skip": true} // EXPERIMENTAL: this endpoint is experimental and is subject to change. // @@ -843,6 +875,12 @@ func (api *API) updateMCPServerConfig(rw http.ResponseWriter, r *http.Request) { } // @Summary Delete MCP server config +// @ID delete-mcp-server-config +// @Security CoderSessionToken +// @Tags MCP +// @Param mcpserverconfig path string true "MCP server config ID" format(uuid) +// @Success 204 +// @Router /api/experimental/mcp-servers/{mcpserverconfig} [delete] // @x-apidocgen {"skip": true} // EXPERIMENTAL: this endpoint is experimental and is subject to change. func (api *API) deleteMCPServerConfig(rw http.ResponseWriter, r *http.Request) { @@ -864,6 +902,12 @@ func (api *API) deleteMCPServerConfig(rw http.ResponseWriter, r *http.Request) { } // @Summary Initiate MCP server OAuth2 connect +// @ID initiate-mcp-server-oauth2-connect +// @Security CoderSessionToken +// @Tags MCP +// @Param mcpserverconfig path string true "MCP server config ID" format(uuid) +// @Success 307 +// @Router /api/experimental/mcp-servers/{mcpserverconfig}/oauth2/connect [get] // @x-apidocgen {"skip": true} // EXPERIMENTAL: this endpoint is experimental and is subject to change. // Redirects the user to the MCP server's OAuth2 authorization URL. @@ -940,6 +984,13 @@ func (api *API) mcpServerOAuth2Connect(rw http.ResponseWriter, r *http.Request) } // @Summary Handle MCP server OAuth2 callback +// @ID handle-mcp-server-oauth2-callback +// @Security CoderSessionToken +// @Tags MCP +// @Produce html +// @Param mcpServer path string true "MCP server config ID" format(uuid) +// @Success 200 +// @Router /api/experimental/mcp/servers/{mcpServer}/oauth2/callback [get] // @x-apidocgen {"skip": true} // EXPERIMENTAL: this endpoint is experimental and is subject to change. // Exchanges the authorization code for tokens and stores them. @@ -1137,6 +1188,13 @@ func (api *API) mcpServerOAuth2Callback(rw http.ResponseWriter, r *http.Request) } // @Summary Disconnect MCP server OAuth2 token +// @ID disconnect-mcp-server-oauth2-token +// @Security CoderSessionToken +// @Tags MCP +// @Produce json +// @Param mcpserverconfig path string true "MCP server config ID" format(uuid) +// @Success 200 {object} codersdk.MCPServerOAuth2DisconnectResponse +// @Router /api/experimental/mcp-servers/{mcpserverconfig}/oauth2/disconnect [delete] // @x-apidocgen {"skip": true} // EXPERIMENTAL: this endpoint is experimental and is subject to change. // Removes the user's stored OAuth2 token for an MCP server. diff --git a/docs/manifest.json b/docs/manifest.json index 221ef88347b..81b64b69a6d 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -1636,6 +1636,10 @@ "title": "Insights", "path": "./reference/api/insights.md" }, + { + "title": "MCP", + "path": "./reference/api/mcp.md" + }, { "title": "Members", "path": "./reference/api/members.md" diff --git a/docs/reference/api/mcp.md b/docs/reference/api/mcp.md new file mode 100644 index 00000000000..9feb9375878 --- /dev/null +++ b/docs/reference/api/mcp.md @@ -0,0 +1,6 @@ +--- +# Code generated by make gen. DO NOT EDIT. +title: MCP +--- + + diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index a37cadd2dd1..840463805f4 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -5087,6 +5087,80 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in | `name` | string | true | | | | `quota_allowance` | integer | false | | | +## codersdk.CreateMCPServerConfigRequest + +```json +{ + "allow_in_plan_mode": true, + "api_key_header": "string", + "api_key_value": "string", + "auth_type": "none", + "availability": "force_on", + "custom_headers": { + "property1": "string", + "property2": "string" + }, + "description": "string", + "display_name": "string", + "enabled": true, + "forward_coder_headers": true, + "icon_url": "string", + "model_intent": true, + "oauth2_auth_url": "string", + "oauth2_client_id": "string", + "oauth2_client_secret": "string", + "oauth2_revocation_url": "string", + "oauth2_scopes": "string", + "oauth2_token_url": "string", + "slug": "string", + "tool_allow_list": [ + "string" + ], + "tool_deny_list": [ + "string" + ], + "transport": "streamable_http", + "url": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|-------------------------|-----------------|----------|--------------|------------------------------------------------------------------------------------------------------------------------| +| `allow_in_plan_mode` | boolean | false | | | +| `api_key_header` | string | false | | | +| `api_key_value` | string | false | | | +| `auth_type` | string | true | | | +| `availability` | string | true | | | +| `custom_headers` | object | false | | | +| » `[any property]` | string | false | | | +| `description` | string | false | | | +| `display_name` | string | true | | | +| `enabled` | boolean | false | | | +| `forward_coder_headers` | boolean | false | | Forward coder headers when true, forwards Coder identity headers on every outgoing MCP request. See MCPServerConfig. | +| `icon_url` | string | false | | | +| `model_intent` | boolean | false | | | +| `oauth2_auth_url` | string | false | | | +| `oauth2_client_id` | string | false | | | +| `oauth2_client_secret` | string | false | | | +| `oauth2_revocation_url` | string | false | | Oauth2 revocation URL is the provider's RFC 7009 revocation endpoint; auto-populated by OAuth2 discovery when omitted. | +| `oauth2_scopes` | string | false | | | +| `oauth2_token_url` | string | false | | | +| `slug` | string | true | | | +| `tool_allow_list` | array of string | false | | | +| `tool_deny_list` | array of string | false | | | +| `transport` | string | true | | | +| `url` | string | true | | | + +#### Enumerated Values + +| Property | Value(s) | +|----------------|------------------------------------------------------------| +| `auth_type` | `api_key`, `custom_headers`, `none`, `oauth2`, `user_oidc` | +| `availability` | `default_off`, `default_on`, `force_on` | +| `transport` | `sse`, `streamable_http` | + ## codersdk.CreateOrganizationRequest ```json @@ -8698,6 +8772,94 @@ Git clone makes use of this by parsing the URL from: 'Username for "https://gith |-----------------|--------|----------|--------------|-------------| | `session_token` | string | true | | | +## codersdk.MCPServerConfig + +```json +{ + "allow_in_plan_mode": true, + "api_key_header": "string", + "auth_connected": true, + "auth_type": "string", + "availability": "string", + "created_at": "2019-08-24T14:15:22Z", + "description": "string", + "display_name": "string", + "enabled": true, + "forward_coder_headers": true, + "has_api_key": true, + "has_custom_headers": true, + "has_oauth2_secret": true, + "icon_url": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "model_intent": true, + "oauth2_auth_url": "string", + "oauth2_client_id": "string", + "oauth2_revocation_url": "string", + "oauth2_scopes": "string", + "oauth2_token_url": "string", + "organization_id": "7c60d51f-b44e-4682-87d6-449835ea4de6", + "slug": "string", + "tool_allow_list": [ + "string" + ], + "tool_deny_list": [ + "string" + ], + "transport": "string", + "updated_at": "2019-08-24T14:15:22Z", + "url": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|-------------------------|-----------------|----------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `allow_in_plan_mode` | boolean | false | | | +| `api_key_header` | string | false | | Api key header key fields (only populated for admins). | +| `auth_connected` | boolean | false | | Per-user state (populated for non-admin requests). | +| `auth_type` | string | false | | "none", "oauth2", "api_key", "custom_headers", "user_oidc" | +| `availability` | string | false | | Availability policy set by admin. | +| `created_at` | string | false | | | +| `description` | string | false | | | +| `display_name` | string | false | | | +| `enabled` | boolean | false | | | +| `forward_coder_headers` | boolean | false | | Forward coder headers forwards the same Coder identity headers we send to LLM providers (X-Coder-Owner-Id, X-Coder-Chat-Id, and the optional X-Coder-Subchat-Id and X-Coder-Workspace-Id) to this MCP server on every request. Off by default to avoid leaking chat identity to third-party servers. | +| `has_api_key` | boolean | false | | | +| `has_custom_headers` | boolean | false | | | +| `has_oauth2_secret` | boolean | false | | | +| `icon_url` | string | false | | | +| `id` | string | false | | | +| `model_intent` | boolean | false | | | +| `oauth2_auth_url` | string | false | | | +| `oauth2_client_id` | string | false | | Oauth2 client ID fields (only populated for admins). | +| `oauth2_revocation_url` | string | false | | | +| `oauth2_scopes` | string | false | | | +| `oauth2_token_url` | string | false | | | +| `organization_id` | string | false | | | +| `slug` | string | false | | | +| `tool_allow_list` | array of string | false | | Tool governance. | +| `tool_deny_list` | array of string | false | | | +| `transport` | string | false | | "streamable_http" or "sse" | +| `updated_at` | string | false | | | +| `url` | string | false | | | + +## codersdk.MCPServerOAuth2DisconnectResponse + +```json +{ + "token_revocation_error": "string", + "token_revoked": true +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|--------------------------|---------|----------|--------------|-------------| +| `token_revocation_error` | string | false | | | +| `token_revoked` | boolean | false | | | + ## codersdk.MatchedProvisioners ```json @@ -14089,6 +14251,80 @@ Restarts will only happen on weekdays in this list on weeks which line up with W | `url` | string | false | | URL to download the latest release of Coder. | | `version` | string | false | | Version is the semantic version for the latest release of Coder. | +## codersdk.UpdateMCPServerConfigRequest + +```json +{ + "allow_in_plan_mode": true, + "api_key_header": "string", + "api_key_value": "string", + "auth_type": "none", + "availability": "force_on", + "custom_headers": { + "property1": "string", + "property2": "string" + }, + "description": "string", + "display_name": "string", + "enabled": true, + "forward_coder_headers": true, + "icon_url": "string", + "model_intent": true, + "oauth2_auth_url": "string", + "oauth2_client_id": "string", + "oauth2_client_secret": "string", + "oauth2_revocation_url": "string", + "oauth2_scopes": "string", + "oauth2_token_url": "string", + "slug": "string", + "tool_allow_list": [ + "string" + ], + "tool_deny_list": [ + "string" + ], + "transport": "streamable_http", + "url": "string" +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|-------------------------|-----------------|----------|--------------|-------------------------------------------------------------------------------------------------------------------------| +| `allow_in_plan_mode` | boolean | false | | | +| `api_key_header` | string | false | | | +| `api_key_value` | string | false | | | +| `auth_type` | string | false | | | +| `availability` | string | false | | | +| `custom_headers` | object | false | | | +| » `[any property]` | string | false | | | +| `description` | string | false | | | +| `display_name` | string | false | | | +| `enabled` | boolean | false | | | +| `forward_coder_headers` | boolean | false | | Forward coder headers when set, updates whether Coder identity headers are forwarded on every outgoing MCP request. | +| `icon_url` | string | false | | | +| `model_intent` | boolean | false | | | +| `oauth2_auth_url` | string | false | | | +| `oauth2_client_id` | string | false | | | +| `oauth2_client_secret` | string | false | | | +| `oauth2_revocation_url` | string | false | | Oauth2 revocation URL is validated in the handler because a validate tag would reject the pointer to "" that clears it. | +| `oauth2_scopes` | string | false | | | +| `oauth2_token_url` | string | false | | | +| `slug` | string | false | | | +| `tool_allow_list` | array of string | false | | | +| `tool_deny_list` | array of string | false | | | +| `transport` | string | false | | | +| `url` | string | false | | | + +#### Enumerated Values + +| Property | Value(s) | +|----------------|------------------------------------------------------------| +| `auth_type` | `api_key`, `custom_headers`, `none`, `oauth2`, `user_oidc` | +| `availability` | `default_off`, `default_on`, `force_on` | +| `transport` | `sse`, `streamable_http` | + ## codersdk.UpdateOrganizationRequest ```json From 2e9fc24f0a6f4dadb1fe9d7f2b18a0bbfcc2b825 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Fri, 14 Aug 2026 01:37:37 +0000 Subject: [PATCH 2/3] docs(coderd): document MCP OAuth callback query parameters --- coderd/apidoc/docs.go | 24 ++++++++++++++++++++++++ coderd/apidoc/swagger.json | 24 ++++++++++++++++++++++++ coderd/mcp.go | 4 ++++ 3 files changed, 52 insertions(+) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index e7afec61b78..2bfba739e32 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1481,6 +1481,30 @@ const docTemplate = `{ "name": "mcpServer", "in": "path", "required": true + }, + { + "type": "string", + "description": "Authorization code issued by the provider. Required together with state on success.", + "name": "code", + "in": "query" + }, + { + "type": "string", + "description": "Opaque state issued by the connect endpoint. Required together with code on success.", + "name": "state", + "in": "query" + }, + { + "type": "string", + "description": "Provider error code. Present instead of code when authorization fails.", + "name": "error", + "in": "query" + }, + { + "type": "string", + "description": "Provider error description accompanying error.", + "name": "error_description", + "in": "query" } ], "responses": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 77c160c2514..b9e2d5c441b 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1318,6 +1318,30 @@ "name": "mcpServer", "in": "path", "required": true + }, + { + "type": "string", + "description": "Authorization code issued by the provider. Required together with state on success.", + "name": "code", + "in": "query" + }, + { + "type": "string", + "description": "Opaque state issued by the connect endpoint. Required together with code on success.", + "name": "state", + "in": "query" + }, + { + "type": "string", + "description": "Provider error code. Present instead of code when authorization fails.", + "name": "error", + "in": "query" + }, + { + "type": "string", + "description": "Provider error description accompanying error.", + "name": "error_description", + "in": "query" } ], "responses": { diff --git a/coderd/mcp.go b/coderd/mcp.go index 429acf4cbed..d14535867f7 100644 --- a/coderd/mcp.go +++ b/coderd/mcp.go @@ -989,6 +989,10 @@ func (api *API) mcpServerOAuth2Connect(rw http.ResponseWriter, r *http.Request) // @Tags MCP // @Produce html // @Param mcpServer path string true "MCP server config ID" format(uuid) +// @Param code query string false "Authorization code issued by the provider. Required together with state on success." +// @Param state query string false "Opaque state issued by the connect endpoint. Required together with code on success." +// @Param error query string false "Provider error code. Present instead of code when authorization fails." +// @Param error_description query string false "Provider error description accompanying error." // @Success 200 // @Router /api/experimental/mcp/servers/{mcpServer}/oauth2/callback [get] // @x-apidocgen {"skip": true} From 6b2d4cecdc2a7472ea1bbd1fae17cd42b6d2158f Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Mon, 17 Aug 2026 10:27:31 +0000 Subject: [PATCH 3/3] docs: align MCP swagger annotations with organization-nested routes --- coderd/apidoc/docs.go | 262 +++++++++++++++++++++---------------- coderd/apidoc/swagger.json | 246 +++++++++++++++++++--------------- coderd/mcp.go | 16 ++- 3 files changed, 296 insertions(+), 228 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 2bfba739e32..bcdf54ed3ad 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -1277,32 +1277,53 @@ const docTemplate = `{ ] } }, - "/api/experimental/mcp-servers/{mcpserverconfig}": { + "/api/experimental/mcp/servers/{mcpServer}/oauth2/callback": { "get": { "produces": [ - "application/json" + "text/html" ], "tags": [ "MCP" ], - "summary": "Get MCP server config", - "operationId": "get-mcp-server-config", + "summary": "Handle MCP server OAuth2 callback", + "operationId": "handle-mcp-server-oauth2-callback", "parameters": [ { "type": "string", "format": "uuid", "description": "MCP server config ID", - "name": "mcpserverconfig", + "name": "mcpServer", "in": "path", "required": true + }, + { + "type": "string", + "description": "Authorization code issued by the provider. Required together with state on success.", + "name": "code", + "in": "query" + }, + { + "type": "string", + "description": "Opaque state issued by the connect endpoint. Required together with code on success.", + "name": "state", + "in": "query" + }, + { + "type": "string", + "description": "Provider error code. Present instead of code when authorization fails.", + "name": "error", + "in": "query" + }, + { + "type": "string", + "description": "Provider error description accompanying error.", + "name": "error_description", + "in": "query" } ], "responses": { "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfig" - } + "description": "OK" } }, "security": [ @@ -1313,26 +1334,34 @@ const docTemplate = `{ "x-apidocgen": { "skip": true } - }, + } + }, + "/api/experimental/mcp/servers/{mcpServer}/oauth2/disconnect": { "delete": { + "produces": [ + "application/json" + ], "tags": [ "MCP" ], - "summary": "Delete MCP server config", - "operationId": "delete-mcp-server-config", + "summary": "Disconnect MCP server OAuth2 token", + "operationId": "disconnect-mcp-server-oauth2-token", "parameters": [ { "type": "string", "format": "uuid", "description": "MCP server config ID", - "name": "mcpserverconfig", + "name": "mcpServer", "in": "path", "required": true } ], "responses": { - "204": { - "description": "No Content" + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerOAuth2DisconnectResponse" + } } }, "security": [ @@ -1343,43 +1372,36 @@ const docTemplate = `{ "x-apidocgen": { "skip": true } - }, - "patch": { - "consumes": [ - "application/json" - ], + } + }, + "/api/experimental/organizations/{organization}/mcp-servers": { + "get": { "produces": [ "application/json" ], "tags": [ "MCP" ], - "summary": "Update MCP server config", - "operationId": "update-mcp-server-config", + "summary": "List MCP server configs", + "operationId": "list-mcp-server-configs", "parameters": [ { "type": "string", "format": "uuid", - "description": "MCP server config ID", - "name": "mcpserverconfig", + "description": "Organization ID", + "name": "organization", "in": "path", "required": true - }, - { - "description": "Update MCP server config request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateMCPServerConfigRequest" - } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfig" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } } } }, @@ -1391,28 +1413,44 @@ const docTemplate = `{ "x-apidocgen": { "skip": true } - } - }, - "/api/experimental/mcp-servers/{mcpserverconfig}/oauth2/connect": { - "get": { + }, + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], "tags": [ "MCP" ], - "summary": "Initiate MCP server OAuth2 connect", - "operationId": "initiate-mcp-server-oauth2-connect", + "summary": "Create MCP server config", + "operationId": "create-mcp-server-config", "parameters": [ { "type": "string", "format": "uuid", - "description": "MCP server config ID", - "name": "mcpserverconfig", + "description": "Organization ID", + "name": "organization", "in": "path", "required": true + }, + { + "description": "Create MCP server config request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateMCPServerConfigRequest" + } } ], "responses": { - "307": { - "description": "Temporary Redirect" + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } } }, "security": [ @@ -1425,17 +1463,25 @@ const docTemplate = `{ } } }, - "/api/experimental/mcp-servers/{mcpserverconfig}/oauth2/disconnect": { - "delete": { + "/api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}": { + "get": { "produces": [ "application/json" ], "tags": [ "MCP" ], - "summary": "Disconnect MCP server OAuth2 token", - "operationId": "disconnect-mcp-server-oauth2-token", + "summary": "Get MCP server config", + "operationId": "get-mcp-server-config", "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + }, { "type": "string", "format": "uuid", @@ -1449,7 +1495,7 @@ const docTemplate = `{ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.MCPServerOAuth2DisconnectResponse" + "$ref": "#/definitions/codersdk.MCPServerConfig" } } }, @@ -1461,55 +1507,34 @@ const docTemplate = `{ "x-apidocgen": { "skip": true } - } - }, - "/api/experimental/mcp/servers/{mcpServer}/oauth2/callback": { - "get": { - "produces": [ - "text/html" - ], + }, + "delete": { "tags": [ "MCP" ], - "summary": "Handle MCP server OAuth2 callback", - "operationId": "handle-mcp-server-oauth2-callback", + "summary": "Delete MCP server config", + "operationId": "delete-mcp-server-config", "parameters": [ { "type": "string", "format": "uuid", - "description": "MCP server config ID", - "name": "mcpServer", + "description": "Organization ID", + "name": "organization", "in": "path", "required": true }, { "type": "string", - "description": "Authorization code issued by the provider. Required together with state on success.", - "name": "code", - "in": "query" - }, - { - "type": "string", - "description": "Opaque state issued by the connect endpoint. Required together with code on success.", - "name": "state", - "in": "query" - }, - { - "type": "string", - "description": "Provider error code. Present instead of code when authorization fails.", - "name": "error", - "in": "query" - }, - { - "type": "string", - "description": "Provider error description accompanying error.", - "name": "error_description", - "in": "query" + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true } ], "responses": { - "200": { - "description": "OK" + "204": { + "description": "No Content" } }, "security": [ @@ -1520,18 +1545,19 @@ const docTemplate = `{ "x-apidocgen": { "skip": true } - } - }, - "/api/experimental/organizations/{organization}/mcp-servers": { - "get": { + }, + "patch": { + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ "MCP" ], - "summary": "List MCP server configs", - "operationId": "list-mcp-server-configs", + "summary": "Update MCP server config", + "operationId": "update-mcp-server-config", "parameters": [ { "type": "string", @@ -1540,16 +1566,30 @@ const docTemplate = `{ "name": "organization", "in": "path", "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + }, + { + "description": "Update MCP server config request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateMCPServerConfigRequest" + } } ], "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.MCPServerConfig" - } + "$ref": "#/definitions/codersdk.MCPServerConfig" } } }, @@ -1561,19 +1601,15 @@ const docTemplate = `{ "x-apidocgen": { "skip": true } - }, - "post": { - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], + } + }, + "/api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect": { + "get": { "tags": [ "MCP" ], - "summary": "Create MCP server config", - "operationId": "create-mcp-server-config", + "summary": "Initiate MCP server OAuth2 connect", + "operationId": "initiate-mcp-server-oauth2-connect", "parameters": [ { "type": "string", @@ -1584,21 +1620,17 @@ const docTemplate = `{ "required": true }, { - "description": "Create MCP server config request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.CreateMCPServerConfigRequest" - } + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true } ], "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfig" - } + "307": { + "description": "Temporary Redirect" } }, "security": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index b9e2d5c441b..a05523cc8d5 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -1136,28 +1136,49 @@ ] } }, - "/api/experimental/mcp-servers/{mcpserverconfig}": { + "/api/experimental/mcp/servers/{mcpServer}/oauth2/callback": { "get": { - "produces": ["application/json"], + "produces": ["text/html"], "tags": ["MCP"], - "summary": "Get MCP server config", - "operationId": "get-mcp-server-config", + "summary": "Handle MCP server OAuth2 callback", + "operationId": "handle-mcp-server-oauth2-callback", "parameters": [ { "type": "string", "format": "uuid", "description": "MCP server config ID", - "name": "mcpserverconfig", + "name": "mcpServer", "in": "path", "required": true + }, + { + "type": "string", + "description": "Authorization code issued by the provider. Required together with state on success.", + "name": "code", + "in": "query" + }, + { + "type": "string", + "description": "Opaque state issued by the connect endpoint. Required together with code on success.", + "name": "state", + "in": "query" + }, + { + "type": "string", + "description": "Provider error code. Present instead of code when authorization fails.", + "name": "error", + "in": "query" + }, + { + "type": "string", + "description": "Provider error description accompanying error.", + "name": "error_description", + "in": "query" } ], "responses": { "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfig" - } + "description": "OK" } }, "security": [ @@ -1168,24 +1189,30 @@ "x-apidocgen": { "skip": true } - }, + } + }, + "/api/experimental/mcp/servers/{mcpServer}/oauth2/disconnect": { "delete": { + "produces": ["application/json"], "tags": ["MCP"], - "summary": "Delete MCP server config", - "operationId": "delete-mcp-server-config", + "summary": "Disconnect MCP server OAuth2 token", + "operationId": "disconnect-mcp-server-oauth2-token", "parameters": [ { "type": "string", "format": "uuid", "description": "MCP server config ID", - "name": "mcpserverconfig", + "name": "mcpServer", "in": "path", "required": true } ], "responses": { - "204": { - "description": "No Content" + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerOAuth2DisconnectResponse" + } } }, "security": [ @@ -1196,37 +1223,32 @@ "x-apidocgen": { "skip": true } - }, - "patch": { - "consumes": ["application/json"], + } + }, + "/api/experimental/organizations/{organization}/mcp-servers": { + "get": { "produces": ["application/json"], "tags": ["MCP"], - "summary": "Update MCP server config", - "operationId": "update-mcp-server-config", + "summary": "List MCP server configs", + "operationId": "list-mcp-server-configs", "parameters": [ { "type": "string", "format": "uuid", - "description": "MCP server config ID", - "name": "mcpserverconfig", + "description": "Organization ID", + "name": "organization", "in": "path", "required": true - }, - { - "description": "Update MCP server config request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.UpdateMCPServerConfigRequest" - } } ], "responses": { "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfig" + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } } } }, @@ -1238,26 +1260,38 @@ "x-apidocgen": { "skip": true } - } - }, - "/api/experimental/mcp-servers/{mcpserverconfig}/oauth2/connect": { - "get": { + }, + "post": { + "consumes": ["application/json"], + "produces": ["application/json"], "tags": ["MCP"], - "summary": "Initiate MCP server OAuth2 connect", - "operationId": "initiate-mcp-server-oauth2-connect", + "summary": "Create MCP server config", + "operationId": "create-mcp-server-config", "parameters": [ { "type": "string", "format": "uuid", - "description": "MCP server config ID", - "name": "mcpserverconfig", + "description": "Organization ID", + "name": "organization", "in": "path", "required": true + }, + { + "description": "Create MCP server config request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.CreateMCPServerConfigRequest" + } } ], "responses": { - "307": { - "description": "Temporary Redirect" + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/codersdk.MCPServerConfig" + } } }, "security": [ @@ -1270,13 +1304,21 @@ } } }, - "/api/experimental/mcp-servers/{mcpserverconfig}/oauth2/disconnect": { - "delete": { + "/api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}": { + "get": { "produces": ["application/json"], "tags": ["MCP"], - "summary": "Disconnect MCP server OAuth2 token", - "operationId": "disconnect-mcp-server-oauth2-token", + "summary": "Get MCP server config", + "operationId": "get-mcp-server-config", "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Organization ID", + "name": "organization", + "in": "path", + "required": true + }, { "type": "string", "format": "uuid", @@ -1290,7 +1332,7 @@ "200": { "description": "OK", "schema": { - "$ref": "#/definitions/codersdk.MCPServerOAuth2DisconnectResponse" + "$ref": "#/definitions/codersdk.MCPServerConfig" } } }, @@ -1302,51 +1344,32 @@ "x-apidocgen": { "skip": true } - } - }, - "/api/experimental/mcp/servers/{mcpServer}/oauth2/callback": { - "get": { - "produces": ["text/html"], + }, + "delete": { "tags": ["MCP"], - "summary": "Handle MCP server OAuth2 callback", - "operationId": "handle-mcp-server-oauth2-callback", + "summary": "Delete MCP server config", + "operationId": "delete-mcp-server-config", "parameters": [ { "type": "string", "format": "uuid", - "description": "MCP server config ID", - "name": "mcpServer", + "description": "Organization ID", + "name": "organization", "in": "path", "required": true }, { "type": "string", - "description": "Authorization code issued by the provider. Required together with state on success.", - "name": "code", - "in": "query" - }, - { - "type": "string", - "description": "Opaque state issued by the connect endpoint. Required together with code on success.", - "name": "state", - "in": "query" - }, - { - "type": "string", - "description": "Provider error code. Present instead of code when authorization fails.", - "name": "error", - "in": "query" - }, - { - "type": "string", - "description": "Provider error description accompanying error.", - "name": "error_description", - "in": "query" + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true } ], "responses": { - "200": { - "description": "OK" + "204": { + "description": "No Content" } }, "security": [ @@ -1357,14 +1380,13 @@ "x-apidocgen": { "skip": true } - } - }, - "/api/experimental/organizations/{organization}/mcp-servers": { - "get": { + }, + "patch": { + "consumes": ["application/json"], "produces": ["application/json"], "tags": ["MCP"], - "summary": "List MCP server configs", - "operationId": "list-mcp-server-configs", + "summary": "Update MCP server config", + "operationId": "update-mcp-server-config", "parameters": [ { "type": "string", @@ -1373,16 +1395,30 @@ "name": "organization", "in": "path", "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true + }, + { + "description": "Update MCP server config request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/codersdk.UpdateMCPServerConfigRequest" + } } ], "responses": { "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/codersdk.MCPServerConfig" - } + "$ref": "#/definitions/codersdk.MCPServerConfig" } } }, @@ -1394,13 +1430,13 @@ "x-apidocgen": { "skip": true } - }, - "post": { - "consumes": ["application/json"], - "produces": ["application/json"], + } + }, + "/api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect": { + "get": { "tags": ["MCP"], - "summary": "Create MCP server config", - "operationId": "create-mcp-server-config", + "summary": "Initiate MCP server OAuth2 connect", + "operationId": "initiate-mcp-server-oauth2-connect", "parameters": [ { "type": "string", @@ -1411,21 +1447,17 @@ "required": true }, { - "description": "Create MCP server config request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/codersdk.CreateMCPServerConfigRequest" - } + "type": "string", + "format": "uuid", + "description": "MCP server config ID", + "name": "mcpserverconfig", + "in": "path", + "required": true } ], "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/codersdk.MCPServerConfig" - } + "307": { + "description": "Temporary Redirect" } }, "security": [ diff --git a/coderd/mcp.go b/coderd/mcp.go index d14535867f7..70074189aad 100644 --- a/coderd/mcp.go +++ b/coderd/mcp.go @@ -449,9 +449,10 @@ func (api *API) createMCPServerConfig(rw http.ResponseWriter, r *http.Request) { // @Security CoderSessionToken // @Tags MCP // @Produce json +// @Param organization path string true "Organization ID" format(uuid) // @Param mcpserverconfig path string true "MCP server config ID" format(uuid) // @Success 200 {object} codersdk.MCPServerConfig -// @Router /api/experimental/mcp-servers/{mcpserverconfig} [get] +// @Router /api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig} [get] // @x-apidocgen {"skip": true} // EXPERIMENTAL: this endpoint is experimental and is subject to change. // @@ -531,10 +532,11 @@ func (api *API) getMCPServerConfigForMutation(rw http.ResponseWriter, r *http.Re // @Tags MCP // @Accept json // @Produce json +// @Param organization path string true "Organization ID" format(uuid) // @Param mcpserverconfig path string true "MCP server config ID" format(uuid) // @Param request body codersdk.UpdateMCPServerConfigRequest true "Update MCP server config request" // @Success 200 {object} codersdk.MCPServerConfig -// @Router /api/experimental/mcp-servers/{mcpserverconfig} [patch] +// @Router /api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig} [patch] // @x-apidocgen {"skip": true} // EXPERIMENTAL: this endpoint is experimental and is subject to change. // @@ -878,9 +880,10 @@ func (api *API) updateMCPServerConfig(rw http.ResponseWriter, r *http.Request) { // @ID delete-mcp-server-config // @Security CoderSessionToken // @Tags MCP +// @Param organization path string true "Organization ID" format(uuid) // @Param mcpserverconfig path string true "MCP server config ID" format(uuid) // @Success 204 -// @Router /api/experimental/mcp-servers/{mcpserverconfig} [delete] +// @Router /api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig} [delete] // @x-apidocgen {"skip": true} // EXPERIMENTAL: this endpoint is experimental and is subject to change. func (api *API) deleteMCPServerConfig(rw http.ResponseWriter, r *http.Request) { @@ -905,9 +908,10 @@ func (api *API) deleteMCPServerConfig(rw http.ResponseWriter, r *http.Request) { // @ID initiate-mcp-server-oauth2-connect // @Security CoderSessionToken // @Tags MCP +// @Param organization path string true "Organization ID" format(uuid) // @Param mcpserverconfig path string true "MCP server config ID" format(uuid) // @Success 307 -// @Router /api/experimental/mcp-servers/{mcpserverconfig}/oauth2/connect [get] +// @Router /api/experimental/organizations/{organization}/mcp-servers/{mcpserverconfig}/oauth2/connect [get] // @x-apidocgen {"skip": true} // EXPERIMENTAL: this endpoint is experimental and is subject to change. // Redirects the user to the MCP server's OAuth2 authorization URL. @@ -1196,9 +1200,9 @@ func (api *API) mcpServerOAuth2Callback(rw http.ResponseWriter, r *http.Request) // @Security CoderSessionToken // @Tags MCP // @Produce json -// @Param mcpserverconfig path string true "MCP server config ID" format(uuid) +// @Param mcpServer path string true "MCP server config ID" format(uuid) // @Success 200 {object} codersdk.MCPServerOAuth2DisconnectResponse -// @Router /api/experimental/mcp-servers/{mcpserverconfig}/oauth2/disconnect [delete] +// @Router /api/experimental/mcp/servers/{mcpServer}/oauth2/disconnect [delete] // @x-apidocgen {"skip": true} // EXPERIMENTAL: this endpoint is experimental and is subject to change. // Removes the user's stored OAuth2 token for an MCP server.