@spacelr/mcp 0.0.5 → 0.0.6

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
@@ -2555,6 +2555,957 @@ function registerEmailTemplateTools(server, api) {
2555
2555
  );
2556
2556
  }
2557
2557
 
2558
+ // libs/mcp-server/src/tools/cronJobs.ts
2559
+ import { z as z9 } from "zod";
2560
+ function registerCronJobTools(server, api) {
2561
+ server.registerTool(
2562
+ "cron_jobs_list",
2563
+ {
2564
+ description: "List all cron jobs for a project",
2565
+ inputSchema: {
2566
+ projectId: z9.string()
2567
+ }
2568
+ },
2569
+ async ({ projectId }) => {
2570
+ try {
2571
+ const result = await api.get(`/projects/${encodeURIComponent(projectId)}/cron-jobs`);
2572
+ return {
2573
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2574
+ };
2575
+ } catch (error) {
2576
+ const message = error instanceof Error ? error.message : String(error);
2577
+ return {
2578
+ content: [{ type: "text", text: `Failed to list cron jobs: ${message}` }],
2579
+ isError: true
2580
+ };
2581
+ }
2582
+ }
2583
+ );
2584
+ server.registerTool(
2585
+ "cron_jobs_get",
2586
+ {
2587
+ description: "Get a single cron job by ID",
2588
+ inputSchema: {
2589
+ projectId: z9.string(),
2590
+ jobId: z9.string()
2591
+ }
2592
+ },
2593
+ async ({ projectId, jobId }) => {
2594
+ try {
2595
+ const result = await api.get(`/projects/${encodeURIComponent(projectId)}/cron-jobs/${encodeURIComponent(jobId)}`);
2596
+ return {
2597
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2598
+ };
2599
+ } catch (error) {
2600
+ const message = error instanceof Error ? error.message : String(error);
2601
+ return {
2602
+ content: [{ type: "text", text: `Failed to get cron job: ${message}` }],
2603
+ isError: true
2604
+ };
2605
+ }
2606
+ }
2607
+ );
2608
+ server.registerTool(
2609
+ "cron_jobs_create",
2610
+ {
2611
+ description: 'Create a new cron job. When type is "webhook", url is required. When type is "function", functionId is required. cronExpression uses 5-field cron format (e.g. "0 0 * * *").',
2612
+ inputSchema: {
2613
+ projectId: z9.string(),
2614
+ name: z9.string(),
2615
+ cronExpression: z9.string().regex(/^(\S+\s){4}\S+$/),
2616
+ timezone: z9.string().optional(),
2617
+ type: z9.enum(["webhook", "function"]).optional(),
2618
+ functionId: z9.string().optional(),
2619
+ url: z9.string().url().optional(),
2620
+ secret: z9.string().optional(),
2621
+ payload: z9.record(z9.string(), z9.unknown()).optional(),
2622
+ retryAttempts: z9.number().int().min(0).max(10).optional(),
2623
+ timeout: z9.number().int().min(1e3).max(12e4).optional(),
2624
+ enabled: z9.boolean().optional()
2625
+ }
2626
+ },
2627
+ async ({ projectId, name, cronExpression, timezone, type, functionId, url, secret, payload, retryAttempts, timeout, enabled }) => {
2628
+ try {
2629
+ const body = { name, cronExpression };
2630
+ if (timezone !== void 0) body.timezone = timezone;
2631
+ if (type !== void 0) body.type = type;
2632
+ if (functionId !== void 0) body.functionId = functionId;
2633
+ if (url !== void 0) body.url = url;
2634
+ if (secret !== void 0) body.secret = secret;
2635
+ if (payload !== void 0) body.payload = payload;
2636
+ if (retryAttempts !== void 0) body.retryAttempts = retryAttempts;
2637
+ if (timeout !== void 0) body.timeout = timeout;
2638
+ if (enabled !== void 0) body.enabled = enabled;
2639
+ const result = await api.post(`/projects/${encodeURIComponent(projectId)}/cron-jobs`, { body });
2640
+ return {
2641
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2642
+ };
2643
+ } catch (error) {
2644
+ const message = error instanceof Error ? error.message : String(error);
2645
+ return {
2646
+ content: [{ type: "text", text: `Failed to create cron job: ${message}` }],
2647
+ isError: true
2648
+ };
2649
+ }
2650
+ }
2651
+ );
2652
+ server.registerTool(
2653
+ "cron_jobs_update",
2654
+ {
2655
+ description: "Update an existing cron job",
2656
+ inputSchema: {
2657
+ projectId: z9.string(),
2658
+ jobId: z9.string(),
2659
+ name: z9.string().optional(),
2660
+ cronExpression: z9.string().regex(/^(\S+\s){4}\S+$/).optional(),
2661
+ timezone: z9.string().optional(),
2662
+ type: z9.enum(["webhook", "function"]).optional(),
2663
+ functionId: z9.string().optional(),
2664
+ url: z9.string().url().optional(),
2665
+ secret: z9.string().optional(),
2666
+ payload: z9.record(z9.string(), z9.unknown()).optional(),
2667
+ retryAttempts: z9.number().int().min(0).max(10).optional(),
2668
+ timeout: z9.number().int().min(1e3).max(12e4).optional(),
2669
+ enabled: z9.boolean().optional()
2670
+ }
2671
+ },
2672
+ async ({ projectId, jobId, name, cronExpression, timezone, type, functionId, url, secret, payload, retryAttempts, timeout, enabled }) => {
2673
+ try {
2674
+ const body = {};
2675
+ if (name !== void 0) body.name = name;
2676
+ if (cronExpression !== void 0) body.cronExpression = cronExpression;
2677
+ if (timezone !== void 0) body.timezone = timezone;
2678
+ if (type !== void 0) body.type = type;
2679
+ if (functionId !== void 0) body.functionId = functionId;
2680
+ if (url !== void 0) body.url = url;
2681
+ if (secret !== void 0) body.secret = secret;
2682
+ if (payload !== void 0) body.payload = payload;
2683
+ if (retryAttempts !== void 0) body.retryAttempts = retryAttempts;
2684
+ if (timeout !== void 0) body.timeout = timeout;
2685
+ if (enabled !== void 0) body.enabled = enabled;
2686
+ if (Object.keys(body).length === 0) {
2687
+ return {
2688
+ content: [{ type: "text", text: "At least one field must be provided" }],
2689
+ isError: true
2690
+ };
2691
+ }
2692
+ const result = await api.patch(`/projects/${encodeURIComponent(projectId)}/cron-jobs/${encodeURIComponent(jobId)}`, { body });
2693
+ return {
2694
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2695
+ };
2696
+ } catch (error) {
2697
+ const message = error instanceof Error ? error.message : String(error);
2698
+ return {
2699
+ content: [{ type: "text", text: `Failed to update cron job: ${message}` }],
2700
+ isError: true
2701
+ };
2702
+ }
2703
+ }
2704
+ );
2705
+ server.registerTool(
2706
+ "cron_jobs_delete",
2707
+ {
2708
+ description: "Delete a cron job",
2709
+ inputSchema: {
2710
+ projectId: z9.string(),
2711
+ jobId: z9.string()
2712
+ }
2713
+ },
2714
+ async ({ projectId, jobId }) => {
2715
+ try {
2716
+ const result = await api.delete(`/projects/${encodeURIComponent(projectId)}/cron-jobs/${encodeURIComponent(jobId)}`);
2717
+ return {
2718
+ content: [{ type: "text", text: result ? JSON.stringify(result, null, 2) : "Cron job deleted" }]
2719
+ };
2720
+ } catch (error) {
2721
+ const message = error instanceof Error ? error.message : String(error);
2722
+ return {
2723
+ content: [{ type: "text", text: `Failed to delete cron job: ${message}` }],
2724
+ isError: true
2725
+ };
2726
+ }
2727
+ }
2728
+ );
2729
+ server.registerTool(
2730
+ "cron_jobs_trigger",
2731
+ {
2732
+ description: "Manually trigger a cron job execution",
2733
+ inputSchema: {
2734
+ projectId: z9.string(),
2735
+ jobId: z9.string()
2736
+ }
2737
+ },
2738
+ async ({ projectId, jobId }) => {
2739
+ try {
2740
+ const result = await api.post(`/projects/${encodeURIComponent(projectId)}/cron-jobs/${encodeURIComponent(jobId)}/trigger`);
2741
+ return {
2742
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2743
+ };
2744
+ } catch (error) {
2745
+ const message = error instanceof Error ? error.message : String(error);
2746
+ return {
2747
+ content: [{ type: "text", text: `Failed to trigger cron job: ${message}` }],
2748
+ isError: true
2749
+ };
2750
+ }
2751
+ }
2752
+ );
2753
+ server.registerTool(
2754
+ "cron_jobs_toggle",
2755
+ {
2756
+ description: "Toggle a cron job enabled/disabled",
2757
+ inputSchema: {
2758
+ projectId: z9.string(),
2759
+ jobId: z9.string(),
2760
+ enabled: z9.boolean()
2761
+ }
2762
+ },
2763
+ async ({ projectId, jobId, enabled }) => {
2764
+ try {
2765
+ const result = await api.post(`/projects/${encodeURIComponent(projectId)}/cron-jobs/${encodeURIComponent(jobId)}/toggle`, { body: { enabled } });
2766
+ return {
2767
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2768
+ };
2769
+ } catch (error) {
2770
+ const message = error instanceof Error ? error.message : String(error);
2771
+ return {
2772
+ content: [{ type: "text", text: `Failed to toggle cron job: ${message}` }],
2773
+ isError: true
2774
+ };
2775
+ }
2776
+ }
2777
+ );
2778
+ server.registerTool(
2779
+ "cron_jobs_executions",
2780
+ {
2781
+ description: "List execution history for a cron job",
2782
+ inputSchema: {
2783
+ projectId: z9.string(),
2784
+ jobId: z9.string(),
2785
+ limit: z9.number().int().min(1).max(100).optional(),
2786
+ offset: z9.number().int().min(0).optional()
2787
+ }
2788
+ },
2789
+ async ({ projectId, jobId, limit, offset }) => {
2790
+ try {
2791
+ const result = await api.get(`/projects/${encodeURIComponent(projectId)}/cron-jobs/${encodeURIComponent(jobId)}/executions`, { params: { limit, offset } });
2792
+ return {
2793
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2794
+ };
2795
+ } catch (error) {
2796
+ const message = error instanceof Error ? error.message : String(error);
2797
+ return {
2798
+ content: [{ type: "text", text: `Failed to list cron job executions: ${message}` }],
2799
+ isError: true
2800
+ };
2801
+ }
2802
+ }
2803
+ );
2804
+ server.registerTool(
2805
+ "cron_jobs_list_all",
2806
+ {
2807
+ description: "List all cron jobs across projects (admin overview)",
2808
+ inputSchema: {
2809
+ projectId: z9.string().optional()
2810
+ }
2811
+ },
2812
+ async ({ projectId }) => {
2813
+ try {
2814
+ const result = await api.get("/cron/jobs", { params: { projectId } });
2815
+ return {
2816
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2817
+ };
2818
+ } catch (error) {
2819
+ const message = error instanceof Error ? error.message : String(error);
2820
+ return {
2821
+ content: [{ type: "text", text: `Failed to list all cron jobs: ${message}` }],
2822
+ isError: true
2823
+ };
2824
+ }
2825
+ }
2826
+ );
2827
+ server.registerTool(
2828
+ "cron_jobs_status",
2829
+ {
2830
+ description: "Get cron queue status",
2831
+ inputSchema: {}
2832
+ },
2833
+ async () => {
2834
+ try {
2835
+ const result = await api.get("/cron/status");
2836
+ return {
2837
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2838
+ };
2839
+ } catch (error) {
2840
+ const message = error instanceof Error ? error.message : String(error);
2841
+ return {
2842
+ content: [{ type: "text", text: `Failed to get cron status: ${message}` }],
2843
+ isError: true
2844
+ };
2845
+ }
2846
+ }
2847
+ );
2848
+ }
2849
+
2850
+ // libs/mcp-server/src/tools/webhooks.ts
2851
+ import { z as z10 } from "zod";
2852
+ var WEBHOOK_EVENT_TYPES = [
2853
+ "user.created",
2854
+ "user.updated",
2855
+ "user.deleted",
2856
+ "user.login",
2857
+ "user.email_verified",
2858
+ "user.password_reset_requested",
2859
+ "user.password_reset",
2860
+ "user.2fa_verified",
2861
+ "database.insert",
2862
+ "database.update",
2863
+ "database.delete",
2864
+ "storage.upload",
2865
+ "storage.delete",
2866
+ "project.created",
2867
+ "project.updated"
2868
+ ];
2869
+ function registerWebhookTools(server, api) {
2870
+ server.registerTool(
2871
+ "webhooks_list",
2872
+ {
2873
+ description: "List all webhooks for a project",
2874
+ inputSchema: {
2875
+ projectId: z10.string()
2876
+ }
2877
+ },
2878
+ async ({ projectId }) => {
2879
+ try {
2880
+ const result = await api.get(`/projects/${encodeURIComponent(projectId)}/webhooks`);
2881
+ return {
2882
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2883
+ };
2884
+ } catch (error) {
2885
+ const message = error instanceof Error ? error.message : String(error);
2886
+ return {
2887
+ content: [{ type: "text", text: `Failed to list webhooks: ${message}` }],
2888
+ isError: true
2889
+ };
2890
+ }
2891
+ }
2892
+ );
2893
+ server.registerTool(
2894
+ "webhooks_get",
2895
+ {
2896
+ description: "Get a single webhook by ID",
2897
+ inputSchema: {
2898
+ projectId: z10.string(),
2899
+ webhookId: z10.string()
2900
+ }
2901
+ },
2902
+ async ({ projectId, webhookId }) => {
2903
+ try {
2904
+ const result = await api.get(`/projects/${encodeURIComponent(projectId)}/webhooks/${encodeURIComponent(webhookId)}`);
2905
+ return {
2906
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2907
+ };
2908
+ } catch (error) {
2909
+ const message = error instanceof Error ? error.message : String(error);
2910
+ return {
2911
+ content: [{ type: "text", text: `Failed to get webhook: ${message}` }],
2912
+ isError: true
2913
+ };
2914
+ }
2915
+ }
2916
+ );
2917
+ server.registerTool(
2918
+ "webhooks_create",
2919
+ {
2920
+ description: "Create a new webhook for a project. WARNING: The response includes a secret that will be visible in the conversation context. Events include: user.created, user.updated, user.deleted, user.login, user.email_verified, user.password_reset_requested, user.password_reset, user.2fa_verified, database.insert, database.update, database.delete, storage.upload, storage.delete, project.created, project.updated.",
2921
+ inputSchema: {
2922
+ projectId: z10.string(),
2923
+ url: z10.string().url(),
2924
+ events: z10.array(z10.enum(WEBHOOK_EVENT_TYPES)).min(1),
2925
+ description: z10.string().optional(),
2926
+ headers: z10.record(z10.string(), z10.string()).optional(),
2927
+ active: z10.boolean().optional()
2928
+ }
2929
+ },
2930
+ async ({ projectId, url, events, description, headers, active }) => {
2931
+ try {
2932
+ const body = { url, events };
2933
+ if (description !== void 0) body.description = description;
2934
+ if (headers !== void 0) body.headers = headers;
2935
+ if (active !== void 0) body.active = active;
2936
+ const result = await api.post(`/projects/${encodeURIComponent(projectId)}/webhooks`, { body });
2937
+ return {
2938
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2939
+ };
2940
+ } catch (error) {
2941
+ const message = error instanceof Error ? error.message : String(error);
2942
+ return {
2943
+ content: [{ type: "text", text: `Failed to create webhook: ${message}` }],
2944
+ isError: true
2945
+ };
2946
+ }
2947
+ }
2948
+ );
2949
+ server.registerTool(
2950
+ "webhooks_update",
2951
+ {
2952
+ description: "Update an existing webhook",
2953
+ inputSchema: {
2954
+ projectId: z10.string(),
2955
+ webhookId: z10.string(),
2956
+ url: z10.string().url().optional(),
2957
+ events: z10.array(z10.enum(WEBHOOK_EVENT_TYPES)).min(1).optional(),
2958
+ description: z10.string().optional(),
2959
+ headers: z10.record(z10.string(), z10.string()).optional(),
2960
+ active: z10.boolean().optional()
2961
+ }
2962
+ },
2963
+ async ({ projectId, webhookId, url, events, description, headers, active }) => {
2964
+ try {
2965
+ const body = {};
2966
+ if (url !== void 0) body.url = url;
2967
+ if (events !== void 0) body.events = events;
2968
+ if (description !== void 0) body.description = description;
2969
+ if (headers !== void 0) body.headers = headers;
2970
+ if (active !== void 0) body.active = active;
2971
+ if (Object.keys(body).length === 0) {
2972
+ return {
2973
+ content: [{ type: "text", text: "At least one field must be provided" }],
2974
+ isError: true
2975
+ };
2976
+ }
2977
+ const result = await api.patch(`/projects/${encodeURIComponent(projectId)}/webhooks/${encodeURIComponent(webhookId)}`, { body });
2978
+ return {
2979
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
2980
+ };
2981
+ } catch (error) {
2982
+ const message = error instanceof Error ? error.message : String(error);
2983
+ return {
2984
+ content: [{ type: "text", text: `Failed to update webhook: ${message}` }],
2985
+ isError: true
2986
+ };
2987
+ }
2988
+ }
2989
+ );
2990
+ server.registerTool(
2991
+ "webhooks_delete",
2992
+ {
2993
+ description: "Delete a webhook",
2994
+ inputSchema: {
2995
+ projectId: z10.string(),
2996
+ webhookId: z10.string()
2997
+ }
2998
+ },
2999
+ async ({ projectId, webhookId }) => {
3000
+ try {
3001
+ const result = await api.delete(`/projects/${encodeURIComponent(projectId)}/webhooks/${encodeURIComponent(webhookId)}`);
3002
+ return {
3003
+ content: [{ type: "text", text: result ? JSON.stringify(result, null, 2) : "Webhook deleted" }]
3004
+ };
3005
+ } catch (error) {
3006
+ const message = error instanceof Error ? error.message : String(error);
3007
+ return {
3008
+ content: [{ type: "text", text: `Failed to delete webhook: ${message}` }],
3009
+ isError: true
3010
+ };
3011
+ }
3012
+ }
3013
+ );
3014
+ server.registerTool(
3015
+ "webhooks_test",
3016
+ {
3017
+ description: "Send a test ping to a webhook",
3018
+ inputSchema: {
3019
+ projectId: z10.string(),
3020
+ webhookId: z10.string()
3021
+ }
3022
+ },
3023
+ async ({ projectId, webhookId }) => {
3024
+ try {
3025
+ const result = await api.post(`/projects/${encodeURIComponent(projectId)}/webhooks/${encodeURIComponent(webhookId)}/test`);
3026
+ return {
3027
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
3028
+ };
3029
+ } catch (error) {
3030
+ const message = error instanceof Error ? error.message : String(error);
3031
+ return {
3032
+ content: [{ type: "text", text: `Failed to test webhook: ${message}` }],
3033
+ isError: true
3034
+ };
3035
+ }
3036
+ }
3037
+ );
3038
+ server.registerTool(
3039
+ "webhooks_deliveries",
3040
+ {
3041
+ description: "List webhook delivery logs for a project",
3042
+ inputSchema: {
3043
+ projectId: z10.string(),
3044
+ webhookId: z10.string().optional(),
3045
+ limit: z10.number().int().min(1).max(100).optional(),
3046
+ offset: z10.number().int().min(0).optional()
3047
+ }
3048
+ },
3049
+ async ({ projectId, webhookId, limit, offset }) => {
3050
+ try {
3051
+ const result = await api.get(`/projects/${encodeURIComponent(projectId)}/webhook-deliveries`, { params: { webhookId, limit, offset } });
3052
+ return {
3053
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
3054
+ };
3055
+ } catch (error) {
3056
+ const message = error instanceof Error ? error.message : String(error);
3057
+ return {
3058
+ content: [{ type: "text", text: `Failed to list webhook deliveries: ${message}` }],
3059
+ isError: true
3060
+ };
3061
+ }
3062
+ }
3063
+ );
3064
+ server.registerTool(
3065
+ "webhooks_delivery_retry",
3066
+ {
3067
+ description: "Retry a failed webhook delivery",
3068
+ inputSchema: {
3069
+ projectId: z10.string(),
3070
+ deliveryId: z10.string()
3071
+ }
3072
+ },
3073
+ async ({ projectId, deliveryId }) => {
3074
+ try {
3075
+ const result = await api.post(`/projects/${encodeURIComponent(projectId)}/webhook-deliveries/${encodeURIComponent(deliveryId)}/retry`);
3076
+ return {
3077
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
3078
+ };
3079
+ } catch (error) {
3080
+ const message = error instanceof Error ? error.message : String(error);
3081
+ return {
3082
+ content: [{ type: "text", text: `Failed to retry webhook delivery: ${message}` }],
3083
+ isError: true
3084
+ };
3085
+ }
3086
+ }
3087
+ );
3088
+ }
3089
+
3090
+ // libs/mcp-server/src/tools/notifications.ts
3091
+ import { z as z11 } from "zod";
3092
+ function registerNotificationTools(server, api) {
3093
+ server.registerTool(
3094
+ "notification_templates_list",
3095
+ {
3096
+ description: "List all notification templates for a project (includes system defaults)",
3097
+ inputSchema: {
3098
+ projectId: z11.string()
3099
+ }
3100
+ },
3101
+ async ({ projectId }) => {
3102
+ try {
3103
+ const result = await api.get(`/projects/${encodeURIComponent(projectId)}/notification-templates`);
3104
+ return {
3105
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
3106
+ };
3107
+ } catch (error) {
3108
+ const message = error instanceof Error ? error.message : String(error);
3109
+ return {
3110
+ content: [{ type: "text", text: `Failed to list notification templates: ${message}` }],
3111
+ isError: true
3112
+ };
3113
+ }
3114
+ }
3115
+ );
3116
+ server.registerTool(
3117
+ "notification_templates_get",
3118
+ {
3119
+ description: "Get a single notification template by ID",
3120
+ inputSchema: {
3121
+ id: z11.string()
3122
+ }
3123
+ },
3124
+ async ({ id }) => {
3125
+ try {
3126
+ const result = await api.get(`/notification-templates/${encodeURIComponent(id)}`);
3127
+ return {
3128
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
3129
+ };
3130
+ } catch (error) {
3131
+ const message = error instanceof Error ? error.message : String(error);
3132
+ return {
3133
+ content: [{ type: "text", text: `Failed to get notification template: ${message}` }],
3134
+ isError: true
3135
+ };
3136
+ }
3137
+ }
3138
+ );
3139
+ server.registerTool(
3140
+ "notification_templates_create",
3141
+ {
3142
+ description: "Create a new project-specific notification template. Variables use Handlebars syntax in titleTemplate and bodyTemplate.",
3143
+ inputSchema: {
3144
+ projectId: z11.string(),
3145
+ type: z11.string(),
3146
+ name: z11.string(),
3147
+ titleTemplate: z11.string(),
3148
+ bodyTemplate: z11.string(),
3149
+ icon: z11.string().optional(),
3150
+ defaultUrl: z11.string().optional(),
3151
+ variables: z11.array(z11.string()).optional(),
3152
+ isActive: z11.boolean().optional()
3153
+ }
3154
+ },
3155
+ async ({ projectId, type, name, titleTemplate, bodyTemplate, icon, defaultUrl, variables, isActive }) => {
3156
+ try {
3157
+ const body = { projectId, type, name, titleTemplate, bodyTemplate };
3158
+ if (icon !== void 0) body.icon = icon;
3159
+ if (defaultUrl !== void 0) body.defaultUrl = defaultUrl;
3160
+ if (variables !== void 0) body.variables = variables;
3161
+ if (isActive !== void 0) body.isActive = isActive;
3162
+ const result = await api.post(`/projects/${encodeURIComponent(projectId)}/notification-templates`, { body });
3163
+ return {
3164
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
3165
+ };
3166
+ } catch (error) {
3167
+ const message = error instanceof Error ? error.message : String(error);
3168
+ return {
3169
+ content: [{ type: "text", text: `Failed to create notification template: ${message}` }],
3170
+ isError: true
3171
+ };
3172
+ }
3173
+ }
3174
+ );
3175
+ server.registerTool(
3176
+ "notification_templates_update",
3177
+ {
3178
+ description: "Update an existing notification template",
3179
+ inputSchema: {
3180
+ id: z11.string(),
3181
+ name: z11.string().optional(),
3182
+ titleTemplate: z11.string().optional(),
3183
+ bodyTemplate: z11.string().optional(),
3184
+ icon: z11.string().optional(),
3185
+ defaultUrl: z11.string().optional(),
3186
+ variables: z11.array(z11.string()).optional(),
3187
+ isActive: z11.boolean().optional()
3188
+ }
3189
+ },
3190
+ async ({ id, name, titleTemplate, bodyTemplate, icon, defaultUrl, variables, isActive }) => {
3191
+ try {
3192
+ const body = {};
3193
+ if (name !== void 0) body.name = name;
3194
+ if (titleTemplate !== void 0) body.titleTemplate = titleTemplate;
3195
+ if (bodyTemplate !== void 0) body.bodyTemplate = bodyTemplate;
3196
+ if (icon !== void 0) body.icon = icon;
3197
+ if (defaultUrl !== void 0) body.defaultUrl = defaultUrl;
3198
+ if (variables !== void 0) body.variables = variables;
3199
+ if (isActive !== void 0) body.isActive = isActive;
3200
+ if (Object.keys(body).length === 0) {
3201
+ return {
3202
+ content: [{ type: "text", text: "At least one field must be provided" }],
3203
+ isError: true
3204
+ };
3205
+ }
3206
+ const result = await api.patch(`/notification-templates/${encodeURIComponent(id)}`, { body });
3207
+ return {
3208
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
3209
+ };
3210
+ } catch (error) {
3211
+ const message = error instanceof Error ? error.message : String(error);
3212
+ return {
3213
+ content: [{ type: "text", text: `Failed to update notification template: ${message}` }],
3214
+ isError: true
3215
+ };
3216
+ }
3217
+ }
3218
+ );
3219
+ server.registerTool(
3220
+ "notification_templates_delete",
3221
+ {
3222
+ description: "Delete a project notification template override (reverts to system default)",
3223
+ inputSchema: {
3224
+ id: z11.string()
3225
+ }
3226
+ },
3227
+ async ({ id }) => {
3228
+ try {
3229
+ const result = await api.delete(`/notification-templates/${encodeURIComponent(id)}`);
3230
+ return {
3231
+ content: [{ type: "text", text: result ? JSON.stringify(result, null, 2) : "Notification template deleted" }]
3232
+ };
3233
+ } catch (error) {
3234
+ const message = error instanceof Error ? error.message : String(error);
3235
+ return {
3236
+ content: [{ type: "text", text: `Failed to delete notification template: ${message}` }],
3237
+ isError: true
3238
+ };
3239
+ }
3240
+ }
3241
+ );
3242
+ server.registerTool(
3243
+ "notification_templates_preview",
3244
+ {
3245
+ description: "Preview a compiled notification template with Handlebars variables applied",
3246
+ inputSchema: {
3247
+ titleTemplate: z11.string(),
3248
+ bodyTemplate: z11.string(),
3249
+ variables: z11.record(z11.string(), z11.string())
3250
+ }
3251
+ },
3252
+ async ({ titleTemplate, bodyTemplate, variables }) => {
3253
+ try {
3254
+ const result = await api.post("/notification-templates/preview", { body: { titleTemplate, bodyTemplate, variables } });
3255
+ return {
3256
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
3257
+ };
3258
+ } catch (error) {
3259
+ const message = error instanceof Error ? error.message : String(error);
3260
+ return {
3261
+ content: [{ type: "text", text: `Failed to preview notification template: ${message}` }],
3262
+ isError: true
3263
+ };
3264
+ }
3265
+ }
3266
+ );
3267
+ server.registerTool(
3268
+ "notification_config_get",
3269
+ {
3270
+ description: "Get push notification provider configuration for a project (VAPID, FCM, APNS). WARNING: Response may include sensitive credentials that will be visible in the conversation context.",
3271
+ inputSchema: {
3272
+ projectId: z11.string()
3273
+ }
3274
+ },
3275
+ async ({ projectId }) => {
3276
+ try {
3277
+ const result = await api.get(`/projects/${encodeURIComponent(projectId)}/notification-config`);
3278
+ return {
3279
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
3280
+ };
3281
+ } catch (error) {
3282
+ const message = error instanceof Error ? error.message : String(error);
3283
+ return {
3284
+ content: [{ type: "text", text: `Failed to get notification config: ${message}` }],
3285
+ isError: true
3286
+ };
3287
+ }
3288
+ }
3289
+ );
3290
+ server.registerTool(
3291
+ "notification_config_update",
3292
+ {
3293
+ description: "Update push notification provider configuration. Supports VAPID, FCM, and APNS providers. WARNING: privateKey and serviceAccountJson values are sensitive credentials that will be visible in the conversation context.",
3294
+ inputSchema: {
3295
+ projectId: z11.string(),
3296
+ vapid: z11.object({
3297
+ publicKey: z11.string().optional(),
3298
+ privateKey: z11.string().optional(),
3299
+ subject: z11.string().optional(),
3300
+ enabled: z11.boolean().optional()
3301
+ }).optional(),
3302
+ fcm: z11.object({
3303
+ serviceAccountJson: z11.string().optional(),
3304
+ enabled: z11.boolean().optional()
3305
+ }).optional(),
3306
+ apns: z11.object({
3307
+ keyId: z11.string().optional(),
3308
+ teamId: z11.string().optional(),
3309
+ privateKey: z11.string().optional(),
3310
+ bundleId: z11.string().optional(),
3311
+ production: z11.boolean().optional(),
3312
+ enabled: z11.boolean().optional()
3313
+ }).optional()
3314
+ }
3315
+ },
3316
+ async ({ projectId, vapid, fcm, apns }) => {
3317
+ try {
3318
+ const body = {};
3319
+ if (vapid !== void 0) body.vapid = vapid;
3320
+ if (fcm !== void 0) body.fcm = fcm;
3321
+ if (apns !== void 0) body.apns = apns;
3322
+ if (Object.keys(body).length === 0) {
3323
+ return {
3324
+ content: [{ type: "text", text: "At least one provider config must be provided" }],
3325
+ isError: true
3326
+ };
3327
+ }
3328
+ const result = await api.patch(`/projects/${encodeURIComponent(projectId)}/notification-config`, { body });
3329
+ return {
3330
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
3331
+ };
3332
+ } catch (error) {
3333
+ const message = error instanceof Error ? error.message : String(error);
3334
+ return {
3335
+ content: [{ type: "text", text: `Failed to update notification config: ${message}` }],
3336
+ isError: true
3337
+ };
3338
+ }
3339
+ }
3340
+ );
3341
+ server.registerTool(
3342
+ "notification_config_delete",
3343
+ {
3344
+ description: "Delete push notification provider configuration. Specify a provider to delete only that one, or omit to delete all.",
3345
+ inputSchema: {
3346
+ projectId: z11.string(),
3347
+ provider: z11.enum(["vapid", "fcm", "apns"]).optional()
3348
+ }
3349
+ },
3350
+ async ({ projectId, provider }) => {
3351
+ try {
3352
+ const result = await api.delete(`/projects/${encodeURIComponent(projectId)}/notification-config`, { params: { provider } });
3353
+ return {
3354
+ content: [{ type: "text", text: result ? JSON.stringify(result, null, 2) : "Notification config deleted" }]
3355
+ };
3356
+ } catch (error) {
3357
+ const message = error instanceof Error ? error.message : String(error);
3358
+ return {
3359
+ content: [{ type: "text", text: `Failed to delete notification config: ${message}` }],
3360
+ isError: true
3361
+ };
3362
+ }
3363
+ }
3364
+ );
3365
+ server.registerTool(
3366
+ "notification_config_generate_vapid",
3367
+ {
3368
+ description: "Generate a new VAPID key pair for web push notifications. WARNING: The private key will be visible in the conversation context.",
3369
+ inputSchema: {}
3370
+ },
3371
+ async () => {
3372
+ try {
3373
+ const result = await api.post("/notification-config/generate-vapid-keys");
3374
+ return {
3375
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
3376
+ };
3377
+ } catch (error) {
3378
+ const message = error instanceof Error ? error.message : String(error);
3379
+ return {
3380
+ content: [{ type: "text", text: `Failed to generate VAPID keys: ${message}` }],
3381
+ isError: true
3382
+ };
3383
+ }
3384
+ }
3385
+ );
3386
+ server.registerTool(
3387
+ "notifications_send",
3388
+ {
3389
+ description: "Send a push notification to selected users",
3390
+ inputSchema: {
3391
+ projectId: z11.string(),
3392
+ userIds: z11.array(z11.string()).min(1),
3393
+ title: z11.string(),
3394
+ body: z11.string(),
3395
+ icon: z11.string().optional(),
3396
+ url: z11.string().optional()
3397
+ }
3398
+ },
3399
+ async ({ projectId, userIds, title, body: notifBody, icon, url }) => {
3400
+ try {
3401
+ const reqBody = { userIds, title, body: notifBody };
3402
+ if (icon !== void 0) reqBody.icon = icon;
3403
+ if (url !== void 0) reqBody.url = url;
3404
+ const result = await api.post(`/projects/${encodeURIComponent(projectId)}/notifications/send`, { body: reqBody });
3405
+ return {
3406
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
3407
+ };
3408
+ } catch (error) {
3409
+ const message = error instanceof Error ? error.message : String(error);
3410
+ return {
3411
+ content: [{ type: "text", text: `Failed to send notification: ${message}` }],
3412
+ isError: true
3413
+ };
3414
+ }
3415
+ }
3416
+ );
3417
+ server.registerTool(
3418
+ "notifications_broadcast",
3419
+ {
3420
+ description: "Broadcast a push notification to all subscribed users in a project",
3421
+ inputSchema: {
3422
+ projectId: z11.string(),
3423
+ title: z11.string(),
3424
+ body: z11.string(),
3425
+ icon: z11.string().optional(),
3426
+ url: z11.string().optional()
3427
+ }
3428
+ },
3429
+ async ({ projectId, title, body: notifBody, icon, url }) => {
3430
+ try {
3431
+ const reqBody = { title, body: notifBody };
3432
+ if (icon !== void 0) reqBody.icon = icon;
3433
+ if (url !== void 0) reqBody.url = url;
3434
+ const result = await api.post(`/projects/${encodeURIComponent(projectId)}/notifications/broadcast`, { body: reqBody });
3435
+ return {
3436
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
3437
+ };
3438
+ } catch (error) {
3439
+ const message = error instanceof Error ? error.message : String(error);
3440
+ return {
3441
+ content: [{ type: "text", text: `Failed to broadcast notification: ${message}` }],
3442
+ isError: true
3443
+ };
3444
+ }
3445
+ }
3446
+ );
3447
+ }
3448
+
3449
+ // libs/mcp-server/src/tools/auditLogs.ts
3450
+ import { z as z12 } from "zod";
3451
+ function registerAuditLogTools(server, api) {
3452
+ server.registerTool(
3453
+ "audit_logs_list",
3454
+ {
3455
+ description: "List audit logs for a project. Filter by entity type, action, and date range.",
3456
+ inputSchema: {
3457
+ projectId: z12.string(),
3458
+ entityType: z12.enum(["user", "client", "project", "auth", "database", "storage"]).optional(),
3459
+ action: z12.string().optional(),
3460
+ startDate: z12.string().datetime().optional(),
3461
+ endDate: z12.string().datetime().optional(),
3462
+ limit: z12.number().int().min(1).max(100).optional(),
3463
+ offset: z12.number().int().min(0).optional()
3464
+ }
3465
+ },
3466
+ async ({ projectId, entityType, action, startDate, endDate, limit, offset }) => {
3467
+ try {
3468
+ const result = await api.get(`/projects/${encodeURIComponent(projectId)}/audit-logs`, {
3469
+ params: { entityType, action, startDate, endDate, limit, offset }
3470
+ });
3471
+ return {
3472
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
3473
+ };
3474
+ } catch (error) {
3475
+ const message = error instanceof Error ? error.message : String(error);
3476
+ return {
3477
+ content: [{ type: "text", text: `Failed to list audit logs: ${message}` }],
3478
+ isError: true
3479
+ };
3480
+ }
3481
+ }
3482
+ );
3483
+ server.registerTool(
3484
+ "audit_logs_get",
3485
+ {
3486
+ description: "Get a single audit log entry by ID",
3487
+ inputSchema: {
3488
+ projectId: z12.string(),
3489
+ logId: z12.string()
3490
+ }
3491
+ },
3492
+ async ({ projectId, logId }) => {
3493
+ try {
3494
+ const result = await api.get(`/projects/${encodeURIComponent(projectId)}/audit-logs/${encodeURIComponent(logId)}`);
3495
+ return {
3496
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
3497
+ };
3498
+ } catch (error) {
3499
+ const message = error instanceof Error ? error.message : String(error);
3500
+ return {
3501
+ content: [{ type: "text", text: `Failed to get audit log: ${message}` }],
3502
+ isError: true
3503
+ };
3504
+ }
3505
+ }
3506
+ );
3507
+ }
3508
+
2558
3509
  // libs/mcp-server/src/tools/index.ts
