@daylight-labs/popdb-mcp 0.1.7

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,348 @@
1
+ export interface Database {
2
+ id: string;
3
+ name: string;
4
+ display_name: string;
5
+ environment: string;
6
+ created_at: string;
7
+ }
8
+ export interface SchemaChange {
9
+ id: string;
10
+ database_id: string;
11
+ migration_id: string | null;
12
+ change_type: string;
13
+ table_name: string;
14
+ column_name: string | null;
15
+ ddl: string;
16
+ reverse_ddl: string | null;
17
+ is_reversible: boolean;
18
+ change_order: number;
19
+ created_at: string;
20
+ }
21
+ export interface Migration {
22
+ id: string;
23
+ database_id: string;
24
+ version: number;
25
+ description: string;
26
+ status: string;
27
+ applied_at: string;
28
+ rolled_back_at: string | null;
29
+ }
30
+ export interface ColumnInfo {
31
+ column_name: string;
32
+ data_type: string;
33
+ is_nullable: boolean;
34
+ column_default: string | null;
35
+ }
36
+ export interface TableSchema {
37
+ table_name: string;
38
+ columns: ColumnInfo[];
39
+ indexes: Array<{
40
+ index_name: string;
41
+ column_name: string;
42
+ is_unique: boolean;
43
+ is_primary: boolean;
44
+ }>;
45
+ constraints: Array<{
46
+ constraint_name: string;
47
+ constraint_type: string;
48
+ column_name: string | null;
49
+ foreign_table_name: string | null;
50
+ foreign_column_name: string | null;
51
+ }>;
52
+ rls_enabled: boolean;
53
+ rls_policies: Array<{
54
+ policy_name: string;
55
+ command: string;
56
+ using_expression: string | null;
57
+ with_check_expression: string | null;
58
+ roles: string[];
59
+ }>;
60
+ }
61
+ export interface DatabaseSchema {
62
+ database: string;
63
+ tables: TableSchema[];
64
+ functions: Array<{
65
+ function_name: string;
66
+ return_type: string;
67
+ arguments: string;
68
+ }>;
69
+ message?: string;
70
+ }
71
+ export interface PostgRESTInfo {
72
+ postgrestUrl: string | null;
73
+ status: string;
74
+ port?: number;
75
+ containerName?: string;
76
+ startedAt?: string;
77
+ }
78
+ export interface LogEntry {
79
+ id: string;
80
+ database_id: string;
81
+ level: 'error' | 'debug';
82
+ message: string;
83
+ metadata: Record<string, unknown>;
84
+ source: 'admin-api' | 'admin-ui' | 'user-app' | 'postgrest';
85
+ request_id: string | null;
86
+ created_at: string;
87
+ }
88
+ export interface LogQueryOptions {
89
+ level?: string;
90
+ source?: string;
91
+ since?: string;
92
+ until?: string;
93
+ limit?: number;
94
+ }
95
+ export interface App {
96
+ id: string;
97
+ name: string;
98
+ display_name: string;
99
+ created_at: string;
100
+ updated_at: string;
101
+ databases: {
102
+ staging?: {
103
+ id: string;
104
+ name: string;
105
+ environment: string;
106
+ };
107
+ production?: {
108
+ id: string;
109
+ name: string;
110
+ environment: string;
111
+ };
112
+ };
113
+ }
114
+ export interface PromotionPreview {
115
+ staging: {
116
+ id: string;
117
+ name: string;
118
+ migrationVersion: number;
119
+ };
120
+ production: {
121
+ id: string;
122
+ name: string;
123
+ migrationVersion: number;
124
+ };
125
+ migrationsToApply: Array<{
126
+ id: string;
127
+ version: number;
128
+ description: string;
129
+ changeCount: number;
130
+ }>;
131
+ destructiveChanges: Array<{
132
+ changeType: string;
133
+ tableName: string;
134
+ columnName: string | null;
135
+ description: string;
136
+ }>;
137
+ warnings: string[];
138
+ totalMigrations: number;
139
+ totalChanges: number;
140
+ hasDestructiveChanges: boolean;
141
+ }
142
+ export interface DeploymentInfo {
143
+ id: string;
144
+ version: number;
145
+ fileCount: number;
146
+ totalSizeBytes: number;
147
+ }
148
+ export interface HostingStatus {
149
+ enabled: boolean;
150
+ url: string | null;
151
+ deployment: {
152
+ id: string;
153
+ version: number;
154
+ fileCount: number;
155
+ totalSizeBytes: number;
156
+ deployedAt: string;
157
+ } | null;
158
+ customDomains: Array<{
159
+ id: string;
160
+ domain: string;
161
+ status: string;
162
+ verified_at: string | null;
163
+ created_at: string;
164
+ }>;
165
+ }
166
+ export interface CustomDomainResult {
167
+ domain: {
168
+ id: string;
169
+ domain: string;
170
+ status: string;
171
+ };
172
+ dns: {
173
+ type: string;
174
+ name: string;
175
+ value: string;
176
+ instructions: string;
177
+ };
178
+ }
179
+ export declare const api: {
180
+ apps: {
181
+ list: () => Promise<{
182
+ apps: Array<{
183
+ id: string;
184
+ name: string;
185
+ display_name: string;
186
+ databases: Array<{
187
+ id: string;
188
+ name: string;
189
+ environment: string;
190
+ postgrest_status: string | null;
191
+ postgrestUrl: string | null;
192
+ }>;
193
+ }>;
194
+ }>;
195
+ get: (id: string) => Promise<{
196
+ app: App;
197
+ }>;
198
+ create: (data: {
199
+ name: string;
200
+ displayName: string;
201
+ }) => Promise<{
202
+ app: App;
203
+ }>;
204
+ delete: (id: string) => Promise<{
205
+ deleted: {
206
+ app: App;
207
+ };
208
+ }>;
209
+ startPostgrest: (id: string) => Promise<{
210
+ results: Record<string, {
211
+ status: string;
212
+ postgrestUrl: string | null;
213
+ }>;
214
+ }>;
215
+ promotePreview: (id: string) => Promise<{
216
+ preview: PromotionPreview | null;
217
+ message?: string;
218
+ }>;
219
+ };
220
+ databases: {
221
+ list: () => Promise<{
222
+ databases: Database[];
223
+ }>;
224
+ get: (id: string) => Promise<{
225
+ database: Database;
226
+ }>;
227
+ create: (data: {
228
+ name: string;
229
+ displayName: string;
230
+ environment: string;
231
+ }) => Promise<{
232
+ database: Database;
233
+ }>;
234
+ schema: (id: string) => Promise<DatabaseSchema>;
235
+ tableDetails: (dbId: string, table: string) => Promise<TableSchema>;
236
+ rlsPolicies: (id: string) => Promise<{
237
+ policies: Array<{
238
+ table_name: string;
239
+ policy_name: string;
240
+ command: string;
241
+ using_expression: string | null;
242
+ with_check_expression: string | null;
243
+ roles: string[];
244
+ }>;
245
+ }>;
246
+ functions: (id: string) => Promise<{
247
+ functions: Array<{
248
+ function_name: string;
249
+ return_type: string;
250
+ arguments: string;
251
+ definition: string;
252
+ }>;
253
+ }>;
254
+ postgrest: (id: string) => Promise<PostgRESTInfo>;
255
+ startPostgrest: (id: string) => Promise<PostgRESTInfo & {
256
+ message: string;
257
+ }>;
258
+ restartPostgrest: (id: string) => Promise<PostgRESTInfo & {
259
+ message: string;
260
+ }>;
261
+ };
262
+ changes: {
263
+ list: (dbId: string) => Promise<{
264
+ changes: SchemaChange[];
265
+ }>;
266
+ stage: (dbId: string, operation: string, params: Record<string, unknown>) => Promise<{
267
+ staged: SchemaChange[];
268
+ }>;
269
+ preview: (dbId: string) => Promise<{
270
+ preview: {
271
+ database: string;
272
+ changeCount: number;
273
+ hasIrreversibleChanges: boolean;
274
+ summary: Array<{
275
+ type: string;
276
+ table: string;
277
+ column: string | null;
278
+ reversible: boolean;
279
+ }>;
280
+ ddlStatements: string[];
281
+ } | null;
282
+ message?: string;
283
+ }>;
284
+ apply: (dbId: string, description?: string) => Promise<{
285
+ migration: Migration;
286
+ }>;
287
+ unstage: (dbId: string, changeId: string) => Promise<{
288
+ deleted: SchemaChange;
289
+ }>;
290
+ };
291
+ migrations: {
292
+ list: (dbId: string) => Promise<{
293
+ migrations: Migration[];
294
+ }>;
295
+ get: (dbId: string, migrationId: string) => Promise<{
296
+ migration: Migration & {
297
+ changes: SchemaChange[];
298
+ };
299
+ }>;
300
+ rollback: (dbId: string, migrationId: string) => Promise<{
301
+ rolledBack: {
302
+ migrationId: string;
303
+ version: number;
304
+ changesReverted: number;
305
+ };
306
+ }>;
307
+ };
308
+ auth: {
309
+ config: () => Promise<{
310
+ endpoints: Record<string, string>;
311
+ jwtStructure: Record<string, string>;
312
+ sessionVariable: string;
313
+ helperFunction: string;
314
+ }>;
315
+ };
316
+ hosting: {
317
+ deploy: (appId: string, tarPath: string) => Promise<{
318
+ deployment: DeploymentInfo;
319
+ url: string | null;
320
+ }>;
321
+ status: (appId: string) => Promise<{
322
+ hosting: HostingStatus;
323
+ }>;
324
+ addDomain: (appId: string, domain: string) => Promise<CustomDomainResult>;
325
+ removeDomain: (appId: string, domainId: string) => Promise<{
326
+ deleted: {
327
+ id: string;
328
+ domain: string;
329
+ };
330
+ }>;
331
+ };
332
+ logs: {
333
+ query: (dbId: string, options?: LogQueryOptions) => Promise<{
334
+ logs: LogEntry[];
335
+ total: number;
336
+ limit: number;
337
+ offset: number;
338
+ }>;
339
+ get: (dbId: string, logId: string) => Promise<{
340
+ log: LogEntry;
341
+ }>;
342
+ };
343
+ };
344
+ export declare function resolveStagingDatabase(identifier: string): Promise<{
345
+ databaseId: string;
346
+ isApp: boolean;
347
+ appId?: string;
348
+ }>;
@@ -0,0 +1,143 @@
1
+ import fs from 'node:fs';
2
+ import { config } from './config.js';
3
+ async function request(path, options) {
4
+ const response = await fetch(`${config.adminApiUrl}${path}`, {
5
+ ...options,
6
+ headers: {
7
+ 'Content-Type': 'application/json',
8
+ Authorization: `Bearer ${config.token}`,
9
+ ...options?.headers,
10
+ },
11
+ });
12
+ if (!response.ok) {
13
+ const error = await response.json().catch(() => ({ error: 'Request failed' }));
14
+ throw new Error(error.error || `Request failed: ${response.status}`);
15
+ }
16
+ return response.json();
17
+ }
18
+ async function uploadFile(path, filePath) {
19
+ const fileBuffer = fs.readFileSync(filePath);
20
+ const blob = new Blob([fileBuffer], { type: 'application/gzip' });
21
+ const formData = new FormData();
22
+ formData.append('file', blob, 'bundle.tar.gz');
23
+ const response = await fetch(`${config.adminApiUrl}${path}`, {
24
+ method: 'POST',
25
+ headers: {
26
+ Authorization: `Bearer ${config.token}`,
27
+ },
28
+ body: formData,
29
+ });
30
+ if (!response.ok) {
31
+ const error = await response.json().catch(() => ({ error: 'Upload failed' }));
32
+ throw new Error(error.error || `Upload failed: ${response.status}`);
33
+ }
34
+ return response.json();
35
+ }
36
+ export const api = {
37
+ apps: {
38
+ list: () => request('/admin/apps'),
39
+ get: (id) => request(`/admin/apps/${id}`),
40
+ create: (data) => request('/admin/apps', {
41
+ method: 'POST',
42
+ body: JSON.stringify(data),
43
+ }),
44
+ delete: (id) => request(`/admin/apps/${id}`, {
45
+ method: 'DELETE',
46
+ }),
47
+ startPostgrest: (id) => request(`/admin/apps/${id}/start-postgrest`, { method: 'POST', body: JSON.stringify({}) }),
48
+ promotePreview: (id) => request(`/admin/apps/${id}/promote/preview`),
49
+ },
50
+ databases: {
51
+ list: () => request('/admin/databases'),
52
+ get: (id) => request(`/admin/databases/${id}`),
53
+ create: (data) => request('/admin/databases', {
54
+ method: 'POST',
55
+ body: JSON.stringify(data),
56
+ }),
57
+ schema: (id) => request(`/admin/databases/${id}/schema`),
58
+ tableDetails: (dbId, table) => request(`/admin/databases/${dbId}/schema/tables/${table}`),
59
+ rlsPolicies: (id) => request(`/admin/databases/${id}/schema/rls-policies`),
60
+ functions: (id) => request(`/admin/databases/${id}/schema/functions`),
61
+ postgrest: (id) => request(`/admin/databases/${id}/postgrest`),
62
+ startPostgrest: (id) => request(`/admin/databases/${id}/postgrest/start`, {
63
+ method: 'POST',
64
+ body: JSON.stringify({}),
65
+ }),
66
+ restartPostgrest: (id) => request(`/admin/databases/${id}/postgrest/restart`, {
67
+ method: 'POST',
68
+ body: JSON.stringify({}),
69
+ }),
70
+ },
71
+ changes: {
72
+ list: (dbId) => request(`/admin/databases/${dbId}/changes`),
73
+ stage: (dbId, operation, params) => request(`/admin/databases/${dbId}/changes`, {
74
+ method: 'POST',
75
+ body: JSON.stringify({ operation, params }),
76
+ }),
77
+ preview: (dbId) => request(`/admin/databases/${dbId}/changes/preview`, {
78
+ method: 'POST',
79
+ body: JSON.stringify({}),
80
+ }),
81
+ apply: (dbId, description) => request(`/admin/databases/${dbId}/changes/apply`, {
82
+ method: 'POST',
83
+ body: JSON.stringify({ description }),
84
+ }),
85
+ unstage: (dbId, changeId) => request(`/admin/databases/${dbId}/changes/${changeId}`, {
86
+ method: 'DELETE',
87
+ }),
88
+ },
89
+ migrations: {
90
+ list: (dbId) => request(`/admin/databases/${dbId}/migrations`),
91
+ get: (dbId, migrationId) => request(`/admin/databases/${dbId}/migrations/${migrationId}`),
92
+ rollback: (dbId, migrationId) => request(`/admin/databases/${dbId}/migrations/${migrationId}/rollback`, { method: 'POST' }),
93
+ },
94
+ auth: {
95
+ config: () => request('/admin/auth/config'),
96
+ },
97
+ hosting: {
98
+ deploy: (appId, tarPath) => uploadFile(`/admin/apps/${appId}/deploy`, tarPath),
99
+ status: (appId) => request(`/admin/apps/${appId}/hosting`),
100
+ addDomain: (appId, domain) => request(`/admin/apps/${appId}/domains`, {
101
+ method: 'POST',
102
+ body: JSON.stringify({ domain }),
103
+ }),
104
+ removeDomain: (appId, domainId) => request(`/admin/apps/${appId}/domains/${domainId}`, { method: 'DELETE' }),
105
+ },
106
+ logs: {
107
+ query: (dbId, options = {}) => {
108
+ const params = new URLSearchParams();
109
+ if (options.level)
110
+ params.append('level', options.level);
111
+ if (options.source)
112
+ params.append('source', options.source);
113
+ if (options.since)
114
+ params.append('since', options.since);
115
+ if (options.until)
116
+ params.append('until', options.until);
117
+ if (options.limit)
118
+ params.append('limit', options.limit.toString());
119
+ const query = params.toString();
120
+ return request(`/admin/databases/${dbId}/logs${query ? `?${query}` : ''}`);
121
+ },
122
+ get: (dbId, logId) => request(`/admin/databases/${dbId}/logs/${logId}`),
123
+ },
124
+ };
125
+ export async function resolveStagingDatabase(identifier) {
126
+ try {
127
+ const { app } = await api.apps.get(identifier);
128
+ if (app.databases.staging) {
129
+ return {
130
+ databaseId: app.databases.staging.id,
131
+ isApp: true,
132
+ appId: app.id,
133
+ };
134
+ }
135
+ throw new Error(`App "${identifier}" has no staging database`);
136
+ }
137
+ catch {
138
+ return {
139
+ databaseId: identifier,
140
+ isApp: false,
141
+ };
142
+ }
143
+ }
@@ -0,0 +1,5 @@
1
+ export declare const config: {
2
+ adminApiUrl: string;
3
+ authApiUrl: string;
4
+ token: string;
5
+ };
package/dist/config.js ADDED
@@ -0,0 +1,6 @@
1
+ const domain = process.env.POPDB_DOMAIN;
2
+ export const config = {
3
+ adminApiUrl: process.env.POPDB_API_URL || (domain ? `https://${domain}/api` : 'http://localhost:3002'),
4
+ authApiUrl: process.env.POPDB_AUTH_URL || (domain ? `https://${domain}/api` : 'http://localhost:3000'),
5
+ token: process.env.POPDB_TOKEN || '',
6
+ };
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
+ import { schemaTools, handleSchemaTool } from './tools/schema.js';
6
+ import { migrationTools, handleMigrationTool } from './tools/migrations.js';
7
+ import { authTools, handleAuthTool } from './tools/auth.js';
8
+ import { dataTools, handleDataTool } from './tools/data.js';
9
+ import { logsTools, handleLogsTool } from './tools/logs.js';
10
+ import { hostingTools, handleHostingTool } from './tools/hosting.js';
11
+ import { resources, readResource } from './resources/index.js';
12
+ const server = new Server({
13
+ name: 'popdb',
14
+ version: '0.1.0',
15
+ }, {
16
+ capabilities: {
17
+ tools: {},
18
+ resources: {},
19
+ },
20
+ });
21
+ const allTools = [...schemaTools, ...migrationTools, ...authTools, ...dataTools, ...logsTools, ...hostingTools];
22
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
23
+ return { tools: allTools };
24
+ });
25
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
26
+ const { name, arguments: args } = request.params;
27
+ const schemaTool = schemaTools.find((t) => t.name === name);
28
+ if (schemaTool) {
29
+ return handleSchemaTool(name, args || {});
30
+ }
31
+ const migrationTool = migrationTools.find((t) => t.name === name);
32
+ if (migrationTool) {
33
+ return handleMigrationTool(name, args || {});
34
+ }
35
+ const authTool = authTools.find((t) => t.name === name);
36
+ if (authTool) {
37
+ return handleAuthTool(name, args || {});
38
+ }
39
+ const dataTool = dataTools.find((t) => t.name === name);
40
+ if (dataTool) {
41
+ return handleDataTool(name, args || {});
42
+ }
43
+ const logsTool = logsTools.find((t) => t.name === name);
44
+ if (logsTool) {
45
+ return handleLogsTool(name, args || {});
46
+ }
47
+ const hostingTool = hostingTools.find((t) => t.name === name);
48
+ if (hostingTool) {
49
+ return handleHostingTool(name, args || {});
50
+ }
51
+ return {
52
+ content: [{ type: 'text', text: `Unknown tool: ${name}` }],
53
+ isError: true,
54
+ };
55
+ });
56
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
57
+ return { resources };
58
+ });
59
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
60
+ const { uri } = request.params;
61
+ return readResource(uri);
62
+ });
63
+ async function main() {
64
+ const transport = new StdioServerTransport();
65
+ await server.connect(transport);
66
+ console.error('PopDB MCP server running on stdio');
67
+ }
68
+ main().catch((error) => {
69
+ console.error('Fatal error:', error);
70
+ process.exit(1);
71
+ });
@@ -0,0 +1,13 @@
1
+ export declare const resources: {
2
+ uri: string;
3
+ name: string;
4
+ description: string;
5
+ mimeType: string;
6
+ }[];
7
+ export declare function readResource(uri: string): {
8
+ contents: Array<{
9
+ uri: string;
10
+ mimeType: string;
11
+ text: string;
12
+ }>;
13
+ };