@xen-orchestra/rest-api 0.31.1 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/acl-privileges/acl-privilege.controller.mjs +7 -2
  2. package/dist/acl-roles/acl-role.controller.mjs +13 -2
  3. package/dist/alarms/alarm.controller.mjs +3 -1
  4. package/dist/backup-archives/backup-archive.controller.mjs +3 -1
  5. package/dist/backup-jobs/backup-job.controller.mjs +12 -1
  6. package/dist/backup-logs/backup-log.controller.mjs +3 -1
  7. package/dist/backup-repositories/backup-repositories.controller.mjs +3 -1
  8. package/dist/events/event.controller.mjs +4 -1
  9. package/dist/groups/group.controller.mjs +10 -1
  10. package/dist/hosts/host.controller.mjs +16 -1
  11. package/dist/index.mjs +2 -0
  12. package/dist/mcp/mcp.controller.mjs +59 -0
  13. package/dist/mcp/mcp.helper.mjs +11 -0
  14. package/dist/messages/message.controller.mjs +3 -1
  15. package/dist/middlewares/mcp-gate.middleware.mjs +30 -0
  16. package/dist/networks/network.controller.mjs +9 -1
  17. package/dist/open-api/routes/routes.js +33 -1
  18. package/dist/pbds/pbd.controller.mjs +5 -1
  19. package/dist/pcis/pci.controller.mjs +3 -1
  20. package/dist/pgpus/pgpu.controller.mjs +3 -1
  21. package/dist/pifs/pif.controller.mjs +6 -1
  22. package/dist/pools/pool.controller.mjs +20 -1
  23. package/dist/proxies/proxy.controller.mjs +3 -1
  24. package/dist/restore-logs/restore-log.controller.mjs +5 -1
  25. package/dist/schedules/schedule.controller.mjs +4 -1
  26. package/dist/servers/server.controller.mjs +8 -1
  27. package/dist/sms/sm.controller.mjs +3 -1
  28. package/dist/srs/sr.controller.mjs +13 -1
  29. package/dist/tasks/task.controller.mjs +6 -1
  30. package/dist/users/user.controller.mjs +13 -2
  31. package/dist/vbds/vbd.controller.mjs +10 -1
  32. package/dist/vdi-snapshots/vdi-snapshot.controller.mjs +10 -1
  33. package/dist/vdis/vdi.controller.mjs +13 -1
  34. package/dist/vifs/vif.controller.mjs +10 -1
  35. package/dist/vm-controller/vm-controller.controller.mjs +9 -1
  36. package/dist/vm-snapshots/vm-snapshot.controller.mjs +11 -1
  37. package/dist/vm-templates/vm-template.controller.mjs +11 -1
  38. package/dist/vms/vm.controller.mjs +29 -1
  39. package/dist/xoa/xoa.controller.mjs +4 -1
  40. package/eslint-rules/index.cjs +7 -0
  41. package/eslint-rules/require-mcp-expose.cjs +129 -0
  42. package/open-api/spec/swagger.json +563 -264
  43. package/package.json +2 -2
  44. package/tsoa.json +2 -1
@@ -0,0 +1,11 @@
1
+ // Single source of truth for reading the MCP kill-switch flag. Defaults to
2
+ // `true` so legacy configs without a `[mcp]` section keep MCP enabled.
3
+ export function isMcpEnabled(restApi) {
4
+ return restApi.xoApp.config.getOptional('mcp.enabled') ?? true;
5
+ }
6
+ // Wire-level identifier used by the kill-switch contract; shared between the
7
+ // gate middleware (`@xen-orchestra/rest-api`) and the controller response.
8
+ export const MCP_DISABLED_ERROR = 'mcp_disabled';
9
+ // Whitelisted by `mcp-gate` so the MCP binary can probe the kill-switch
10
+ // state at boot even when MCP is globally disabled.
11
+ export const MCP_STATUS_PATH = '/mcp/status';
@@ -7,7 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __param = (this && this.__param) || function (paramIndex, decorator) {
8
8
  return function (target, key) { decorator(target, key, paramIndex); }
9
9
  };
