@spacelr/mcp 0.0.2 → 0.0.4

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.mjs CHANGED
@@ -1948,6 +1948,368 @@ function registerStorageTools(server, api) {
1948
1948
  );
1949
1949
  }
1950
1950
 
1951
+ // libs/mcp-server/src/tools/functions.ts
1952
+ import { z as z7 } from "zod";
1953
+ function registerFunctionTools(server, api) {
1954
+ server.registerTool(
1955
+ "functions_list",
1956
+ {
1957
+ description: "List all functions for a project",
1958
+ inputSchema: {
1959
+ projectId: z7.string()
1960
+ }
1961
+ },
1962
+ async ({ projectId }) => {
1963
+ try {
1964
+ const result = await api.get(
1965
+ `/projects/${encodeURIComponent(projectId)}/functions`
1966
+ );
1967
+ return {
1968
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
1969
+ };
1970
+ } catch (error) {
1971
+ const message = error instanceof Error ? error.message : String(error);
1972
+ return {
1973
+ content: [{ type: "text", text: `Failed to list functions: ${message}` }],
1974
+ isError: true
1975
+ };
1976
+ }
1977
+ }
1978
+ );
1979
+ server.registerTool(
1980
+ "functions_get",
1981
+ {
1982
+ description: "Get details of a specific function",
1983
+ inputSchema: {
1984
+ projectId: z7.string(),
1985
+ functionId: z7.string()
1986
+ }
1987
+ },
1988
+ async ({ projectId, functionId }) => {
1989
+ try {
1990
+ const result = await api.get(
1991
+ `/projects/${encodeURIComponent(projectId)}/functions/${encodeURIComponent(functionId)}`
1992
+ );
1993
+ return {
1994
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
1995
+ };
1996
+ } catch (error) {
1997
+ const message = error instanceof Error ? error.message : String(error);
1998
+ return {
1999
+ content: [{ type: "text", text: `Failed to get function: ${message}` }],
2000
+ isError: true
2001
+ };
2002
+ }
2003
+ }
2004
+ );
2005
+ server.registerTool(
2006
+ "functions_create",
2007
+ {
2008
+ description: "Create a new serverless function in a project",
2009
+ inputSchema: {
2010
+ projectId: z7.string(),
2011
+ name: z7.string().min(1).max(255),
2012
+ description: z7.string().max(1e3).optional(),
2013
+ entryPoint: z7.string().max(255).optional(),
2014
+ cronExpression: z7.string().optional(),
2015
+ cronTimezone: z7.string().optional(),
2016
+ timeout: z7.number().int().min(1e3).max(12e4).optional(),
2017
+ memoryLimitMb: z7.number().int().min(16).max(512).optional(),
2018
+ enabled: z7.boolean().optional()
2019
+ }
2020
+ },
2021
+ async ({ projectId, ...body }) => {
2022
+ try {
2023
+ const result = await api.post(
2024
+ `/projects/${encodeURIComponent(projectId)}/functions`,
2025
+ { body }
2026
+ );
2027
+ return {
2028
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2029
+ };
2030
+ } catch (error) {
2031
+ const message = error instanceof Error ? error.message : String(error);
2032
+ return {
2033
+ content: [{ type: "text", text: `Failed to create function: ${message}` }],
2034
+ isError: true
2035
+ };
2036
+ }
2037
+ }
2038
+ );
2039
+ server.registerTool(
2040
+ "functions_update",
2041
+ {
2042
+ description: "Update an existing function",
2043
+ inputSchema: {
2044
+ projectId: z7.string(),
2045
+ functionId: z7.string(),
2046
+ name: z7.string().min(1).max(255).optional(),
2047
+ description: z7.string().max(1e3).optional(),
2048
+ entryPoint: z7.string().max(255).optional(),
2049
+ cronExpression: z7.string().optional(),
2050
+ cronTimezone: z7.string().optional(),
2051
+ timeout: z7.number().int().min(1e3).max(12e4).optional(),
2052
+ memoryLimitMb: z7.number().int().min(16).max(512).optional(),
2053
+ enabled: z7.boolean().optional()
2054
+ }
2055
+ },
2056
+ async ({ projectId, functionId, ...body }) => {
2057
+ try {
2058
+ const result = await api.patch(
2059
+ `/projects/${encodeURIComponent(projectId)}/functions/${encodeURIComponent(functionId)}`,
2060
+ { body }
2061
+ );
2062
+ return {
2063
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2064
+ };
2065
+ } catch (error) {
2066
+ const message = error instanceof Error ? error.message : String(error);
2067
+ return {
2068
+ content: [{ type: "text", text: `Failed to update function: ${message}` }],
2069
+ isError: true
2070
+ };
2071
+ }
2072
+ }
2073
+ );
2074
+ server.registerTool(
2075
+ "functions_delete",
2076
+ {
2077
+ description: "Delete a function and its associated cron job",
2078
+ inputSchema: {
2079
+ projectId: z7.string(),
2080
+ functionId: z7.string()
2081
+ }
2082
+ },
2083
+ async ({ projectId, functionId }) => {
2084
+ try {
2085
+ const result = await api.delete(
2086
+ `/projects/${encodeURIComponent(projectId)}/functions/${encodeURIComponent(functionId)}`
2087
+ );
2088
+ return {
2089
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2090
+ };
2091
+ } catch (error) {
2092
+ const message = error instanceof Error ? error.message : String(error);
2093
+ return {
2094
+ content: [{ type: "text", text: `Failed to delete function: ${message}` }],
2095
+ isError: true
2096
+ };
2097
+ }
2098
+ }
2099
+ );
2100
+ server.registerTool(
2101
+ "functions_get_code",
2102
+ {
2103
+ description: "Get the deployed source code of a function with syntax-highlighted file contents",
2104
+ inputSchema: {
2105
+ projectId: z7.string(),
2106
+ functionId: z7.string()
2107
+ }
2108
+ },
2109
+ async ({ projectId, functionId }) => {
2110
+ try {
2111
+ const result = await api.get(
2112
+ `/projects/${encodeURIComponent(projectId)}/functions/${encodeURIComponent(functionId)}/code`
2113
+ );
2114
+ return {
2115
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2116
+ };
2117
+ } catch (error) {
2118
+ const message = error instanceof Error ? error.message : String(error);
2119
+ return {
2120
+ content: [{ type: "text", text: `Failed to get function code: ${message}` }],
2121
+ isError: true
2122
+ };
2123
+ }
2124
+ }
2125
+ );
2126
+ server.registerTool(
2127
+ "functions_deployments_list",
2128
+ {
2129
+ description: "List all deployments (versions) of a function",
2130
+ inputSchema: {
2131
+ projectId: z7.string(),
2132
+ functionId: z7.string()
2133
+ }
2134
+ },
2135
+ async ({ projectId, functionId }) => {
2136
+ try {
2137
+ const result = await api.get(
2138
+ `/projects/${encodeURIComponent(projectId)}/functions/${encodeURIComponent(functionId)}/deployments`
2139
+ );
2140
+ return {
2141
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2142
+ };
2143
+ } catch (error) {
2144
+ const message = error instanceof Error ? error.message : String(error);
2145
+ return {
2146
+ content: [{ type: "text", text: `Failed to list deployments: ${message}` }],
2147
+ isError: true
2148
+ };
2149
+ }
2150
+ }
2151
+ );
2152
+ server.registerTool(
2153
+ "functions_trigger",
2154
+ {
2155
+ description: "Manually trigger a function execution",
2156
+ inputSchema: {
2157
+ projectId: z7.string(),
2158
+ functionId: z7.string()
2159
+ }
2160
+ },
2161
+ async ({ projectId, functionId }) => {
2162
+ try {
2163
+ const result = await api.post(
2164
+ `/projects/${encodeURIComponent(projectId)}/functions/${encodeURIComponent(functionId)}/trigger`
2165
+ );
2166
+ return {
2167
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2168
+ };
2169
+ } catch (error) {
2170
+ const message = error instanceof Error ? error.message : String(error);
2171
+ return {
2172
+ content: [{ type: "text", text: `Failed to trigger function: ${message}` }],
2173
+ isError: true
2174
+ };
2175
+ }
2176
+ }
2177
+ );
2178
+ server.registerTool(
2179
+ "functions_executions_list",
2180
+ {
2181
+ description: "List recent executions of a function with status, duration, and logs",
2182
+ inputSchema: {
2183
+ projectId: z7.string(),
2184
+ functionId: z7.string(),
2185
+ limit: z7.number().int().min(1).max(100).optional(),
2186
+ offset: z7.number().int().min(0).optional()
2187
+ }
2188
+ },
2189
+ async ({ projectId, functionId, limit, offset }) => {
2190
+ try {
2191
+ const result = await api.get(
2192
+ `/projects/${encodeURIComponent(projectId)}/functions/${encodeURIComponent(functionId)}/executions`,
2193
+ { params: { limit, offset } }
2194
+ );
2195
+ return {
2196
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2197
+ };
2198
+ } catch (error) {
2199
+ const message = error instanceof Error ? error.message : String(error);
2200
+ return {
2201
+ content: [{ type: "text", text: `Failed to list executions: ${message}` }],
2202
+ isError: true
2203
+ };
2204
+ }
2205
+ }
2206
+ );
2207
+ server.registerTool(
2208
+ "functions_enable_webhook",
2209
+ {
2210
+ description: "Enable webhook trigger for a function. Returns a one-time webhook secret.",
2211
+ inputSchema: {
2212
+ projectId: z7.string(),
2213
+ functionId: z7.string()
2214
+ }
2215
+ },
2216
+ async ({ projectId, functionId }) => {
2217
+ try {
2218
+ const result = await api.post(
2219
+ `/projects/${encodeURIComponent(projectId)}/functions/${encodeURIComponent(functionId)}/webhook/enable`
2220
+ );
2221
+ return {
2222
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2223
+ };
2224
+ } catch (error) {
2225
+ const message = error instanceof Error ? error.message : String(error);
2226
+ return {
2227
+ content: [{ type: "text", text: `Failed to enable webhook: ${message}` }],
2228
+ isError: true
2229
+ };
2230
+ }
2231
+ }
2232
+ );
2233
+ server.registerTool(
2234
+ "functions_regenerate_webhook_secret",
2235
+ {
2236
+ description: "Regenerate the webhook secret for a function. Old secret stops working immediately.",
2237
+ inputSchema: {
2238
+ projectId: z7.string(),
2239
+ functionId: z7.string()
2240
+ }
2241
+ },
2242
+ async ({ projectId, functionId }) => {
2243
+ try {
2244
+ const result = await api.post(
2245
+ `/projects/${encodeURIComponent(projectId)}/functions/${encodeURIComponent(functionId)}/webhook/regenerate-secret`
2246
+ );
2247
+ return {
2248
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2249
+ };
2250
+ } catch (error) {
2251
+ const message = error instanceof Error ? error.message : String(error);
2252
+ return {
2253
+ content: [{ type: "text", text: `Failed to regenerate webhook secret: ${message}` }],
2254
+ isError: true
2255
+ };
2256
+ }
2257
+ }
2258
+ );
2259
+ server.registerTool(
2260
+ "functions_disable_webhook",
2261
+ {
2262
+ description: "Disable webhook trigger for a function. The webhook secret is deleted.",
2263
+ inputSchema: {
2264
+ projectId: z7.string(),
2265
+ functionId: z7.string()
2266
+ }
2267
+ },
2268
+ async ({ projectId, functionId }) => {
2269
+ try {
2270
+ const result = await api.post(
2271
+ `/projects/${encodeURIComponent(projectId)}/functions/${encodeURIComponent(functionId)}/webhook/disable`
2272
+ );
2273
+ return {
2274
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2275
+ };
2276
+ } catch (error) {
2277
+ const message = error instanceof Error ? error.message : String(error);
2278
+ return {
2279
+ content: [{ type: "text", text: `Failed to disable webhook: ${message}` }],
2280
+ isError: true
2281
+ };
2282
+ }
2283
+ }
2284
+ );
2285
+ server.registerTool(
2286
+ "functions_kv_list",
2287
+ {
2288
+ description: "List KV store keys for a function",
2289
+ inputSchema: {
2290
+ projectId: z7.string(),
2291
+ functionId: z7.string()
2292
+ }
2293
+ },
2294
+ async ({ projectId, functionId }) => {
2295
+ try {
2296
+ const result = await api.get(
2297
+ `/projects/${encodeURIComponent(projectId)}/functions/${encodeURIComponent(functionId)}/kv`
2298
+ );
2299
+ return {
2300
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2301
+ };
2302
+ } catch (error) {
2303
+ const message = error instanceof Error ? error.message : String(error);
2304
+ return {
2305
+ content: [{ type: "text", text: `Failed to list KV keys: ${message}` }],
2306
+ isError: true
2307
+ };
2308
+ }
2309
+ }
2310
+ );
2311
+ }
2312
+
1951
2313
  // libs/mcp-server/src/tools/index.ts
