@daylight-labs/sharedb-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,126 @@
1
+ import { api } from '../api-client.js';
2
+ import { config } from '../config.js';
3
+ async function getPostgRESTUrl(databaseId) {
4
+ const info = await api.databases.postgrest(databaseId);
5
+ return info.postgrestUrl;
6
+ }
7
+ export const authTools = [
8
+ {
9
+ name: 'get_api_endpoints',
10
+ description: 'Get all ShareDB API endpoints (Auth, PostgREST, Admin). Use this to get the correct URLs for implementing API calls in your app. Pass a database_id to see endpoints for specific tables.',
11
+ inputSchema: {
12
+ type: 'object',
13
+ properties: {
14
+ database_id: {
15
+ type: 'string',
16
+ description: 'Optional database ID to list specific table endpoints',
17
+ },
18
+ },
19
+ required: [],
20
+ },
21
+ },
22
+ {
23
+ name: 'get_auth_config',
24
+ description: 'Get authentication configuration including endpoints, JWT structure, and helper functions for integrating auth into your app',
25
+ inputSchema: {
26
+ type: 'object',
27
+ properties: {},
28
+ required: [],
29
+ },
30
+ },
31
+ ];
32
+ export async function handleAuthTool(name, args) {
33
+ try {
34
+ switch (name) {
35
+ case 'get_api_endpoints': {
36
+ const databaseId = args.database_id;
37
+ let tableEndpoints = {};
38
+ let tableList = [];
39
+ let postgrestUrl = '<PostgREST URL - specify database_id to get actual URL>';
40
+ if (databaseId) {
41
+ try {
42
+ const [schema, url] = await Promise.all([
43
+ api.databases.schema(databaseId),
44
+ getPostgRESTUrl(databaseId),
45
+ ]);
46
+ tableList = schema.tables.map(t => t.table_name);
47
+ postgrestUrl = url || '<PostgREST not running - use start_postgrest tool>';
48
+ for (const tableName of tableList) {
49
+ tableEndpoints[tableName] = {
50
+ list: `GET ${postgrestUrl}/${tableName}`,
51
+ getOne: `GET ${postgrestUrl}/${tableName}?id=eq.<uuid>`,
52
+ create: `POST ${postgrestUrl}/${tableName}`,
53
+ update: `PATCH ${postgrestUrl}/${tableName}?id=eq.<uuid>`,
54
+ delete: `DELETE ${postgrestUrl}/${tableName}?id=eq.<uuid>`,
55
+ };
56
+ }
57
+ }
58
+ catch {
59
+ // Database might not have tables yet
60
+ }
61
+ }
62
+ const endpoints = {
63
+ auth: {
64
+ baseUrl: config.authApiUrl,
65
+ endpoints: {
66
+ register: `POST ${config.authApiUrl}/auth/register`,
67
+ login: `POST ${config.authApiUrl}/auth/login`,
68
+ refresh: `POST ${config.authApiUrl}/auth/refresh`,
69
+ logout: `POST ${config.authApiUrl}/auth/logout`,
70
+ me: `GET ${config.authApiUrl}/auth/me`,
71
+ },
72
+ },
73
+ postgrest: {
74
+ baseUrl: postgrestUrl,
75
+ description: 'REST API auto-generated from your database schema (per-database)',
76
+ authentication: 'Include JWT in Authorization header: Bearer <token>',
77
+ tables: tableList.length > 0 ? tableEndpoints : {
78
+ '<table_name>': {
79
+ list: `GET ${postgrestUrl}/<table>`,
80
+ getOne: `GET ${postgrestUrl}/<table>?id=eq.<uuid>`,
81
+ create: `POST ${postgrestUrl}/<table>`,
82
+ update: `PATCH ${postgrestUrl}/<table>?id=eq.<uuid>`,
83
+ delete: `DELETE ${postgrestUrl}/<table>?id=eq.<uuid>`,
84
+ },
85
+ },
86
+ },
87
+ };
88
+ return {
89
+ content: [
90
+ {
91
+ type: 'text',
92
+ text: JSON.stringify(endpoints, null, 2),
93
+ },
94
+ ],
95
+ };
96
+ }
97
+ case 'get_auth_config': {
98
+ const config = await api.auth.config();
99
+ return {
100
+ content: [
101
+ {
102
+ type: 'text',
103
+ text: JSON.stringify(config, null, 2),
104
+ },
105
+ ],
106
+ };
107
+ }
108
+ default:
109
+ return {
110
+ content: [{ type: 'text', text: `Unknown auth tool: ${name}` }],
111
+ isError: true,
112
+ };
113
+ }
114
+ }
115
+ catch (error) {
116
+ return {
117
+ content: [
118
+ {
119
+ type: 'text',
120
+ text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
121
+ },
122
+ ],
123
+ isError: true,
124
+ };
125
+ }
126
+ }
@@ -0,0 +1,127 @@
1
+ export declare const dataTools: ({
2
+ name: string;
3
+ description: string;
4
+ inputSchema: {
5
+ type: "object";
6
+ properties: {
7
+ database: {
8
+ type: string;
9
+ description: string;
10
+ };
11
+ table: {
12
+ type: string;
13
+ description: string;
14
+ };
15
+ rows: {
16
+ type: string;
17
+ items: {
18
+ type: string;
19
+ };
20
+ description: string;
21
+ };
22
+ select?: undefined;
23
+ filter?: undefined;
24
+ order?: undefined;
25
+ limit?: undefined;
26
+ data?: undefined;
27
+ };
28
+ required: string[];
29
+ };
30
+ } | {
31
+ name: string;
32
+ description: string;
33
+ inputSchema: {
34
+ type: "object";
35
+ properties: {
36
+ database: {
37
+ type: string;
38
+ description: string;
39
+ };
40
+ table: {
41
+ type: string;
42
+ description: string;
43
+ };
44
+ select: {
45
+ type: string;
46
+ description: string;
47
+ };
48
+ filter: {
49
+ type: string;
50
+ description: string;
51
+ };
52
+ order: {
53
+ type: string;
54
+ description: string;
55
+ };
56
+ limit: {
57
+ type: string;
58
+ description: string;
59
+ };
60
+ rows?: undefined;
61
+ data?: undefined;
62
+ };
63
+ required: string[];
64
+ };
65
+ } | {
66
+ name: string;
67
+ description: string;
68
+ inputSchema: {
69
+ type: "object";
70
+ properties: {
71
+ database: {
72
+ type: string;
73
+ description: string;
74
+ };
75
+ table: {
76
+ type: string;
77
+ description: string;
78
+ };
79
+ filter: {
80
+ type: string;
81
+ description: string;
82
+ };
83
+ data: {
84
+ type: string;
85
+ description: string;
86
+ };
87
+ rows?: undefined;
88
+ select?: undefined;
89
+ order?: undefined;
90
+ limit?: undefined;
91
+ };
92
+ required: string[];
93
+ };
94
+ } | {
95
+ name: string;
96
+ description: string;
97
+ inputSchema: {
98
+ type: "object";
99
+ properties: {
100
+ database: {
101
+ type: string;
102
+ description: string;
103
+ };
104
+ table: {
105
+ type: string;
106
+ description: string;
107
+ };
108
+ filter: {
109
+ type: string;
110
+ description: string;
111
+ };
112
+ rows?: undefined;
113
+ select?: undefined;
114
+ order?: undefined;
115
+ limit?: undefined;
116
+ data?: undefined;
117
+ };
118
+ required: string[];
119
+ };
120
+ })[];
121
+ export declare function handleDataTool(name: string, args: Record<string, unknown>): Promise<{
122
+ content: Array<{
123
+ type: 'text';
124
+ text: string;
125
+ }>;
126
+ isError?: boolean;
127
+ }>;
@@ -0,0 +1,290 @@
1
+ import { config } from '../config.js';
2
+ import { api } from '../api-client.js';
3
+ export const dataTools = [
4
+ {
5
+ name: 'insert_rows',
6
+ description: 'Insert one or more rows into a table via PostgREST. Use this to add seed data or default data to tables.',
7
+ inputSchema: {
8
+ type: 'object',
9
+ properties: {
10
+ database: {
11
+ type: 'string',
12
+ description: 'The database name or ID to operate on',
13
+ },
14
+ table: {
15
+ type: 'string',
16
+ description: 'The table name to insert into',
17
+ },
18
+ rows: {
19
+ type: 'array',
20
+ items: { type: 'object' },
21
+ description: 'Array of row objects to insert. Each object should have column names as keys.',
22
+ },
23
+ },
24
+ required: ['database', 'table', 'rows'],
25
+ },
26
+ },
27
+ {
28
+ name: 'query_rows',
29
+ description: 'Query rows from a table via PostgREST. Supports filtering, ordering, and pagination.',
30
+ inputSchema: {
31
+ type: 'object',
32
+ properties: {
33
+ database: {
34
+ type: 'string',
35
+ description: 'The database name or ID to operate on',
36
+ },
37
+ table: {
38
+ type: 'string',
39
+ description: 'The table name to query',
40
+ },
41
+ select: {
42
+ type: 'string',
43
+ description: 'Columns to select (default: *). Example: "id,name,created_at"',
44
+ },
45
+ filter: {
46
+ type: 'object',
47
+ description: 'Filter conditions as key-value pairs. Example: { "status": "eq.active", "price": "gt.100" }',
48
+ },
49
+ order: {
50
+ type: 'string',
51
+ description: 'Order by column(s). Example: "created_at.desc" or "name.asc,id.desc"',
52
+ },
53
+ limit: {
54
+ type: 'number',
55
+ description: 'Maximum number of rows to return',
56
+ },
57
+ },
58
+ required: ['database', 'table'],
59
+ },
60
+ },
61
+ {
62
+ name: 'update_rows',
63
+ description: 'Update rows in a table via PostgREST. Requires a filter to identify which rows to update.',
64
+ inputSchema: {
65
+ type: 'object',
66
+ properties: {
67
+ database: {
68
+ type: 'string',
69
+ description: 'The database name or ID to operate on',
70
+ },
71
+ table: {
72
+ type: 'string',
73
+ description: 'The table name to update',
74
+ },
75
+ filter: {
76
+ type: 'object',
77
+ description: 'Filter conditions to identify rows to update. Example: { "id": "eq.uuid-here" }',
78
+ },
79
+ data: {
80
+ type: 'object',
81
+ description: 'Object with column names and new values to set',
82
+ },
83
+ },
84
+ required: ['database', 'table', 'filter', 'data'],
85
+ },
86
+ },
87
+ {
88
+ name: 'delete_rows',
89
+ description: 'Delete rows from a table via PostgREST. Requires a filter to identify which rows to delete.',
90
+ inputSchema: {
91
+ type: 'object',
92
+ properties: {
93
+ database: {
94
+ type: 'string',
95
+ description: 'The database name or ID to operate on',
96
+ },
97
+ table: {
98
+ type: 'string',
99
+ description: 'The table name to delete from',
100
+ },
101
+ filter: {
102
+ type: 'object',
103
+ description: 'Filter conditions to identify rows to delete. Example: { "id": "eq.uuid-here" }',
104
+ },
105
+ },
106
+ required: ['database', 'table', 'filter'],
107
+ },
108
+ },
109
+ ];
110
+ async function getPostgRESTUrl(database) {
111
+ const info = await api.databases.postgrest(database);
112
+ if (!info.postgrestUrl) {
113
+ throw new Error(`PostgREST is not available for database "${database}". Status: ${info.status}`);
114
+ }
115
+ return info.postgrestUrl;
116
+ }
117
+ async function postgrestRequest(postgrestUrl, path, options = {}) {
118
+ try {
119
+ const response = await fetch(`${postgrestUrl}${path}`, {
120
+ ...options,
121
+ headers: {
122
+ 'Content-Type': 'application/json',
123
+ Authorization: `Bearer ${config.token}`,
124
+ Prefer: 'return=representation',
125
+ ...options.headers,
126
+ },
127
+ });
128
+ if (!response.ok) {
129
+ const error = await response.json().catch(() => ({ message: response.statusText }));
130
+ return { data: null, error: error.message || error.details || `HTTP ${response.status}` };
131
+ }
132
+ const data = await response.json().catch(() => null);
133
+ return { data };
134
+ }
135
+ catch (error) {
136
+ return { data: null, error: error instanceof Error ? error.message : 'Request failed' };
137
+ }
138
+ }
139
+ function buildFilterQuery(filter) {
140
+ const params = new URLSearchParams();
141
+ for (const [key, value] of Object.entries(filter)) {
142
+ params.append(key, value);
143
+ }
144
+ return params.toString();
145
+ }
146
+ export async function handleDataTool(name, args) {
147
+ try {
148
+ const database = args.database;
149
+ if (!database) {
150
+ return {
151
+ content: [{ type: 'text', text: 'Error: database parameter is required' }],
152
+ isError: true,
153
+ };
154
+ }
155
+ const postgrestUrl = await getPostgRESTUrl(database);
156
+ switch (name) {
157
+ case 'insert_rows': {
158
+ const table = args.table;
159
+ const rows = args.rows;
160
+ const { data, error } = await postgrestRequest(postgrestUrl, `/${table}`, {
161
+ method: 'POST',
162
+ body: JSON.stringify(rows),
163
+ });
164
+ if (error) {
165
+ return {
166
+ content: [{ type: 'text', text: `Error inserting rows: ${error}` }],
167
+ isError: true,
168
+ };
169
+ }
170
+ const inserted = Array.isArray(data) ? data : [data];
171
+ return {
172
+ content: [
173
+ {
174
+ type: 'text',
175
+ text: `Successfully inserted ${inserted.length} row(s) into ${table}:\n\n${JSON.stringify(inserted, null, 2)}`,
176
+ },
177
+ ],
178
+ };
179
+ }
180
+ case 'query_rows': {
181
+ const table = args.table;
182
+ const select = args.select || '*';
183
+ const filter = args.filter || {};
184
+ const order = args.order;
185
+ const limit = args.limit;
186
+ const params = new URLSearchParams();
187
+ params.append('select', select);
188
+ for (const [key, value] of Object.entries(filter)) {
189
+ params.append(key, value);
190
+ }
191
+ if (order)
192
+ params.append('order', order);
193
+ if (limit)
194
+ params.append('limit', limit.toString());
195
+ const { data, error } = await postgrestRequest(postgrestUrl, `/${table}?${params.toString()}`);
196
+ if (error) {
197
+ return {
198
+ content: [{ type: 'text', text: `Error querying rows: ${error}` }],
199
+ isError: true,
200
+ };
201
+ }
202
+ const rows = Array.isArray(data) ? data : [];
203
+ return {
204
+ content: [
205
+ {
206
+ type: 'text',
207
+ text: `Found ${rows.length} row(s) in ${table}:\n\n${JSON.stringify(rows, null, 2)}`,
208
+ },
209
+ ],
210
+ };
211
+ }
212
+ case 'update_rows': {
213
+ const table = args.table;
214
+ const filter = args.filter;
215
+ const updateData = args.data;
216
+ if (!filter || Object.keys(filter).length === 0) {
217
+ return {
218
+ content: [{ type: 'text', text: 'Error: filter is required to update rows (to prevent accidental mass updates)' }],
219
+ isError: true,
220
+ };
221
+ }
222
+ const query = buildFilterQuery(filter);
223
+ const { data, error } = await postgrestRequest(postgrestUrl, `/${table}?${query}`, {
224
+ method: 'PATCH',
225
+ body: JSON.stringify(updateData),
226
+ });
227
+ if (error) {
228
+ return {
229
+ content: [{ type: 'text', text: `Error updating rows: ${error}` }],
230
+ isError: true,
231
+ };
232
+ }
233
+ const updated = Array.isArray(data) ? data : [data];
234
+ return {
235
+ content: [
236
+ {
237
+ type: 'text',
238
+ text: `Successfully updated ${updated.length} row(s) in ${table}:\n\n${JSON.stringify(updated, null, 2)}`,
239
+ },
240
+ ],
241
+ };
242
+ }
243
+ case 'delete_rows': {
244
+ const table = args.table;
245
+ const filter = args.filter;
246
+ if (!filter || Object.keys(filter).length === 0) {
247
+ return {
248
+ content: [{ type: 'text', text: 'Error: filter is required to delete rows (to prevent accidental mass deletes)' }],
249
+ isError: true,
250
+ };
251
+ }
252
+ const query = buildFilterQuery(filter);
253
+ const { data, error } = await postgrestRequest(postgrestUrl, `/${table}?${query}`, {
254
+ method: 'DELETE',
255
+ });
256
+ if (error) {
257
+ return {
258
+ content: [{ type: 'text', text: `Error deleting rows: ${error}` }],
259
+ isError: true,
260
+ };
261
+ }
262
+ const deleted = Array.isArray(data) ? data : data ? [data] : [];
263
+ return {
264
+ content: [
265
+ {
266
+ type: 'text',
267
+ text: `Successfully deleted ${deleted.length} row(s) from ${table}`,
268
+ },
269
+ ],
270
+ };
271
+ }
272
+ default:
273
+ return {
274
+ content: [{ type: 'text', text: `Unknown data tool: ${name}` }],
275
+ isError: true,
276
+ };
277
+ }
278
+ }
279
+ catch (error) {
280
+ return {
281
+ content: [
282
+ {
283
+ type: 'text',
284
+ text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
285
+ },
286
+ ],
287
+ isError: true,
288
+ };
289
+ }
290
+ }
@@ -0,0 +1,114 @@
1
+ export declare const migrationTools: ({
2
+ name: string;
3
+ description: string;
4
+ inputSchema: {
5
+ type: "object";
6
+ properties: {
7
+ database_id: {
8
+ type: string;
9
+ description: string;
10
+ };
11
+ operation: {
12
+ type: string;
13
+ enum: string[];
14
+ description: string;
15
+ };
16
+ params: {
17
+ type: string;
18
+ description: string;
19
+ };
20
+ description?: undefined;
21
+ change_id?: undefined;
22
+ migration_id?: undefined;
23
+ };
24
+ required: string[];
25
+ };
26
+ } | {
27
+ name: string;
28
+ description: string;
29
+ inputSchema: {
30
+ type: "object";
31
+ properties: {
32
+ database_id: {
33
+ type: string;
34
+ description: string;
35
+ };
36
+ operation?: undefined;
37
+ params?: undefined;
38
+ description?: undefined;
39
+ change_id?: undefined;
40
+ migration_id?: undefined;
41
+ };
42
+ required: string[];
43
+ };
44
+ } | {
45
+ name: string;
46
+ description: string;
47
+ inputSchema: {
48
+ type: "object";
49
+ properties: {
50
+ database_id: {
51
+ type: string;
52
+ description: string;
53
+ };
54
+ description: {
55
+ type: string;
56
+ description: string;
57
+ };
58
+ operation?: undefined;
59
+ params?: undefined;
60
+ change_id?: undefined;
61
+ migration_id?: undefined;
62
+ };
63
+ required: string[];
64
+ };
65
+ } | {
66
+ name: string;
67
+ description: string;
68
+ inputSchema: {
69
+ type: "object";
70
+ properties: {
71
+ database_id: {
72
+ type: string;
73
+ description: string;
74
+ };
75
+ change_id: {
76
+ type: string;
77
+ description: string;
78
+ };
79
+ operation?: undefined;
80
+ params?: undefined;
81
+ description?: undefined;
82
+ migration_id?: undefined;
83
+ };
84
+ required: string[];
85
+ };
86
+ } | {
87
+ name: string;
88
+ description: string;
89
+ inputSchema: {
90
+ type: "object";
91
+ properties: {
92
+ database_id: {
93
+ type: string;
94
+ description: string;
95
+ };
96
+ migration_id: {
97
+ type: string;
98
+ description: string;
99
+ };
100
+ operation?: undefined;
101
+ params?: undefined;
102
+ description?: undefined;
103
+ change_id?: undefined;
104
+ };
105
+ required: string[];
106
+ };
107
+ })[];
108
+ export declare function handleMigrationTool(name: string, args: Record<string, unknown>): Promise<{
109
+ content: Array<{
110
+ type: 'text';
111
+ text: string;
112
+ }>;
113
+ isError?: boolean;
114
+ }>;