2559
3510
  function registerAllTools(server, api) {
2560
3511
  registerAuthTools(server, api);
@@ -2565,10 +3516,14 @@ function registerAllTools(server, api) {
2565
3516
  registerStorageTools(server, api);
2566
3517
  registerFunctionTools(server, api);
2567
3518
  registerEmailTemplateTools(server, api);
3519
+ registerCronJobTools(server, api);
3520
+ registerWebhookTools(server, api);
3521
+ registerNotificationTools(server, api);
3522
+ registerAuditLogTools(server, api);
2568
3523
  }
2569
3524
 
2570
3525
  // libs/mcp-server/src/prompts/database.ts
2571
- import { z as z9 } from "zod";
3526
+ import { z as z13 } from "zod";
2572
3527
  function registerDatabasePrompts(server) {
2573
3528
  server.registerPrompt(
2574
3529
  "setup-collection",
@@ -2576,9 +3531,9 @@ function registerDatabasePrompts(server) {
2576
3531
  title: "Setup Database Collection",
2577
3532
  description: "Guide for creating a new database collection with proper security rules. Collections MUST have rules defined before data can be inserted.",
2578
3533
  argsSchema: {
2579
- projectId: z9.string().describe("Project ID"),
2580
- collectionName: z9.string().describe("Name of the collection to create"),
2581
- access: z9.enum(["public-read", "authenticated", "admin-only"]).optional().describe("Access level (default: admin-only)")
3534
+ projectId: z13.string().describe("Project ID"),
3535
+ collectionName: z13.string().describe("Name of the collection to create"),
3536
+ access: z13.enum(["public-read", "authenticated", "admin-only"]).optional().describe("Access level (default: admin-only)")
2582
3537
  }
2583
3538
  },
2584
3539
  async ({ projectId, collectionName, access }) => {
@@ -2643,7 +3598,7 @@ function registerDatabasePrompts(server) {
2643
3598
  title: "Database Workflow Guide",
2644
3599
  description: "Explains how the Spacelr database system works: rules-first approach, collection lifecycle, and security model.",
2645
3600
  argsSchema: {
2646
- projectId: z9.string().describe("Project ID")
3601
+ projectId: z13.string().describe("Project ID")
2647
3602
  }
2648
3603
  },
2649
3604
  async ({ projectId }) => {
@@ -2692,7 +3647,7 @@ function registerDatabasePrompts(server) {
2692
3647
  }
2693
3648
 
2694
3649
  // libs/mcp-server/src/prompts/functions.ts
2695
- import { z as z10 } from "zod";
3650
+ import { z as z14 } from "zod";
2696
3651
  function registerFunctionPrompts(server) {
2697
3652
  server.registerPrompt(
2698
3653
  "deploy-function",
@@ -2700,9 +3655,9 @@ function registerFunctionPrompts(server) {
2700
3655
  title: "Deploy a Serverless Function",
2701
3656
  description: "Step-by-step guide for creating, deploying, and testing a serverless function. Covers the full lifecycle from creation to execution.",
2702
3657
  argsSchema: {
2703
- projectId: z10.string().describe("Project ID"),
2704
- name: z10.string().describe("Function name"),
2705
- useCase: z10.string().optional().describe('What the function should do (e.g. "fetch data from API and store in DB")')
3658
+ projectId: z14.string().describe("Project ID"),
3659
+ name: z14.string().describe("Function name"),
3660
+ useCase: z14.string().optional().describe('What the function should do (e.g. "fetch data from API and store in DB")')
2706
3661
  }
2707
3662
  },
2708
3663
  async ({ projectId, name, useCase }) => {