1952
2314
  function registerAllTools(server, api) {
1953
2315
  registerAuthTools(server, api);
@@ -1956,6 +2318,289 @@ function registerAllTools(server, api) {
1956
2318
  registerDatabaseTools(server, api);
1957
2319
  registerHostingTools(server, api);
1958
2320
  registerStorageTools(server, api);
2321
+ registerFunctionTools(server, api);
2322
+ }
2323
+
2324
+ // libs/mcp-server/src/prompts/database.ts
2325
+ import { z as z8 } from "zod";
2326
+ function registerDatabasePrompts(server) {
2327
+ server.registerPrompt(
2328
+ "setup-collection",
2329
+ {
2330
+ title: "Setup Database Collection",
2331
+ description: "Guide for creating a new database collection with proper security rules. Collections MUST have rules defined before data can be inserted.",
2332
+ argsSchema: {
2333
+ projectId: z8.string().describe("Project ID"),
2334
+ collectionName: z8.string().describe("Name of the collection to create"),
2335
+ access: z8.enum(["public-read", "authenticated", "admin-only"]).optional().describe("Access level (default: admin-only)")
2336
+ }
2337
+ },
2338
+ async ({ projectId, collectionName, access }) => {
2339
+ const accessLevel = access || "admin-only";
2340
+ const rules = {
2341
+ "public-read": {
2342
+ ".create": "true",
2343
+ ".read": "true",
2344
+ ".update": "false",
2345
+ ".delete": "auth.role === 'owner' || auth.role === 'admin'"
2346
+ },
2347
+ authenticated: {
2348
+ ".create": "!!auth.uid",
2349
+ ".read": "!!auth.uid",
2350
+ ".update": "!!auth.uid",
2351
+ ".delete": "auth.role === 'owner' || auth.role === 'admin'"
2352
+ },
2353
+ "admin-only": {
2354
+ ".create": "auth.role === 'owner' || auth.role === 'admin'",
2355
+ ".read": "auth.role === 'owner' || auth.role === 'admin'",
2356
+ ".update": "auth.role === 'owner' || auth.role === 'admin'",
2357
+ ".delete": "auth.role === 'owner' || auth.role === 'admin'"
2358
+ }
2359
+ };
2360
+ const collectionRules = rules[accessLevel];
2361
+ const rulesJson = JSON.stringify(collectionRules, null, 2);
2362
+ return {
2363
+ messages: [
2364
+ {
2365
+ role: "user",
2366
+ content: {
2367
+ type: "text",
2368
+ text: [
2369
+ `Set up a new database collection "${collectionName}" in project ${projectId}.`,
2370
+ "",
2371
+ "IMPORTANT WORKFLOW:",
2372
+ "1. First, get the current rules with database_rules_get",
2373
+ "2. Add the new collection rules to the existing rules object",
2374
+ "3. Set the updated rules with database_rules_set \u2014 this automatically creates the collection",
2375
+ '4. Do NOT try to insert documents before the rules are set \u2014 it will fail with "Rule denied"',
2376
+ "",
2377
+ `Suggested rules for "${collectionName}" (${accessLevel}):`,
2378
+ rulesJson,
2379
+ "",
2380
+ "The $other catch-all rule should deny access to undefined collections:",
2381
+ ' "$other": { ".read": "false", ".write": "false" }',
2382
+ "",
2383
+ "After rules are set, the collection is ready for use via:",
2384
+ "- database_documents_insert",
2385
+ "- database_documents_find",
2386
+ '- Serverless functions using spacelr.db.collection("' + collectionName + '")'
2387
+ ].join("\n")
2388
+ }
2389
+ }
2390
+ ]
2391
+ };
2392
+ }
2393
+ );
2394
+ server.registerPrompt(
2395
+ "database-workflow",
2396
+ {
2397
+ title: "Database Workflow Guide",
2398
+ description: "Explains how the Spacelr database system works: rules-first approach, collection lifecycle, and security model.",
2399
+ argsSchema: {
2400
+ projectId: z8.string().describe("Project ID")
2401
+ }
2402
+ },
2403
+ async ({ projectId }) => {
2404
+ return {
2405
+ messages: [
2406
+ {
2407
+ role: "user",
2408
+ content: {
2409
+ type: "text",
2410
+ text: [
2411
+ `Guide me through the Spacelr database system for project ${projectId}.`,
2412
+ "",
2413
+ "CORE CONCEPTS:",
2414
+ "",
2415
+ "1. RULES-FIRST: Collections are defined through security rules.",
2416
+ " - Use database_rules_get to see current rules",
2417
+ " - Use database_rules_set to add/modify collection rules",
2418
+ " - Setting rules automatically creates the collection definition",
2419
+ " - Without rules, all operations are denied by the $other catch-all",
2420
+ "",
2421
+ "2. SECURITY RULES SYNTAX:",
2422
+ ' - ".create": Expression that must evaluate to true for inserts',
2423
+ ' - ".read": Expression for reads/queries',
2424
+ ' - ".update": Expression for document updates',
2425
+ ' - ".delete": Expression for document deletion',
2426
+ ' - ".validate": Per-field validation expressions',
2427
+ ' - ".schema": Field type declarations',
2428
+ " Available context: auth.uid, auth.role, newData (for validation)",
2429
+ "",
2430
+ "3. WORKFLOW:",
2431
+ " a) Define rules for the collection",
2432
+ " b) Optionally create indexes (database_indexes_create)",
2433
+ " c) Insert/query documents",
2434
+ "",
2435
+ "4. DATA ISOLATION:",
2436
+ " - Each project has its own database (project_{projectId})",
2437
+ " - Collections are scoped to the project automatically",
2438
+ " - No cross-project data access is possible"
2439
+ ].join("\n")
2440
+ }
2441
+ }
2442
+ ]
2443
+ };
2444
+ }
2445
+ );
2446
+ }
2447
+
2448
+ // libs/mcp-server/src/prompts/functions.ts
2449
+ import { z as z9 } from "zod";
2450
+ function registerFunctionPrompts(server) {
2451
+ server.registerPrompt(
2452
+ "deploy-function",
2453
+ {
2454
+ title: "Deploy a Serverless Function",
2455
+ description: "Step-by-step guide for creating, deploying, and testing a serverless function. Covers the full lifecycle from creation to execution.",
2456
+ argsSchema: {
2457
+ projectId: z9.string().describe("Project ID"),
2458
+ name: z9.string().describe("Function name"),
2459
+ useCase: z9.string().optional().describe('What the function should do (e.g. "fetch data from API and store in DB")')
2460
+ }
2461
+ },
2462
+ async ({ projectId, name, useCase }) => {
2463
+ return {
2464
+ messages: [
2465
+ {
2466
+ role: "user",
2467
+ content: {
2468
+ type: "text",
2469
+ text: [
2470
+ `Deploy a serverless function "${name}" in project ${projectId}.`,
2471
+ useCase ? `Use case: ${useCase}` : "",
2472
+ "",
2473
+ "DEPLOYMENT WORKFLOW:",
2474
+ "",
2475
+ "1. CREATE the function:",
2476
+ " Use functions_create with name, entryPoint (default: index.js),",
2477
+ " timeout (ms, default: 30000), memoryLimitMb (default: 128)",
2478
+ " Optional: cronExpression for scheduled execution",
2479
+ "",
2480
+ "2. WRITE the code:",
2481
+ " Create an index.js file. Available APIs inside the sandbox:",
2482
+ "",
2483
+ " - console.log/warn/error/info() \u2014 captured to execution logs",
2484
+ " - await fetch(url, options) \u2014 HTTP client (10s timeout, 5MB limit)",
2485
+ " Response: { status, headers, body } where body is a STRING",
2486
+ " Use JSON.parse(response.body) for JSON APIs",
2487
+ " - env.get(key) \u2014 read environment variables",
2488
+ " - await kv.get/set/delete/list() \u2014 Redis KV store (256KB values, 1000 keys)",
2489
+ " - await spacelr.db.collection(name).find/insertOne/insertMany()",
2490
+ " - await spacelr.storage.list/getInfo/getDownloadUrl()",
2491
+ "",
2492
+ " IMPORTANT: Top-level await is supported. No imports/require available.",
2493
+ "",
2494
+ "3. IF USING spacelr.db:",
2495
+ " Database rules MUST be set BEFORE the function writes to a collection.",
2496
+ " Use the setup-collection prompt or manually set rules via database_rules_set.",
2497
+ ' Without rules, inserts will fail with "Rule denied".',
2498
+ "",
2499
+ "4. DEPLOY the code:",
2500
+ " Zip the file(s) and upload via the CLI:",
2501
+ " spacelr functions deploy ./my-function --name " + name,
2502
+ " Or use the admin API: POST /projects/{projectId}/functions/{id}/deploy",
2503
+ "",
2504
+ "5. TRIGGER:",
2505
+ " Functions support multiple trigger types:",
2506
+ "",
2507
+ " a) Manual: Use functions_trigger to execute on demand.",
2508
+ " b) Cron: Set cronExpression when creating/updating the function.",
2509
+ " c) Webhook: Enable with functions_enable_webhook to get a secret,",
2510
+ " then POST to /api/v1/functions/{projectId}/{functionId}/invoke",
2511
+ " with the X-Webhook-Secret header. Use functions_regenerate_webhook_secret",
2512
+ " to rotate the secret (old secret stops working immediately).",
2513
+ " d) Event: Configure event triggers to run the function in response",
2514
+ " to platform events (e.g. database changes, storage uploads).",
2515
+ "",
2516
+ " Check results with functions_executions_list.",
2517
+ "",
2518
+ "6. VERIFY:",
2519
+ " Check execution status, logs, and duration.",
2520
+ " Use functions_get_code to review the deployed source."
2521
+ ].join("\n")
2522
+ }
2523
+ }
2524
+ ]
2525
+ };
2526
+ }
2527
+ );
2528
+ server.registerPrompt(
2529
+ "function-sandbox-reference",
2530
+ {
2531
+ title: "Function Sandbox API Reference",
2532
+ description: "Complete reference for all APIs available inside a serverless function sandbox.",
2533
+ argsSchema: {}
2534
+ },
2535
+ async () => {
2536
+ return {
2537
+ messages: [
2538
+ {
2539
+ role: "user",
2540
+ content: {
2541
+ type: "text",
2542
+ text: [
2543
+ "Show me the complete API reference for the Spacelr function sandbox.",
2544
+ "",
2545
+ "\u2550\u2550\u2550 CONSOLE \u2550\u2550\u2550",
2546
+ "console.log(...args) \u2014 log level",
2547
+ "console.info(...args) \u2014 info level",
2548
+ "console.warn(...args) \u2014 warn level",
2549
+ "console.error(...args) \u2014 error level",
2550
+ "All logs are captured and stored in the execution record.",
2551
+ "Log buffer is capped at 1MB.",
2552
+ "",
2553
+ "\u2550\u2550\u2550 FETCH \u2550\u2550\u2550",
2554
+ "const response = await fetch(url, options?)",
2555
+ " options: { method, headers, body }",
2556
+ " returns: { status: number, headers: Record<string,string>, body: string }",
2557
+ " NOTE: body is always a string \u2014 use JSON.parse(response.body) for JSON",
2558
+ " Limits: 10s timeout, 5MB response, no private/internal URLs (SSRF blocked)",
2559
+ "",
2560
+ "\u2550\u2550\u2550 ENV \u2550\u2550\u2550",
2561
+ "env.get(key) \u2192 string | undefined",
2562
+ "Read-only access to function environment variables.",
2563
+ "",
2564
+ "\u2550\u2550\u2550 KV STORE \u2550\u2550\u2550",
2565
+ "await kv.get(key) \u2192 string | null",
2566
+ "await kv.set(key, value, ttlSeconds?) \u2192 void",
2567
+ "await kv.delete(key) \u2192 boolean",
2568
+ "await kv.list(prefix?) \u2192 string[]",
2569
+ "Backed by Redis. Scoped per function.",
2570
+ "Limits: 256KB per value, 1000 keys per function, 30-day default TTL.",
2571
+ "",
2572
+ "\u2550\u2550\u2550 DATABASE \u2550\u2550\u2550",
2573
+ "const docs = await spacelr.db.collection(name).find(filter?, options?)",
2574
+ " options: { sort, limit, offset }",
2575
+ "await spacelr.db.collection(name).insertOne(doc)",
2576
+ "await spacelr.db.collection(name).insertMany(docs)",
2577
+ "Scoped to the function's project. Requires database rules to be set first.",
2578
+ "",
2579
+ "\u2550\u2550\u2550 STORAGE \u2550\u2550\u2550",
2580
+ "await spacelr.storage.list(options?) \u2192 FileInfo[]",
2581
+ "await spacelr.storage.getInfo(fileId) \u2192 FileInfo",
2582
+ "await spacelr.storage.getDownloadUrl(fileId) \u2192 string",
2583
+ "Scoped to the function's project.",
2584
+ "",
2585
+ "\u2550\u2550\u2550 RESOURCE LIMITS \u2550\u2550\u2550",
2586
+ "Execution timeout: 30s default, 120s max",
2587
+ "Memory: 128MB default, 512MB max",
2588
+ "Code bundle: 10MB max (zip)",
2589
+ "Concurrent executions: 10 per project",
2590
+ "CPU time: limited by watchdog (equals timeout)"
2591
+ ].join("\n")
2592
+ }
2593
+ }
2594
+ ]
2595
+ };
2596
+ }
2597
+ );
2598
+ }
2599
+
2600
+ // libs/mcp-server/src/prompts/index.ts
2601
+ function registerAllPrompts(server) {
2602
+ registerDatabasePrompts(server);
2603
+ registerFunctionPrompts(server);
1959
2604
  }
1960
2605
 
1961
2606
  // libs/mcp-server/src/index.ts
@@ -1967,6 +2612,7 @@ async function main() {
1967
2612
  version: "0.1.0"
1968
2613
  });
1969
2614
  registerAllTools(server, api);
2615
+ registerAllPrompts(server);
1970
2616
  const transport = new StdioServerTransport();
1971
2617
  await server.connect(transport);
1972
2618
  const shutdown = async () => {