@baasix/mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2694 @@
1
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError } from "@modelcontextprotocol/sdk/types.js";
4
+ import { z } from "zod";
5
+ import axios from "axios";
6
+ import { loadEnvironmentConfig } from "./config.js";
7
+
8
+ // Load configuration
9
+ const config = loadEnvironmentConfig();
10
+
11
+ // Configuration from loaded config
12
+ const BAASIX_URL = config.BAASIX_URL || "http://localhost:8056";
13
+ const BAASIX_AUTH_TOKEN = config.BAASIX_AUTH_TOKEN;
14
+ const BAASIX_EMAIL = config.BAASIX_EMAIL;
15
+ const BAASIX_PASSWORD = config.BAASIX_PASSWORD;
16
+
17
+ // Authentication state
18
+ let authToken = null;
19
+ let authExpiry = null;
20
+
21
+ // Helper function to get valid auth token
22
+ async function getAuthToken() {
23
+ // Priority 1: Use provided token if available
24
+ if (BAASIX_AUTH_TOKEN) {
25
+ return BAASIX_AUTH_TOKEN;
26
+ }
27
+
28
+ // Priority 2: Check if current token is still valid
29
+ if (authToken && authExpiry && Date.now() < authExpiry) {
30
+ return authToken;
31
+ }
32
+
33
+ // Priority 3: Auto-authenticate using email/password
34
+ if (BAASIX_EMAIL && BAASIX_PASSWORD) {
35
+ try {
36
+ const response = await axios.post(`${BAASIX_URL}/auth/login`, {
37
+ email: BAASIX_EMAIL,
38
+ password: BAASIX_PASSWORD,
39
+ });
40
+
41
+ authToken = response.data.token;
42
+ authExpiry = Date.now() + 60 * 60 * 1000; // 1 hour
43
+
44
+ return authToken;
45
+ } catch (error) {
46
+ throw new Error(`Authentication failed: ${error.response?.data?.message || error.message}`);
47
+ }
48
+ }
49
+
50
+ throw new Error(
51
+ "No authentication method available. Please provide BAASIX_AUTH_TOKEN or BAASIX_EMAIL/BAASIX_PASSWORD"
52
+ );
53
+ }
54
+
55
+ // Helper function to make authenticated requests
56
+ async function baasixRequest(endpoint, options = {}) {
57
+ const token = await getAuthToken();
58
+
59
+ const config = {
60
+ baseURL: BAASIX_URL,
61
+ ...options,
62
+ headers: {
63
+ "Content-Type": "application/json",
64
+ ...(token && { Authorization: `Bearer ${token}` }),
65
+ ...options.headers,
66
+ },
67
+ };
68
+
69
+ try {
70
+ const response = await axios(endpoint, config);
71
+ return response.data;
72
+ } catch (error) {
73
+ // If auth error and using auto-login, clear token and retry once
74
+ if (error.response?.status === 401 && authToken && !BAASIX_AUTH_TOKEN) {
75
+ authToken = null;
76
+ authExpiry = null;
77
+
78
+ try {
79
+ const newToken = await getAuthToken();
80
+ if (newToken) {
81
+ config.headers.Authorization = `Bearer ${newToken}`;
82
+ const retryResponse = await axios(endpoint, config);
83
+ return retryResponse.data;
84
+ }
85
+ } catch (retryError) {
86
+ throw new Error(`Baasix API Error: ${retryError.response?.data?.message || retryError.message}`);
87
+ }
88
+ }
89
+
90
+ throw new Error(`Baasix API Error: ${error.response?.data?.message || error.message}`);
91
+ }
92
+ }
93
+
94
+ class BaasixMCPServer {
95
+ server;
96
+
97
+ constructor() {
98
+ this.server = new Server(
99
+ {
100
+ name: "baasix-mcp-server",
101
+ version: "0.1.0",
102
+ },
103
+ {
104
+ capabilities: {
105
+ tools: {},
106
+ },
107
+ }
108
+ );
109
+
110
+ this.setupHandlers();
111
+ this.setupErrorHandling();
112
+ }
113
+
114
+ setupErrorHandling() {
115
+ this.server.onerror = (error) => {
116
+ console.error("[MCP Error]", error);
117
+ };
118
+
119
+ process.on("SIGINT", async () => {
120
+ await this.server.close();
121
+ process.exit(0);
122
+ });
123
+ }
124
+
125
+ setupHandlers() {
126
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => {
127
+ return {
128
+ tools: [
129
+ // Schema Management Tools
130
+ {
131
+ name: "baasix_list_schemas",
132
+ description:
133
+ "Get all available collections/schemas in Baasix with optional search and pagination",
134
+ inputSchema: {
135
+ type: "object",
136
+ properties: {
137
+ search: {
138
+ type: "string",
139
+ description: "Search term to filter schemas by collection name or schema name",
140
+ },
141
+ page: {
142
+ type: "number",
143
+ description: "Page number for pagination (default: 1)",
144
+ default: 1,
145
+ },
146
+ limit: {
147
+ type: "number",
148
+ description: "Number of schemas per page (default: 10)",
149
+ default: 10,
150
+ },
151
+ sort: {
152
+ type: "string",
153
+ description:
154
+ 'Sort field and direction (e.g., "collectionName:asc", "collectionName:desc")',
155
+ default: "collectionName:asc",
156
+ },
157
+ },
158
+ additionalProperties: false,
159
+ },
160
+ },
161
+ {
162
+ name: "baasix_get_schema",
163
+ description: "Get detailed schema information for a specific collection",
164
+ inputSchema: {
165
+ type: "object",
166
+ properties: {
167
+ collection: {
168
+ type: "string",
169
+ description: "Collection name",
170
+ },
171
+ },
172
+ required: ["collection"],
173
+ },
174
+ },
175
+ {
176
+ name: "baasix_create_schema",
177
+ description: `Create a new collection schema in Baasix.
178
+
179
+ FIELD TYPES:
180
+ - String: VARCHAR with values.length (e.g., 255)
181
+ - Text: Unlimited text
182
+ - Integer, BigInt: Whole numbers
183
+ - Decimal: values.precision & values.scale
184
+ - Float, Real, Double: Floating point
185
+ - Boolean: true/false
186
+ - Date, DateTime, Time: Date/time
187
+ - UUID: With defaultValue.type: "UUIDV4"
188
+ - SUID: Short unique ID with defaultValue.type: "SUID"
189
+ - JSONB: JSON with indexing
190
+ - Array: values.type specifies element type
191
+ - Geometry, Geography: PostGIS spatial
192
+ - Enum: values.values array
193
+
194
+ DEFAULT VALUE TYPES:
195
+ - { type: "UUIDV4" } - Random UUID v4
196
+ - { type: "SUID" } - Short unique ID
197
+ - { type: "NOW" } - Current timestamp
198
+ - { type: "AUTOINCREMENT" } - Auto-incrementing integer
199
+ - { type: "SQL", value: "..." } - Custom SQL expression
200
+ - Static values: "active", false, 0, etc.
201
+
202
+ VALIDATION RULES:
203
+ - min: number - Minimum value (numeric fields)
204
+ - max: number - Maximum value (numeric fields)
205
+ - isInt: true - Must be integer
206
+ - notEmpty: true - String cannot be empty
207
+ - isEmail: true - Valid email format
208
+ - isUrl: true - Valid URL format
209
+ - len: [min, max] - String length range
210
+ - is/matches: "regex" - Pattern matching
211
+
212
+ SCHEMA OPTIONS:
213
+ - timestamps: true adds createdAt/updatedAt
214
+ - paranoid: true enables soft deletes (deletedAt)
215
+
216
+ EXAMPLE:
217
+ {
218
+ "name": "Product",
219
+ "timestamps": true,
220
+ "fields": {
221
+ "id": {"type": "UUID", "primaryKey": true, "defaultValue": {"type": "UUIDV4"}},
222
+ "sku": {"type": "SUID", "unique": true, "defaultValue": {"type": "SUID"}},
223
+ "name": {"type": "String", "allowNull": false, "values": {"length": 255}, "validate": {"notEmpty": true}},
224
+ "price": {"type": "Decimal", "values": {"precision": 10, "scale": 2}, "validate": {"min": 0}},
225
+ "email": {"type": "String", "validate": {"isEmail": true}},
226
+ "quantity": {"type": "Integer", "defaultValue": 0, "validate": {"isInt": true, "min": 0}}
227
+ }
228
+ }`,
229
+ inputSchema: {
230
+ type: "object",
231
+ properties: {
232
+ collection: {
233
+ type: "string",
234
+ description: "Collection name (lowercase, snake_case recommended)",
235
+ },
236
+ schema: {
237
+ type: "object",
238
+ description: "Schema definition with name, fields, timestamps, paranoid options",
239
+ },
240
+ },
241
+ required: ["collection", "schema"],
242
+ },
243
+ },
244
+ {
245
+ name: "baasix_update_schema",
246
+ description: "Update an existing collection schema",
247
+ inputSchema: {
248
+ type: "object",
249
+ properties: {
250
+ collection: {
251
+ type: "string",
252
+ description: "Collection name",
253
+ },
254
+ schema: {
255
+ type: "object",
256
+ description: "Updated schema definition",
257
+ },
258
+ },
259
+ required: ["collection", "schema"],
260
+ },
261
+ },
262
+ {
263
+ name: "baasix_delete_schema",
264
+ description: "Delete a collection schema",
265
+ inputSchema: {
266
+ type: "object",
267
+ properties: {
268
+ collection: {
269
+ type: "string",
270
+ description: "Collection name",
271
+ },
272
+ },
273
+ required: ["collection"],
274
+ },
275
+ },
276
+ {
277
+ name: "baasix_add_index",
278
+ description: "Add an index to a collection schema",
279
+ inputSchema: {
280
+ type: "object",
281
+ properties: {
282
+ collection: {
283
+ type: "string",
284
+ description: "Collection name",
285
+ },
286
+ indexDefinition: {
287
+ type: "object",
288
+ description: "Index definition with fields and options",
289
+ properties: {
290
+ name: {
291
+ type: "string",
292
+ description: "Index name",
293
+ },
294
+ fields: {
295
+ type: "array",
296
+ items: { type: "string" },
297
+ description: "Array of field names to index",
298
+ },
299
+ unique: {
300
+ type: "boolean",
301
+ description: "Whether the index should be unique",
302
+ },
303
+ nullsNotDistinct: {
304
+ type: "boolean",
305
+ description: "When true, NULL values are considered equal for unique indexes (PostgreSQL 15+). Only applies when unique is true.",
306
+ },
307
+ },
308
+ required: ["name", "fields"],
309
+ },
310
+ },
311
+ required: ["collection", "indexDefinition"],
312
+ },
313
+ },
314
+ {
315
+ name: "baasix_remove_index",
316
+ description: "Remove an index from a collection schema",
317
+ inputSchema: {
318
+ type: "object",
319
+ properties: {
320
+ collection: {
321
+ type: "string",
322
+ description: "Collection name",
323
+ },
324
+ indexName: {
325
+ type: "string",
326
+ description: "Name of the index to remove",
327
+ },
328
+ },
329
+ required: ["collection", "indexName"],
330
+ },
331
+ },
332
+ {
333
+ name: "baasix_create_relationship",
334
+ description: `Create a relationship between collections.
335
+
336
+ RELATIONSHIP TYPES:
337
+ - M2O (Many-to-One): Creates foreign key with auto-index. products.category → categories
338
+ - O2M (One-to-Many): Virtual reverse of M2O. categories.products → products
339
+ - O2O (One-to-One): Creates foreign key with auto-index. user.profile → profiles
340
+ - M2M (Many-to-Many): Creates junction table with auto-indexed FKs. products ↔ tags
341
+ - M2A (Many-to-Any): Polymorphic junction table. comments → posts OR products
342
+
343
+ AUTO-INDEXING:
344
+ All foreign key columns are automatically indexed for better query performance:
345
+ - M2O/O2O: Index on the FK column (e.g., category_Id)
346
+ - M2M/M2A: Indexes on both FK columns in junction tables
347
+
348
+ JUNCTION TABLES (M2M/M2A):
349
+ - Auto-generated name: {source}_{target}_{name}_junction
350
+ - Custom name: Use "through" property (max 63 chars for PostgreSQL)
351
+ - Junction tables are marked with isJunction: true in schema
352
+
353
+ EXAMPLE M2O:
354
+ {
355
+ "name": "category", // Creates category_Id field + index
356
+ "type": "M2O",
357
+ "target": "categories",
358
+ "alias": "products", // Reverse relation name
359
+ "onDelete": "CASCADE" // CASCADE, RESTRICT, SET NULL
360
+ }
361
+
362
+ EXAMPLE M2M:
363
+ {
364
+ "name": "tags",
365
+ "type": "M2M",
366
+ "target": "tags",
367
+ "alias": "products"
368
+ }
369
+
370
+ EXAMPLE M2M with custom junction table:
371
+ {
372
+ "name": "tags",
373
+ "type": "M2M",
374
+ "target": "tags",
375
+ "alias": "products",
376
+ "through": "product_tag_mapping" // Custom junction table name
377
+ }`,
378
+ inputSchema: {
379
+ type: "object",
380
+ properties: {
381
+ sourceCollection: {
382
+ type: "string",
383
+ description: "Source collection name",
384
+ },
385
+ relationshipData: {
386
+ type: "object",
387
+ description: "Relationship configuration",
388
+ properties: {
389
+ name: {
390
+ type: "string",
391
+ description: "Relationship field name (creates fieldName_Id for M2O)",
392
+ },
393
+ type: {
394
+ type: "string",
395
+ enum: ["M2O", "O2M", "O2O", "M2M", "M2A"],
396
+ description:
397
+ "M2O=Many-to-One, O2M=One-to-Many, M2M=Many-to-Many, M2A=Many-to-Any (polymorphic)",
398
+ },
399
+ target: {
400
+ type: "string",
401
+ description: "Target collection name",
402
+ },
403
+ alias: {
404
+ type: "string",
405
+ description:
406
+ "Alias for reverse relationship (required for bidirectional access)",
407
+ },
408
+ description: {
409
+ type: "string",
410
+ description: "Relationship description",
411
+ },
412
+ onDelete: {
413
+ type: "string",
414
+ enum: ["CASCADE", "RESTRICT", "SET NULL"],
415
+ description: "Delete behavior (default: CASCADE)",
416
+ },
417
+ onUpdate: {
418
+ type: "string",
419
+ enum: ["CASCADE", "RESTRICT", "SET NULL"],
420
+ description: "Update behavior",
421
+ },
422
+ tables: {
423
+ type: "array",
424
+ items: { type: "string" },
425
+ description: "Target tables for M2A (polymorphic) relationships",
426
+ },
427
+ through: {
428
+ type: "string",
429
+ description:
430
+ "Custom junction table name for M2M/M2A relationships (max 63 chars). If not provided, auto-generated as {source}_{target}_{name}_junction",
431
+ },
432
+ },
433
+ required: ["name", "type"],
434
+ },
435
+ },
436
+ required: ["sourceCollection", "relationshipData"],
437
+ },
438
+ },
439
+ {
440
+ name: "baasix_update_relationship",
441
+ description: "Update an existing relationship",
442
+ inputSchema: {
443
+ type: "object",
444
+ properties: {
445
+ sourceCollection: {
446
+ type: "string",
447
+ description: "Source collection name",
448
+ },
449
+ fieldName: {
450
+ type: "string",
451
+ description: "Relationship field name",
452
+ },
453
+ updateData: {
454
+ type: "object",
455
+ description: "Update data for the relationship",
456
+ },
457
+ },
458
+ required: ["sourceCollection", "fieldName", "updateData"],
459
+ },
460
+ },
461
+ {
462
+ name: "baasix_delete_relationship",
463
+ description: "Delete a relationship",
464
+ inputSchema: {
465
+ type: "object",
466
+ properties: {
467
+ sourceCollection: {
468
+ type: "string",
469
+ description: "Source collection name",
470
+ },
471
+ fieldName: {
472
+ type: "string",
473
+ description: "Relationship field name",
474
+ },
475
+ },
476
+ required: ["sourceCollection", "fieldName"],
477
+ },
478
+ },
479
+ {
480
+ name: "baasix_export_schemas",
481
+ description: "Export all schemas as JSON",
482
+ inputSchema: {
483
+ type: "object",
484
+ properties: {},
485
+ additionalProperties: false,
486
+ },
487
+ },
488
+ {
489
+ name: "baasix_import_schemas",
490
+ description: "Import schemas from JSON data",
491
+ inputSchema: {
492
+ type: "object",
493
+ properties: {
494
+ schemas: {
495
+ type: "object",
496
+ description: "Schema data to import",
497
+ },
498
+ },
499
+ required: ["schemas"],
500
+ },
501
+ },
502
+
503
+ // Item Management Tools
504
+ {
505
+ name: "baasix_list_items",
506
+ description: `Query items from a collection with powerful filtering, sorting, pagination, relations, and aggregation.
507
+
508
+ FILTER OPERATORS (50+):
509
+ - Comparison: eq, neq, gt, gte, lt, lte
510
+ - String: contains, icontains, startswith, endswith, like, ilike, regex
511
+ - Null: isNull (true/false), empty (true/false)
512
+ - List: in, nin, between, nbetween
513
+ - Array: arraycontains, arraycontainsany, arraylength, arrayempty
514
+ - JSONB: jsoncontains, jsonhaskey, jsonhasanykeys, jsonhasallkeys, jsonpath
515
+ - Geospatial: dwithin, intersects, contains, within, overlaps
516
+ - Logical: AND, OR, NOT
517
+
518
+ DYNAMIC VARIABLES:
519
+ - $CURRENT_USER: Current user's ID
520
+ - $NOW: Current timestamp
521
+ - $NOW-DAYS_7: 7 days ago
522
+ - $NOW+MONTHS_1: 1 month from now
523
+
524
+ FILTER EXAMPLES:
525
+ - {"status": {"eq": "active"}}
526
+ - {"AND": [{"price": {"gte": 10}}, {"price": {"lte": 100}}]}
527
+ - {"tags": {"arraycontains": ["featured"]}}
528
+ - {"author_Id": {"eq": "$CURRENT_USER"}}
529
+ - {"category.name": {"eq": "Electronics"}} (relation filter)`,
530
+ inputSchema: {
531
+ type: "object",
532
+ properties: {
533
+ collection: {
534
+ type: "string",
535
+ description: "Collection name",
536
+ },
537
+ filter: {
538
+ type: "object",
539
+ description:
540
+ "Filter criteria using operators like eq, neq, gt, gte, lt, lte, contains, in, between, etc.",
541
+ },
542
+ fields: {
543
+ type: "array",
544
+ items: { type: "string" },
545
+ description:
546
+ 'Fields to return. Use ["*"] for all, ["*", "relation.*"] to include relations',
547
+ },
548
+ sort: {
549
+ type: "string",
550
+ description: 'Sort field and direction (e.g., "createdAt:desc", "name:asc")',
551
+ },
552
+ page: {
553
+ type: "number",
554
+ description: "Page number (default: 1)",
555
+ default: 1,
556
+ },
557
+ limit: {
558
+ type: "number",
559
+ description: "Items per page (default: 10, use -1 for all)",
560
+ default: 10,
561
+ },
562
+ search: {
563
+ type: "string",
564
+ description: "Full-text search query",
565
+ },
566
+ searchFields: {
567
+ type: "array",
568
+ items: { type: "string" },
569
+ description: 'Fields to search in (e.g., ["name", "description"])',
570
+ },
571
+ aggregate: {
572
+ type: "object",
573
+ description:
574
+ 'Aggregation functions: {alias: {function: "sum|avg|count|min|max", field: "fieldName"}}',
575
+ },
576
+ groupBy: {
577
+ type: "array",
578
+ items: { type: "string" },
579
+ description: "Fields to group by for aggregation",
580
+ },
581
+ relConditions: {
582
+ type: "object",
583
+ description:
584
+ 'Filter conditions for related records: {"reviews": {"approved": {"eq": true}}}',
585
+ },
586
+ },
587
+ required: ["collection"],
588
+ },
589
+ },
590
+ {
591
+ name: "baasix_get_item",
592
+ description: "Get a specific item by ID from a collection, optionally including related data",
593
+ inputSchema: {
594
+ type: "object",
595
+ properties: {
596
+ collection: {
597
+ type: "string",
598
+ description: "Collection name",
599
+ },
600
+ id: {
601
+ type: "string",
602
+ description: "Item ID (UUID)",
603
+ },
604
+ fields: {
605
+ type: "array",
606
+ items: { type: "string" },
607
+ description: 'Fields to return. Use ["*", "relation.*"] to include relations',
608
+ },
609
+ },
610
+ required: ["collection", "id"],
611
+ },
612
+ },
613
+ {
614
+ name: "baasix_create_item",
615
+ description: "Create a new item in a collection",
616
+ inputSchema: {
617
+ type: "object",
618
+ properties: {
619
+ collection: {
620
+ type: "string",
621
+ description: "Collection name",
622
+ },
623
+ data: {
624
+ type: "object",
625
+ description: "Item data",
626
+ },
627
+ },
628
+ required: ["collection", "data"],
629
+ },
630
+ },
631
+ {
632
+ name: "baasix_update_item",
633
+ description: "Update an existing item in a collection",
634
+ inputSchema: {
635
+ type: "object",
636
+ properties: {
637
+ collection: {
638
+ type: "string",
639
+ description: "Collection name",
640
+ },
641
+ id: {
642
+ type: "string",
643
+ description: "Item ID",
644
+ },
645
+ data: {
646
+ type: "object",
647
+ description: "Updated item data",
648
+ },
649
+ },
650
+ required: ["collection", "id", "data"],
651
+ },
652
+ },
653
+ {
654
+ name: "baasix_delete_item",
655
+ description: "Delete an item from a collection",
656
+ inputSchema: {
657
+ type: "object",
658
+ properties: {
659
+ collection: {
660
+ type: "string",
661
+ description: "Collection name",
662
+ },
663
+ id: {
664
+ type: "string",
665
+ description: "Item ID",
666
+ },
667
+ },
668
+ required: ["collection", "id"],
669
+ },
670
+ },
671
+
672
+ // File Management Tools
673
+ {
674
+ name: "baasix_list_files",
675
+ description: "List files with metadata and optional filtering",
676
+ inputSchema: {
677
+ type: "object",
678
+ properties: {
679
+ filter: {
680
+ type: "object",
681
+ description: "Filter criteria",
682
+ },
683
+ page: {
684
+ type: "number",
685
+ description: "Page number (default: 1)",
686
+ default: 1,
687
+ },
688
+ limit: {
689
+ type: "number",
690
+ description: "Files per page (default: 10)",
691
+ default: 10,
692
+ },
693
+ },
694
+ },
695
+ },
696
+ {
697
+ name: "baasix_get_file_info",
698
+ description: "Get detailed information about a specific file",
699
+ inputSchema: {
700
+ type: "object",
701
+ properties: {
702
+ id: {
703
+ type: "string",
704
+ description: "File ID",
705
+ },
706
+ },
707
+ required: ["id"],
708
+ },
709
+ },
710
+ {
711
+ name: "baasix_delete_file",
712
+ description: "Delete a file",
713
+ inputSchema: {
714
+ type: "object",
715
+ properties: {
716
+ id: {
717
+ type: "string",
718
+ description: "File ID",
719
+ },
720
+ },
721
+ required: ["id"],
722
+ },
723
+ },
724
+
725
+ // Authentication Tools
726
+ {
727
+ name: "baasix_auth_status",
728
+ description: "Check the current authentication status and token validity",
729
+ inputSchema: {
730
+ type: "object",
731
+ properties: {},
732
+ additionalProperties: false,
733
+ },
734
+ },
735
+ {
736
+ name: "baasix_refresh_auth",
737
+ description: "Force refresh the authentication token (only works for email/password auth)",
738
+ inputSchema: {
739
+ type: "object",
740
+ properties: {},
741
+ additionalProperties: false,
742
+ },
743
+ },
744
+
745
+ // Reports and Analytics Tools
746
+ {
747
+ name: "baasix_generate_report",
748
+ description: "Generate reports with grouping and aggregation for a collection",
749
+ inputSchema: {
750
+ type: "object",
751
+ properties: {
752
+ collection: {
753
+ type: "string",
754
+ description: "Collection name",
755
+ },
756
+ groupBy: {
757
+ type: "string",
758
+ description: "Field to group by",
759
+ },
760
+ filter: {
761
+ type: "object",
762
+ description: "Filter criteria",
763
+ },
764
+ dateRange: {
765
+ type: "object",
766
+ properties: {
767
+ start: { type: "string" },
768
+ end: { type: "string" },
769
+ },
770
+ description: "Date range filter",
771
+ },
772
+ },
773
+ required: ["collection"],
774
+ },
775
+ },
776
+ {
777
+ name: "baasix_collection_stats",
778
+ description: "Get collection statistics and analytics",
779
+ inputSchema: {
780
+ type: "object",
781
+ properties: {
782
+ collections: {
783
+ type: "array",
784
+ items: { type: "string" },
785
+ description: "Specific collections to get stats for",
786
+ },
787
+ timeframe: {
788
+ type: "string",
789
+ description: 'Timeframe for stats (e.g., "24h", "7d", "30d")',
790
+ },
791
+ },
792
+ },
793
+ },
794
+
795
+ // Notification Tools
796
+ {
797
+ name: "baasix_list_notifications",
798
+ description: "List notifications for the authenticated user",
799
+ inputSchema: {
800
+ type: "object",
801
+ properties: {
802
+ page: {
803
+ type: "number",
804
+ description: "Page number (default: 1)",
805
+ default: 1,
806
+ },
807
+ limit: {
808
+ type: "number",
809
+ description: "Notifications per page (default: 10)",
810
+ default: 10,
811
+ },
812
+ seen: {
813
+ type: "boolean",
814
+ description: "Filter by seen status",
815
+ },
816
+ },
817
+ },
818
+ },
819
+ {
820
+ name: "baasix_send_notification",
821
+ description: "Send a notification to specified users",
822
+ inputSchema: {
823
+ type: "object",
824
+ properties: {
825
+ recipients: {
826
+ type: "array",
827
+ items: { type: "string" },
828
+ description: "Array of user IDs to send notification to",
829
+ },
830
+ title: {
831
+ type: "string",
832
+ description: "Notification title",
833
+ },
834
+ message: {
835
+ type: "string",
836
+ description: "Notification message",
837
+ },
838
+ type: {
839
+ type: "string",
840
+ description: "Notification type",
841
+ default: "info",
842
+ },
843
+ },
844
+ required: ["recipients", "title", "message"],
845
+ },
846
+ },
847
+ {
848
+ name: "baasix_mark_notification_seen",
849
+ description: "Mark a notification as seen",
850
+ inputSchema: {
851
+ type: "object",
852
+ properties: {
853
+ id: {
854
+ type: "string",
855
+ description: "Notification ID",
856
+ },
857
+ },
858
+ required: ["id"],
859
+ },
860
+ },
861
+
862
+ // Settings Tools
863
+ {
864
+ name: "baasix_get_settings",
865
+ description: "Get application settings",
866
+ inputSchema: {
867
+ type: "object",
868
+ properties: {
869
+ key: {
870
+ type: "string",
871
+ description: "Specific setting key to retrieve",
872
+ },
873
+ },
874
+ },
875
+ },
876
+ {
877
+ name: "baasix_update_settings",
878
+ description: "Update application settings",
879
+ inputSchema: {
880
+ type: "object",
881
+ properties: {
882
+ settings: {
883
+ type: "object",
884
+ description: "Settings object to update",
885
+ },
886
+ },
887
+ required: ["settings"],
888
+ },
889
+ },
890
+
891
+ // Email Template Tools
892
+ {
893
+ name: "baasix_list_templates",
894
+ description: "List all email templates with optional filtering",
895
+ inputSchema: {
896
+ type: "object",
897
+ properties: {
898
+ filter: {
899
+ type: "object",
900
+ description: "Filter criteria (e.g., {type: {eq: 'magic_link'}})",
901
+ },
902
+ page: {
903
+ type: "number",
904
+ description: "Page number (default: 1)",
905
+ default: 1,
906
+ },
907
+ limit: {
908
+ type: "number",
909
+ description: "Templates per page (default: 10)",
910
+ default: 10,
911
+ },
912
+ },
913
+ },
914
+ },
915
+ {
916
+ name: "baasix_get_template",
917
+ description: "Get a specific email template by ID",
918
+ inputSchema: {
919
+ type: "object",
920
+ properties: {
921
+ id: {
922
+ type: "string",
923
+ description: "Template ID (UUID)",
924
+ },
925
+ },
926
+ required: ["id"],
927
+ },
928
+ },
929
+ {
930
+ name: "baasix_update_template",
931
+ description: `Update an email template's subject, description, or body content.
932
+
933
+ TEMPLATE TYPES:
934
+ - magic_link: Magic link authentication emails
935
+ - invite: User invitation emails
936
+ - password_reset: Password reset emails
937
+ - welcome: Welcome emails
938
+ - verification: Email verification emails
939
+
940
+ AVAILABLE VARIABLES:
941
+ - User: {{user.firstName}}, {{user.lastName}}, {{user.fullName}}, {{user.email}}
942
+ - Tenant: {{tenant.name}}, {{tenant.logo}}, {{tenant.website}}
943
+ - Auth: {{magicLink}}, {{magicCode}}, {{resetPasswordLink}}, {{inviteLink}}
944
+ - DateTime: {{currentDate}}, {{currentTime}}, {{currentYear}}`,
945
+ inputSchema: {
946
+ type: "object",
947
+ properties: {
948
+ id: {
949
+ type: "string",
950
+ description: "Template ID (UUID)",
951
+ },
952
+ subject: {
953
+ type: "string",
954
+ description: "Email subject line (supports variables like {{user.firstName}})",
955
+ },
956
+ description: {
957
+ type: "string",
958
+ description: "Template description",
959
+ },
960
+ body: {
961
+ type: "string",
962
+ description: "Template body as HTML string or GrapesJS project JSON",
963
+ },
964
+ isActive: {
965
+ type: "boolean",
966
+ description: "Whether the template is active",
967
+ },
968
+ },
969
+ required: ["id"],
970
+ },
971
+ },
972
+
973
+ // Permission Tools
974
+ {
975
+ name: "baasix_list_roles",
976
+ description: "List all available roles",
977
+ inputSchema: {
978
+ type: "object",
979
+ properties: {},
980
+ additionalProperties: false,
981
+ },
982
+ },
983
+ {
984
+ name: "baasix_list_permissions",
985
+ description: "List all permissions with optional filtering",
986
+ inputSchema: {
987
+ type: "object",
988
+ properties: {
989
+ filter: {
990
+ type: "object",
991
+ description: "Filter criteria",
992
+ },
993
+ sort: {
994
+ type: "string",
995
+ description: 'Sort field and direction (e.g., "collection:asc")',
996
+ },
997
+ page: {
998
+ type: "number",
999
+ description: "Page number (default: 1)",
1000
+ default: 1,
1001
+ },
1002
+ limit: {
1003
+ type: "number",
1004
+ description: "Permissions per page (default: 10)",
1005
+ default: 10,
1006
+ },
1007
+ },
1008
+ },
1009
+ },
1010
+ {
1011
+ name: "baasix_get_permission",
1012
+ description: "Get a specific permission by ID",
1013
+ inputSchema: {
1014
+ type: "object",
1015
+ properties: {
1016
+ id: {
1017
+ type: "string",
1018
+ description: "Permission ID",
1019
+ },
1020
+ },
1021
+ required: ["id"],
1022
+ },
1023
+ },
1024
+ {
1025
+ name: "baasix_get_permissions",
1026
+ description: "Get permissions for a specific role",
1027
+ inputSchema: {
1028
+ type: "object",
1029
+ properties: {
1030
+ role: {
1031
+ type: "string",
1032
+ description: "Role name",
1033
+ },
1034
+ },
1035
+ required: ["role"],
1036
+ },
1037
+ },
1038
+ {
1039
+ name: "baasix_create_permission",
1040
+ description: `Create a new permission for role-based access control.
1041
+
1042
+ ACTIONS: create, read, update, delete
1043
+
1044
+ FIELDS:
1045
+ - ["*"] for all fields
1046
+ - ["name", "price"] for specific fields
1047
+
1048
+ CONDITIONS (Row-level security):
1049
+ - Uses same filter operators as queries
1050
+ - {"published": {"eq": true}} - only published records
1051
+ - {"author_Id": {"eq": "$CURRENT_USER"}} - only own records
1052
+
1053
+ RELCONDITIONS (Filter related data):
1054
+ - {"reviews": {"approved": {"eq": true}}} - only approved reviews in response
1055
+
1056
+ EXAMPLE:
1057
+ {
1058
+ "role_Id": "uuid",
1059
+ "collection": "products",
1060
+ "action": "read",
1061
+ "fields": ["*"],
1062
+ "conditions": {"published": {"eq": true}}
1063
+ }`,
1064
+ inputSchema: {
1065
+ type: "object",
1066
+ properties: {
1067
+ role_Id: {
1068
+ type: "string",
1069
+ description: "Role ID (UUID)",
1070
+ },
1071
+ collection: {
1072
+ type: "string",
1073
+ description: "Collection name",
1074
+ },
1075
+ action: {
1076
+ type: "string",
1077
+ enum: ["create", "read", "update", "delete"],
1078
+ description: "Permission action",
1079
+ },
1080
+ fields: {
1081
+ type: "array",
1082
+ items: { type: "string" },
1083
+ description: 'Allowed fields (["*"] for all)',
1084
+ },
1085
+ conditions: {
1086
+ type: "object",
1087
+ description: "Row-level security conditions using filter operators",
1088
+ },
1089
+ defaultValues: {
1090
+ type: "object",
1091
+ description:
1092
+ 'Default values auto-set on creation (e.g., {"author_Id": "$CURRENT_USER"})',
1093
+ },
1094
+ relConditions: {
1095
+ type: "object",
1096
+ description: "Filter conditions for related records in response",
1097
+ },
1098
+ },
1099
+ required: ["role_Id", "collection", "action"],
1100
+ },
1101
+ },
1102
+ {
1103
+ name: "baasix_update_permission",
1104
+ description: "Update an existing permission",
1105
+ inputSchema: {
1106
+ type: "object",
1107
+ properties: {
1108
+ id: {
1109
+ type: "string",
1110
+ description: "Permission ID",
1111
+ },
1112
+ role_Id: {
1113
+ type: "string",
1114
+ description: "Role ID",
1115
+ },
1116
+ collection: {
1117
+ type: "string",
1118
+ description: "Collection name",
1119
+ },
1120
+ action: {
1121
+ type: "string",
1122
+ enum: ["create", "read", "update", "delete"],
1123
+ description: "Permission action",
1124
+ },
1125
+ fields: {
1126
+ type: "array",
1127
+ items: { type: "string" },
1128
+ description: "Allowed fields",
1129
+ },
1130
+ conditions: {
1131
+ type: "object",
1132
+ description: "Permission conditions",
1133
+ },
1134
+ defaultValues: {
1135
+ type: "object",
1136
+ description: "Default values for creation",
1137
+ },
1138
+ relConditions: {
1139
+ type: "object",
1140
+ description: "Relationship conditions",
1141
+ },
1142
+ },
1143
+ required: ["id"],
1144
+ },
1145
+ },
1146
+ {
1147
+ name: "baasix_delete_permission",
1148
+ description: "Delete a permission",
1149
+ inputSchema: {
1150
+ type: "object",
1151
+ properties: {
1152
+ id: {
1153
+ type: "string",
1154
+ description: "Permission ID",
1155
+ },
1156
+ },
1157
+ required: ["id"],
1158
+ },
1159
+ },
1160
+ {
1161
+ name: "baasix_reload_permissions",
1162
+ description: "Reload the permission cache",
1163
+ inputSchema: {
1164
+ type: "object",
1165
+ properties: {},
1166
+ additionalProperties: false,
1167
+ },
1168
+ },
1169
+ {
1170
+ name: "baasix_update_permissions",
1171
+ description: "Update permissions for a role",
1172
+ inputSchema: {
1173
+ type: "object",
1174
+ properties: {
1175
+ role: {
1176
+ type: "string",
1177
+ description: "Role name",
1178
+ },
1179
+ permissions: {
1180
+ type: "object",
1181
+ description: "Permissions object",
1182
+ },
1183
+ },
1184
+ required: ["role", "permissions"],
1185
+ },
1186
+ },
1187
+
1188
+ // Realtime Tools
1189
+ {
1190
+ name: "baasix_realtime_status",
1191
+ description: `Get the status of the realtime service including WAL configuration.
1192
+
1193
+ Returns information about:
1194
+ - Whether realtime is initialized and consuming WAL
1195
+ - PostgreSQL replication configuration (wal_level, max_replication_slots)
1196
+ - Publication and replication slot status
1197
+ - Collections with realtime enabled`,
1198
+ inputSchema: {
1199
+ type: "object",
1200
+ properties: {},
1201
+ additionalProperties: false,
1202
+ },
1203
+ },
1204
+ {
1205
+ name: "baasix_realtime_config",
1206
+ description: `Check PostgreSQL replication configuration for WAL-based realtime.
1207
+
1208
+ Returns:
1209
+ - walLevel: Should be 'logical' for realtime to work
1210
+ - maxReplicationSlots: Number of available replication slots
1211
+ - maxWalSenders: Number of WAL sender processes
1212
+ - replicationSlotExists: Whether the baasix slot exists
1213
+ - publicationExists: Whether the baasix publication exists
1214
+ - tablesInPublication: List of tables currently in the publication`,
1215
+ inputSchema: {
1216
+ type: "object",
1217
+ properties: {},
1218
+ additionalProperties: false,
1219
+ },
1220
+ },
1221
+ {
1222
+ name: "baasix_realtime_collections",
1223
+ description: "Get list of collections with realtime enabled and their action configurations",
1224
+ inputSchema: {
1225
+ type: "object",
1226
+ properties: {},
1227
+ additionalProperties: false,
1228
+ },
1229
+ },
1230
+ {
1231
+ name: "baasix_realtime_enable",
1232
+ description: `Enable realtime for a collection. Changes will be broadcast via WebSocket when data is modified.
1233
+
1234
+ The realtime config is stored in the schema definition and can include specific actions to broadcast.`,
1235
+ inputSchema: {
1236
+ type: "object",
1237
+ properties: {
1238
+ collection: {
1239
+ type: "string",
1240
+ description: "Collection name to enable realtime for",
1241
+ },
1242
+ actions: {
1243
+ type: "array",
1244
+ items: { type: "string", enum: ["insert", "update", "delete"] },
1245
+ description: 'Actions to broadcast (default: ["insert", "update", "delete"])',
1246
+ },
1247
+ replicaIdentityFull: {
1248
+ type: "boolean",
1249
+ description: "Set REPLICA IDENTITY FULL for old values on UPDATE/DELETE (default: false)",
1250
+ },
1251
+ },
1252
+ required: ["collection"],
1253
+ },
1254
+ },
1255
+ {
1256
+ name: "baasix_realtime_disable",
1257
+ description: "Disable realtime for a collection",
1258
+ inputSchema: {
1259
+ type: "object",
1260
+ properties: {
1261
+ collection: {
1262
+ type: "string",
1263
+ description: "Collection name to disable realtime for",
1264
+ },
1265
+ },
1266
+ required: ["collection"],
1267
+ },
1268
+ },
1269
+
1270
+ // Utility Tools
1271
+ {
1272
+ name: "baasix_server_info",
1273
+ description: "Get Baasix server information and health status",
1274
+ inputSchema: {
1275
+ type: "object",
1276
+ properties: {},
1277
+ additionalProperties: false,
1278
+ },
1279
+ },
1280
+ {
1281
+ name: "baasix_sort_items",
1282
+ description: "Sort items within a collection (move item before/after another)",
1283
+ inputSchema: {
1284
+ type: "object",
1285
+ properties: {
1286
+ collection: {
1287
+ type: "string",
1288
+ description: "Collection name",
1289
+ },
1290
+ item: {
1291
+ type: "string",
1292
+ description: "ID of item to move",
1293
+ },
1294
+ to: {
1295
+ type: "string",
1296
+ description: "ID of target item to move before",
1297
+ },
1298
+ },
1299
+ required: ["collection", "item", "to"],
1300
+ },
1301
+ },
1302
+
1303
+ // Auth Tools
1304
+ {
1305
+ name: "baasix_register_user",
1306
+ description: "Register a new user",
1307
+ inputSchema: {
1308
+ type: "object",
1309
+ properties: {
1310
+ email: {
1311
+ type: "string",
1312
+ format: "email",
1313
+ description: "User email address",
1314
+ },
1315
+ password: {
1316
+ type: "string",
1317
+ description: "User password",
1318
+ },
1319
+ firstName: {
1320
+ type: "string",
1321
+ description: "User first name",
1322
+ },
1323
+ lastName: {
1324
+ type: "string",
1325
+ description: "User last name",
1326
+ },
1327
+ tenant: {
1328
+ type: "object",
1329
+ description: "Tenant information for multi-tenant mode",
1330
+ },
1331
+ roleName: {
1332
+ type: "string",
1333
+ description: "Role name to assign",
1334
+ },
1335
+ inviteToken: {
1336
+ type: "string",
1337
+ description: "Invitation token",
1338
+ },
1339
+ authMode: {
1340
+ type: "string",
1341
+ enum: ["jwt", "cookie"],
1342
+ description: "Authentication mode",
1343
+ default: "jwt",
1344
+ },
1345
+ },
1346
+ required: ["email", "password"],
1347
+ },
1348
+ },
1349
+ {
1350
+ name: "baasix_login",
1351
+ description: "Login user with email and password",
1352
+ inputSchema: {
1353
+ type: "object",
1354
+ properties: {
1355
+ email: {
1356
+ type: "string",
1357
+ format: "email",
1358
+ description: "User email address",
1359
+ },
1360
+ password: {
1361
+ type: "string",
1362
+ description: "User password",
1363
+ },
1364
+ tenant_Id: {
1365
+ type: "string",
1366
+ description: "Tenant ID for multi-tenant mode",
1367
+ },
1368
+ authMode: {
1369
+ type: "string",
1370
+ enum: ["jwt", "cookie"],
1371
+ description: "Authentication mode",
1372
+ default: "jwt",
1373
+ },
1374
+ },
1375
+ required: ["email", "password"],
1376
+ },
1377
+ },
1378
+ {
1379
+ name: "baasix_send_invite",
1380
+ description: "Send an invitation to a user",
1381
+ inputSchema: {
1382
+ type: "object",
1383
+ properties: {
1384
+ email: {
1385
+ type: "string",
1386
+ format: "email",
1387
+ description: "Email address to invite",
1388
+ },
1389
+ role_Id: {
1390
+ type: "string",
1391
+ description: "Role ID to assign",
1392
+ },
1393
+ tenant_Id: {
1394
+ type: "string",
1395
+ description: "Tenant ID",
1396
+ },
1397
+ link: {
1398
+ type: "string",
1399
+ format: "uri",
1400
+ description: "Application URL for the invitation link",
1401
+ },
1402
+ },
1403
+ required: ["email", "role_Id", "link"],
1404
+ },
1405
+ },
1406
+ {
1407
+ name: "baasix_verify_invite",
1408
+ description: "Verify an invitation token",
1409
+ inputSchema: {
1410
+ type: "object",
1411
+ properties: {
1412
+ token: {
1413
+ type: "string",
1414
+ description: "Invitation token",
1415
+ },
1416
+ link: {
1417
+ type: "string",
1418
+ format: "uri",
1419
+ description: "Application URL to validate",
1420
+ },
1421
+ },
1422
+ required: ["token"],
1423
+ },
1424
+ },
1425
+ {
1426
+ name: "baasix_send_magic_link",
1427
+ description: "Send magic link or code for authentication",
1428
+ inputSchema: {
1429
+ type: "object",
1430
+ properties: {
1431
+ email: {
1432
+ type: "string",
1433
+ format: "email",
1434
+ description: "User email address",
1435
+ },
1436
+ link: {
1437
+ type: "string",
1438
+ format: "uri",
1439
+ description: "Application URL for magic link",
1440
+ },
1441
+ mode: {
1442
+ type: "string",
1443
+ enum: ["link", "code"],
1444
+ description: "Magic authentication mode",
1445
+ default: "link",
1446
+ },
1447
+ },
1448
+ required: ["email"],
1449
+ },
1450
+ },
1451
+ {
1452
+ name: "baasix_get_user_tenants",
1453
+ description: "Get available tenants for the current user",
1454
+ inputSchema: {
1455
+ type: "object",
1456
+ properties: {},
1457
+ additionalProperties: false,
1458
+ },
1459
+ },
1460
+ {
1461
+ name: "baasix_switch_tenant",
1462
+ description: "Switch to a different tenant context",
1463
+ inputSchema: {
1464
+ type: "object",
1465
+ properties: {
1466
+ tenant_Id: {
1467
+ type: "string",
1468
+ description: "Tenant ID to switch to",
1469
+ },
1470
+ },
1471
+ required: ["tenant_Id"],
1472
+ },
1473
+ },
1474
+ {
1475
+ name: "baasix_logout",
1476
+ description: "Logout the current user",
1477
+ inputSchema: {
1478
+ type: "object",
1479
+ properties: {},
1480
+ additionalProperties: false,
1481
+ },
1482
+ },
1483
+ {
1484
+ name: "baasix_get_current_user",
1485
+ description: "Get current user information with role and permissions",
1486
+ inputSchema: {
1487
+ type: "object",
1488
+ properties: {
1489
+ fields: {
1490
+ type: "array",
1491
+ items: { type: "string" },
1492
+ description: "Specific fields to retrieve",
1493
+ },
1494
+ },
1495
+ },
1496
+ },
1497
+ ],
1498
+ };
1499
+ });
1500
+
1501
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
1502
+ const { name, arguments: args } = request.params;
1503
+
1504
+ try {
1505
+ switch (name) {
1506
+ // Schema Management
1507
+ case "baasix_list_schemas":
1508
+ return await this.handleListSchemas(args);
1509
+ case "baasix_get_schema":
1510
+ return await this.handleGetSchema(args);
1511
+ case "baasix_create_schema":
1512
+ return await this.handleCreateSchema(args);
1513
+ case "baasix_update_schema":
1514
+ return await this.handleUpdateSchema(args);
1515
+ case "baasix_delete_schema":
1516
+ return await this.handleDeleteSchema(args);
1517
+ case "baasix_add_index":
1518
+ return await this.handleAddIndex(args);
1519
+ case "baasix_remove_index":
1520
+ return await this.handleRemoveIndex(args);
1521
+ case "baasix_create_relationship":
1522
+ return await this.handleCreateRelationship(args);
1523
+ case "baasix_update_relationship":
1524
+ return await this.handleUpdateRelationship(args);
1525
+ case "baasix_delete_relationship":
1526
+ return await this.handleDeleteRelationship(args);
1527
+ case "baasix_export_schemas":
1528
+ return await this.handleExportSchemas(args);
1529
+ case "baasix_import_schemas":
1530
+ return await this.handleImportSchemas(args);
1531
+
1532
+ // Item Management
1533
+ case "baasix_list_items":
1534
+ return await this.handleListItems(args);
1535
+ case "baasix_get_item":
1536
+ return await this.handleGetItem(args);
1537
+ case "baasix_create_item":
1538
+ return await this.handleCreateItem(args);
1539
+ case "baasix_update_item":
1540
+ return await this.handleUpdateItem(args);
1541
+ case "baasix_delete_item":
1542
+ return await this.handleDeleteItem(args);
1543
+
1544
+ // File Management
1545
+ case "baasix_list_files":
1546
+ return await this.handleListFiles(args);
1547
+ case "baasix_get_file_info":
1548
+ return await this.handleGetFileInfo(args);
1549
+ case "baasix_delete_file":
1550
+ return await this.handleDeleteFile(args);
1551
+
1552
+ // Authentication
1553
+ case "baasix_auth_status":
1554
+ return await this.handleAuthStatus(args);
1555
+ case "baasix_refresh_auth":
1556
+ return await this.handleRefreshAuth(args);
1557
+
1558
+ // Reports and Analytics
1559
+ case "baasix_generate_report":
1560
+ return await this.handleGenerateReport(args);
1561
+ case "baasix_collection_stats":
1562
+ return await this.handleGetStats(args);
1563
+
1564
+ // Notifications
1565
+ case "baasix_list_notifications":
1566
+ return await this.handleListNotifications(args);
1567
+ case "baasix_send_notification":
1568
+ return await this.handleSendNotification(args);
1569
+ case "baasix_mark_notification_seen":
1570
+ return await this.handleMarkNotificationSeen(args);
1571
+
1572
+ // Settings
1573
+ case "baasix_get_settings":
1574
+ return await this.handleGetSettings(args);
1575
+ case "baasix_update_settings":
1576
+ return await this.handleUpdateSettings(args);
1577
+
1578
+ // Email Templates
1579
+ case "baasix_list_templates":
1580
+ return await this.handleListTemplates(args);
1581
+ case "baasix_get_template":
1582
+ return await this.handleGetTemplate(args);
1583
+ case "baasix_update_template":
1584
+ return await this.handleUpdateTemplate(args);
1585
+
1586
+ // Permissions
1587
+ case "baasix_list_roles":
1588
+ return await this.handleListRoles(args);
1589
+ case "baasix_list_permissions":
1590
+ return await this.handleListPermissions(args);
1591
+ case "baasix_get_permission":
1592
+ return await this.handleGetPermission(args);
1593
+ case "baasix_get_permissions":
1594
+ return await this.handleGetPermissions(args);
1595
+ case "baasix_create_permission":
1596
+ return await this.handleCreatePermission(args);
1597
+ case "baasix_update_permission":
1598
+ return await this.handleUpdatePermission(args);
1599
+ case "baasix_delete_permission":
1600
+ return await this.handleDeletePermission(args);
1601
+ case "baasix_reload_permissions":
1602
+ return await this.handleReloadPermissions(args);
1603
+ case "baasix_update_permissions":
1604
+ return await this.handleUpdatePermissions(args);
1605
+
1606
+ // Realtime
1607
+ case "baasix_realtime_status":
1608
+ return await this.handleRealtimeStatus(args);
1609
+ case "baasix_realtime_config":
1610
+ return await this.handleRealtimeConfig(args);
1611
+ case "baasix_realtime_collections":
1612
+ return await this.handleRealtimeCollections(args);
1613
+ case "baasix_realtime_enable":
1614
+ return await this.handleRealtimeEnable(args);
1615
+ case "baasix_realtime_disable":
1616
+ return await this.handleRealtimeDisable(args);
1617
+
1618
+ // Utilities
1619
+ case "baasix_server_info":
1620
+ return await this.handleServerInfo(args);
1621
+ case "baasix_sort_items":
1622
+ return await this.handleSortItems(args);
1623
+
1624
+ // Auth
1625
+ case "baasix_register_user":
1626
+ return await this.handleRegisterUser(args);
1627
+ case "baasix_login":
1628
+ return await this.handleLogin(args);
1629
+ case "baasix_send_invite":
1630
+ return await this.handleSendInvite(args);
1631
+ case "baasix_verify_invite":
1632
+ return await this.handleVerifyInvite(args);
1633
+ case "baasix_send_magic_link":
1634
+ return await this.handleSendMagicLink(args);
1635
+ case "baasix_get_user_tenants":
1636
+ return await this.handleGetUserTenants(args);
1637
+ case "baasix_switch_tenant":
1638
+ return await this.handleSwitchTenant(args);
1639
+ case "baasix_logout":
1640
+ return await this.handleLogout(args);
1641
+ case "baasix_get_current_user":
1642
+ return await this.handleGetCurrentUser(args);
1643
+
1644
+ default:
1645
+ throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
1646
+ }
1647
+ } catch (error) {
1648
+ if (error instanceof McpError) {
1649
+ throw error;
1650
+ }
1651
+
1652
+ console.error(`Error in tool ${name}:`, error);
1653
+ throw new McpError(
1654
+ ErrorCode.InternalError,
1655
+ `Tool execution failed: ${error instanceof Error ? error.message : "Unknown error"}`
1656
+ );
1657
+ }
1658
+ });
1659
+ }
1660
+
1661
+ // Schema Management Methods
1662
+ async handleListSchemas(args) {
1663
+ const { search, page, limit, sort = "collectionName:asc" } = args;
1664
+ const params = new URLSearchParams();
1665
+ if (search) params.append("search", search);
1666
+ if (page !== undefined) params.append("page", page.toString());
1667
+ if (limit !== undefined) params.append("limit", limit.toString());
1668
+ params.append("sort", sort);
1669
+
1670
+ const schemas = await baasixRequest(`/schemas?${params}`);
1671
+ return {
1672
+ content: [
1673
+ {
1674
+ type: "text",
1675
+ text: JSON.stringify(schemas, null, 2),
1676
+ },
1677
+ ],
1678
+ };
1679
+ }
1680
+
1681
+ async handleGetSchema(args) {
1682
+ const { collection } = args;
1683
+ const schema = await baasixRequest(`/schemas/${collection}`);
1684
+ return {
1685
+ content: [
1686
+ {
1687
+ type: "text",
1688
+ text: JSON.stringify(schema, null, 2),
1689
+ },
1690
+ ],
1691
+ };
1692
+ }
1693
+
1694
+ async handleCreateSchema(args) {
1695
+ const { collection, schema } = args;
1696
+ const result = await baasixRequest(`/schemas`, {
1697
+ method: "POST",
1698
+ data: { collectionName: collection, schema },
1699
+ });
1700
+ return {
1701
+ content: [
1702
+ {
1703
+ type: "text",
1704
+ text: JSON.stringify(result, null, 2),
1705
+ },
1706
+ ],
1707
+ };
1708
+ }
1709
+
1710
+ async handleUpdateSchema(args) {
1711
+ const { collection, schema } = args;
1712
+ const result = await baasixRequest(`/schemas/${collection}`, {
1713
+ method: "PATCH",
1714
+ data: { schema },
1715
+ });
1716
+ return {
1717
+ content: [
1718
+ {
1719
+ type: "text",
1720
+ text: JSON.stringify(result, null, 2),
1721
+ },
1722
+ ],
1723
+ };
1724
+ }
1725
+
1726
+ async handleDeleteSchema(args) {
1727
+ const { collection } = args;
1728
+ const result = await baasixRequest(`/schemas/${collection}`, {
1729
+ method: "DELETE",
1730
+ });
1731
+ return {
1732
+ content: [
1733
+ {
1734
+ type: "text",
1735
+ text: JSON.stringify(result, null, 2),
1736
+ },
1737
+ ],
1738
+ };
1739
+ }
1740
+
1741
+ async handleAddIndex(args) {
1742
+ const { collection, indexDefinition } = args;
1743
+ const result = await baasixRequest(`/schemas/${collection}/indexes`, {
1744
+ method: "POST",
1745
+ data: indexDefinition,
1746
+ });
1747
+ return {
1748
+ content: [
1749
+ {
1750
+ type: "text",
1751
+ text: JSON.stringify(result, null, 2),
1752
+ },
1753
+ ],
1754
+ };
1755
+ }
1756
+
1757
+ async handleRemoveIndex(args) {
1758
+ const { collection, indexName } = args;
1759
+ const result = await baasixRequest(`/schemas/${collection}/indexes/${indexName}`, {
1760
+ method: "DELETE",
1761
+ });
1762
+ return {
1763
+ content: [
1764
+ {
1765
+ type: "text",
1766
+ text: JSON.stringify(result, null, 2),
1767
+ },
1768
+ ],
1769
+ };
1770
+ }
1771
+
1772
+ async handleCreateRelationship(args) {
1773
+ const { sourceCollection, relationshipData } = args;
1774
+ const result = await baasixRequest(`/schemas/${sourceCollection}/relationships`, {
1775
+ method: "POST",
1776
+ data: relationshipData,
1777
+ });
1778
+ return {
1779
+ content: [
1780
+ {
1781
+ type: "text",
1782
+ text: JSON.stringify(result, null, 2),
1783
+ },
1784
+ ],
1785
+ };
1786
+ }
1787
+
1788
+ async handleUpdateRelationship(args) {
1789
+ const { sourceCollection, fieldName, updateData } = args;
1790
+ const result = await baasixRequest(`/schemas/${sourceCollection}/relationships/${fieldName}`, {
1791
+ method: "PATCH",
1792
+ data: updateData,
1793
+ });
1794
+ return {
1795
+ content: [
1796
+ {
1797
+ type: "text",
1798
+ text: JSON.stringify(result, null, 2),
1799
+ },
1800
+ ],
1801
+ };
1802
+ }
1803
+
1804
+ async handleDeleteRelationship(args) {
1805
+ const { sourceCollection, fieldName } = args;
1806
+ const result = await baasixRequest(`/schemas/${sourceCollection}/relationships/${fieldName}`, {
1807
+ method: "DELETE",
1808
+ });
1809
+ return {
1810
+ content: [
1811
+ {
1812
+ type: "text",
1813
+ text: JSON.stringify(result, null, 2),
1814
+ },
1815
+ ],
1816
+ };
1817
+ }
1818
+
1819
+ async handleExportSchemas(args) {
1820
+ const result = await baasixRequest("/schemas-export");
1821
+ return {
1822
+ content: [
1823
+ {
1824
+ type: "text",
1825
+ text: JSON.stringify(result, null, 2),
1826
+ },
1827
+ ],
1828
+ };
1829
+ }
1830
+
1831
+ async handleImportSchemas(args) {
1832
+ const { schemas } = args;
1833
+ // Note: This is a simplified version. The actual API uses file upload.
1834
+ // For MCP, we'll accept JSON data directly.
1835
+ const result = await baasixRequest("/schemas-import", {
1836
+ method: "POST",
1837
+ data: schemas,
1838
+ });
1839
+ return {
1840
+ content: [
1841
+ {
1842
+ type: "text",
1843
+ text: JSON.stringify(result, null, 2),
1844
+ },
1845
+ ],
1846
+ };
1847
+ }
1848
+
1849
+ // Item Management Methods
1850
+ async handleListItems(args) {
1851
+ const {
1852
+ collection,
1853
+ filter,
1854
+ sort,
1855
+ page = 1,
1856
+ limit = 10,
1857
+ fields,
1858
+ search,
1859
+ searchFields,
1860
+ aggregate,
1861
+ groupBy,
1862
+ relConditions,
1863
+ } = args;
1864
+
1865
+ const params = new URLSearchParams();
1866
+ if (filter) params.append("filter", JSON.stringify(filter));
1867
+ if (sort) params.append("sort", sort);
1868
+ params.append("page", page.toString());
1869
+ params.append("limit", limit.toString());
1870
+ if (fields) params.append("fields", JSON.stringify(fields));
1871
+ if (search) params.append("search", search);
1872
+ if (searchFields) params.append("searchFields", JSON.stringify(searchFields));
1873
+ if (aggregate) params.append("aggregate", JSON.stringify(aggregate));
1874
+ if (groupBy) params.append("groupBy", JSON.stringify(groupBy));
1875
+ if (relConditions) params.append("relConditions", JSON.stringify(relConditions));
1876
+
1877
+ const items = await baasixRequest(`/items/${collection}?${params}`);
1878
+ return {
1879
+ content: [
1880
+ {
1881
+ type: "text",
1882
+ text: JSON.stringify(items, null, 2),
1883
+ },
1884
+ ],
1885
+ };
1886
+ }
1887
+
1888
+ async handleGetItem(args) {
1889
+ const { collection, id, fields } = args;
1890
+ const params = new URLSearchParams();
1891
+ if (fields) params.append("fields", JSON.stringify(fields));
1892
+
1893
+ const queryString = params.toString() ? `?${params}` : "";
1894
+ const item = await baasixRequest(`/items/${collection}/${id}${queryString}`);
1895
+ return {
1896
+ content: [
1897
+ {
1898
+ type: "text",
1899
+ text: JSON.stringify(item, null, 2),
1900
+ },
1901
+ ],
1902
+ };
1903
+ }
1904
+
1905
+ async handleCreateItem(args) {
1906
+ const { collection, data } = args;
1907
+ const result = await baasixRequest(`/items/${collection}`, {
1908
+ method: "POST",
1909
+ data,
1910
+ });
1911
+ return {
1912
+ content: [
1913
+ {
1914
+ type: "text",
1915
+ text: JSON.stringify(result, null, 2),
1916
+ },
1917
+ ],
1918
+ };
1919
+ }
1920
+
1921
+ async handleUpdateItem(args) {
1922
+ const { collection, id, data } = args;
1923
+ const result = await baasixRequest(`/items/${collection}/${id}`, {
1924
+ method: "PUT",
1925
+ data,
1926
+ });
1927
+ return {
1928
+ content: [
1929
+ {
1930
+ type: "text",
1931
+ text: JSON.stringify(result, null, 2),
1932
+ },
1933
+ ],
1934
+ };
1935
+ }
1936
+
1937
+ async handleDeleteItem(args) {
1938
+ const { collection, id } = args;
1939
+ const result = await baasixRequest(`/items/${collection}/${id}`, {
1940
+ method: "DELETE",
1941
+ });
1942
+ return {
1943
+ content: [
1944
+ {
1945
+ type: "text",
1946
+ text: JSON.stringify(result, null, 2),
1947
+ },
1948
+ ],
1949
+ };
1950
+ }
1951
+
1952
+ // File Management Methods
1953
+ async handleListFiles(args) {
1954
+ const { filter, page = 1, limit = 10 } = args;
1955
+ const params = new URLSearchParams();
1956
+ if (filter) params.append("filter", JSON.stringify(filter));
1957
+ params.append("page", page.toString());
1958
+ params.append("limit", limit.toString());
1959
+
1960
+ const files = await baasixRequest(`/files?${params}`);
1961
+ return {
1962
+ content: [
1963
+ {
1964
+ type: "text",
1965
+ text: JSON.stringify(files, null, 2),
1966
+ },
1967
+ ],
1968
+ };
1969
+ }
1970
+
1971
+ async handleGetFileInfo(args) {
1972
+ const { id } = args;
1973
+ const file = await baasixRequest(`/files/${id}`);
1974
+ return {
1975
+ content: [
1976
+ {
1977
+ type: "text",
1978
+ text: JSON.stringify(file, null, 2),
1979
+ },
1980
+ ],
1981
+ };
1982
+ }
1983
+
1984
+ async handleDeleteFile(args) {
1985
+ const { id } = args;
1986
+ const result = await baasixRequest(`/files/${id}`, {
1987
+ method: "DELETE",
1988
+ });
1989
+ return {
1990
+ content: [
1991
+ {
1992
+ type: "text",
1993
+ text: JSON.stringify(result, null, 2),
1994
+ },
1995
+ ],
1996
+ };
1997
+ }
1998
+
1999
+ // Authentication Methods
2000
+ async handleAuthStatus(args) {
2001
+ try {
2002
+ const token = await getAuthToken();
2003
+ const status = {
2004
+ auth_method: BAASIX_AUTH_TOKEN ? "Manual Token" : BAASIX_EMAIL ? "Auto-login" : "None",
2005
+ configured_user: BAASIX_EMAIL || "Not configured",
2006
+ manual_token_provided: !!BAASIX_AUTH_TOKEN,
2007
+ authenticated: !!token,
2008
+ auto_login_expires: authExpiry ? new Date(authExpiry).toISOString() : "N/A",
2009
+ auto_login_valid: !!(authToken && authExpiry && Date.now() < authExpiry),
2010
+ };
2011
+
2012
+ return {
2013
+ content: [
2014
+ {
2015
+ type: "text",
2016
+ text: JSON.stringify(status, null, 2),
2017
+ },
2018
+ ],
2019
+ };
2020
+ } catch (error) {
2021
+ return {
2022
+ content: [
2023
+ {
2024
+ type: "text",
2025
+ text: JSON.stringify(
2026
+ {
2027
+ error: error.message,
2028
+ auth_method: "None",
2029
+ manual_token_provided: !!BAASIX_AUTH_TOKEN,
2030
+ configured_user: BAASIX_EMAIL || "Not configured",
2031
+ },
2032
+ null,
2033
+ 2
2034
+ ),
2035
+ },
2036
+ ],
2037
+ };
2038
+ }
2039
+ }
2040
+
2041
+ async handleRefreshAuth(args) {
2042
+ if (BAASIX_AUTH_TOKEN) {
2043
+ return {
2044
+ content: [
2045
+ {
2046
+ type: "text",
2047
+ text: JSON.stringify(
2048
+ {
2049
+ message: "Using manual token (BAASIX_AUTH_TOKEN), no refresh needed",
2050
+ auth_method: "Manual Token",
2051
+ },
2052
+ null,
2053
+ 2
2054
+ ),
2055
+ },
2056
+ ],
2057
+ };
2058
+ }
2059
+
2060
+ // Clear current auto-login token to force refresh
2061
+ authToken = null;
2062
+ authExpiry = null;
2063
+
2064
+ try {
2065
+ const token = await getAuthToken();
2066
+ const result = {
2067
+ success: !!token,
2068
+ user: BAASIX_EMAIL || "Not configured",
2069
+ token_received: !!token,
2070
+ expires: authExpiry ? new Date(authExpiry).toISOString() : "Unknown",
2071
+ auth_method: "Auto-login",
2072
+ };
2073
+
2074
+ return {
2075
+ content: [
2076
+ {
2077
+ type: "text",
2078
+ text: JSON.stringify(result, null, 2),
2079
+ },
2080
+ ],
2081
+ };
2082
+ } catch (error) {
2083
+ return {
2084
+ content: [
2085
+ {
2086
+ type: "text",
2087
+ text: JSON.stringify(
2088
+ {
2089
+ success: false,
2090
+ error: error.message,
2091
+ user: BAASIX_EMAIL || "Not configured",
2092
+ },
2093
+ null,
2094
+ 2
2095
+ ),
2096
+ },
2097
+ ],
2098
+ };
2099
+ }
2100
+ }
2101
+
2102
+ // Reports and Analytics Methods
2103
+ async handleGenerateReport(args) {
2104
+ const { collection, groupBy, filter, dateRange } = args;
2105
+ const params = new URLSearchParams();
2106
+ if (groupBy) params.append("groupBy", groupBy);
2107
+ if (filter) params.append("filter", JSON.stringify(filter));
2108
+ if (dateRange) params.append("dateRange", JSON.stringify(dateRange));
2109
+
2110
+ const report = await baasixRequest(`/reports/${collection}?${params}`);
2111
+ return {
2112
+ content: [
2113
+ {
2114
+ type: "text",
2115
+ text: JSON.stringify(report, null, 2),
2116
+ },
2117
+ ],
2118
+ };
2119
+ }
2120
+
2121
+ async handleGetStats(args) {
2122
+ const { collections, timeframe } = args;
2123
+ const params = new URLSearchParams();
2124
+ if (collections) params.append("collections", JSON.stringify(collections));
2125
+ if (timeframe) params.append("timeframe", timeframe);
2126
+
2127
+ const stats = await baasixRequest(`/reports/stats?${params}`);
2128
+ return {
2129
+ content: [
2130
+ {
2131
+ type: "text",
2132
+ text: JSON.stringify(stats, null, 2),
2133
+ },
2134
+ ],
2135
+ };
2136
+ }
2137
+
2138
+ // Notification Methods
2139
+ async handleListNotifications(args) {
2140
+ const { page = 1, limit = 10, seen } = args;
2141
+ const params = new URLSearchParams();
2142
+ params.append("page", page.toString());
2143
+ params.append("limit", limit.toString());
2144
+ if (seen !== undefined) params.append("seen", seen.toString());
2145
+
2146
+ const notifications = await baasixRequest(`/notifications?${params}`);
2147
+ return {
2148
+ content: [
2149
+ {
2150
+ type: "text",
2151
+ text: JSON.stringify(notifications, null, 2),
2152
+ },
2153
+ ],
2154
+ };
2155
+ }
2156
+
2157
+ async handleSendNotification(args) {
2158
+ const { recipients, title, message, type = "info" } = args;
2159
+ const result = await baasixRequest("/notifications", {
2160
+ method: "POST",
2161
+ data: { recipients, title, message, type },
2162
+ });
2163
+ return {
2164
+ content: [
2165
+ {
2166
+ type: "text",
2167
+ text: JSON.stringify(result, null, 2),
2168
+ },
2169
+ ],
2170
+ };
2171
+ }
2172
+
2173
+ async handleMarkNotificationSeen(args) {
2174
+ const { id } = args;
2175
+ const result = await baasixRequest(`/notifications/${id}/seen`, {
2176
+ method: "PUT",
2177
+ });
2178
+ return {
2179
+ content: [
2180
+ {
2181
+ type: "text",
2182
+ text: JSON.stringify(result, null, 2),
2183
+ },
2184
+ ],
2185
+ };
2186
+ }
2187
+
2188
+ // Settings Methods
2189
+ async handleGetSettings(args) {
2190
+ const { key } = args;
2191
+ const endpoint = key ? `/settings/${key}` : "/settings";
2192
+ const settings = await baasixRequest(endpoint);
2193
+ return {
2194
+ content: [
2195
+ {
2196
+ type: "text",
2197
+ text: JSON.stringify(settings, null, 2),
2198
+ },
2199
+ ],
2200
+ };
2201
+ }
2202
+
2203
+ async handleUpdateSettings(args) {
2204
+ const { settings } = args;
2205
+ const result = await baasixRequest("/settings", {
2206
+ method: "PUT",
2207
+ data: settings,
2208
+ });
2209
+ return {
2210
+ content: [
2211
+ {
2212
+ type: "text",
2213
+ text: JSON.stringify(result, null, 2),
2214
+ },
2215
+ ],
2216
+ };
2217
+ }
2218
+
2219
+ // Email Template Methods
2220
+ async handleListTemplates(args) {
2221
+ const { filter, page = 1, limit = 10 } = args;
2222
+ const params = new URLSearchParams();
2223
+ if (filter) params.append("filter", JSON.stringify(filter));
2224
+ params.append("page", page.toString());
2225
+ params.append("limit", limit.toString());
2226
+
2227
+ const templates = await baasixRequest(`/items/baasix_Template?${params}`);
2228
+ return {
2229
+ content: [
2230
+ {
2231
+ type: "text",
2232
+ text: JSON.stringify(templates, null, 2),
2233
+ },
2234
+ ],
2235
+ };
2236
+ }
2237
+
2238
+ async handleGetTemplate(args) {
2239
+ const { id } = args;
2240
+ const template = await baasixRequest(`/items/baasix_Template/${id}`);
2241
+ return {
2242
+ content: [
2243
+ {
2244
+ type: "text",
2245
+ text: JSON.stringify(template, null, 2),
2246
+ },
2247
+ ],
2248
+ };
2249
+ }
2250
+
2251
+ async handleUpdateTemplate(args) {
2252
+ const { id, ...updateData } = args;
2253
+ const result = await baasixRequest(`/items/baasix_Template/${id}`, {
2254
+ method: "PATCH",
2255
+ data: updateData,
2256
+ });
2257
+ return {
2258
+ content: [
2259
+ {
2260
+ type: "text",
2261
+ text: JSON.stringify(result, null, 2),
2262
+ },
2263
+ ],
2264
+ };
2265
+ }
2266
+
2267
+ // Permission Methods
2268
+ async handleListRoles(args) {
2269
+ const roles = await baasixRequest("/permissions/roles");
2270
+ return {
2271
+ content: [
2272
+ {
2273
+ type: "text",
2274
+ text: JSON.stringify(roles, null, 2),
2275
+ },
2276
+ ],
2277
+ };
2278
+ }
2279
+
2280
+ async handleListPermissions(args) {
2281
+ const { filter, sort, page = 1, limit = 10 } = args;
2282
+ const params = new URLSearchParams();
2283
+ if (filter) params.append("filter", JSON.stringify(filter));
2284
+ if (sort) params.append("sort", sort);
2285
+ params.append("page", page.toString());
2286
+ params.append("limit", limit.toString());
2287
+
2288
+ const permissions = await baasixRequest(`/permissions?${params}`);
2289
+ return {
2290
+ content: [
2291
+ {
2292
+ type: "text",
2293
+ text: JSON.stringify(permissions, null, 2),
2294
+ },
2295
+ ],
2296
+ };
2297
+ }
2298
+
2299
+ async handleGetPermission(args) {
2300
+ const { id } = args;
2301
+ const permission = await baasixRequest(`/permissions/${id}`);
2302
+ return {
2303
+ content: [
2304
+ {
2305
+ type: "text",
2306
+ text: JSON.stringify(permission, null, 2),
2307
+ },
2308
+ ],
2309
+ };
2310
+ }
2311
+
2312
+ async handleGetPermissions(args) {
2313
+ const { role } = args;
2314
+ const permissions = await baasixRequest(`/permissions/${role}`);
2315
+ return {
2316
+ content: [
2317
+ {
2318
+ type: "text",
2319
+ text: JSON.stringify(permissions, null, 2),
2320
+ },
2321
+ ],
2322
+ };
2323
+ }
2324
+
2325
+ async handleCreatePermission(args) {
2326
+ const { role_Id, collection, action, fields, conditions, defaultValues, relConditions } = args;
2327
+ const result = await baasixRequest("/permissions", {
2328
+ method: "POST",
2329
+ data: { role_Id, collection, action, fields, conditions, defaultValues, relConditions },
2330
+ });
2331
+ return {
2332
+ content: [
2333
+ {
2334
+ type: "text",
2335
+ text: JSON.stringify(result, null, 2),
2336
+ },
2337
+ ],
2338
+ };
2339
+ }
2340
+
2341
+ async handleUpdatePermission(args) {
2342
+ const { id, role_Id, collection, action, fields, conditions, defaultValues, relConditions } = args;
2343
+ const result = await baasixRequest(`/permissions/${id}`, {
2344
+ method: "PATCH",
2345
+ data: { role_Id, collection, action, fields, conditions, defaultValues, relConditions },
2346
+ });
2347
+ return {
2348
+ content: [
2349
+ {
2350
+ type: "text",
2351
+ text: JSON.stringify(result, null, 2),
2352
+ },
2353
+ ],
2354
+ };
2355
+ }
2356
+
2357
+ async handleDeletePermission(args) {
2358
+ const { id } = args;
2359
+ const result = await baasixRequest(`/permissions/${id}`, {
2360
+ method: "DELETE",
2361
+ });
2362
+ return {
2363
+ content: [
2364
+ {
2365
+ type: "text",
2366
+ text: JSON.stringify(result, null, 2),
2367
+ },
2368
+ ],
2369
+ };
2370
+ }
2371
+
2372
+ async handleReloadPermissions(args) {
2373
+ const result = await baasixRequest("/permissions/reload", {
2374
+ method: "POST",
2375
+ });
2376
+ return {
2377
+ content: [
2378
+ {
2379
+ type: "text",
2380
+ text: JSON.stringify(result, null, 2),
2381
+ },
2382
+ ],
2383
+ };
2384
+ }
2385
+
2386
+ async handleUpdatePermissions(args) {
2387
+ const { role, permissions } = args;
2388
+ const result = await baasixRequest(`/permissions/${role}`, {
2389
+ method: "PUT",
2390
+ data: permissions,
2391
+ });
2392
+ return {
2393
+ content: [
2394
+ {
2395
+ type: "text",
2396
+ text: JSON.stringify(result, null, 2),
2397
+ },
2398
+ ],
2399
+ };
2400
+ }
2401
+
2402
+ // Realtime Methods
2403
+ async handleRealtimeStatus(args) {
2404
+ const result = await baasixRequest("/realtime/status");
2405
+ return {
2406
+ content: [
2407
+ {
2408
+ type: "text",
2409
+ text: JSON.stringify(result, null, 2),
2410
+ },
2411
+ ],
2412
+ };
2413
+ }
2414
+
2415
+ async handleRealtimeConfig(args) {
2416
+ const result = await baasixRequest("/realtime/config");
2417
+ return {
2418
+ content: [
2419
+ {
2420
+ type: "text",
2421
+ text: JSON.stringify(result, null, 2),
2422
+ },
2423
+ ],
2424
+ };
2425
+ }
2426
+
2427
+ async handleRealtimeCollections(args) {
2428
+ const result = await baasixRequest("/realtime/collections");
2429
+ return {
2430
+ content: [
2431
+ {
2432
+ type: "text",
2433
+ text: JSON.stringify(result, null, 2),
2434
+ },
2435
+ ],
2436
+ };
2437
+ }
2438
+
2439
+ async handleRealtimeEnable(args) {
2440
+ const { collection, actions, replicaIdentityFull } = args;
2441
+ const result = await baasixRequest(`/realtime/collections/${collection}/enable`, {
2442
+ method: "POST",
2443
+ data: {
2444
+ actions: actions || ["insert", "update", "delete"],
2445
+ replicaIdentityFull: replicaIdentityFull || false,
2446
+ },
2447
+ });
2448
+ return {
2449
+ content: [
2450
+ {
2451
+ type: "text",
2452
+ text: JSON.stringify(result, null, 2),
2453
+ },
2454
+ ],
2455
+ };
2456
+ }
2457
+
2458
+ async handleRealtimeDisable(args) {
2459
+ const { collection } = args;
2460
+ const result = await baasixRequest(`/realtime/collections/${collection}/disable`, {
2461
+ method: "POST",
2462
+ });
2463
+ return {
2464
+ content: [
2465
+ {
2466
+ type: "text",
2467
+ text: JSON.stringify(result, null, 2),
2468
+ },
2469
+ ],
2470
+ };
2471
+ }
2472
+
2473
+ // Utility Methods
2474
+ async handleServerInfo(args) {
2475
+ try {
2476
+ const info = await baasixRequest("/utils/info");
2477
+ return {
2478
+ content: [
2479
+ {
2480
+ type: "text",
2481
+ text: JSON.stringify(
2482
+ {
2483
+ baasix_server: info,
2484
+ mcp_server: {
2485
+ name: "baasix-mcp-server",
2486
+ version: "0.1.0",
2487
+ timestamp: new Date().toISOString(),
2488
+ uptime: process.uptime(),
2489
+ memory: process.memoryUsage(),
2490
+ nodejs: process.version,
2491
+ baasix_url: BAASIX_URL,
2492
+ },
2493
+ },
2494
+ null,
2495
+ 2
2496
+ ),
2497
+ },
2498
+ ],
2499
+ };
2500
+ } catch (error) {
2501
+ return {
2502
+ content: [
2503
+ {
2504
+ type: "text",
2505
+ text: JSON.stringify(
2506
+ {
2507
+ error: "Could not fetch Baasix server info",
2508
+ mcp_server: {
2509
+ name: "baasix-mcp-server",
2510
+ version: "0.1.0",
2511
+ timestamp: new Date().toISOString(),
2512
+ uptime: process.uptime(),
2513
+ memory: process.memoryUsage(),
2514
+ nodejs: process.version,
2515
+ baasix_url: BAASIX_URL,
2516
+ },
2517
+ },
2518
+ null,
2519
+ 2
2520
+ ),
2521
+ },
2522
+ ],
2523
+ };
2524
+ }
2525
+ }
2526
+
2527
+ async handleSortItems(args) {
2528
+ const { collection, item, to } = args;
2529
+ const result = await baasixRequest(`/utils/sort/${collection}`, {
2530
+ method: "POST",
2531
+ data: { item, to },
2532
+ });
2533
+ return {
2534
+ content: [
2535
+ {
2536
+ type: "text",
2537
+ text: JSON.stringify(result, null, 2),
2538
+ },
2539
+ ],
2540
+ };
2541
+ }
2542
+
2543
+ // Auth Methods
2544
+ async handleRegisterUser(args) {
2545
+ const { email, password, firstName, lastName, tenant, roleName, inviteToken, authMode, ...customParams } = args;
2546
+ const result = await baasixRequest("/auth/register", {
2547
+ method: "POST",
2548
+ data: { email, password, firstName, lastName, tenant, roleName, inviteToken, authMode, ...customParams },
2549
+ });
2550
+ return {
2551
+ content: [
2552
+ {
2553
+ type: "text",
2554
+ text: JSON.stringify(result, null, 2),
2555
+ },
2556
+ ],
2557
+ };
2558
+ }
2559
+
2560
+ async handleLogin(args) {
2561
+ const { email, password, tenant_Id, authMode } = args;
2562
+ const result = await baasixRequest("/auth/login", {
2563
+ method: "POST",
2564
+ data: { email, password, tenant_Id, authMode },
2565
+ });
2566
+ return {
2567
+ content: [
2568
+ {
2569
+ type: "text",
2570
+ text: JSON.stringify(result, null, 2),
2571
+ },
2572
+ ],
2573
+ };
2574
+ }
2575
+
2576
+ async handleSendInvite(args) {
2577
+ const { email, role_Id, tenant_Id, link } = args;
2578
+ const result = await baasixRequest("/auth/invite", {
2579
+ method: "POST",
2580
+ data: { email, role_Id, tenant_Id, link },
2581
+ });
2582
+ return {
2583
+ content: [
2584
+ {
2585
+ type: "text",
2586
+ text: JSON.stringify(result, null, 2),
2587
+ },
2588
+ ],
2589
+ };
2590
+ }
2591
+
2592
+ async handleVerifyInvite(args) {
2593
+ const { token, link } = args;
2594
+ const params = new URLSearchParams();
2595
+ if (link) params.append("link", link);
2596
+
2597
+ const result = await baasixRequest(`/auth/verify-invite/${token}?${params}`);
2598
+ return {
2599
+ content: [
2600
+ {
2601
+ type: "text",
2602
+ text: JSON.stringify(result, null, 2),
2603
+ },
2604
+ ],
2605
+ };
2606
+ }
2607
+
2608
+ async handleSendMagicLink(args) {
2609
+ const { email, link, mode } = args;
2610
+ const result = await baasixRequest("/auth/magiclink", {
2611
+ method: "POST",
2612
+ data: { email, link, mode },
2613
+ });
2614
+ return {
2615
+ content: [
2616
+ {
2617
+ type: "text",
2618
+ text: JSON.stringify(result, null, 2),
2619
+ },
2620
+ ],
2621
+ };
2622
+ }
2623
+
2624
+ async handleGetUserTenants() {
2625
+ const result = await baasixRequest("/auth/tenants");
2626
+ return {
2627
+ content: [
2628
+ {
2629
+ type: "text",
2630
+ text: JSON.stringify(result, null, 2),
2631
+ },
2632
+ ],
2633
+ };
2634
+ }
2635
+
2636
+ async handleSwitchTenant(args) {
2637
+ const { tenant_Id } = args;
2638
+ const result = await baasixRequest("/auth/switch-tenant", {
2639
+ method: "POST",
2640
+ data: { tenant_Id },
2641
+ });
2642
+ return {
2643
+ content: [
2644
+ {
2645
+ type: "text",
2646
+ text: JSON.stringify(result, null, 2),
2647
+ },
2648
+ ],
2649
+ };
2650
+ }
2651
+
2652
+ async handleLogout() {
2653
+ const result = await baasixRequest("/auth/logout");
2654
+ return {
2655
+ content: [
2656
+ {
2657
+ type: "text",
2658
+ text: JSON.stringify(result, null, 2),
2659
+ },
2660
+ ],
2661
+ };
2662
+ }
2663
+
2664
+ async handleGetCurrentUser(args) {
2665
+ const { fields } = args;
2666
+ const params = new URLSearchParams();
2667
+ if (fields) params.append("fields", fields.join(","));
2668
+
2669
+ const result = await baasixRequest(`/auth/me?${params}`);
2670
+ return {
2671
+ content: [
2672
+ {
2673
+ type: "text",
2674
+ text: JSON.stringify(result, null, 2),
2675
+ },
2676
+ ],
2677
+ };
2678
+ }
2679
+
2680
+ async run() {
2681
+ const transport = new StdioServerTransport();
2682
+ await this.server.connect(transport);
2683
+ console.error("Baasix MCP Server running on stdio");
2684
+ }
2685
+ }
2686
+
2687
+ // Export the server class
2688
+ export { BaasixMCPServer };
2689
+
2690
+ // Export the main function to start MCP server
2691
+ export const startMCPServer = async () => {
2692
+ const server = new BaasixMCPServer();
2693
+ return server.run();
2694
+ };