10
- import { Example, Get, Middlewares, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
10
+ import { Example, Extension, Get, Middlewares, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
11
11
  import { inject } from 'inversify';
12
12
  import { noSuchObject } from 'xo-common/api-errors.js';
13
13
  import { provide } from 'inversify-binding-decorators';
@@ -73,6 +73,7 @@ let MessageController = class MessageController extends XapiXoController {
73
73
  __decorate([
74
74
  Example(messageIds),
75
75
  Example(partialMessages),
76
+ Extension('x-mcp-exposure', 'allow'),
76
77
  Get(''),
77
78
  Security('*', ['acl']),
78
79
  __param(0, Request()),
@@ -84,6 +85,7 @@ __decorate([
84
85
  ], MessageController.prototype, "getMessages", null);
85
86
  __decorate([
86
87
  Example(message),
88
+ Extension('x-mcp-exposure', 'allow'),
87
89
  Get('{id}'),
88
90
  Middlewares(acl({
89
91
  resource: 'message',
@@ -0,0 +1,30 @@
1
+ import { ApiError } from '../helpers/error.helper.mjs';
2
+ import { iocContainer } from '../ioc/ioc.mjs';
3
+ import { isMcpEnabled, MCP_DISABLED_ERROR, MCP_STATUS_PATH } from '../mcp/mcp.helper.mjs';
4
+ import { RestApi } from '../rest-api/rest-api.mjs';
5
+ // Identifies requests originating from the `@xen-orchestra/mcp` binary.
6
+ //
7
+ // SECURITY: the header is NOT authenticated — any client can spoof it. Use it
8
+ // only to RESTRICT behaviour (e.g. the MCP kill-switch), never to grant
9
+ // privileges.
10
+ function isMcpRequest(req) {
11
+ // Node lowercases header names; values keep their original case so we
12
+ // normalize before comparing. `Array.isArray` covers the unusual case of
13
+ // a duplicated header value (a malformed or hostile client).
14
+ const header = req.headers['x-xo-client'];
15
+ const value = Array.isArray(header) ? header[0] : header;
16
+ return typeof value === 'string' && value.toLowerCase() === 'mcp';
17
+ }
18
+ export function mcpGateMiddleware(req, _res, next) {
19
+ if (!isMcpRequest(req)) {
20
+ return next();
21
+ }
22
+ // Always let the kill-switch probe through so the MCP binary can fail-fast.
23
+ if (req.path === MCP_STATUS_PATH) {
24
+ return next();
25
+ }
26
+ if (!isMcpEnabled(iocContainer.get(RestApi))) {
27
+ return next(new ApiError('MCP is disabled by administrator', 503, { data: { error: MCP_DISABLED_ERROR } }));
28
+ }
29
+ next();
30
+ }
@@ -7,7 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __param = (this && this.__param) || function (paramIndex, decorator) {
8
8
  return function (target, key) { decorator(target, key, paramIndex); }
9
9
  };
10
- import { Example, Get, Path, Query, Response, Request, Route, Security, Tags, Delete, SuccessResponse, Put, Middlewares, } from 'tsoa';
10
+ import { Delete, Example, Extension, Get, Middlewares, Path, Put, Query, Request, Response, Route, Security, SuccessResponse, Tags, } from 'tsoa';
11
11
  import { inject } from 'inversify';
12
12
  import { provide } from 'inversify-binding-decorators';
13
13
  import { AlarmService } from '../alarms/alarm.service.mjs';
@@ -139,6 +139,7 @@ let NetworkController = class NetworkController extends XapiXoController {
139
139
  __decorate([
140
140
  Example(networkIds),
141
141
  Example(partialNetworks),
142
+ Extension('x-mcp-exposure', 'allow'),
142
143
  Get(''),
143
144
  Security('*', ['acl']),
144
145
  __param(0, Request()),
@@ -150,6 +151,7 @@ __decorate([
150
151
  ], NetworkController.prototype, "getNetworks", null);
151
152
  __decorate([
152
153
  Example(network),
154
+ Extension('x-mcp-exposure', 'allow'),
153
155
  Get('{id}'),
154
156
  Middlewares(acl({ resource: 'network', action: 'read', objectId: 'params.id' })),
155
157
  Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
@@ -157,6 +159,7 @@ __decorate([
157
159
  __param(0, Path())
158
160
  ], NetworkController.prototype, "getNetwork", null);
159
161
  __decorate([
162
+ Extension('x-mcp-exposure', 'confirm'),
160
163
  Delete('{id}'),
161
164
  Middlewares(acl({ resource: 'network', action: 'delete', objectId: 'params.id' })),
162
165
  Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
@@ -166,6 +169,7 @@ __decorate([
166
169
  ], NetworkController.prototype, "deleteNetwork", null);
167
170
  __decorate([
168
171
  Example(genericAlarmsExample),
172
+ Extension('x-mcp-exposure', 'allow'),
169
173
  Get('{id}/alarms'),
170
174
  Security('*', ['acl']),
171
175
  Tags('alarms'),
@@ -181,6 +185,7 @@ __decorate([
181
185
  __decorate([
182
186
  Example(messageIds),
183
187
  Example(partialMessages),
188
+ Extension('x-mcp-exposure', 'allow'),
184
189
  Get('{id}/messages'),
185
190
  Security('*', ['acl']),
186
191
  Tags('messages'),
@@ -196,6 +201,7 @@ __decorate([
196
201
  __decorate([
197
202
  Example(taskIds),
198
203
  Example(partialTasks),
204
+ Extension('x-mcp-exposure', 'allow'),
199
205
  Get('{id}/tasks'),
200
206
  Security('*', ['acl']),
201
207
  Tags('tasks'),
@@ -209,6 +215,7 @@ __decorate([
209
215
  __param(6, Query())
210
216
  ], NetworkController.prototype, "getNetworkTasks", null);
211
217
  __decorate([
218
+ Extension('x-mcp-exposure', 'confirm'),
212
219
  Put('{id}/tags/{tag}'),
213
220
  Middlewares(acl({ resource: 'network', action: 'update:tags', objectId: 'params.id' })),
214
221
  SuccessResponse(noContentResp.status, noContentResp.description),
@@ -218,6 +225,7 @@ __decorate([
218
225
  __param(1, Path())
219
226
  ], NetworkController.prototype, "putNetworkTag", null);
220
227
  __decorate([
228
+ Extension('x-mcp-exposure', 'confirm'),
221
229
  Delete('{id}/tags/{tag}'),
222
230
  Middlewares(acl({ resource: 'network', action: 'update:tags', objectId: 'params.id' })),
223
231
  SuccessResponse(noContentResp.status, noContentResp.description),
@@ -50,6 +50,8 @@ import { NetworkController } from './../../networks/network.controller.mjs';
50
50
  // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
51
51
  import { MessageController } from './../../messages/message.controller.mjs';
52
52
  // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
53
+ import { McpController } from './../../mcp/mcp.controller.mjs';
54
+ // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
53
55
  import { HostController } from './../../hosts/host.controller.mjs';
54
56
  // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
55
57
  import { GroupController } from './../../groups/group.controller.mjs';
@@ -534,7 +536,7 @@ const models = {
534
536
  // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
535
537
  "CreateVdiBody": {
536
538
  "dataType": "refAlias",
537
- "type": { "dataType": "intersection", "subSchemas": [{ "ref": "Omit_CreateVdiParams_91_0_93_.SR-or-other_config_" }, { "dataType": "nestedObjectLiteral", "nestedProperties": { "other_config": { "dataType": "nestedObjectLiteral", "nestedProperties": {}, "additionalProperties": { "dataType": "string" }, "required": true }, "srId": { "dataType": "string", "required": true } } }, { "dataType": "union", "subSchemas": [{ "dataType": "nestedObjectLiteral", "nestedProperties": { "sm_config": { "ref": "Record_string.string_" } } }, { "dataType": "undefined" }] }], "validators": {} },
539
+ "type": { "dataType": "intersection", "subSchemas": [{ "ref": "Omit_CreateVdiParams_91_0_93_.SR-or-other_config_" }, { "dataType": "nestedObjectLiteral", "nestedProperties": { "other_config": { "dataType": "nestedObjectLiteral", "nestedProperties": {}, "additionalProperties": { "dataType": "string" } }, "srId": { "dataType": "string", "required": true } } }, { "dataType": "union", "subSchemas": [{ "dataType": "nestedObjectLiteral", "nestedProperties": { "sm_config": { "ref": "Record_string.string_" } } }, { "dataType": "undefined" }] }], "validators": {} },
538
540
  },
539
541
  // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
540
542
  "CreateActionReturnType__id-Unbrand_XoVdi__91_id_93___": {
@@ -1072,6 +1074,11 @@ const models = {
1072
1074
  "type": { "ref": "Unbrand_XoMessage_", "validators": {} },
1073
1075
  },
1074
1076
  // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
1077
+ "McpStatus": {
1078
+ "dataType": "refAlias",
1079
+ "type": { "dataType": "nestedObjectLiteral", "nestedProperties": { "enabled": { "dataType": "enum", "enums": [true], "required": true } }, "validators": {} },
1080
+ },
1081
+ // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
1075
1082
  "Record_string.HOST_ALLOWED_OPERATIONS_": {
1076
1083
  "dataType": "refAlias",
1077
1084
  "type": { "dataType": "nestedObjectLiteral", "nestedProperties": {}, "validators": {} },
@@ -6865,6 +6872,31 @@ export function RegisterRoutes(app) {
6865
6872
  }
6866
6873
  });
6867
6874
  // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
6875
+ const argsMcpController_getMcpStatus = {};
6876
+ app.get('/rest/v0/mcp/status', authenticateMiddleware([{ "none": [] }]), ...(fetchMiddlewares(McpController)), ...(fetchMiddlewares(McpController.prototype.getMcpStatus)), async function McpController_getMcpStatus(request, response, next) {
6877
+ // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
6878
+ let validatedArgs = [];
6879
+ try {
6880
+ validatedArgs = templateService.getValidatedArgs({ args: argsMcpController_getMcpStatus, request, response });
6881
+ const container = typeof iocContainer === 'function' ? iocContainer(request) : iocContainer;
6882
+ const controller = await container.get(McpController);
6883
+ if (typeof controller['setStatus'] === 'function') {
6884
+ controller.setStatus(undefined);
6885
+ }
6886
+ await templateService.apiHandler({
6887
+ methodName: 'getMcpStatus',
6888
+ controller,
6889
+ response,
6890
+ next,
6891
+ validatedArgs,
6892
+ successStatus: 200,
6893
+ });
6894
+ }
6895
+ catch (err) {
6896
+ return next(err);
6897
+ }
6898
+ });
6899
+ // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
6868
6900
  const argsHostController_getHosts = {
6869
6901
  req: { "in": "request", "name": "req", "required": true, "dataType": "object" },
6870
6902
  fields: { "in": "query", "name": "fields", "dataType": "string" },
@@ -9,7 +9,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
9
9
  };
10
10
  import { inject } from 'inversify';
11
11
  import { provide } from 'inversify-binding-decorators';
12
- import { Middlewares, Route, Security, Request, Response, Get, Query, Path, Tags, Example, Post, SuccessResponse } from 'tsoa';
12
+ import { Example, Extension, Get, Middlewares, Path, Post, Query, Request, Response, Route, Security, SuccessResponse, Tags, } from 'tsoa';
13
13
  import { acl } from '../middlewares/acl.middleware.mjs';
14
14
  import { asynchronousActionResp, badRequestResp, internalServerErrorResp, invalidParameters as invalidParametersResp, noContentResp, forbiddenOperationResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
15
15
  import { RestApi } from '../rest-api/rest-api.mjs';
@@ -83,6 +83,7 @@ let PbdController = class PbdController extends XapiXoController {
83
83
  __decorate([
84
84
  Example(pbdIds),
85
85
  Example(partialPbds),
86
+ Extension('x-mcp-exposure', 'allow'),
86
87
  Get(''),
87
88
  Security('*', ['acl']),
88
89
  __param(0, Request()),
@@ -94,6 +95,7 @@ __decorate([
94
95
  ], PbdController.prototype, "getPbds", null);
95
96
  __decorate([
96
97
  Example(pbd),
98
+ Extension('x-mcp-exposure', 'allow'),
97
99
  Get('{id}'),
98
100
  Middlewares(acl({ resource: 'pbd', action: 'read', objectId: 'params.id' })),
99
101
  Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
@@ -102,6 +104,7 @@ __decorate([
102
104
  ], PbdController.prototype, "getPbd", null);
103
105
  __decorate([
104
106
  Example(taskLocation),
107
+ Extension('x-mcp-exposure', 'confirm'),
105
108
  Post('{id}/actions/plug'),
106
109
  SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
107
110
  Response(noContentResp.status, noContentResp.description),
@@ -113,6 +116,7 @@ __decorate([
113
116
  ], PbdController.prototype, "plugPbd", null);
114
117
  __decorate([
115
118
  Example(taskLocation),
119
+ Extension('x-mcp-exposure', 'confirm'),
116
120
  Post('{id}/actions/unplug'),
117
121
  SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
118
122
  Response(noContentResp.status, noContentResp.description),
@@ -7,7 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __param = (this && this.__param) || function (paramIndex, decorator) {
8
8
  return function (target, key) { decorator(target, key, paramIndex); }
9
9
  };
10
- import { Example, Get, Middlewares, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
10
+ import { Example, Extension, Get, Middlewares, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
11
11
  import { inject } from 'inversify';
12
12
  import { provide } from 'inversify-binding-decorators';
13
13
  import { acl } from '../middlewares/acl.middleware.mjs';
@@ -46,6 +46,7 @@ let PciController = class PciController extends XapiXoController {
46
46
  __decorate([
47
47
  Example(pciIds),
48
48
  Example(partialPcis),
49
+ Extension('x-mcp-exposure', 'allow'),
49
50
  Get(''),
50
51
  Security('*', ['acl']),
51
52
  __param(0, Request()),
@@ -57,6 +58,7 @@ __decorate([
57
58
  ], PciController.prototype, "getPcis", null);
58
59
  __decorate([
59
60
  Example(pci),
61
+ Extension('x-mcp-exposure', 'allow'),
60
62
  Get('{id}'),
61
63
  Middlewares(acl({ resource: 'pci', action: 'read', objectId: 'params.id' })),
62
64
  Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
@@ -10,7 +10,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
10
10
  import { inject } from 'inversify';
11
11
  import { RestApi } from '../rest-api/rest-api.mjs';
12
12
  import { XapiXoController } from '../abstract-classes/xapi-xo-controller.mjs';
13
- import { Example, Get, Middlewares, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
13
+ import { Example, Extension, Get, Middlewares, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
14
14
  import { acl } from '../middlewares/acl.middleware.mjs';
15
15
  import { badRequestResp, forbiddenOperationResp, notFoundResp, unauthorizedResp, } from '../open-api/common/response.common.mjs';
16
16
  import { provide } from 'inversify-binding-decorators';
@@ -46,6 +46,7 @@ let PgpuController = class PgpuController extends XapiXoController {
46
46
  __decorate([
47
47
  Example(pgpuIds),
48
48
  Example(partialPgpus),
49
+ Extension('x-mcp-exposure', 'allow'),
49
50
  Get(''),
50
51
  Security('*', ['acl']),
51
52
  __param(0, Request()),
@@ -57,6 +58,7 @@ __decorate([
57
58
  ], PgpuController.prototype, "getPgpus", null);
58
59
  __decorate([
59
60
  Example(pgpu),
61
+ Extension('x-mcp-exposure', 'allow'),
60
62
  Get('{id}'),
61
63
  Middlewares(acl({ resource: 'pgpu', action: 'read', objectId: 'params.id' })),
62
64
  Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
@@ -7,7 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __param = (this && this.__param) || function (paramIndex, decorator) {
8
8
  return function (target, key) { decorator(target, key, paramIndex); }
9
9
  };
10
- import { Example, Get, Middlewares, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
10
+ import { Example, Extension, Get, Middlewares, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
11
11
  import { inject } from 'inversify';
12
12
  import { provide } from 'inversify-binding-decorators';
13
13
  import { AlarmService } from '../alarms/alarm.service.mjs';
@@ -107,6 +107,7 @@ let PifController = class PifController extends XapiXoController {
107
107
  __decorate([
108
108
  Example(pifIds),
109
109
  Example(partialPifs),
110
+ Extension('x-mcp-exposure', 'allow'),
110
111
  Get(''),
111
112
  Security('*', ['acl']),
112
113
  __param(0, Request()),
@@ -118,6 +119,7 @@ __decorate([
118
119
  ], PifController.prototype, "getPifs", null);
119
120
  __decorate([
120
121
  Example(pif),
122
+ Extension('x-mcp-exposure', 'allow'),
121
123
  Get('{id}'),
122
124
  Middlewares(acl({ resource: 'pif', action: 'read', objectId: 'params.id' })),
123
125
  Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
@@ -126,6 +128,7 @@ __decorate([
126
128
  ], PifController.prototype, "getPif", null);
127
129
  __decorate([
128
130
  Example(genericAlarmsExample),
131
+ Extension('x-mcp-exposure', 'allow'),
129
132
  Get('{id}/alarms'),
130
133
  Security('*', ['acl']),
131
134
  Tags('alarms'),
@@ -141,6 +144,7 @@ __decorate([
141
144
  __decorate([
142
145
  Example(messageIds),
143
146
  Example(partialMessages),
147
+ Extension('x-mcp-exposure', 'allow'),
144
148
  Get('{id}/messages'),
145
149
  Security('*', ['acl']),
146
150
  Tags('messages'),
@@ -156,6 +160,7 @@ __decorate([
156
160
  __decorate([
157
161
  Example(taskIds),
158
162
  Example(partialTasks),
163
+ Extension('x-mcp-exposure', 'allow'),
159
164
  Get('{id}/tasks'),
160
165
  Security('*', ['acl']),
161
166
  Tags('tasks'),
@@ -7,7 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __param = (this && this.__param) || function (paramIndex, decorator) {
8
8
  return function (target, key) { decorator(target, key, paramIndex); }
9
9
  };
10
- import { Example, Get, Path, Query, Response, Request, Route, Security, Tags, Post, Middlewares, Body, SuccessResponse, Put, Delete, } from 'tsoa';
10
+ import { Body, Delete, Example, Extension, Get, Middlewares, Path, Post, Put, Query, Request, Response, Route, Security, SuccessResponse, Tags, } from 'tsoa';
11
11
  import { inject } from 'inversify';
12
12
  import { invalidParameters } from 'xo-common/api-errors.js';
13
13
  import { PassThrough } from 'node:stream';
@@ -427,6 +427,7 @@ let PoolController = class PoolController extends XapiXoController {
427
427
  __decorate([
428
428
  Example(poolIds),
429
429
  Example(partialPools),
430
+ Extension('x-mcp-exposure', 'allow'),
430
431
  Get(''),
431
432
  Security('*', ['acl']),
432
433
  __param(0, Request()),
@@ -438,6 +439,7 @@ __decorate([
438
439
  ], PoolController.prototype, "getPools", null);
439
440
  __decorate([
440
441
  Example(pool),
442
+ Extension('x-mcp-exposure', 'allow'),
441
443
  Get('{id}'),
442
444
  Middlewares(acl({ resource: 'pool', action: 'read', objectId: 'params.id' })),
443
445
  Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
@@ -447,6 +449,7 @@ __decorate([
447
449
  __decorate([
448
450
  Example(taskLocation),
449
451
  Example(createNetwork),
452
+ Extension('x-mcp-exposure', 'confirm'),
450
453
  Post('{id}/actions/create_network'),
451
454
  Middlewares([
452
455
  json(),
@@ -470,6 +473,7 @@ __decorate([
470
473
  __decorate([
471
474
  Example(taskLocation),
472
475
  Example(createNetwork),
476
+ Extension('x-mcp-exposure', 'confirm'),
473
477
  Post('{id}/actions/create_bonded_network'),
474
478
  Middlewares(json()),
475
479
  Tags('networks'),
@@ -485,6 +489,7 @@ __decorate([
485
489
  __decorate([
486
490
  Example(taskLocation),
487
491
  Example(createNetwork),
492
+ Extension('x-mcp-exposure', 'confirm'),
488
493
  Post('{id}/actions/create_internal_network'),
489
494
  Middlewares(json()),
490
495
  Tags('networks'),
@@ -499,6 +504,7 @@ __decorate([
499
504
  ], PoolController.prototype, "createInternalNetwork", null);
500
505
  __decorate([
501
506
  Example(taskLocation),
507
+ Extension('x-mcp-exposure', 'confirm'),
502
508
  Post('{id}/actions/emergency_shutdown'),
503
509
  Middlewares(acl({ resource: 'pool', action: 'emergency-shutdown', objectId: 'params.id' })),
504
510
  SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
@@ -511,6 +517,7 @@ __decorate([
511
517
  ], PoolController.prototype, "emergencyShutdown", null);
512
518
  __decorate([
513
519
  Example(taskLocation),
520
+ Extension('x-mcp-exposure', 'confirm'),
514
521
  Post('{id}/actions/rolling_reboot'),
515
522
  Middlewares(acl({ resource: 'pool', action: 'rolling-reboot', objectId: 'params.id' })),
516
523
  SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
@@ -523,6 +530,7 @@ __decorate([
523
530
  ], PoolController.prototype, "rollingReboot", null);
524
531
  __decorate([
525
532
  Example(taskLocation),
533
+ Extension('x-mcp-exposure', 'confirm'),
526
534
  Post('{id}/actions/rolling_update'),
527
535
  Middlewares(acl({ resource: 'pool', action: 'rolling-update', objectId: 'params.id' })),
528
536
  SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
@@ -535,6 +543,7 @@ __decorate([
535
543
  ], PoolController.prototype, "rollingUpdate", null);
536
544
  __decorate([
537
545
  Example(importVm),
546
+ Extension('x-mcp-exposure', 'confirm'),
538
547
  Post('{id}/vms'),
539
548
  Middlewares(acl([
540
549
  {
@@ -563,6 +572,7 @@ __decorate([
563
572
  __decorate([
564
573
  Example(taskLocation),
565
574
  Example(createVm),
575
+ Extension('x-mcp-exposure', 'confirm'),
566
576
  Post('{id}/actions/create_vm'),
567
577
  Middlewares([
568
578
  json(),
@@ -607,6 +617,7 @@ __decorate([
607
617
  ], PoolController.prototype, "createVm", null);
608
618
  __decorate([
609
619
  Example(poolStats),
620
+ Extension('x-mcp-exposure', 'deny'),
610
621
  Get('{id}/stats'),
611
622
  Middlewares(acl({ resource: 'pool', action: 'read', objectId: 'params.id' })),
612
623
  Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
@@ -617,6 +628,7 @@ __decorate([
617
628
  ], PoolController.prototype, "getStats", null);
618
629
  __decorate([
619
630
  Example(poolDashboard),
631
+ Extension('x-mcp-exposure', 'allow'),
620
632
  Get('{id}/dashboard'),
621
633
  Middlewares(acl({ resource: 'pool', action: 'read', objectId: 'params.id' })),
622
634
  Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
@@ -627,6 +639,7 @@ __decorate([
627
639
  ], PoolController.prototype, "getPoolDashboard", null);
628
640
  __decorate([
629
641
  Example(genericAlarmsExample),
642
+ Extension('x-mcp-exposure', 'allow'),
630
643
  Get('{id}/alarms'),
631
644
  Security('*', ['acl']),
632
645
  Tags('alarms'),
@@ -641,6 +654,7 @@ __decorate([
641
654
  ], PoolController.prototype, "getPoolAlarms", null);
642
655
  __decorate([
643
656
  Example(poolMissingPatches),
657
+ Extension('x-mcp-exposure', 'allow'),
644
658
  Get('{id}/missing_patches'),
645
659
  Middlewares(acl({ resource: 'pool', action: 'read', objectId: 'params.id' })),
646
660
  Response(forbiddenOperationResp.status, forbiddenOperationResp.description),
@@ -651,6 +665,7 @@ __decorate([
651
665
  __decorate([
652
666
  Example(messageIds),
653
667
  Example(partialMessages),
668
+ Extension('x-mcp-exposure', 'allow'),
654
669
  Get('{id}/messages'),
655
670
  Security('*', ['acl']),
656
671
  Tags('messages'),
@@ -664,6 +679,7 @@ __decorate([
664
679
  __param(6, Query())
665
680
  ], PoolController.prototype, "getPoolMessages", null);
666
681
  __decorate([
682
+ Extension('x-mcp-exposure', 'confirm'),
667
683
  Put('{id}/tags/{tag}'),
668
684
  Middlewares(acl({ resource: 'pool', action: 'update:tags', objectId: 'params.id' })),
669
685
  SuccessResponse(noContentResp.status, noContentResp.description),
@@ -673,6 +689,7 @@ __decorate([
673
689
  __param(1, Path())
674
690
  ], PoolController.prototype, "putPoolTag", null);
675
691
  __decorate([
692
+ Extension('x-mcp-exposure', 'confirm'),
676
693
  Delete('{id}/tags/{tag}'),
677
694
  Middlewares(acl({ resource: 'pool', action: 'update:tags', objectId: 'params.id' })),
678
695
  SuccessResponse(noContentResp.status, noContentResp.description),
@@ -684,6 +701,7 @@ __decorate([
684
701
  __decorate([
685
702
  Example(taskIds),
686
703
  Example(partialTasks),
704
+ Extension('x-mcp-exposure', 'allow'),
687
705
  Get('{id}/tasks'),
688
706
  Security('*', ['acl']),
689
707
  Tags('tasks'),
@@ -698,6 +716,7 @@ __decorate([
698
716
  ], PoolController.prototype, "getPoolTasks", null);
699
717
  __decorate([
700
718
  Example(taskLocation),
719
+ Extension('x-mcp-exposure', 'confirm'),
701
720
  Post('{id}/actions/management_reconfigure'),
702
721
  Middlewares(json()),
703
722
  SuccessResponse(asynchronousActionResp.status, asynchronousActionResp.description),
@@ -7,7 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __param = (this && this.__param) || function (paramIndex, decorator) {
8
8
  return function (target, key) { decorator(target, key, paramIndex); }
9
9
  };
10
- import { Example, Get, Middlewares, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
10
+ import { Example, Extension, Get, Middlewares, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
11
11
  import { inject } from 'inversify';
12
12
  import { provide } from 'inversify-binding-decorators';
13
13
  import { acl } from '../middlewares/acl.middleware.mjs';
@@ -53,6 +53,7 @@ let ProxyController = class ProxyController extends XoController {
53
53
  __decorate([
54
54
  Example(proxyIds),
55
55
  Example(partialProxies),
56
+ Extension('x-mcp-exposure', 'allow'),
56
57
  Get(''),
57
58
  Security('*', ['acl']),
58
59
  __param(0, Request()),
@@ -64,6 +65,7 @@ __decorate([
64
65
  ], ProxyController.prototype, "getProxies", null);
65
66
  __decorate([
66
67
  Example(proxy),
68
+ Extension('x-mcp-exposure', 'allow'),
67
69
  Get('{id}'),
68
70
  Middlewares(acl({
69
71
  resource: 'proxy',
@@ -8,7 +8,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
8
8
  return function (target, key) { decorator(target, key, paramIndex); }
9
9
  };
10
10
  import { createLogger } from '@xen-orchestra/log';
11
- import { Deprecated, Example, Get, Middlewares, Path, Query, Request, Response, Route, Security, Tags } from 'tsoa';
11
+ import { Deprecated, Example, Extension, Get, Middlewares, Path, Query, Request, Response, Route, Security, Tags, } from 'tsoa';
12
12
  import { inject } from 'inversify';
13
13
  import { provide } from 'inversify-binding-decorators';
14
14
  import { acl, autoBindService } from '../middlewares/acl.middleware.mjs';
@@ -59,6 +59,7 @@ let RestoreLogController = class RestoreLogController extends XoController {
59
59
  __decorate([
60
60
  Example(restoreLogIds),
61
61
  Example(partialRestoreLogs),
62
+ Extension('x-mcp-exposure', 'allow'),
62
63
  Get(''),
63
64
  Security('*', ['acl']),
64
65
  __param(0, Request()),
@@ -70,6 +71,7 @@ __decorate([
70
71
  ], RestoreLogController.prototype, "getRestoreLogs", null);
71
72
  __decorate([
72
73
  Example(restoreLog),
74
+ Extension('x-mcp-exposure', 'allow'),
73
75
  Get('{id}'),
74
76
  Middlewares(acl({
75
77
  resource: 'restore-log',
@@ -133,6 +135,7 @@ __decorate([
133
135
  Example(restoreLogIds),
134
136
  Example(partialRestoreLogs),
135
137
  Deprecated(),
138
+ Extension('x-mcp-exposure', 'allow'),
136
139
  Get(''),
137
140
  __param(0, Request()),
138
141
  __param(1, Query()),
@@ -144,6 +147,7 @@ __decorate([
144
147
  __decorate([
145
148
  Example(restoreLog),
146
149
  Deprecated(),
150
+ Extension('x-mcp-exposure', 'allow'),
147
151
  Get('{id}'),
148
152
  __param(0, Path())
149
153
  ], DeprecatedRestoreController.prototype, "getDeprecatedRestoreLog", null);
@@ -7,7 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __param = (this && this.__param) || function (paramIndex, decorator) {
8
8
  return function (target, key) { decorator(target, key, paramIndex); }
9
9
  };
10
- import { Example, Get, Middlewares, Path, Post, Query, Request, Response, Route, Security, SuccessResponse, Tags, } from 'tsoa';
10
+ import { Example, Extension, Get, Middlewares, Path, Post, Query, Request, Response, Route, Security, SuccessResponse, Tags, } from 'tsoa';
11
11
  import { inject } from 'inversify';
12
12
  import { provide } from 'inversify-binding-decorators';
13
13
  import { acl } from '../middlewares/acl.middleware.mjs';
@@ -76,6 +76,7 @@ let ScheduleController = class ScheduleController extends XoController {
76
76
  __decorate([
77
77
  Example(scheduleIds),
78
78
  Example(partialSchedules),
79
+ Extension('x-mcp-exposure', 'allow'),
79
80
  Get(''),
80
81
  Security('*', ['acl']),
81
82
  __param(0, Request()),
@@ -87,6 +88,7 @@ __decorate([
87
88
  ], ScheduleController.prototype, "getSchedules", null);
88
89
  __decorate([
89
90
  Example(schedule),
91
+ Extension('x-mcp-exposure', 'allow'),
90
92
  Get('{id}'),
91
93
  Middlewares(acl({
92
94
  resource: 'schedule',
@@ -100,6 +102,7 @@ __decorate([
100
102
  ], ScheduleController.prototype, "getSchedule", null);
101
103
  __decorate([
102
104
  Example(taskLocation),
105
+ Extension('x-mcp-exposure', 'confirm'),
103
106
  Post('{id}/actions/run'),
104
107
  Middlewares(acl({
105
108
  resource: 'schedule',
@@ -7,7 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __param = (this && this.__param) || function (paramIndex, decorator) {
8
8
  return function (target, key) { decorator(target, key, paramIndex); }
9
9
  };
10
- import { Body, Delete, Example, Get, Middlewares, Path, Post, Query, Request, Response, Route, Security, SuccessResponse, Tags, } from 'tsoa';
10
+ import { Body, Delete, Example, Extension, Get, Middlewares, Path, Post, Query, Request, Response, Route, Security, SuccessResponse, Tags, } from 'tsoa';
11
11
  import { json } from 'express';
12
12
  import { inject } from 'inversify';
13
13
  import { provide } from 'inversify-binding-decorators';
@@ -131,6 +131,7 @@ let ServerController = class ServerController extends XoController {
131
131
  __decorate([
132
132
  Example(serverIds),
133
133
  Example(partialServers),
134
+ Extension('x-mcp-exposure', 'allow'),
134
135
  Get(''),
135
136
  Security('*', ['acl']),
136
137
  __param(0, Request()),
@@ -142,6 +143,7 @@ __decorate([
142
143
  ], ServerController.prototype, "getServers", null);
143
144
  __decorate([
144
145
  Example(server),
146
+ Extension('x-mcp-exposure', 'allow'),
145
147
  Get('{id}'),
146
148
  Middlewares(acl({
147
149
  resource: 'server',
@@ -154,6 +156,7 @@ __decorate([
154
156
  __param(0, Path())
155
157
  ], ServerController.prototype, "getServer", null);
156
158
  __decorate([
159
+ Extension('x-mcp-exposure', 'confirm'),
157
160
  Delete('{id}'),
158
161
  Middlewares(acl({
159
162
  resource: 'server',
@@ -168,6 +171,7 @@ __decorate([
168
171
  ], ServerController.prototype, "deleteServer", null);
169
172
  __decorate([
170
173
  Example(serverId),
174
+ Extension('x-mcp-exposure', 'confirm'),
171
175
  Post(''),
172
176
  Middlewares([json(), acl({ resource: 'server', action: 'create', object: ({ req }) => req.body })]),
173
177
  SuccessResponse(createdResp.status, createdResp.description),
@@ -178,6 +182,7 @@ __decorate([
178
182
  ], ServerController.prototype, "addServer", null);
179
183
  __decorate([
180
184
  Example(taskLocation),
185
+ Extension('x-mcp-exposure', 'confirm'),
181
186
  Post('{id}/actions/connect'),
182
187
  Middlewares(acl({
183
188
  resource: 'server',
@@ -195,6 +200,7 @@ __decorate([
195
200
  ], ServerController.prototype, "connectServer", null);
196
201
  __decorate([
197
202
  Example(taskLocation),
203
+ Extension('x-mcp-exposure', 'confirm'),
198
204
  Post('{id}/actions/disconnect'),
199
205
  Middlewares(acl({
200
206
  resource: 'server',
@@ -213,6 +219,7 @@ __decorate([
213
219
  __decorate([
214
220
  Example(taskIds),
215
221
  Example(partialTasks),
222
+ Extension('x-mcp-exposure', 'allow'),
216
223
  Get('{id}/tasks'),
217
224
  Security('*', ['acl']),
218
225
  Tags('tasks'),