mirror of
https://github.com/vatesfr/xen-orchestra.git
synced 2026-11-09 17:06:13 -06:00
Replace the hard-coded CONFIRM_PATTERNS list in the MCP server with a per-endpoint exposure decision carried by an x-mcp-exposure OpenAPI vendor extension. Each REST endpoint now declares its MCP exposure (allow / confirm / deny) with @Extension('x-mcp-exposure', ...) next to its HTTP method decorator. A new local ESLint rule mcp/require-mcp-expose makes the decision mandatory: any new @Get/@Post/@Put/@Patch/@Delete method without an exposure tag fails lint.
The MCP parseSwagger now reads the extension and applies a default-deny policy:
'allow' → exposed as a query/action tool
'confirm' → exposed only when XO_MCP_ENABLE_ACTIONS=1, with confirm-token round-trip
'deny' / absent → never exposed
Defense in depth is preserved: route-filter regex (binaries, streams, stats) is applied before the exposure check, so a binary endpoint mistakenly tagged 'allow' still stays out of the MCP surface. The confirm_token UUID + TTL machinery in risk-config.mts is untouched.
All 248 endpoints across 34 controllers have been annotated with conservative defaults (GET → allow, destructive POST/DELETE/PUT/PATCH → confirm, binary exports → deny).
96 lines
2.3 KiB
JSON
96 lines
2.3 KiB
JSON
{
|
|
"entryFile": "./src/index.mts",
|
|
"noImplicitAdditionalProperties": "throw-on-extras",
|
|
"controllerPathGlobs": ["./src/**/*.controller.mts"],
|
|
"spec": {
|
|
"outputDirectory": "./open-api/spec",
|
|
"specVersion": 3,
|
|
"basePath": "/rest/v0",
|
|
"securityDefinitions": {
|
|
"token": {
|
|
"type": "apiKey",
|
|
"name": "authenticationToken",
|
|
"in": "cookie"
|
|
},
|
|
"basic": {
|
|
"type": "http",
|
|
"scheme": "basic"
|
|
}
|
|
},
|
|
"specMerging": "recursive",
|
|
"spec": {
|
|
"paths": {
|
|
"/pools/{id}/vms": {
|
|
"post": {
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/octet-stream": {
|
|
"schema": {
|
|
"type": "string",
|
|
"format": "binary"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/srs/{id}/vdis": {
|
|
"post": {
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/octet-stream": {
|
|
"schema": {
|
|
"type": "string",
|
|
"format": "binary"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/vdis/{id}.{format}": {
|
|
"put": {
|
|
"requestBody": {
|
|
"required": true,
|
|
"content": {
|
|
"application/octet-stream": {
|
|
"schema": {
|
|
"type": "string",
|
|
"format": "binary"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"/docs/swagger.json": {
|
|
"get": {
|
|
"operationId": "swaggerSpec",
|
|
"description": "Returns the swagger json ready to use for REST API clients",
|
|
"responses": {
|
|
"200": {
|
|
"description": "Ok"
|
|
}
|
|
},
|
|
"tags": ["docs"],
|
|
"x-mcp-exposure": "deny"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"routes": {
|
|
"routesDir": "./src/open-api/routes",
|
|
"esm": true,
|
|
"middleware": "express",
|
|
"basePath": "/rest/v0",
|
|
"authenticationModule": "./src/middlewares/authentication.middleware.mts",
|
|
"iocModule": "./src/ioc/ioc.mts"
|
|
},
|
|
"compilerOptions": {
|
|
"strictNullChecks": true
|
|
}
|
|
}
|