@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,638 @@
1
+ export const resources = [
2
+ {
3
+ uri: 'sharedb://getting-started',
4
+ name: 'ShareDB: Getting Started',
5
+ description: 'Overview of ShareDB - a managed PostgreSQL backend for apps. READ THIS FIRST when building an app that needs a database.',
6
+ mimeType: 'text/markdown',
7
+ },
8
+ {
9
+ uri: 'sharedb://rls-templates/row-ownership',
10
+ name: 'RLS Pattern: Row Ownership',
11
+ description: 'RLS policy pattern for user-owned rows',
12
+ mimeType: 'text/markdown',
13
+ },
14
+ {
15
+ uri: 'sharedb://rls-templates/public-read',
16
+ name: 'RLS Pattern: Public Read, Owner Write',
17
+ description: 'RLS policy pattern for public read access with owner-only write',
18
+ mimeType: 'text/markdown',
19
+ },
20
+ {
21
+ uri: 'sharedb://rls-templates/team-based',
22
+ name: 'RLS Pattern: Team-Based Access',
23
+ description: 'RLS policy pattern for team/organization-based access',
24
+ mimeType: 'text/markdown',
25
+ },
26
+ {
27
+ uri: 'sharedb://guides/auth-integration',
28
+ name: 'Auth Integration Guide',
29
+ description: 'How to integrate ShareDB authentication into your app',
30
+ mimeType: 'text/markdown',
31
+ },
32
+ {
33
+ uri: 'sharedb://guides/postgrest-usage',
34
+ name: 'PostgREST Usage Guide',
35
+ description: 'How to query data via PostgREST API',
36
+ mimeType: 'text/markdown',
37
+ },
38
+ ];
39
+ const resourceContent = {
40
+ 'sharedb://getting-started': `# ShareDB: Your App's Backend
41
+
42
+ ShareDB is a managed PostgreSQL backend that provides everything you need to build data-driven applications.
43
+
44
+ ## What ShareDB Provides
45
+
46
+ - **PostgreSQL Database**: A dedicated database for your app with full SQL support
47
+ - **Authentication Service**: User registration, login, JWT tokens, and session management
48
+ - **PostgREST API**: Automatic REST API generated from your database schema
49
+ - **Row Level Security**: Built-in patterns for securing data with two role types:
50
+ - **Regular users**: App users who own their own data
51
+ - **Admins**: App builders who manage reference/shared data
52
+ - **Schema Migrations**: Version-controlled database changes with rollback support
53
+
54
+ ## When to Use ShareDB
55
+
56
+ Use ShareDB when building ANY app that needs:
57
+ - Persistent data storage
58
+ - A REST API for your frontend
59
+ - User accounts and authentication (optional - works great for single-user personal apps too!)
60
+
61
+ **Works for all app sizes**:
62
+ - **Personal apps**: Budget trackers, habit trackers, personal wikis, journals - even single-user apps benefit from a real database
63
+ - **Multi-user apps**: Todo apps, note-taking apps, project management tools, social apps, dashboards, CRMs
64
+
65
+ ## Quick Start
66
+
67
+ ### 1. Create a Database
68
+
69
+ \`\`\`
70
+ Use the create_database tool:
71
+ - name: "my_app" (lowercase, underscores ok)
72
+ - display_name: "My App"
73
+ - environment: "development"
74
+ \`\`\`
75
+
76
+ ### 2. Design Your Schema
77
+
78
+ \`\`\`
79
+ Use the stage_change tool to add tables:
80
+
81
+ stage_change({
82
+ database_id: "my_app",
83
+ operation: "CREATE_TABLE",
84
+ params: {
85
+ tableName: "todos",
86
+ columns: [
87
+ { name: "id", type: "UUID", primaryKey: true, defaultValue: "uuid_generate_v4()" },
88
+ { name: "user_id", type: "UUID", nullable: false, defaultValue: "current_user_id()" },
89
+ { name: "title", type: "TEXT", nullable: false },
90
+ { name: "completed", type: "BOOLEAN", defaultValue: "false" },
91
+ { name: "created_at", type: "TIMESTAMPTZ", defaultValue: "NOW()" }
92
+ ]
93
+ }
94
+ })
95
+ \`\`\`
96
+
97
+ ### 3. Preview and Apply
98
+
99
+ \`\`\`
100
+ preview_changes({ database_id: "my_app" })
101
+ apply_changes({ database_id: "my_app", description: "Add todos table" })
102
+ \`\`\`
103
+
104
+ ### 4. Connect Your App
105
+
106
+ Your app can now:
107
+ - Authenticate users via \`/auth/login\`, \`/auth/register\`
108
+ - Query data via PostgREST: \`GET /api/todos\`, \`POST /api/todos\`, etc.
109
+ - Include the JWT in the Authorization header
110
+
111
+ ## Available Tools
112
+
113
+ | Tool | Purpose |
114
+ |------|---------|
115
+ | \`create_database\` | Create a new app database |
116
+ | \`list_databases\` | List all your databases |
117
+ | \`get_database_schema\` | View tables, columns, indexes |
118
+ | \`stage_change\` | Stage schema changes (CREATE_TABLE, ADD_COLUMN, etc.) |
119
+ | \`preview_changes\` | See DDL before applying |
120
+ | \`apply_changes\` | Apply staged changes as a migration |
121
+ | \`rollback_migration\` | Undo the last migration |
122
+
123
+ ## Resources
124
+
125
+ - \`sharedb://guides/auth-integration\` - How to add auth to your app
126
+ - \`sharedb://guides/postgrest-usage\` - How to query data via REST
127
+ - \`sharedb://rls-templates/*\` - Security patterns for multi-user apps
128
+ `,
129
+ 'sharedb://rls-templates/row-ownership': `# Row Ownership RLS Pattern
130
+
131
+ This pattern restricts row access to the user who owns the row.
132
+
133
+ ## SQL Implementation
134
+
135
+ \`\`\`sql
136
+ -- Enable RLS on the table
137
+ ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;
138
+
139
+ -- Create policy for user-owned rows
140
+ CREATE POLICY user_owns_row ON your_table
141
+ FOR ALL
142
+ USING (user_id = current_user_id());
143
+ \`\`\`
144
+
145
+ ## Usage
146
+
147
+ 1. Ensure your table has a \`user_id\` column of type UUID
148
+ 2. The \`current_user_id()\` function extracts the user ID from the JWT claims
149
+ 3. Users can only see/modify rows where \`user_id\` matches their ID
150
+
151
+ ## Example Table
152
+
153
+ \`\`\`sql
154
+ CREATE TABLE todos (
155
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
156
+ user_id UUID NOT NULL DEFAULT current_user_id(),
157
+ title TEXT NOT NULL,
158
+ completed BOOLEAN DEFAULT FALSE,
159
+ created_at TIMESTAMPTZ DEFAULT NOW()
160
+ );
161
+
162
+ ALTER TABLE todos ENABLE ROW LEVEL SECURITY;
163
+
164
+ CREATE POLICY todos_user_isolation ON todos
165
+ FOR ALL
166
+ USING (user_id = current_user_id());
167
+ \`\`\`
168
+ `,
169
+ 'sharedb://rls-templates/public-read': `# Public Read, Owner Write RLS Pattern
170
+
171
+ This pattern allows anyone to read rows but only the owner can modify them.
172
+
173
+ ## SQL Implementation
174
+
175
+ \`\`\`sql
176
+ ALTER TABLE your_table ENABLE ROW LEVEL SECURITY;
177
+
178
+ -- Anyone can read
179
+ CREATE POLICY public_read ON your_table
180
+ FOR SELECT
181
+ USING (true);
182
+
183
+ -- Only owner can insert
184
+ CREATE POLICY owner_insert ON your_table
185
+ FOR INSERT
186
+ WITH CHECK (user_id = current_user_id());
187
+
188
+ -- Only owner can update
189
+ CREATE POLICY owner_update ON your_table
190
+ FOR UPDATE
191
+ USING (user_id = current_user_id())
192
+ WITH CHECK (user_id = current_user_id());
193
+
194
+ -- Only owner can delete
195
+ CREATE POLICY owner_delete ON your_table
196
+ FOR DELETE
197
+ USING (user_id = current_user_id());
198
+ \`\`\`
199
+
200
+ ## Example: Blog Posts
201
+
202
+ \`\`\`sql
203
+ CREATE TABLE posts (
204
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
205
+ user_id UUID NOT NULL DEFAULT current_user_id(),
206
+ title TEXT NOT NULL,
207
+ content TEXT NOT NULL,
208
+ published BOOLEAN DEFAULT FALSE,
209
+ created_at TIMESTAMPTZ DEFAULT NOW()
210
+ );
211
+
212
+ ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
213
+
214
+ -- Public can read published posts
215
+ CREATE POLICY posts_public_read ON posts
216
+ FOR SELECT
217
+ USING (published = true OR user_id = current_user_id());
218
+
219
+ -- Author can insert
220
+ CREATE POLICY posts_author_insert ON posts
221
+ FOR INSERT
222
+ WITH CHECK (user_id = current_user_id());
223
+
224
+ -- Author can update own posts
225
+ CREATE POLICY posts_author_update ON posts
226
+ FOR UPDATE
227
+ USING (user_id = current_user_id());
228
+
229
+ -- Author can delete own posts
230
+ CREATE POLICY posts_author_delete ON posts
231
+ FOR DELETE
232
+ USING (user_id = current_user_id());
233
+ \`\`\`
234
+ `,
235
+ 'sharedb://rls-templates/team-based': `# Team-Based Access RLS Pattern
236
+
237
+ This pattern restricts access based on team/organization membership.
238
+
239
+ ## Required Tables
240
+
241
+ \`\`\`sql
242
+ -- Teams table
243
+ CREATE TABLE teams (
244
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
245
+ name TEXT NOT NULL,
246
+ created_at TIMESTAMPTZ DEFAULT NOW()
247
+ );
248
+
249
+ -- Team memberships
250
+ CREATE TABLE team_members (
251
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
252
+ team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE,
253
+ user_id UUID NOT NULL,
254
+ role TEXT NOT NULL DEFAULT 'member',
255
+ created_at TIMESTAMPTZ DEFAULT NOW(),
256
+ UNIQUE(team_id, user_id)
257
+ );
258
+ \`\`\`
259
+
260
+ ## Helper Function
261
+
262
+ \`\`\`sql
263
+ CREATE OR REPLACE FUNCTION user_team_ids()
264
+ RETURNS UUID[] AS $$
265
+ SELECT COALESCE(
266
+ ARRAY_AGG(team_id),
267
+ ARRAY[]::UUID[]
268
+ )
269
+ FROM team_members
270
+ WHERE user_id = current_user_id();
271
+ $$ LANGUAGE sql STABLE;
272
+ \`\`\`
273
+
274
+ ## Team Resource Policy
275
+
276
+ \`\`\`sql
277
+ CREATE TABLE projects (
278
+ id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
279
+ team_id UUID NOT NULL REFERENCES teams(id),
280
+ name TEXT NOT NULL,
281
+ created_at TIMESTAMPTZ DEFAULT NOW()
282
+ );
283
+
284
+ ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
285
+
286
+ -- Team members can access their team's projects
287
+ CREATE POLICY projects_team_access ON projects
288
+ FOR ALL
289
+ USING (team_id = ANY(user_team_ids()));
290
+ \`\`\`
291
+ `,
292
+ 'sharedb://guides/auth-integration': `# ShareDB Auth Integration Guide
293
+
294
+ This guide explains how to integrate ShareDB authentication into your app.
295
+
296
+ ## Overview
297
+
298
+ ShareDB provides a centralized authentication service that issues JWTs. Your app:
299
+ 1. Collects user credentials (email/password or OAuth)
300
+ 2. Calls ShareDB auth endpoints
301
+ 3. Stores the returned tokens
302
+ 4. Includes the access token in PostgREST requests
303
+
304
+ ## Authentication Flow
305
+
306
+ \`\`\`
307
+ ┌──────────────────┐ ┌──────────────────┐
308
+ │ Your App │ │ ShareDB Auth │
309
+ └────────┬─────────┘ └────────┬─────────┘
310
+ │ │
311
+ │ 1. POST /auth/login │
312
+ │ { email, password } │
313
+ │───────────────────────────>│
314
+ │ │
315
+ │ 2. { accessToken, │
316
+ │ refreshToken } │
317
+ │<───────────────────────────│
318
+ │ │
319
+ │ Store tokens locally │
320
+ │ │
321
+ │ 3. GET /api/todos │
322
+ │ Authorization: Bearer ... │
323
+ │───────────────────────────>│ PostgREST
324
+ │ │
325
+ │ 4. [ user's todos ] │
326
+ │<───────────────────────────│
327
+ │ │
328
+ \`\`\`
329
+
330
+ ## Auth Endpoints
331
+
332
+ ### Register
333
+ \`\`\`
334
+ POST /auth/register
335
+ Content-Type: application/json
336
+
337
+ {
338
+ "email": "user@example.com",
339
+ "password": "secure-password",
340
+ "name": "John Doe"
341
+ }
342
+
343
+ Response: { accessToken, refreshToken, user }
344
+ \`\`\`
345
+
346
+ ### Login
347
+ \`\`\`
348
+ POST /auth/login
349
+ Content-Type: application/json
350
+
351
+ {
352
+ "email": "user@example.com",
353
+ "password": "secure-password"
354
+ }
355
+
356
+ Response: { accessToken, refreshToken, user }
357
+ \`\`\`
358
+
359
+ ### Refresh Token
360
+ \`\`\`
361
+ POST /auth/refresh
362
+ Content-Type: application/json
363
+
364
+ {
365
+ "refreshToken": "..."
366
+ }
367
+
368
+ Response: { accessToken, refreshToken }
369
+ \`\`\`
370
+
371
+ ### Get Current User
372
+ \`\`\`
373
+ GET /auth/me
374
+ Authorization: Bearer <accessToken>
375
+
376
+ Response: { id, email, name, ... }
377
+ \`\`\`
378
+
379
+ ## React Example
380
+
381
+ \`\`\`tsx
382
+ import { useState, createContext, useContext } from 'react';
383
+
384
+ interface AuthContext {
385
+ user: User | null;
386
+ login: (email: string, password: string) => Promise<void>;
387
+ logout: () => void;
388
+ getToken: () => string | null;
389
+ }
390
+
391
+ const AuthContext = createContext<AuthContext | null>(null);
392
+
393
+ export function AuthProvider({ children }) {
394
+ const [user, setUser] = useState<User | null>(null);
395
+ const [accessToken, setAccessToken] = useState<string | null>(
396
+ localStorage.getItem('accessToken')
397
+ );
398
+
399
+ async function login(email: string, password: string) {
400
+ const res = await fetch('/auth/login', {
401
+ method: 'POST',
402
+ headers: { 'Content-Type': 'application/json' },
403
+ body: JSON.stringify({ email, password }),
404
+ });
405
+
406
+ if (!res.ok) throw new Error('Login failed');
407
+
408
+ const { accessToken, refreshToken, user } = await res.json();
409
+ localStorage.setItem('accessToken', accessToken);
410
+ localStorage.setItem('refreshToken', refreshToken);
411
+ setAccessToken(accessToken);
412
+ setUser(user);
413
+ }
414
+
415
+ function logout() {
416
+ localStorage.removeItem('accessToken');
417
+ localStorage.removeItem('refreshToken');
418
+ setAccessToken(null);
419
+ setUser(null);
420
+ }
421
+
422
+ function getToken() {
423
+ return accessToken;
424
+ }
425
+
426
+ return (
427
+ <AuthContext.Provider value={{ user, login, logout, getToken }}>
428
+ {children}
429
+ </AuthContext.Provider>
430
+ );
431
+ }
432
+
433
+ export function useAuth() {
434
+ const ctx = useContext(AuthContext);
435
+ if (!ctx) throw new Error('useAuth must be used within AuthProvider');
436
+ return ctx;
437
+ }
438
+ \`\`\`
439
+
440
+ ## Making Authenticated API Requests
441
+
442
+ \`\`\`tsx
443
+ function useFetch() {
444
+ const { getToken } = useAuth();
445
+
446
+ return async function fetchWithAuth(url: string, options?: RequestInit) {
447
+ const token = getToken();
448
+ return fetch(url, {
449
+ ...options,
450
+ headers: {
451
+ ...options?.headers,
452
+ Authorization: \`Bearer \${token}\`,
453
+ },
454
+ });
455
+ };
456
+ }
457
+ \`\`\`
458
+ `,
459
+ 'sharedb://guides/postgrest-usage': `# PostgREST Usage Guide
460
+
461
+ PostgREST automatically generates a REST API from your PostgreSQL schema.
462
+
463
+ ## Base URL
464
+
465
+ Your app's PostgREST endpoint is:
466
+ \`\`\`
467
+ https://your-app.sharedb.example.com/api
468
+ \`\`\`
469
+
470
+ ## Authentication
471
+
472
+ Include the JWT in the Authorization header:
473
+ \`\`\`
474
+ Authorization: Bearer <your-jwt-token>
475
+ \`\`\`
476
+
477
+ ## Basic CRUD Operations
478
+
479
+ ### Read All Rows
480
+ \`\`\`
481
+ GET /api/todos
482
+
483
+ Response: [{ id, title, completed, ... }, ...]
484
+ \`\`\`
485
+
486
+ ### Read Single Row
487
+ \`\`\`
488
+ GET /api/todos?id=eq.uuid-here
489
+
490
+ Response: [{ id, title, completed, ... }]
491
+ \`\`\`
492
+
493
+ ### Create Row
494
+ \`\`\`
495
+ POST /api/todos
496
+ Content-Type: application/json
497
+
498
+ { "title": "New todo", "completed": false }
499
+
500
+ Response: { id, title, completed, ... }
501
+ \`\`\`
502
+
503
+ ### Update Row
504
+ \`\`\`
505
+ PATCH /api/todos?id=eq.uuid-here
506
+ Content-Type: application/json
507
+
508
+ { "completed": true }
509
+
510
+ Response: { id, title, completed, ... }
511
+ \`\`\`
512
+
513
+ ### Delete Row
514
+ \`\`\`
515
+ DELETE /api/todos?id=eq.uuid-here
516
+ \`\`\`
517
+
518
+ ## Filtering
519
+
520
+ PostgREST supports powerful filtering:
521
+
522
+ | Operator | Meaning | Example |
523
+ |----------|---------|---------|
524
+ | eq | equals | \`?status=eq.active\` |
525
+ | neq | not equals | \`?status=neq.deleted\` |
526
+ | gt | greater than | \`?price=gt.100\` |
527
+ | gte | greater or equal | \`?price=gte.100\` |
528
+ | lt | less than | \`?price=lt.50\` |
529
+ | lte | less or equal | \`?price=lte.50\` |
530
+ | like | pattern match | \`?title=like.*foo*\` |
531
+ | ilike | case-insensitive | \`?title=ilike.*foo*\` |
532
+ | in | in list | \`?status=in.(active,pending)\` |
533
+ | is | is null/true/false | \`?deleted=is.null\` |
534
+
535
+ ## Ordering
536
+
537
+ \`\`\`
538
+ GET /api/todos?order=created_at.desc
539
+ GET /api/todos?order=priority.asc,created_at.desc
540
+ \`\`\`
541
+
542
+ ## Pagination
543
+
544
+ \`\`\`
545
+ GET /api/todos?limit=10&offset=0
546
+ \`\`\`
547
+
548
+ ## Selecting Columns
549
+
550
+ \`\`\`
551
+ GET /api/todos?select=id,title,completed
552
+ \`\`\`
553
+
554
+ ## Embedding Related Data
555
+
556
+ If you have foreign key relationships:
557
+
558
+ \`\`\`
559
+ GET /api/todos?select=*,user:users(name,email)
560
+ \`\`\`
561
+
562
+ ## JavaScript Client Example
563
+
564
+ \`\`\`javascript
565
+ class PostgRESTClient {
566
+ constructor(baseUrl, getToken) {
567
+ this.baseUrl = baseUrl;
568
+ this.getToken = getToken;
569
+ }
570
+
571
+ async request(path, options = {}) {
572
+ const response = await fetch(\`\${this.baseUrl}\${path}\`, {
573
+ ...options,
574
+ headers: {
575
+ 'Content-Type': 'application/json',
576
+ Authorization: \`Bearer \${this.getToken()}\`,
577
+ ...options.headers,
578
+ },
579
+ });
580
+
581
+ if (!response.ok) {
582
+ throw new Error(\`API error: \${response.status}\`);
583
+ }
584
+
585
+ return response.json();
586
+ }
587
+
588
+ async list(table, params = {}) {
589
+ const query = new URLSearchParams(params).toString();
590
+ return this.request(\`/\${table}?\${query}\`);
591
+ }
592
+
593
+ async get(table, id) {
594
+ const [row] = await this.request(\`/\${table}?id=eq.\${id}\`);
595
+ return row;
596
+ }
597
+
598
+ async create(table, data) {
599
+ return this.request(\`/\${table}\`, {
600
+ method: 'POST',
601
+ body: JSON.stringify(data),
602
+ headers: { Prefer: 'return=representation' },
603
+ });
604
+ }
605
+
606
+ async update(table, id, data) {
607
+ return this.request(\`/\${table}?id=eq.\${id}\`, {
608
+ method: 'PATCH',
609
+ body: JSON.stringify(data),
610
+ headers: { Prefer: 'return=representation' },
611
+ });
612
+ }
613
+
614
+ async delete(table, id) {
615
+ return this.request(\`/\${table}?id=eq.\${id}\`, {
616
+ method: 'DELETE',
617
+ });
618
+ }
619
+ }
620
+ \`\`\`
621
+ `,
622
+ };
623
+ export function readResource(uri) {
624
+ const content = resourceContent[uri];
625
+ if (!content) {
626
+ throw new Error(`Resource not found: ${uri}`);
627
+ }
628
+ const resource = resources.find((r) => r.uri === uri);
629
+ return {
630
+ contents: [
631
+ {
632
+ uri,
633
+ mimeType: resource?.mimeType || 'text/plain',
634
+ text: content,
635
+ },
636
+ ],
637
+ };
638
+ }
@@ -0,0 +1,31 @@
1
+ export declare const authTools: ({
2
+ name: string;
3
+ description: string;
4
+ inputSchema: {
5
+ type: "object";
6
+ properties: {
7
+ database_id: {
8
+ type: string;
9
+ description: string;
10
+ };
11
+ };
12
+ required: never[];
13
+ };
14
+ } | {
15
+ name: string;
16
+ description: string;
17
+ inputSchema: {
18
+ type: "object";
19
+ properties: {
20
+ database_id?: undefined;
21
+ };
22
+ required: never[];
23
+ };
24
+ })[];
25
+ export declare function handleAuthTool(name: string, args: Record<string, unknown>): Promise<{
26
+ content: Array<{
27
+ type: 'text';
28
+ text: string;
29
+ }>;
30
+ isError?: boolean;
31
+ }>;