feat(cc-config): add and remove MCP servers through the claude CLI

Mutation goes through `claude mcp add|remove`, never a direct edit of
~/.claude.json — the running CLI rewrites that file concurrently, so a
dashboard write would race it. Reads stay on readMcpServers.

The plan specified a --url flag; the real CLI (v2.1.266) has none —
commandOrUrl is positional, and --header must trail it or the variadic
flag swallows the url. Built against the verified shape.
This commit is contained in:
2026-09-09 09:15:53 +07:00
parent 22fd4797aa
commit fd4edaac98
11 changed files with 878 additions and 18 deletions
+117
View File
@@ -2094,6 +2094,21 @@ components:
description: MCP servers scoped to the current project root in ~/.claude.json.
items:
$ref: '#/components/schemas/CcConfigMcpServer'
CcConfigCliResult:
type: object
required:
- ok
- stdout
- stderr
properties:
ok:
type: boolean
enum:
- true
stdout:
type: string
stderr:
type: string
CcConfigHookEntry:
type: object
description: A single flattened hook command bound to a matcher.
@@ -7996,6 +8011,108 @@ paths:
url: https://mcp.example.com/sse
headers:
- Authorization
/api/cc-config/mcp/add:
post:
tags:
- CcConfig
summary: Add an MCP server
description: 'Mutating. Runs `claude mcp add` — never a direct edit of ~/.claude.json, which the running CLI rewrites concurrently. Body carries either `url` (+ optional `transport: http|sse` and `headers`) for a remote server, or `command` + `args` (+ optional `env`) for a stdio server; exactly one of the two forms is required. `scope` is required and is `user`|`project` only — not the settings union''s third `project-local` value, which is a different axis. A non-zero CLI exit is returned as 422, not 500. Emits a `cc_config_changed` websocket event on success.'
operationId: ccConfigPostMcpAdd
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- name
- scope
properties:
name:
type: string
example: vercel
scope:
type: string
enum:
- user
- project
example: user
transport:
type: string
enum:
- http
- sse
url:
type: string
example: https://mcp.vercel.com
headers:
type: object
additionalProperties:
type: string
example:
Authorization: Bearer xyz
command:
type: string
example: npx
args:
type: array
items:
type: string
example:
- '-y'
- server-github
env:
type: object
additionalProperties:
type: string
example:
GITHUB_TOKEN: xxx
responses:
'200':
description: The CLI's stdout/stderr on success.
content:
application/json:
schema:
$ref: '#/components/schemas/CcConfigCliResult'
'400':
description: Bad name/scope/args, or an incompatible combination.
'422':
description: The CLI exited non-zero (e.g. the name is already taken).
/api/cc-config/mcp/remove:
post:
tags:
- CcConfig
summary: Remove an MCP server
description: Mutating. Runs `claude mcp remove`. `scope` (`user`|`project`) narrows which config layer to remove from; omit it to let the CLI find the server wherever it exists. A non-zero CLI exit (e.g. the name does not exist) is returned as 422. Emits a `cc_config_changed` websocket event on success.
operationId: ccConfigPostMcpRemove
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- name
properties:
name:
type: string
example: vercel
scope:
type: string
enum:
- user
- project
responses:
'200':
description: The CLI's stdout/stderr on success.
content:
application/json:
schema:
$ref: '#/components/schemas/CcConfigCliResult'
'400':
description: Bad name/scope.
'422':
description: The CLI exited non-zero (e.g. no such server).
/api/cc-config/hooks:
get:
tags: