@spacelr/mcp 0.0.1
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.
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +1986 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +36 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1986 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// libs/mcp-server/src/index.ts
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
|
|
7
|
+
// libs/mcp-server/src/config.ts
|
|
8
|
+
import * as fs from "fs";
|
|
9
|
+
import * as os from "os";
|
|
10
|
+
import * as path from "path";
|
|
11
|
+
var REFRESH_TIMEOUT_MS = 1e4;
|
|
12
|
+
function getCredentialsFile() {
|
|
13
|
+
const home = os.homedir();
|
|
14
|
+
return path.join(home, ".spacelr", "credentials.json");
|
|
15
|
+
}
|
|
16
|
+
function readStoredCredentials() {
|
|
17
|
+
try {
|
|
18
|
+
const file = getCredentialsFile();
|
|
19
|
+
if (!fs.existsSync(file)) return null;
|
|
20
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
21
|
+
const parsed = JSON.parse(content);
|
|
22
|
+
if (typeof parsed !== "object" || parsed === null || typeof parsed.accessToken !== "string" || !Number.isFinite(parsed.expiresAt) || typeof parsed.apiUrl !== "string") {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
return parsed;
|
|
26
|
+
} catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function storeCredentials(credentials) {
|
|
31
|
+
const file = getCredentialsFile();
|
|
32
|
+
fs.mkdirSync(path.dirname(file), { recursive: true, mode: 448 });
|
|
33
|
+
fs.writeFileSync(file, JSON.stringify(credentials, null, 2), {
|
|
34
|
+
mode: 384
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
async function refreshToken(credentials) {
|
|
38
|
+
if (!credentials.refreshToken || !credentials.apiUrl) return null;
|
|
39
|
+
const apiUrl = credentials.apiUrl.replace(/\/+$/, "");
|
|
40
|
+
const controller = new AbortController();
|
|
41
|
+
const timer = setTimeout(() => controller.abort(), REFRESH_TIMEOUT_MS);
|
|
42
|
+
try {
|
|
43
|
+
console.error("Refreshing access token...");
|
|
44
|
+
const response = await fetch(`${apiUrl}/auth/refresh`, {
|
|
45
|
+
method: "POST",
|
|
46
|
+
headers: { "Content-Type": "application/json" },
|
|
47
|
+
body: JSON.stringify({ refreshToken: credentials.refreshToken }),
|
|
48
|
+
signal: controller.signal
|
|
49
|
+
});
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
console.error(`Token refresh failed (HTTP ${response.status})`);
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const data = await response.json();
|
|
55
|
+
if (typeof data.access_token !== "string") {
|
|
56
|
+
console.error("Token refresh returned unexpected response shape");
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const updated = {
|
|
60
|
+
accessToken: data.access_token,
|
|
61
|
+
refreshToken: data.refresh_token ?? credentials.refreshToken,
|
|
62
|
+
expiresAt: Date.now() + (data.expires_in ?? 3600) * 1e3,
|
|
63
|
+
apiUrl: credentials.apiUrl
|
|
64
|
+
};
|
|
65
|
+
storeCredentials(updated);
|
|
66
|
+
console.error("Token refreshed successfully");
|
|
67
|
+
return data.access_token;
|
|
68
|
+
} catch (err) {
|
|
69
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
70
|
+
console.error("Token refresh timed out");
|
|
71
|
+
} else {
|
|
72
|
+
console.error("Token refresh error");
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
} finally {
|
|
76
|
+
clearTimeout(timer);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function resolveAuthToken() {
|
|
80
|
+
const envToken = process.env["SPACELR_AUTH_TOKEN"];
|
|
81
|
+
if (envToken) return envToken;
|
|
82
|
+
const credentials = readStoredCredentials();
|
|
83
|
+
if (!credentials) return null;
|
|
84
|
+
const isExpired = Date.now() >= credentials.expiresAt - 6e4;
|
|
85
|
+
if (!isExpired) return credentials.accessToken;
|
|
86
|
+
return refreshToken(credentials);
|
|
87
|
+
}
|
|
88
|
+
function warnIfInsecureUrl(url) {
|
|
89
|
+
if (url.startsWith("http://") && !url.includes("localhost") && !/^http:\/\/(127\.|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|0\.0\.0\.0|(\[::1\]))(\/|:|$)/.test(url)) {
|
|
90
|
+
console.error(`Warning: API URL uses plain HTTP (${url}). Credentials will be transmitted unencrypted.`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function resolveApiUrl(credentials) {
|
|
94
|
+
const envUrl = process.env["SPACELR_API_URL"];
|
|
95
|
+
if (envUrl) {
|
|
96
|
+
try {
|
|
97
|
+
const parsed = new URL(envUrl);
|
|
98
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
99
|
+
throw new Error(`Unsupported protocol: ${parsed.protocol}`);
|
|
100
|
+
}
|
|
101
|
+
} catch (err) {
|
|
102
|
+
throw new Error(`Invalid SPACELR_API_URL: ${envUrl} \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
103
|
+
}
|
|
104
|
+
const resolved = envUrl.replace(/\/+$/, "");
|
|
105
|
+
warnIfInsecureUrl(resolved);
|
|
106
|
+
return resolved;
|
|
107
|
+
}
|
|
108
|
+
if (credentials?.apiUrl) {
|
|
109
|
+
const base = credentials.apiUrl.replace(/\/+$/, "");
|
|
110
|
+
if (base.includes("/api/v1/admin")) return base;
|
|
111
|
+
if (base.includes("localhost") || base.includes("127.0.0.1") || base.includes("[::1]")) {
|
|
112
|
+
return "http://localhost:3003/api/v1/admin";
|
|
113
|
+
}
|
|
114
|
+
throw new Error(
|
|
115
|
+
`SPACELR_API_URL is required for production. The CLI credentials point to ${base} (public gateway), but the admin gateway runs on a separate host. Set SPACELR_API_URL to the admin gateway URL (e.g. https://api-console.spacelr.com/api/v1/admin).`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
const defaultUrl = "http://localhost:3003/api/v1/admin";
|
|
119
|
+
console.error(`Warning: No API URL configured, using default: ${defaultUrl}`);
|
|
120
|
+
return defaultUrl;
|
|
121
|
+
}
|
|
122
|
+
async function loadConfig() {
|
|
123
|
+
const credentials = readStoredCredentials();
|
|
124
|
+
const authToken = await resolveAuthToken();
|
|
125
|
+
if (!authToken) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
'No auth token found. Either set SPACELR_AUTH_TOKEN or run "spacelr login" first.'
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
apiBaseUrl: resolveApiUrl(credentials),
|
|
132
|
+
authToken,
|
|
133
|
+
clientId: process.env["SPACELR_CLIENT_ID"],
|
|
134
|
+
projectId: process.env["SPACELR_PROJECT_ID"]
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// libs/mcp-server/src/api-client.ts
|
|
139
|
+
var REQUEST_TIMEOUT_MS = 3e4;
|
|
140
|
+
var ApiError = class extends Error {
|
|
141
|
+
constructor(status, statusText, responseBody) {
|
|
142
|
+
const truncated = responseBody.length > 500 ? responseBody.slice(0, 500) + "..." : responseBody;
|
|
143
|
+
super(`HTTP ${status} ${statusText}: ${truncated}`);
|
|
144
|
+
this.status = status;
|
|
145
|
+
this.statusText = statusText;
|
|
146
|
+
this.name = "ApiError";
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
var ApiClient = class {
|
|
150
|
+
constructor(config) {
|
|
151
|
+
this.refreshPromise = null;
|
|
152
|
+
this.baseUrl = config.apiBaseUrl;
|
|
153
|
+
this.headers = {
|
|
154
|
+
Authorization: `Bearer ${config.authToken}`
|
|
155
|
+
};
|
|
156
|
+
if (config.clientId) {
|
|
157
|
+
this.headers["x-client-id"] = config.clientId;
|
|
158
|
+
}
|
|
159
|
+
if (config.projectId) {
|
|
160
|
+
this.headers["x-project-id"] = config.projectId;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
async get(path2, opts) {
|
|
164
|
+
return this.requestWithRetry("GET", path2, opts);
|
|
165
|
+
}
|
|
166
|
+
async post(path2, opts) {
|
|
167
|
+
return this.requestWithRetry("POST", path2, opts);
|
|
168
|
+
}
|
|
169
|
+
async patch(path2, opts) {
|
|
170
|
+
return this.requestWithRetry("PATCH", path2, opts);
|
|
171
|
+
}
|
|
172
|
+
async put(path2, opts) {
|
|
173
|
+
return this.requestWithRetry("PUT", path2, opts);
|
|
174
|
+
}
|
|
175
|
+
async delete(path2, opts) {
|
|
176
|
+
return this.requestWithRetry("DELETE", path2, opts);
|
|
177
|
+
}
|
|
178
|
+
async refreshAuthToken() {
|
|
179
|
+
if (!this.refreshPromise) {
|
|
180
|
+
this.refreshPromise = resolveAuthToken().finally(() => {
|
|
181
|
+
this.refreshPromise = null;
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
return this.refreshPromise;
|
|
185
|
+
}
|
|
186
|
+
async requestWithRetry(method, path2, opts) {
|
|
187
|
+
try {
|
|
188
|
+
return await this.request(method, path2, opts);
|
|
189
|
+
} catch (error) {
|
|
190
|
+
if (error instanceof ApiError && error.status === 401) {
|
|
191
|
+
const currentToken = this.headers["Authorization"];
|
|
192
|
+
const newToken = await this.refreshAuthToken();
|
|
193
|
+
if (newToken && `Bearer ${newToken}` !== currentToken) {
|
|
194
|
+
this.headers["Authorization"] = `Bearer ${newToken}`;
|
|
195
|
+
return this.request(method, path2, opts);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
throw error;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
async request(method, path2, opts) {
|
|
202
|
+
let url = `${this.baseUrl}${path2}`;
|
|
203
|
+
if (opts?.params) {
|
|
204
|
+
const searchParams = new URLSearchParams();
|
|
205
|
+
for (const [key, value] of Object.entries(opts.params)) {
|
|
206
|
+
if (value !== void 0) {
|
|
207
|
+
searchParams.set(key, String(value));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const qs = searchParams.toString();
|
|
211
|
+
if (qs) {
|
|
212
|
+
url += `?${qs}`;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const controller = new AbortController();
|
|
216
|
+
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
217
|
+
try {
|
|
218
|
+
const headers = { ...this.headers };
|
|
219
|
+
if (opts?.body !== void 0) {
|
|
220
|
+
headers["Content-Type"] = "application/json";
|
|
221
|
+
}
|
|
222
|
+
const response = await fetch(url, {
|
|
223
|
+
method,
|
|
224
|
+
headers,
|
|
225
|
+
body: opts?.body !== void 0 ? JSON.stringify(opts.body) : void 0,
|
|
226
|
+
signal: controller.signal
|
|
227
|
+
});
|
|
228
|
+
const text = await response.text();
|
|
229
|
+
if (!response.ok) {
|
|
230
|
+
throw new ApiError(response.status, response.statusText, text);
|
|
231
|
+
}
|
|
232
|
+
if (!text) {
|
|
233
|
+
return void 0;
|
|
234
|
+
}
|
|
235
|
+
try {
|
|
236
|
+
return JSON.parse(text);
|
|
237
|
+
} catch {
|
|
238
|
+
return text;
|
|
239
|
+
}
|
|
240
|
+
} finally {
|
|
241
|
+
clearTimeout(timer);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// libs/mcp-server/src/tools/auth.ts
|
|
247
|
+
import { z } from "zod";
|
|
248
|
+
|
|
249
|
+
// libs/mcp-server/src/utils.ts
|
|
250
|
+
var TOKEN_KEYS = /* @__PURE__ */ new Set([
|
|
251
|
+
"access_token",
|
|
252
|
+
"refresh_token",
|
|
253
|
+
"id_token",
|
|
254
|
+
"accessToken",
|
|
255
|
+
"refreshToken",
|
|
256
|
+
"idToken"
|
|
257
|
+
]);
|
|
258
|
+
function redactTokens(data) {
|
|
259
|
+
if (typeof data !== "object" || data === null) return data;
|
|
260
|
+
if (Array.isArray(data)) return data.map(redactTokens);
|
|
261
|
+
const record = data;
|
|
262
|
+
const redacted = {};
|
|
263
|
+
for (const [key, value] of Object.entries(record)) {
|
|
264
|
+
if (TOKEN_KEYS.has(key)) {
|
|
265
|
+
redacted[key] = "[REDACTED]";
|
|
266
|
+
} else {
|
|
267
|
+
redacted[key] = redactTokens(value);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return redacted;
|
|
271
|
+
}
|
|
272
|
+
function redactSecrets(data) {
|
|
273
|
+
if (typeof data !== "object" || data === null) return data;
|
|
274
|
+
if (Array.isArray(data)) return data.map(redactSecrets);
|
|
275
|
+
const record = data;
|
|
276
|
+
const redacted = {};
|
|
277
|
+
for (const [key, value] of Object.entries(record)) {
|
|
278
|
+
if (key === "clientSecret" || key === "secret") {
|
|
279
|
+
redacted[key] = "[REDACTED]";
|
|
280
|
+
} else {
|
|
281
|
+
redacted[key] = redactSecrets(value);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return redacted;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// libs/mcp-server/src/tools/auth.ts
|
|
288
|
+
function registerAuthTools(server, api) {
|
|
289
|
+
server.registerTool(
|
|
290
|
+
"auth_login",
|
|
291
|
+
{
|
|
292
|
+
description: "Authenticate with email and password. WARNING: The password will be visible in the conversation context. Token values are redacted in the response.",
|
|
293
|
+
inputSchema: {
|
|
294
|
+
email: z.string().email(),
|
|
295
|
+
password: z.string().min(1)
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
async ({ email, password }) => {
|
|
299
|
+
try {
|
|
300
|
+
const result = await api.post("/auth/login", {
|
|
301
|
+
body: { email, password }
|
|
302
|
+
});
|
|
303
|
+
return {
|
|
304
|
+
content: [{ type: "text", text: JSON.stringify(redactTokens(result), null, 2) }]
|
|
305
|
+
};
|
|
306
|
+
} catch (error) {
|
|
307
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
308
|
+
return {
|
|
309
|
+
content: [{ type: "text", text: `Login failed: ${message}` }],
|
|
310
|
+
isError: true
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
);
|
|
315
|
+
server.registerTool(
|
|
316
|
+
"auth_me",
|
|
317
|
+
{
|
|
318
|
+
description: "Get the currently authenticated user profile"
|
|
319
|
+
},
|
|
320
|
+
async () => {
|
|
321
|
+
try {
|
|
322
|
+
const result = await api.get("/auth/me");
|
|
323
|
+
return {
|
|
324
|
+
content: [{ type: "text", text: JSON.stringify(redactTokens(result), null, 2) }]
|
|
325
|
+
};
|
|
326
|
+
} catch (error) {
|
|
327
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
328
|
+
return {
|
|
329
|
+
content: [{ type: "text", text: `Failed to get profile: ${message}` }],
|
|
330
|
+
isError: true
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
);
|
|
335
|
+
server.registerTool(
|
|
336
|
+
"auth_update_profile",
|
|
337
|
+
{
|
|
338
|
+
description: "Update the currently authenticated user profile",
|
|
339
|
+
inputSchema: {
|
|
340
|
+
firstName: z.string().optional(),
|
|
341
|
+
lastName: z.string().optional(),
|
|
342
|
+
displayName: z.string().optional()
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
async ({ firstName, lastName, displayName }) => {
|
|
346
|
+
try {
|
|
347
|
+
const body = {};
|
|
348
|
+
if (firstName !== void 0) body.firstName = firstName;
|
|
349
|
+
if (lastName !== void 0) body.lastName = lastName;
|
|
350
|
+
if (displayName !== void 0) body.displayName = displayName;
|
|
351
|
+
if (Object.keys(body).length === 0) {
|
|
352
|
+
return {
|
|
353
|
+
content: [{ type: "text", text: "At least one field must be provided" }],
|
|
354
|
+
isError: true
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
const result = await api.patch("/auth/me", { body });
|
|
358
|
+
return {
|
|
359
|
+
content: [{ type: "text", text: JSON.stringify(redactTokens(result), null, 2) }]
|
|
360
|
+
};
|
|
361
|
+
} catch (error) {
|
|
362
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
363
|
+
return {
|
|
364
|
+
content: [{ type: "text", text: `Profile update failed: ${message}` }],
|
|
365
|
+
isError: true
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// libs/mcp-server/src/tools/projects.ts
|
|
373
|
+
import { z as z2 } from "zod";
|
|
374
|
+
function registerProjectTools(server, api) {
|
|
375
|
+
server.registerTool(
|
|
376
|
+
"projects_list",
|
|
377
|
+
{
|
|
378
|
+
description: "List all projects, optionally filtered by role",
|
|
379
|
+
inputSchema: {
|
|
380
|
+
role: z2.string().optional()
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
async ({ role }) => {
|
|
384
|
+
try {
|
|
385
|
+
const result = await api.get("/projects", {
|
|
386
|
+
params: { role }
|
|
387
|
+
});
|
|
388
|
+
return {
|
|
389
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
390
|
+
};
|
|
391
|
+
} catch (error) {
|
|
392
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
393
|
+
return {
|
|
394
|
+
content: [{ type: "text", text: `Failed to list projects: ${message}` }],
|
|
395
|
+
isError: true
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
);
|
|
400
|
+
server.registerTool(
|
|
401
|
+
"projects_get",
|
|
402
|
+
{
|
|
403
|
+
description: "Get a project by its ID",
|
|
404
|
+
inputSchema: {
|
|
405
|
+
projectId: z2.string()
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
async ({ projectId }) => {
|
|
409
|
+
try {
|
|
410
|
+
const result = await api.get(`/projects/${encodeURIComponent(projectId)}`);
|
|
411
|
+
return {
|
|
412
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
413
|
+
};
|
|
414
|
+
} catch (error) {
|
|
415
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
416
|
+
return {
|
|
417
|
+
content: [{ type: "text", text: `Failed to get project: ${message}` }],
|
|
418
|
+
isError: true
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
);
|
|
423
|
+
server.registerTool(
|
|
424
|
+
"projects_get_by_slug",
|
|
425
|
+
{
|
|
426
|
+
description: "Get a project by its slug",
|
|
427
|
+
inputSchema: {
|
|
428
|
+
slug: z2.string()
|
|
429
|
+
}
|
|
430
|
+
},
|
|
431
|
+
async ({ slug }) => {
|
|
432
|
+
try {
|
|
433
|
+
const result = await api.get(`/projects/slug/${encodeURIComponent(slug)}`);
|
|
434
|
+
return {
|
|
435
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
436
|
+
};
|
|
437
|
+
} catch (error) {
|
|
438
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
439
|
+
return {
|
|
440
|
+
content: [{ type: "text", text: `Failed to get project by slug: ${message}` }],
|
|
441
|
+
isError: true
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
);
|
|
446
|
+
server.registerTool(
|
|
447
|
+
"projects_create",
|
|
448
|
+
{
|
|
449
|
+
description: "Create a new project",
|
|
450
|
+
inputSchema: {
|
|
451
|
+
name: z2.string(),
|
|
452
|
+
slug: z2.string(),
|
|
453
|
+
description: z2.string().optional(),
|
|
454
|
+
logoUrl: z2.string().url().optional(),
|
|
455
|
+
websiteUrl: z2.string().url().optional()
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
async ({ name, slug, description, logoUrl, websiteUrl }) => {
|
|
459
|
+
try {
|
|
460
|
+
const body = { name, slug };
|
|
461
|
+
if (description !== void 0) body.description = description;
|
|
462
|
+
if (logoUrl !== void 0) body.logoUrl = logoUrl;
|
|
463
|
+
if (websiteUrl !== void 0) body.websiteUrl = websiteUrl;
|
|
464
|
+
const result = await api.post("/projects", { body });
|
|
465
|
+
return {
|
|
466
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
467
|
+
};
|
|
468
|
+
} catch (error) {
|
|
469
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
470
|
+
return {
|
|
471
|
+
content: [{ type: "text", text: `Failed to create project: ${message}` }],
|
|
472
|
+
isError: true
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
);
|
|
477
|
+
server.registerTool(
|
|
478
|
+
"projects_update",
|
|
479
|
+
{
|
|
480
|
+
description: "Update an existing project",
|
|
481
|
+
inputSchema: {
|
|
482
|
+
projectId: z2.string(),
|
|
483
|
+
name: z2.string().optional(),
|
|
484
|
+
description: z2.string().optional(),
|
|
485
|
+
logoUrl: z2.string().url().optional(),
|
|
486
|
+
websiteUrl: z2.string().url().optional()
|
|
487
|
+
}
|
|
488
|
+
},
|
|
489
|
+
async ({ projectId, name, description, logoUrl, websiteUrl }) => {
|
|
490
|
+
try {
|
|
491
|
+
const body = {};
|
|
492
|
+
if (name !== void 0) body.name = name;
|
|
493
|
+
if (description !== void 0) body.description = description;
|
|
494
|
+
if (logoUrl !== void 0) body.logoUrl = logoUrl;
|
|
495
|
+
if (websiteUrl !== void 0) body.websiteUrl = websiteUrl;
|
|
496
|
+
if (Object.keys(body).length === 0) {
|
|
497
|
+
return {
|
|
498
|
+
content: [{ type: "text", text: "At least one field must be provided" }],
|
|
499
|
+
isError: true
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
const result = await api.patch(`/projects/${encodeURIComponent(projectId)}`, { body });
|
|
503
|
+
return {
|
|
504
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
505
|
+
};
|
|
506
|
+
} catch (error) {
|
|
507
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
508
|
+
return {
|
|
509
|
+
content: [{ type: "text", text: `Failed to update project: ${message}` }],
|
|
510
|
+
isError: true
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
);
|
|
515
|
+
server.registerTool(
|
|
516
|
+
"projects_delete",
|
|
517
|
+
{
|
|
518
|
+
description: "Delete a project by its ID",
|
|
519
|
+
inputSchema: {
|
|
520
|
+
projectId: z2.string()
|
|
521
|
+
}
|
|
522
|
+
},
|
|
523
|
+
async ({ projectId }) => {
|
|
524
|
+
try {
|
|
525
|
+
const result = await api.delete(`/projects/${encodeURIComponent(projectId)}`);
|
|
526
|
+
return {
|
|
527
|
+
content: [{ type: "text", text: JSON.stringify(result ?? "Project deleted", null, 2) }]
|
|
528
|
+
};
|
|
529
|
+
} catch (error) {
|
|
530
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
531
|
+
return {
|
|
532
|
+
content: [{ type: "text", text: `Failed to delete project: ${message}` }],
|
|
533
|
+
isError: true
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
);
|
|
538
|
+
server.registerTool(
|
|
539
|
+
"projects_members_list",
|
|
540
|
+
{
|
|
541
|
+
description: "List members of a project with optional pagination",
|
|
542
|
+
inputSchema: {
|
|
543
|
+
projectId: z2.string(),
|
|
544
|
+
limit: z2.number().int().min(1).max(100).optional(),
|
|
545
|
+
offset: z2.number().int().min(0).optional()
|
|
546
|
+
}
|
|
547
|
+
},
|
|
548
|
+
async ({ projectId, limit, offset }) => {
|
|
549
|
+
try {
|
|
550
|
+
const result = await api.get(`/projects/${encodeURIComponent(projectId)}/members`, {
|
|
551
|
+
params: { limit, offset }
|
|
552
|
+
});
|
|
553
|
+
return {
|
|
554
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
555
|
+
};
|
|
556
|
+
} catch (error) {
|
|
557
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
558
|
+
return {
|
|
559
|
+
content: [{ type: "text", text: `Failed to list members: ${message}` }],
|
|
560
|
+
isError: true
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
);
|
|
565
|
+
server.registerTool(
|
|
566
|
+
"projects_members_add",
|
|
567
|
+
{
|
|
568
|
+
description: "Add a member to a project by email with a specified role",
|
|
569
|
+
inputSchema: {
|
|
570
|
+
projectId: z2.string(),
|
|
571
|
+
email: z2.string().email(),
|
|
572
|
+
role: z2.enum(["owner", "admin", "developer", "member", "viewer"]),
|
|
573
|
+
permissions: z2.array(z2.string()).optional()
|
|
574
|
+
}
|
|
575
|
+
},
|
|
576
|
+
async ({ projectId, email, role, permissions }) => {
|
|
577
|
+
try {
|
|
578
|
+
const body = { email, role };
|
|
579
|
+
if (permissions !== void 0) body.permissions = permissions;
|
|
580
|
+
const result = await api.post(`/projects/${encodeURIComponent(projectId)}/members`, {
|
|
581
|
+
body
|
|
582
|
+
});
|
|
583
|
+
return {
|
|
584
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
585
|
+
};
|
|
586
|
+
} catch (error) {
|
|
587
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
588
|
+
return {
|
|
589
|
+
content: [{ type: "text", text: `Failed to add member: ${message}` }],
|
|
590
|
+
isError: true
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
);
|
|
595
|
+
server.registerTool(
|
|
596
|
+
"projects_members_update",
|
|
597
|
+
{
|
|
598
|
+
description: "Update a project member's role, permissions, or console access",
|
|
599
|
+
inputSchema: {
|
|
600
|
+
projectId: z2.string(),
|
|
601
|
+
memberId: z2.string(),
|
|
602
|
+
role: z2.enum(["owner", "admin", "developer", "member", "viewer"]).optional(),
|
|
603
|
+
permissions: z2.array(z2.string()).optional(),
|
|
604
|
+
grantConsoleAccess: z2.boolean().optional()
|
|
605
|
+
}
|
|
606
|
+
},
|
|
607
|
+
async ({ projectId, memberId, role, permissions, grantConsoleAccess }) => {
|
|
608
|
+
try {
|
|
609
|
+
const body = {};
|
|
610
|
+
if (role !== void 0) body.role = role;
|
|
611
|
+
if (permissions !== void 0) body.permissions = permissions;
|
|
612
|
+
if (grantConsoleAccess !== void 0) body.grantConsoleAccess = grantConsoleAccess;
|
|
613
|
+
if (Object.keys(body).length === 0) {
|
|
614
|
+
return {
|
|
615
|
+
content: [{ type: "text", text: "At least one field must be provided" }],
|
|
616
|
+
isError: true
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
const result = await api.patch(`/projects/${encodeURIComponent(projectId)}/members/${encodeURIComponent(memberId)}`, {
|
|
620
|
+
body
|
|
621
|
+
});
|
|
622
|
+
return {
|
|
623
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
624
|
+
};
|
|
625
|
+
} catch (error) {
|
|
626
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
627
|
+
return {
|
|
628
|
+
content: [{ type: "text", text: `Failed to update member: ${message}` }],
|
|
629
|
+
isError: true
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
);
|
|
634
|
+
server.registerTool(
|
|
635
|
+
"projects_members_remove",
|
|
636
|
+
{
|
|
637
|
+
description: "Remove a member from a project",
|
|
638
|
+
inputSchema: {
|
|
639
|
+
projectId: z2.string(),
|
|
640
|
+
memberId: z2.string()
|
|
641
|
+
}
|
|
642
|
+
},
|
|
643
|
+
async ({ projectId, memberId }) => {
|
|
644
|
+
try {
|
|
645
|
+
const result = await api.delete(`/projects/${encodeURIComponent(projectId)}/members/${encodeURIComponent(memberId)}`);
|
|
646
|
+
return {
|
|
647
|
+
content: [{ type: "text", text: JSON.stringify(result ?? "Member removed", null, 2) }]
|
|
648
|
+
};
|
|
649
|
+
} catch (error) {
|
|
650
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
651
|
+
return {
|
|
652
|
+
content: [{ type: "text", text: `Failed to remove member: ${message}` }],
|
|
653
|
+
isError: true
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
);
|
|
658
|
+
server.registerTool(
|
|
659
|
+
"projects_users_create",
|
|
660
|
+
{
|
|
661
|
+
description: "Create a new user within a project. WARNING: The password will be visible in the conversation context.",
|
|
662
|
+
inputSchema: {
|
|
663
|
+
projectId: z2.string(),
|
|
664
|
+
username: z2.string(),
|
|
665
|
+
email: z2.string().email(),
|
|
666
|
+
password: z2.string().min(1),
|
|
667
|
+
role: z2.enum(["owner", "admin", "developer", "member", "viewer"]),
|
|
668
|
+
firstName: z2.string().optional(),
|
|
669
|
+
lastName: z2.string().optional(),
|
|
670
|
+
displayName: z2.string().optional(),
|
|
671
|
+
permissions: z2.array(z2.string()).optional(),
|
|
672
|
+
grantConsoleAccess: z2.boolean().optional()
|
|
673
|
+
}
|
|
674
|
+
},
|
|
675
|
+
async ({ projectId, username, email, password, role, firstName, lastName, displayName, permissions, grantConsoleAccess }) => {
|
|
676
|
+
try {
|
|
677
|
+
const body = { username, email, password, role };
|
|
678
|
+
if (firstName !== void 0) body.firstName = firstName;
|
|
679
|
+
if (lastName !== void 0) body.lastName = lastName;
|
|
680
|
+
if (displayName !== void 0) body.displayName = displayName;
|
|
681
|
+
if (permissions !== void 0) body.permissions = permissions;
|
|
682
|
+
if (grantConsoleAccess !== void 0) body.grantConsoleAccess = grantConsoleAccess;
|
|
683
|
+
const result = await api.post(`/projects/${encodeURIComponent(projectId)}/users`, { body });
|
|
684
|
+
return {
|
|
685
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
686
|
+
};
|
|
687
|
+
} catch (error) {
|
|
688
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
689
|
+
return {
|
|
690
|
+
content: [{ type: "text", text: `Failed to create user: ${message}` }],
|
|
691
|
+
isError: true
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
);
|
|
696
|
+
server.registerTool(
|
|
697
|
+
"projects_role",
|
|
698
|
+
{
|
|
699
|
+
description: "Get the current user's role in a project",
|
|
700
|
+
inputSchema: {
|
|
701
|
+
projectId: z2.string()
|
|
702
|
+
}
|
|
703
|
+
},
|
|
704
|
+
async ({ projectId }) => {
|
|
705
|
+
try {
|
|
706
|
+
const result = await api.get(`/projects/${encodeURIComponent(projectId)}/role`);
|
|
707
|
+
return {
|
|
708
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
709
|
+
};
|
|
710
|
+
} catch (error) {
|
|
711
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
712
|
+
return {
|
|
713
|
+
content: [{ type: "text", text: `Failed to get role: ${message}` }],
|
|
714
|
+
isError: true
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// libs/mcp-server/src/tools/clients.ts
|
|
722
|
+
import { z as z3 } from "zod";
|
|
723
|
+
function registerClientTools(server, api) {
|
|
724
|
+
server.registerTool(
|
|
725
|
+
"clients_list",
|
|
726
|
+
{
|
|
727
|
+
description: "List all OAuth clients for a project",
|
|
728
|
+
inputSchema: {
|
|
729
|
+
projectId: z3.string()
|
|
730
|
+
}
|
|
731
|
+
},
|
|
732
|
+
async ({ projectId }) => {
|
|
733
|
+
try {
|
|
734
|
+
const result = await api.get(`/clients/project/${encodeURIComponent(projectId)}`);
|
|
735
|
+
return {
|
|
736
|
+
content: [{ type: "text", text: JSON.stringify(redactSecrets(result), null, 2) }]
|
|
737
|
+
};
|
|
738
|
+
} catch (error) {
|
|
739
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
740
|
+
return {
|
|
741
|
+
content: [{ type: "text", text: `Failed to list clients: ${message}` }],
|
|
742
|
+
isError: true
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
);
|
|
747
|
+
server.registerTool(
|
|
748
|
+
"clients_get",
|
|
749
|
+
{
|
|
750
|
+
description: "Get details of a specific OAuth client",
|
|
751
|
+
inputSchema: {
|
|
752
|
+
clientId: z3.string()
|
|
753
|
+
}
|
|
754
|
+
},
|
|
755
|
+
async ({ clientId }) => {
|
|
756
|
+
try {
|
|
757
|
+
const result = await api.get(`/clients/${encodeURIComponent(clientId)}`);
|
|
758
|
+
return {
|
|
759
|
+
content: [{ type: "text", text: JSON.stringify(redactSecrets(result), null, 2) }]
|
|
760
|
+
};
|
|
761
|
+
} catch (error) {
|
|
762
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
763
|
+
return {
|
|
764
|
+
content: [{ type: "text", text: `Failed to get client: ${message}` }],
|
|
765
|
+
isError: true
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
);
|
|
770
|
+
server.registerTool(
|
|
771
|
+
"clients_create",
|
|
772
|
+
{
|
|
773
|
+
description: "Create a new OAuth client for a project",
|
|
774
|
+
inputSchema: {
|
|
775
|
+
projectId: z3.string(),
|
|
776
|
+
name: z3.string(),
|
|
777
|
+
grantTypes: z3.array(z3.string()),
|
|
778
|
+
redirectUris: z3.array(z3.string().url()).optional(),
|
|
779
|
+
scopes: z3.array(z3.string()).optional(),
|
|
780
|
+
isConfidential: z3.boolean().optional(),
|
|
781
|
+
corsOrigins: z3.array(z3.string()).optional(),
|
|
782
|
+
accessTokenTtl: z3.number().optional()
|
|
783
|
+
}
|
|
784
|
+
},
|
|
785
|
+
async ({ projectId, name, grantTypes, redirectUris, scopes, isConfidential, corsOrigins, accessTokenTtl }) => {
|
|
786
|
+
try {
|
|
787
|
+
const body = { projectId, name, grantTypes };
|
|
788
|
+
if (redirectUris !== void 0) body.redirectUris = redirectUris;
|
|
789
|
+
if (scopes !== void 0) body.scopes = scopes;
|
|
790
|
+
if (isConfidential !== void 0) body.isConfidential = isConfidential;
|
|
791
|
+
if (corsOrigins !== void 0) body.corsOrigins = corsOrigins;
|
|
792
|
+
if (accessTokenTtl !== void 0) body.accessTokenTtl = accessTokenTtl;
|
|
793
|
+
const result = await api.post("/clients", { body });
|
|
794
|
+
return {
|
|
795
|
+
content: [{ type: "text", text: JSON.stringify(redactSecrets(result), null, 2) }]
|
|
796
|
+
};
|
|
797
|
+
} catch (error) {
|
|
798
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
799
|
+
return {
|
|
800
|
+
content: [{ type: "text", text: `Failed to create client: ${message}` }],
|
|
801
|
+
isError: true
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
);
|
|
806
|
+
server.registerTool(
|
|
807
|
+
"clients_update",
|
|
808
|
+
{
|
|
809
|
+
description: "Update an existing OAuth client",
|
|
810
|
+
inputSchema: {
|
|
811
|
+
clientId: z3.string(),
|
|
812
|
+
name: z3.string().optional(),
|
|
813
|
+
redirectUris: z3.array(z3.string().url()).optional(),
|
|
814
|
+
scopes: z3.array(z3.string()).optional(),
|
|
815
|
+
grantTypes: z3.array(z3.string()).optional(),
|
|
816
|
+
isActive: z3.boolean().optional(),
|
|
817
|
+
corsOrigins: z3.array(z3.string()).optional(),
|
|
818
|
+
accessTokenTtl: z3.number().optional()
|
|
819
|
+
}
|
|
820
|
+
},
|
|
821
|
+
async ({ clientId, name, redirectUris, scopes, grantTypes, isActive, corsOrigins, accessTokenTtl }) => {
|
|
822
|
+
try {
|
|
823
|
+
const body = {};
|
|
824
|
+
if (name !== void 0) body.name = name;
|
|
825
|
+
if (redirectUris !== void 0) body.redirectUris = redirectUris;
|
|
826
|
+
if (scopes !== void 0) body.scopes = scopes;
|
|
827
|
+
if (grantTypes !== void 0) body.grantTypes = grantTypes;
|
|
828
|
+
if (isActive !== void 0) body.isActive = isActive;
|
|
829
|
+
if (corsOrigins !== void 0) body.corsOrigins = corsOrigins;
|
|
830
|
+
if (accessTokenTtl !== void 0) body.accessTokenTtl = accessTokenTtl;
|
|
831
|
+
if (Object.keys(body).length === 0) {
|
|
832
|
+
return {
|
|
833
|
+
content: [{ type: "text", text: "At least one field must be provided" }],
|
|
834
|
+
isError: true
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
const result = await api.patch(`/clients/${encodeURIComponent(clientId)}`, { body });
|
|
838
|
+
return {
|
|
839
|
+
content: [{ type: "text", text: JSON.stringify(redactSecrets(result), null, 2) }]
|
|
840
|
+
};
|
|
841
|
+
} catch (error) {
|
|
842
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
843
|
+
return {
|
|
844
|
+
content: [{ type: "text", text: `Failed to update client: ${message}` }],
|
|
845
|
+
isError: true
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
);
|
|
850
|
+
server.registerTool(
|
|
851
|
+
"clients_delete",
|
|
852
|
+
{
|
|
853
|
+
description: "Delete an OAuth client",
|
|
854
|
+
inputSchema: {
|
|
855
|
+
clientId: z3.string()
|
|
856
|
+
}
|
|
857
|
+
},
|
|
858
|
+
async ({ clientId }) => {
|
|
859
|
+
try {
|
|
860
|
+
const result = await api.delete(`/clients/${encodeURIComponent(clientId)}`);
|
|
861
|
+
return {
|
|
862
|
+
content: [{ type: "text", text: JSON.stringify(result ?? "Client deleted", null, 2) }]
|
|
863
|
+
};
|
|
864
|
+
} catch (error) {
|
|
865
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
866
|
+
return {
|
|
867
|
+
content: [{ type: "text", text: `Failed to delete client: ${message}` }],
|
|
868
|
+
isError: true
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
);
|
|
873
|
+
server.registerTool(
|
|
874
|
+
"clients_regenerate_secret",
|
|
875
|
+
{
|
|
876
|
+
description: "Regenerate the secret for an OAuth client. WARNING: The new secret will be visible in the conversation context. This is the only time the secret can be retrieved.",
|
|
877
|
+
inputSchema: {
|
|
878
|
+
clientId: z3.string()
|
|
879
|
+
}
|
|
880
|
+
},
|
|
881
|
+
async ({ clientId }) => {
|
|
882
|
+
try {
|
|
883
|
+
const result = await api.post(`/clients/${encodeURIComponent(clientId)}/regenerate-secret`);
|
|
884
|
+
return {
|
|
885
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
886
|
+
};
|
|
887
|
+
} catch (error) {
|
|
888
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
889
|
+
return {
|
|
890
|
+
content: [{ type: "text", text: `Failed to regenerate client secret: ${message}` }],
|
|
891
|
+
isError: true
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// libs/mcp-server/src/tools/database.ts
|
|
899
|
+
import { z as z4 } from "zod";
|
|
900
|
+
function registerDatabaseTools(server, api) {
|
|
901
|
+
server.registerTool(
|
|
902
|
+
"database_collections_list",
|
|
903
|
+
{
|
|
904
|
+
description: "List all collections in a project database",
|
|
905
|
+
inputSchema: {
|
|
906
|
+
projectId: z4.string()
|
|
907
|
+
}
|
|
908
|
+
},
|
|
909
|
+
async ({ projectId }) => {
|
|
910
|
+
try {
|
|
911
|
+
const result = await api.get(`/databases/${encodeURIComponent(projectId)}/collections`);
|
|
912
|
+
return {
|
|
913
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
914
|
+
};
|
|
915
|
+
} catch (error) {
|
|
916
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
917
|
+
return {
|
|
918
|
+
content: [{ type: "text", text: `Failed to list collections: ${message}` }],
|
|
919
|
+
isError: true
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
);
|
|
924
|
+
server.registerTool(
|
|
925
|
+
"database_collections_create",
|
|
926
|
+
{
|
|
927
|
+
description: "Create a new collection in a project database",
|
|
928
|
+
inputSchema: {
|
|
929
|
+
projectId: z4.string(),
|
|
930
|
+
name: z4.string(),
|
|
931
|
+
description: z4.string().optional()
|
|
932
|
+
}
|
|
933
|
+
},
|
|
934
|
+
async ({ projectId, name, description }) => {
|
|
935
|
+
try {
|
|
936
|
+
const body = { name };
|
|
937
|
+
if (description !== void 0) body.description = description;
|
|
938
|
+
const result = await api.post(`/databases/${encodeURIComponent(projectId)}/collections`, { body });
|
|
939
|
+
return {
|
|
940
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
941
|
+
};
|
|
942
|
+
} catch (error) {
|
|
943
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
944
|
+
return {
|
|
945
|
+
content: [{ type: "text", text: `Failed to create collection: ${message}` }],
|
|
946
|
+
isError: true
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
);
|
|
951
|
+
server.registerTool(
|
|
952
|
+
"database_collections_delete",
|
|
953
|
+
{
|
|
954
|
+
description: "Delete a collection from a project database",
|
|
955
|
+
inputSchema: {
|
|
956
|
+
projectId: z4.string(),
|
|
957
|
+
name: z4.string()
|
|
958
|
+
}
|
|
959
|
+
},
|
|
960
|
+
async ({ projectId, name }) => {
|
|
961
|
+
try {
|
|
962
|
+
const result = await api.delete(`/databases/${encodeURIComponent(projectId)}/collections/${encodeURIComponent(name)}`);
|
|
963
|
+
return {
|
|
964
|
+
content: [{ type: "text", text: JSON.stringify(result ?? "Collection deleted", null, 2) }]
|
|
965
|
+
};
|
|
966
|
+
} catch (error) {
|
|
967
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
968
|
+
return {
|
|
969
|
+
content: [{ type: "text", text: `Failed to delete collection: ${message}` }],
|
|
970
|
+
isError: true
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
);
|
|
975
|
+
server.registerTool(
|
|
976
|
+
"database_rules_get",
|
|
977
|
+
{
|
|
978
|
+
description: "Get the security rules for a project database",
|
|
979
|
+
inputSchema: {
|
|
980
|
+
projectId: z4.string()
|
|
981
|
+
}
|
|
982
|
+
},
|
|
983
|
+
async ({ projectId }) => {
|
|
984
|
+
try {
|
|
985
|
+
const result = await api.get(`/databases/${encodeURIComponent(projectId)}/rules`);
|
|
986
|
+
return {
|
|
987
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
988
|
+
};
|
|
989
|
+
} catch (error) {
|
|
990
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
991
|
+
return {
|
|
992
|
+
content: [{ type: "text", text: `Failed to get rules: ${message}` }],
|
|
993
|
+
isError: true
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
);
|
|
998
|
+
server.registerTool(
|
|
999
|
+
"database_rules_set",
|
|
1000
|
+
{
|
|
1001
|
+
description: "Set security rules for a project database",
|
|
1002
|
+
inputSchema: {
|
|
1003
|
+
projectId: z4.string(),
|
|
1004
|
+
rules: z4.record(z4.string(), z4.unknown()),
|
|
1005
|
+
expectedVersion: z4.number().optional()
|
|
1006
|
+
}
|
|
1007
|
+
},
|
|
1008
|
+
async ({ projectId, rules, expectedVersion }) => {
|
|
1009
|
+
try {
|
|
1010
|
+
const body = { rules };
|
|
1011
|
+
if (expectedVersion !== void 0) body.expectedVersion = expectedVersion;
|
|
1012
|
+
const result = await api.put(`/databases/${encodeURIComponent(projectId)}/rules`, { body });
|
|
1013
|
+
return {
|
|
1014
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1015
|
+
};
|
|
1016
|
+
} catch (error) {
|
|
1017
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1018
|
+
return {
|
|
1019
|
+
content: [{ type: "text", text: `Failed to set rules: ${message}` }],
|
|
1020
|
+
isError: true
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
);
|
|
1025
|
+
server.registerTool(
|
|
1026
|
+
"database_rules_validate",
|
|
1027
|
+
{
|
|
1028
|
+
description: "Validate security rules without applying them",
|
|
1029
|
+
inputSchema: {
|
|
1030
|
+
projectId: z4.string(),
|
|
1031
|
+
rules: z4.record(z4.string(), z4.unknown())
|
|
1032
|
+
}
|
|
1033
|
+
},
|
|
1034
|
+
async ({ projectId, rules }) => {
|
|
1035
|
+
try {
|
|
1036
|
+
const result = await api.post(`/databases/${encodeURIComponent(projectId)}/rules/validate`, {
|
|
1037
|
+
body: { rules }
|
|
1038
|
+
});
|
|
1039
|
+
return {
|
|
1040
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1041
|
+
};
|
|
1042
|
+
} catch (error) {
|
|
1043
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1044
|
+
return {
|
|
1045
|
+
content: [{ type: "text", text: `Failed to validate rules: ${message}` }],
|
|
1046
|
+
isError: true
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
);
|
|
1051
|
+
server.registerTool(
|
|
1052
|
+
"database_index_rules_get",
|
|
1053
|
+
{
|
|
1054
|
+
description: "Get the index rules for a project database",
|
|
1055
|
+
inputSchema: {
|
|
1056
|
+
projectId: z4.string()
|
|
1057
|
+
}
|
|
1058
|
+
},
|
|
1059
|
+
async ({ projectId }) => {
|
|
1060
|
+
try {
|
|
1061
|
+
const result = await api.get(`/databases/${encodeURIComponent(projectId)}/index-rules`);
|
|
1062
|
+
return {
|
|
1063
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1064
|
+
};
|
|
1065
|
+
} catch (error) {
|
|
1066
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1067
|
+
return {
|
|
1068
|
+
content: [{ type: "text", text: `Failed to get index rules: ${message}` }],
|
|
1069
|
+
isError: true
|
|
1070
|
+
};
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
);
|
|
1074
|
+
server.registerTool(
|
|
1075
|
+
"database_index_rules_sync",
|
|
1076
|
+
{
|
|
1077
|
+
description: "Sync index rules for a project database",
|
|
1078
|
+
inputSchema: {
|
|
1079
|
+
projectId: z4.string(),
|
|
1080
|
+
indexRules: z4.record(z4.string(), z4.unknown())
|
|
1081
|
+
}
|
|
1082
|
+
},
|
|
1083
|
+
async ({ projectId, indexRules }) => {
|
|
1084
|
+
try {
|
|
1085
|
+
const result = await api.put(`/databases/${encodeURIComponent(projectId)}/index-rules`, {
|
|
1086
|
+
body: { indexRules }
|
|
1087
|
+
});
|
|
1088
|
+
return {
|
|
1089
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1090
|
+
};
|
|
1091
|
+
} catch (error) {
|
|
1092
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1093
|
+
return {
|
|
1094
|
+
content: [{ type: "text", text: `Failed to sync index rules: ${message}` }],
|
|
1095
|
+
isError: true
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
);
|
|
1100
|
+
server.registerTool(
|
|
1101
|
+
"database_indexes_list",
|
|
1102
|
+
{
|
|
1103
|
+
description: "List all indexes for a collection",
|
|
1104
|
+
inputSchema: {
|
|
1105
|
+
projectId: z4.string(),
|
|
1106
|
+
collectionName: z4.string()
|
|
1107
|
+
}
|
|
1108
|
+
},
|
|
1109
|
+
async ({ projectId, collectionName }) => {
|
|
1110
|
+
try {
|
|
1111
|
+
const result = await api.get(`/databases/${encodeURIComponent(projectId)}/collections/${encodeURIComponent(collectionName)}/indexes`);
|
|
1112
|
+
return {
|
|
1113
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1114
|
+
};
|
|
1115
|
+
} catch (error) {
|
|
1116
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1117
|
+
return {
|
|
1118
|
+
content: [{ type: "text", text: `Failed to list indexes: ${message}` }],
|
|
1119
|
+
isError: true
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
);
|
|
1124
|
+
server.registerTool(
|
|
1125
|
+
"database_indexes_create",
|
|
1126
|
+
{
|
|
1127
|
+
description: "Create a new index on a collection",
|
|
1128
|
+
inputSchema: {
|
|
1129
|
+
projectId: z4.string(),
|
|
1130
|
+
collectionName: z4.string(),
|
|
1131
|
+
fields: z4.record(z4.string(), z4.union([z4.literal(1), z4.literal(-1), z4.literal("text")])),
|
|
1132
|
+
unique: z4.boolean().optional(),
|
|
1133
|
+
sparse: z4.boolean().optional(),
|
|
1134
|
+
ttlSeconds: z4.number().optional()
|
|
1135
|
+
}
|
|
1136
|
+
},
|
|
1137
|
+
async ({ projectId, collectionName, fields, unique, sparse, ttlSeconds }) => {
|
|
1138
|
+
try {
|
|
1139
|
+
const body = { fields };
|
|
1140
|
+
if (unique !== void 0) body.unique = unique;
|
|
1141
|
+
if (sparse !== void 0) body.sparse = sparse;
|
|
1142
|
+
if (ttlSeconds !== void 0) body.ttlSeconds = ttlSeconds;
|
|
1143
|
+
const result = await api.post(`/databases/${encodeURIComponent(projectId)}/collections/${encodeURIComponent(collectionName)}/indexes`, { body });
|
|
1144
|
+
return {
|
|
1145
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1146
|
+
};
|
|
1147
|
+
} catch (error) {
|
|
1148
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1149
|
+
return {
|
|
1150
|
+
content: [{ type: "text", text: `Failed to create index: ${message}` }],
|
|
1151
|
+
isError: true
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
);
|
|
1156
|
+
server.registerTool(
|
|
1157
|
+
"database_indexes_delete",
|
|
1158
|
+
{
|
|
1159
|
+
description: "Delete an index from a collection",
|
|
1160
|
+
inputSchema: {
|
|
1161
|
+
projectId: z4.string(),
|
|
1162
|
+
collectionName: z4.string(),
|
|
1163
|
+
indexName: z4.string()
|
|
1164
|
+
}
|
|
1165
|
+
},
|
|
1166
|
+
async ({ projectId, collectionName, indexName }) => {
|
|
1167
|
+
try {
|
|
1168
|
+
const result = await api.delete(
|
|
1169
|
+
`/databases/${encodeURIComponent(projectId)}/collections/${encodeURIComponent(collectionName)}/indexes/${encodeURIComponent(indexName)}`
|
|
1170
|
+
);
|
|
1171
|
+
return {
|
|
1172
|
+
content: [{ type: "text", text: JSON.stringify(result ?? "Index deleted", null, 2) }]
|
|
1173
|
+
};
|
|
1174
|
+
} catch (error) {
|
|
1175
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1176
|
+
return {
|
|
1177
|
+
content: [{ type: "text", text: `Failed to delete index: ${message}` }],
|
|
1178
|
+
isError: true
|
|
1179
|
+
};
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
);
|
|
1183
|
+
server.registerTool(
|
|
1184
|
+
"database_documents_find",
|
|
1185
|
+
{
|
|
1186
|
+
description: "Query documents in a collection with optional filter, sort, field projection, limit (max 100), and offset",
|
|
1187
|
+
inputSchema: {
|
|
1188
|
+
projectId: z4.string(),
|
|
1189
|
+
collectionName: z4.string(),
|
|
1190
|
+
filter: z4.record(z4.string(), z4.unknown()).optional(),
|
|
1191
|
+
sort: z4.record(z4.string(), z4.union([z4.literal(1), z4.literal(-1)])).optional(),
|
|
1192
|
+
fields: z4.array(z4.string()).optional(),
|
|
1193
|
+
limit: z4.number().int().min(1).max(100).optional(),
|
|
1194
|
+
offset: z4.number().int().min(0).optional()
|
|
1195
|
+
}
|
|
1196
|
+
},
|
|
1197
|
+
async ({ projectId, collectionName, filter, sort, fields, limit, offset }) => {
|
|
1198
|
+
try {
|
|
1199
|
+
const params = {};
|
|
1200
|
+
if (filter !== void 0) params.filter = JSON.stringify(filter);
|
|
1201
|
+
if (sort !== void 0) params.sort = JSON.stringify(sort);
|
|
1202
|
+
if (fields !== void 0) params.fields = JSON.stringify(fields);
|
|
1203
|
+
if (limit !== void 0) params.limit = limit;
|
|
1204
|
+
if (offset !== void 0) params.offset = offset;
|
|
1205
|
+
const result = await api.get(
|
|
1206
|
+
`/databases/${encodeURIComponent(projectId)}/collections/${encodeURIComponent(collectionName)}/documents`,
|
|
1207
|
+
{ params }
|
|
1208
|
+
);
|
|
1209
|
+
return {
|
|
1210
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1211
|
+
};
|
|
1212
|
+
} catch (error) {
|
|
1213
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1214
|
+
return {
|
|
1215
|
+
content: [{ type: "text", text: `Failed to find documents: ${message}` }],
|
|
1216
|
+
isError: true
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
);
|
|
1221
|
+
server.registerTool(
|
|
1222
|
+
"database_documents_insert",
|
|
1223
|
+
{
|
|
1224
|
+
description: "Insert one or more documents into a collection",
|
|
1225
|
+
inputSchema: {
|
|
1226
|
+
projectId: z4.string(),
|
|
1227
|
+
collectionName: z4.string(),
|
|
1228
|
+
documents: z4.array(z4.record(z4.string(), z4.unknown())).min(1).max(500)
|
|
1229
|
+
}
|
|
1230
|
+
},
|
|
1231
|
+
async ({ projectId, collectionName, documents }) => {
|
|
1232
|
+
try {
|
|
1233
|
+
const result = await api.post(
|
|
1234
|
+
`/databases/${encodeURIComponent(projectId)}/collections/${encodeURIComponent(collectionName)}/documents`,
|
|
1235
|
+
{ body: { documents } }
|
|
1236
|
+
);
|
|
1237
|
+
return {
|
|
1238
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1239
|
+
};
|
|
1240
|
+
} catch (error) {
|
|
1241
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1242
|
+
return {
|
|
1243
|
+
content: [{ type: "text", text: `Failed to insert documents: ${message}` }],
|
|
1244
|
+
isError: true
|
|
1245
|
+
};
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
);
|
|
1249
|
+
server.registerTool(
|
|
1250
|
+
"database_documents_update",
|
|
1251
|
+
{
|
|
1252
|
+
description: "Update a specific document in a collection",
|
|
1253
|
+
inputSchema: {
|
|
1254
|
+
projectId: z4.string(),
|
|
1255
|
+
collectionName: z4.string(),
|
|
1256
|
+
docId: z4.string(),
|
|
1257
|
+
update: z4.record(z4.string(), z4.unknown())
|
|
1258
|
+
}
|
|
1259
|
+
},
|
|
1260
|
+
async ({ projectId, collectionName, docId, update }) => {
|
|
1261
|
+
try {
|
|
1262
|
+
const result = await api.patch(
|
|
1263
|
+
`/databases/${encodeURIComponent(projectId)}/collections/${encodeURIComponent(collectionName)}/documents/${encodeURIComponent(docId)}`,
|
|
1264
|
+
{ body: { update } }
|
|
1265
|
+
);
|
|
1266
|
+
return {
|
|
1267
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1268
|
+
};
|
|
1269
|
+
} catch (error) {
|
|
1270
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1271
|
+
return {
|
|
1272
|
+
content: [{ type: "text", text: `Failed to update document: ${message}` }],
|
|
1273
|
+
isError: true
|
|
1274
|
+
};
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
);
|
|
1278
|
+
server.registerTool(
|
|
1279
|
+
"database_documents_delete",
|
|
1280
|
+
{
|
|
1281
|
+
description: "Delete a specific document from a collection",
|
|
1282
|
+
inputSchema: {
|
|
1283
|
+
projectId: z4.string(),
|
|
1284
|
+
collectionName: z4.string(),
|
|
1285
|
+
docId: z4.string()
|
|
1286
|
+
}
|
|
1287
|
+
},
|
|
1288
|
+
async ({ projectId, collectionName, docId }) => {
|
|
1289
|
+
try {
|
|
1290
|
+
const result = await api.delete(
|
|
1291
|
+
`/databases/${encodeURIComponent(projectId)}/collections/${encodeURIComponent(collectionName)}/documents/${encodeURIComponent(docId)}`
|
|
1292
|
+
);
|
|
1293
|
+
return {
|
|
1294
|
+
content: [{ type: "text", text: JSON.stringify(result ?? "Document deleted", null, 2) }]
|
|
1295
|
+
};
|
|
1296
|
+
} catch (error) {
|
|
1297
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1298
|
+
return {
|
|
1299
|
+
content: [{ type: "text", text: `Failed to delete document: ${message}` }],
|
|
1300
|
+
isError: true
|
|
1301
|
+
};
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
);
|
|
1305
|
+
server.registerTool(
|
|
1306
|
+
"database_stats",
|
|
1307
|
+
{
|
|
1308
|
+
description: "Get database statistics for a project",
|
|
1309
|
+
inputSchema: {
|
|
1310
|
+
projectId: z4.string()
|
|
1311
|
+
}
|
|
1312
|
+
},
|
|
1313
|
+
async ({ projectId }) => {
|
|
1314
|
+
try {
|
|
1315
|
+
const result = await api.get(`/databases/${encodeURIComponent(projectId)}/stats`);
|
|
1316
|
+
return {
|
|
1317
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1318
|
+
};
|
|
1319
|
+
} catch (error) {
|
|
1320
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1321
|
+
return {
|
|
1322
|
+
content: [{ type: "text", text: `Failed to get database stats: ${message}` }],
|
|
1323
|
+
isError: true
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
);
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
// libs/mcp-server/src/tools/hosting.ts
|
|
1331
|
+
import { z as z5 } from "zod";
|
|
1332
|
+
function registerHostingTools(server, api) {
|
|
1333
|
+
server.registerTool(
|
|
1334
|
+
"hosting_deployments_list",
|
|
1335
|
+
{
|
|
1336
|
+
description: "List deployments for a hosting project",
|
|
1337
|
+
inputSchema: {
|
|
1338
|
+
projectId: z5.string(),
|
|
1339
|
+
limit: z5.number().int().min(1).max(100).optional(),
|
|
1340
|
+
offset: z5.number().int().min(0).optional()
|
|
1341
|
+
}
|
|
1342
|
+
},
|
|
1343
|
+
async ({ projectId, limit, offset }) => {
|
|
1344
|
+
try {
|
|
1345
|
+
const result = await api.get(`/hosting/projects/${encodeURIComponent(projectId)}/deployments`, {
|
|
1346
|
+
params: { limit, offset }
|
|
1347
|
+
});
|
|
1348
|
+
return {
|
|
1349
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1350
|
+
};
|
|
1351
|
+
} catch (error) {
|
|
1352
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1353
|
+
return {
|
|
1354
|
+
content: [{ type: "text", text: `Failed to list deployments: ${message}` }],
|
|
1355
|
+
isError: true
|
|
1356
|
+
};
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
);
|
|
1360
|
+
server.registerTool(
|
|
1361
|
+
"hosting_deployments_get",
|
|
1362
|
+
{
|
|
1363
|
+
description: "Get details of a specific deployment",
|
|
1364
|
+
inputSchema: {
|
|
1365
|
+
projectId: z5.string(),
|
|
1366
|
+
deploymentId: z5.string()
|
|
1367
|
+
}
|
|
1368
|
+
},
|
|
1369
|
+
async ({ projectId, deploymentId }) => {
|
|
1370
|
+
try {
|
|
1371
|
+
const result = await api.get(`/hosting/projects/${encodeURIComponent(projectId)}/deployments/${encodeURIComponent(deploymentId)}`);
|
|
1372
|
+
return {
|
|
1373
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1374
|
+
};
|
|
1375
|
+
} catch (error) {
|
|
1376
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1377
|
+
return {
|
|
1378
|
+
content: [{ type: "text", text: `Failed to get deployment: ${message}` }],
|
|
1379
|
+
isError: true
|
|
1380
|
+
};
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
);
|
|
1384
|
+
server.registerTool(
|
|
1385
|
+
"hosting_deployments_create",
|
|
1386
|
+
{
|
|
1387
|
+
description: "Create a new deployment for a hosting project",
|
|
1388
|
+
inputSchema: {
|
|
1389
|
+
projectId: z5.string(),
|
|
1390
|
+
description: z5.string().optional(),
|
|
1391
|
+
framework: z5.string().optional()
|
|
1392
|
+
}
|
|
1393
|
+
},
|
|
1394
|
+
async ({ projectId, description, framework }) => {
|
|
1395
|
+
try {
|
|
1396
|
+
const body = { projectId };
|
|
1397
|
+
if (description !== void 0) body.description = description;
|
|
1398
|
+
if (framework !== void 0) body.framework = framework;
|
|
1399
|
+
const result = await api.post("/hosting/deployments", { body });
|
|
1400
|
+
return {
|
|
1401
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1402
|
+
};
|
|
1403
|
+
} catch (error) {
|
|
1404
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1405
|
+
return {
|
|
1406
|
+
content: [{ type: "text", text: `Failed to create deployment: ${message}` }],
|
|
1407
|
+
isError: true
|
|
1408
|
+
};
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
);
|
|
1412
|
+
server.registerTool(
|
|
1413
|
+
"hosting_deployments_activate",
|
|
1414
|
+
{
|
|
1415
|
+
description: "Activate a specific deployment",
|
|
1416
|
+
inputSchema: {
|
|
1417
|
+
projectId: z5.string(),
|
|
1418
|
+
deploymentId: z5.string()
|
|
1419
|
+
}
|
|
1420
|
+
},
|
|
1421
|
+
async ({ projectId, deploymentId }) => {
|
|
1422
|
+
try {
|
|
1423
|
+
const result = await api.post(`/hosting/deployments/${encodeURIComponent(deploymentId)}/activate`, {
|
|
1424
|
+
body: { projectId }
|
|
1425
|
+
});
|
|
1426
|
+
return {
|
|
1427
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1428
|
+
};
|
|
1429
|
+
} catch (error) {
|
|
1430
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1431
|
+
return {
|
|
1432
|
+
content: [{ type: "text", text: `Failed to activate deployment: ${message}` }],
|
|
1433
|
+
isError: true
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
);
|
|
1438
|
+
server.registerTool(
|
|
1439
|
+
"hosting_deployments_delete",
|
|
1440
|
+
{
|
|
1441
|
+
description: "Delete a specific deployment",
|
|
1442
|
+
inputSchema: {
|
|
1443
|
+
projectId: z5.string(),
|
|
1444
|
+
deploymentId: z5.string()
|
|
1445
|
+
}
|
|
1446
|
+
},
|
|
1447
|
+
async ({ projectId, deploymentId }) => {
|
|
1448
|
+
try {
|
|
1449
|
+
const result = await api.delete(`/hosting/projects/${encodeURIComponent(projectId)}/deployments/${encodeURIComponent(deploymentId)}`);
|
|
1450
|
+
return {
|
|
1451
|
+
content: [{ type: "text", text: JSON.stringify(result ?? "Deployment deleted", null, 2) }]
|
|
1452
|
+
};
|
|
1453
|
+
} catch (error) {
|
|
1454
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1455
|
+
return {
|
|
1456
|
+
content: [{ type: "text", text: `Failed to delete deployment: ${message}` }],
|
|
1457
|
+
isError: true
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
);
|
|
1462
|
+
server.registerTool(
|
|
1463
|
+
"hosting_deployments_status",
|
|
1464
|
+
{
|
|
1465
|
+
description: "Get the status of a specific deployment",
|
|
1466
|
+
inputSchema: {
|
|
1467
|
+
projectId: z5.string(),
|
|
1468
|
+
deploymentId: z5.string()
|
|
1469
|
+
}
|
|
1470
|
+
},
|
|
1471
|
+
async ({ projectId, deploymentId }) => {
|
|
1472
|
+
try {
|
|
1473
|
+
const result = await api.get(`/hosting/projects/${encodeURIComponent(projectId)}/deployments/${encodeURIComponent(deploymentId)}/status`);
|
|
1474
|
+
return {
|
|
1475
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1476
|
+
};
|
|
1477
|
+
} catch (error) {
|
|
1478
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1479
|
+
return {
|
|
1480
|
+
content: [{ type: "text", text: `Failed to get deployment status: ${message}` }],
|
|
1481
|
+
isError: true
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
);
|
|
1486
|
+
server.registerTool(
|
|
1487
|
+
"hosting_config_get",
|
|
1488
|
+
{
|
|
1489
|
+
description: "Get hosting configuration for a project",
|
|
1490
|
+
inputSchema: {
|
|
1491
|
+
projectId: z5.string()
|
|
1492
|
+
}
|
|
1493
|
+
},
|
|
1494
|
+
async ({ projectId }) => {
|
|
1495
|
+
try {
|
|
1496
|
+
const result = await api.get(`/hosting/projects/${encodeURIComponent(projectId)}/config`);
|
|
1497
|
+
return {
|
|
1498
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1499
|
+
};
|
|
1500
|
+
} catch (error) {
|
|
1501
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1502
|
+
return {
|
|
1503
|
+
content: [{ type: "text", text: `Failed to get hosting config: ${message}` }],
|
|
1504
|
+
isError: true
|
|
1505
|
+
};
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
);
|
|
1509
|
+
server.registerTool(
|
|
1510
|
+
"hosting_config_update",
|
|
1511
|
+
{
|
|
1512
|
+
description: "Update hosting configuration for a project",
|
|
1513
|
+
inputSchema: {
|
|
1514
|
+
projectId: z5.string(),
|
|
1515
|
+
spaMode: z5.boolean().optional(),
|
|
1516
|
+
defaultFile: z5.string().optional(),
|
|
1517
|
+
headers: z5.record(z5.string(), z5.string()).optional(),
|
|
1518
|
+
enabled: z5.boolean().optional()
|
|
1519
|
+
}
|
|
1520
|
+
},
|
|
1521
|
+
async ({ projectId, spaMode, defaultFile, headers, enabled }) => {
|
|
1522
|
+
try {
|
|
1523
|
+
const body = {};
|
|
1524
|
+
if (spaMode !== void 0) body.spaMode = spaMode;
|
|
1525
|
+
if (defaultFile !== void 0) body.defaultFile = defaultFile;
|
|
1526
|
+
if (headers !== void 0) body.headers = headers;
|
|
1527
|
+
if (enabled !== void 0) body.enabled = enabled;
|
|
1528
|
+
if (Object.keys(body).length === 0) {
|
|
1529
|
+
return {
|
|
1530
|
+
content: [{ type: "text", text: "At least one config field must be provided" }],
|
|
1531
|
+
isError: true
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
1534
|
+
const result = await api.patch(`/hosting/projects/${encodeURIComponent(projectId)}/config`, {
|
|
1535
|
+
body
|
|
1536
|
+
});
|
|
1537
|
+
return {
|
|
1538
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1539
|
+
};
|
|
1540
|
+
} catch (error) {
|
|
1541
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1542
|
+
return {
|
|
1543
|
+
content: [{ type: "text", text: `Failed to update hosting config: ${message}` }],
|
|
1544
|
+
isError: true
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
);
|
|
1549
|
+
server.registerTool(
|
|
1550
|
+
"hosting_domains_list",
|
|
1551
|
+
{
|
|
1552
|
+
description: "List custom domains for a hosting project",
|
|
1553
|
+
inputSchema: {
|
|
1554
|
+
projectId: z5.string()
|
|
1555
|
+
}
|
|
1556
|
+
},
|
|
1557
|
+
async ({ projectId }) => {
|
|
1558
|
+
try {
|
|
1559
|
+
const result = await api.get(`/hosting/projects/${encodeURIComponent(projectId)}/domains`);
|
|
1560
|
+
return {
|
|
1561
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1562
|
+
};
|
|
1563
|
+
} catch (error) {
|
|
1564
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1565
|
+
return {
|
|
1566
|
+
content: [{ type: "text", text: `Failed to list domains: ${message}` }],
|
|
1567
|
+
isError: true
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
);
|
|
1572
|
+
server.registerTool(
|
|
1573
|
+
"hosting_domains_get",
|
|
1574
|
+
{
|
|
1575
|
+
description: "Get details of a specific custom domain",
|
|
1576
|
+
inputSchema: {
|
|
1577
|
+
projectId: z5.string(),
|
|
1578
|
+
domain: z5.string()
|
|
1579
|
+
}
|
|
1580
|
+
},
|
|
1581
|
+
async ({ projectId, domain }) => {
|
|
1582
|
+
try {
|
|
1583
|
+
const result = await api.get(`/hosting/projects/${encodeURIComponent(projectId)}/domains/${encodeURIComponent(domain)}`);
|
|
1584
|
+
return {
|
|
1585
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1586
|
+
};
|
|
1587
|
+
} catch (error) {
|
|
1588
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1589
|
+
return {
|
|
1590
|
+
content: [{ type: "text", text: `Failed to get domain: ${message}` }],
|
|
1591
|
+
isError: true
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
);
|
|
1596
|
+
server.registerTool(
|
|
1597
|
+
"hosting_domains_add",
|
|
1598
|
+
{
|
|
1599
|
+
description: "Add a custom domain to a hosting project",
|
|
1600
|
+
inputSchema: {
|
|
1601
|
+
projectId: z5.string(),
|
|
1602
|
+
domain: z5.string(),
|
|
1603
|
+
projectSlug: z5.string(),
|
|
1604
|
+
verificationMethod: z5.enum(["cname", "txt"]).optional()
|
|
1605
|
+
}
|
|
1606
|
+
},
|
|
1607
|
+
async ({ projectId, domain, projectSlug, verificationMethod }) => {
|
|
1608
|
+
try {
|
|
1609
|
+
const body = { domain, projectSlug };
|
|
1610
|
+
if (verificationMethod !== void 0) body.verificationMethod = verificationMethod;
|
|
1611
|
+
const result = await api.post(`/hosting/projects/${encodeURIComponent(projectId)}/domains`, { body });
|
|
1612
|
+
return {
|
|
1613
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1614
|
+
};
|
|
1615
|
+
} catch (error) {
|
|
1616
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1617
|
+
return {
|
|
1618
|
+
content: [{ type: "text", text: `Failed to add domain: ${message}` }],
|
|
1619
|
+
isError: true
|
|
1620
|
+
};
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
);
|
|
1624
|
+
server.registerTool(
|
|
1625
|
+
"hosting_domains_verify",
|
|
1626
|
+
{
|
|
1627
|
+
description: "Verify a custom domain for a hosting project",
|
|
1628
|
+
inputSchema: {
|
|
1629
|
+
projectId: z5.string(),
|
|
1630
|
+
domain: z5.string()
|
|
1631
|
+
}
|
|
1632
|
+
},
|
|
1633
|
+
async ({ projectId, domain }) => {
|
|
1634
|
+
try {
|
|
1635
|
+
const result = await api.post(`/hosting/projects/${encodeURIComponent(projectId)}/domains/${encodeURIComponent(domain)}/verify`);
|
|
1636
|
+
return {
|
|
1637
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1638
|
+
};
|
|
1639
|
+
} catch (error) {
|
|
1640
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1641
|
+
return {
|
|
1642
|
+
content: [{ type: "text", text: `Failed to verify domain: ${message}` }],
|
|
1643
|
+
isError: true
|
|
1644
|
+
};
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
);
|
|
1648
|
+
server.registerTool(
|
|
1649
|
+
"hosting_domains_remove",
|
|
1650
|
+
{
|
|
1651
|
+
description: "Remove a custom domain from a hosting project",
|
|
1652
|
+
inputSchema: {
|
|
1653
|
+
projectId: z5.string(),
|
|
1654
|
+
domain: z5.string()
|
|
1655
|
+
}
|
|
1656
|
+
},
|
|
1657
|
+
async ({ projectId, domain }) => {
|
|
1658
|
+
try {
|
|
1659
|
+
const result = await api.delete(`/hosting/projects/${encodeURIComponent(projectId)}/domains/${encodeURIComponent(domain)}`);
|
|
1660
|
+
return {
|
|
1661
|
+
content: [{ type: "text", text: JSON.stringify(result ?? "Domain removed", null, 2) }]
|
|
1662
|
+
};
|
|
1663
|
+
} catch (error) {
|
|
1664
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1665
|
+
return {
|
|
1666
|
+
content: [{ type: "text", text: `Failed to remove domain: ${message}` }],
|
|
1667
|
+
isError: true
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
);
|
|
1672
|
+
server.registerTool(
|
|
1673
|
+
"hosting_domains_set_primary",
|
|
1674
|
+
{
|
|
1675
|
+
description: "Set a custom domain as the primary domain for a hosting project",
|
|
1676
|
+
inputSchema: {
|
|
1677
|
+
projectId: z5.string(),
|
|
1678
|
+
domain: z5.string()
|
|
1679
|
+
}
|
|
1680
|
+
},
|
|
1681
|
+
async ({ projectId, domain }) => {
|
|
1682
|
+
try {
|
|
1683
|
+
const result = await api.patch(`/hosting/projects/${encodeURIComponent(projectId)}/domains/${encodeURIComponent(domain)}/primary`);
|
|
1684
|
+
return {
|
|
1685
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1686
|
+
};
|
|
1687
|
+
} catch (error) {
|
|
1688
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1689
|
+
return {
|
|
1690
|
+
content: [{ type: "text", text: `Failed to set primary domain: ${message}` }],
|
|
1691
|
+
isError: true
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
);
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
// libs/mcp-server/src/tools/storage.ts
|
|
1699
|
+
import { z as z6 } from "zod";
|
|
1700
|
+
function registerStorageTools(server, api) {
|
|
1701
|
+
server.registerTool(
|
|
1702
|
+
"storage_files_list",
|
|
1703
|
+
{
|
|
1704
|
+
description: "List files in storage for a project and user",
|
|
1705
|
+
inputSchema: {
|
|
1706
|
+
projectId: z6.string(),
|
|
1707
|
+
userId: z6.string(),
|
|
1708
|
+
limit: z6.number().int().min(1).max(100).optional(),
|
|
1709
|
+
offset: z6.number().int().min(0).optional()
|
|
1710
|
+
}
|
|
1711
|
+
},
|
|
1712
|
+
async ({ projectId, userId, limit, offset }) => {
|
|
1713
|
+
try {
|
|
1714
|
+
const result = await api.get("/storage/files", {
|
|
1715
|
+
params: { projectId, userId, limit, offset }
|
|
1716
|
+
});
|
|
1717
|
+
return {
|
|
1718
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1719
|
+
};
|
|
1720
|
+
} catch (error) {
|
|
1721
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1722
|
+
return {
|
|
1723
|
+
content: [{ type: "text", text: `Failed to list files: ${message}` }],
|
|
1724
|
+
isError: true
|
|
1725
|
+
};
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
);
|
|
1729
|
+
server.registerTool(
|
|
1730
|
+
"storage_files_get",
|
|
1731
|
+
{
|
|
1732
|
+
description: "Get details of a specific file in storage",
|
|
1733
|
+
inputSchema: {
|
|
1734
|
+
projectId: z6.string(),
|
|
1735
|
+
userId: z6.string(),
|
|
1736
|
+
fileId: z6.string()
|
|
1737
|
+
}
|
|
1738
|
+
},
|
|
1739
|
+
async ({ projectId, userId, fileId }) => {
|
|
1740
|
+
try {
|
|
1741
|
+
const result = await api.get(`/storage/files/${encodeURIComponent(fileId)}`, {
|
|
1742
|
+
params: { projectId, userId }
|
|
1743
|
+
});
|
|
1744
|
+
return {
|
|
1745
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1746
|
+
};
|
|
1747
|
+
} catch (error) {
|
|
1748
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1749
|
+
return {
|
|
1750
|
+
content: [{ type: "text", text: `Failed to get file: ${message}` }],
|
|
1751
|
+
isError: true
|
|
1752
|
+
};
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
);
|
|
1756
|
+
server.registerTool(
|
|
1757
|
+
"storage_files_delete",
|
|
1758
|
+
{
|
|
1759
|
+
description: "Delete a specific file from storage",
|
|
1760
|
+
inputSchema: {
|
|
1761
|
+
projectId: z6.string(),
|
|
1762
|
+
userId: z6.string(),
|
|
1763
|
+
fileId: z6.string()
|
|
1764
|
+
}
|
|
1765
|
+
},
|
|
1766
|
+
async ({ projectId, userId, fileId }) => {
|
|
1767
|
+
try {
|
|
1768
|
+
const result = await api.delete(`/storage/files/${encodeURIComponent(fileId)}`, {
|
|
1769
|
+
params: { projectId, userId }
|
|
1770
|
+
});
|
|
1771
|
+
return {
|
|
1772
|
+
content: [{ type: "text", text: JSON.stringify(result ?? "File deleted", null, 2) }]
|
|
1773
|
+
};
|
|
1774
|
+
} catch (error) {
|
|
1775
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1776
|
+
return {
|
|
1777
|
+
content: [{ type: "text", text: `Failed to delete file: ${message}` }],
|
|
1778
|
+
isError: true
|
|
1779
|
+
};
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
);
|
|
1783
|
+
server.registerTool(
|
|
1784
|
+
"storage_files_visibility",
|
|
1785
|
+
{
|
|
1786
|
+
description: "Update the visibility of a specific file",
|
|
1787
|
+
inputSchema: {
|
|
1788
|
+
projectId: z6.string(),
|
|
1789
|
+
userId: z6.string(),
|
|
1790
|
+
fileId: z6.string(),
|
|
1791
|
+
visibility: z6.enum(["public", "private"])
|
|
1792
|
+
}
|
|
1793
|
+
},
|
|
1794
|
+
async ({ projectId, userId, fileId, visibility }) => {
|
|
1795
|
+
try {
|
|
1796
|
+
const result = await api.patch(`/storage/files/${encodeURIComponent(fileId)}/visibility`, {
|
|
1797
|
+
params: { projectId, userId },
|
|
1798
|
+
body: { visibility }
|
|
1799
|
+
});
|
|
1800
|
+
return {
|
|
1801
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1802
|
+
};
|
|
1803
|
+
} catch (error) {
|
|
1804
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1805
|
+
return {
|
|
1806
|
+
content: [{ type: "text", text: `Failed to update file visibility: ${message}` }],
|
|
1807
|
+
isError: true
|
|
1808
|
+
};
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
);
|
|
1812
|
+
server.registerTool(
|
|
1813
|
+
"storage_files_share",
|
|
1814
|
+
{
|
|
1815
|
+
description: "Share a file with other users",
|
|
1816
|
+
inputSchema: {
|
|
1817
|
+
projectId: z6.string(),
|
|
1818
|
+
userId: z6.string(),
|
|
1819
|
+
fileId: z6.string(),
|
|
1820
|
+
userIds: z6.array(z6.string()),
|
|
1821
|
+
permission: z6.enum(["read", "write"]).optional()
|
|
1822
|
+
}
|
|
1823
|
+
},
|
|
1824
|
+
async ({ projectId, userId, fileId, userIds, permission }) => {
|
|
1825
|
+
try {
|
|
1826
|
+
const result = await api.post(`/storage/files/${encodeURIComponent(fileId)}/share`, {
|
|
1827
|
+
params: { projectId, userId },
|
|
1828
|
+
body: { userIds, permission }
|
|
1829
|
+
});
|
|
1830
|
+
return {
|
|
1831
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1832
|
+
};
|
|
1833
|
+
} catch (error) {
|
|
1834
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1835
|
+
return {
|
|
1836
|
+
content: [{ type: "text", text: `Failed to share file: ${message}` }],
|
|
1837
|
+
isError: true
|
|
1838
|
+
};
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
);
|
|
1842
|
+
server.registerTool(
|
|
1843
|
+
"storage_files_unshare",
|
|
1844
|
+
{
|
|
1845
|
+
description: "Remove file sharing for specific users",
|
|
1846
|
+
inputSchema: {
|
|
1847
|
+
projectId: z6.string(),
|
|
1848
|
+
userId: z6.string(),
|
|
1849
|
+
fileId: z6.string(),
|
|
1850
|
+
userIds: z6.array(z6.string())
|
|
1851
|
+
}
|
|
1852
|
+
},
|
|
1853
|
+
async ({ projectId, userId, fileId, userIds }) => {
|
|
1854
|
+
try {
|
|
1855
|
+
const result = await api.post(`/storage/files/${encodeURIComponent(fileId)}/unshare`, {
|
|
1856
|
+
params: { projectId, userId },
|
|
1857
|
+
body: { userIds }
|
|
1858
|
+
});
|
|
1859
|
+
return {
|
|
1860
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1861
|
+
};
|
|
1862
|
+
} catch (error) {
|
|
1863
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1864
|
+
return {
|
|
1865
|
+
content: [{ type: "text", text: `Failed to unshare file: ${message}` }],
|
|
1866
|
+
isError: true
|
|
1867
|
+
};
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
);
|
|
1871
|
+
server.registerTool(
|
|
1872
|
+
"storage_quota_get",
|
|
1873
|
+
{
|
|
1874
|
+
description: "Get storage quota information for a user in a project",
|
|
1875
|
+
inputSchema: {
|
|
1876
|
+
projectId: z6.string(),
|
|
1877
|
+
userId: z6.string()
|
|
1878
|
+
}
|
|
1879
|
+
},
|
|
1880
|
+
async ({ projectId, userId }) => {
|
|
1881
|
+
try {
|
|
1882
|
+
const result = await api.get("/storage/quota", {
|
|
1883
|
+
params: { projectId, userId }
|
|
1884
|
+
});
|
|
1885
|
+
return {
|
|
1886
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1887
|
+
};
|
|
1888
|
+
} catch (error) {
|
|
1889
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1890
|
+
return {
|
|
1891
|
+
content: [{ type: "text", text: `Failed to get quota: ${message}` }],
|
|
1892
|
+
isError: true
|
|
1893
|
+
};
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
);
|
|
1897
|
+
server.registerTool(
|
|
1898
|
+
"storage_quota_set",
|
|
1899
|
+
{
|
|
1900
|
+
description: "Set storage quota for a user in a project",
|
|
1901
|
+
inputSchema: {
|
|
1902
|
+
projectId: z6.string(),
|
|
1903
|
+
targetUserId: z6.string(),
|
|
1904
|
+
quotaBytes: z6.number().int().min(0)
|
|
1905
|
+
}
|
|
1906
|
+
},
|
|
1907
|
+
async ({ projectId, targetUserId, quotaBytes }) => {
|
|
1908
|
+
try {
|
|
1909
|
+
const result = await api.put("/storage/quota", {
|
|
1910
|
+
body: { projectId, targetUserId, quotaBytes }
|
|
1911
|
+
});
|
|
1912
|
+
return {
|
|
1913
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1914
|
+
};
|
|
1915
|
+
} catch (error) {
|
|
1916
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1917
|
+
return {
|
|
1918
|
+
content: [{ type: "text", text: `Failed to set quota: ${message}` }],
|
|
1919
|
+
isError: true
|
|
1920
|
+
};
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
);
|
|
1924
|
+
server.registerTool(
|
|
1925
|
+
"storage_project_stats",
|
|
1926
|
+
{
|
|
1927
|
+
description: "Get storage statistics for a project",
|
|
1928
|
+
inputSchema: {
|
|
1929
|
+
projectId: z6.string()
|
|
1930
|
+
}
|
|
1931
|
+
},
|
|
1932
|
+
async ({ projectId }) => {
|
|
1933
|
+
try {
|
|
1934
|
+
const result = await api.get("/storage/project-stats", {
|
|
1935
|
+
params: { projectId }
|
|
1936
|
+
});
|
|
1937
|
+
return {
|
|
1938
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
|
|
1939
|
+
};
|
|
1940
|
+
} catch (error) {
|
|
1941
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1942
|
+
return {
|
|
1943
|
+
content: [{ type: "text", text: `Failed to get project stats: ${message}` }],
|
|
1944
|
+
isError: true
|
|
1945
|
+
};
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
);
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
// libs/mcp-server/src/tools/index.ts
|
|
1952
|
+
function registerAllTools(server, api) {
|
|
1953
|
+
registerAuthTools(server, api);
|
|
1954
|
+
registerProjectTools(server, api);
|
|
1955
|
+
registerClientTools(server, api);
|
|
1956
|
+
registerDatabaseTools(server, api);
|
|
1957
|
+
registerHostingTools(server, api);
|
|
1958
|
+
registerStorageTools(server, api);
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
// libs/mcp-server/src/index.ts
|
|
1962
|
+
async function main() {
|
|
1963
|
+
const config = await loadConfig();
|
|
1964
|
+
const api = new ApiClient(config);
|
|
1965
|
+
const server = new McpServer({
|
|
1966
|
+
name: "spacelr",
|
|
1967
|
+
version: "0.1.0"
|
|
1968
|
+
});
|
|
1969
|
+
registerAllTools(server, api);
|
|
1970
|
+
const transport = new StdioServerTransport();
|
|
1971
|
+
await server.connect(transport);
|
|
1972
|
+
const shutdown = async () => {
|
|
1973
|
+
console.error("Shutting down MCP server...");
|
|
1974
|
+
await server.close();
|
|
1975
|
+
process.exit(0);
|
|
1976
|
+
};
|
|
1977
|
+
process.on("SIGINT", shutdown);
|
|
1978
|
+
process.on("SIGTERM", shutdown);
|
|
1979
|
+
process.on("SIGPIPE", shutdown);
|
|
1980
|
+
console.error("Spacelr MCP server running on stdio");
|
|
1981
|
+
}
|
|
1982
|
+
main().catch((error) => {
|
|
1983
|
+
console.error("Fatal error:", error);
|
|
1984
|
+
process.exit(1);
|
|
1985
|
+
});
|
|
1986
|
+
//# sourceMappingURL=index.mjs.map
|