@daylight-labs/sharedb-mcp 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tools/auth.js +80 -0
- package/package.json +1 -1
package/dist/tools/auth.js
CHANGED
|
@@ -4,6 +4,22 @@ async function getPostgRESTUrl(databaseId) {
|
|
|
4
4
|
const info = await api.databases.postgrest(databaseId);
|
|
5
5
|
return info.postgrestUrl;
|
|
6
6
|
}
|
|
7
|
+
async function getEmailIntegrationInfo(appId) {
|
|
8
|
+
try {
|
|
9
|
+
const response = await fetch(`${config.adminApiUrl}/admin/apps/${appId}/integrations/email`, {
|
|
10
|
+
headers: {
|
|
11
|
+
'Content-Type': 'application/json',
|
|
12
|
+
Authorization: `Bearer ${config.token}`,
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
if (!response.ok)
|
|
16
|
+
return null;
|
|
17
|
+
return response.json();
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
7
23
|
export const authTools = [
|
|
8
24
|
{
|
|
9
25
|
name: 'get_api_endpoints',
|
|
@@ -37,6 +53,8 @@ export async function handleAuthTool(name, args) {
|
|
|
37
53
|
let tableEndpoints = {};
|
|
38
54
|
let tableList = [];
|
|
39
55
|
let postgrestUrl = '<PostgREST URL - specify database_id to get actual URL>';
|
|
56
|
+
let appId;
|
|
57
|
+
let appName;
|
|
40
58
|
if (databaseId) {
|
|
41
59
|
try {
|
|
42
60
|
const [schema, url] = await Promise.all([
|
|
@@ -54,11 +72,69 @@ export async function handleAuthTool(name, args) {
|
|
|
54
72
|
delete: `DELETE ${postgrestUrl}/${tableName}?id=eq.<uuid>`,
|
|
55
73
|
};
|
|
56
74
|
}
|
|
75
|
+
const dbInfo = await api.databases.get(databaseId);
|
|
76
|
+
if (dbInfo.database) {
|
|
77
|
+
const dbName = dbInfo.database.name;
|
|
78
|
+
const match = dbName.match(/^sharedb_(.+?)_(staging|production)$/);
|
|
79
|
+
if (match) {
|
|
80
|
+
appName = match[1];
|
|
81
|
+
try {
|
|
82
|
+
const { app } = await api.apps.get(appName);
|
|
83
|
+
appId = app.id;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// App lookup failed, continue without email info
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
57
90
|
}
|
|
58
91
|
catch {
|
|
59
92
|
// Database might not have tables yet
|
|
60
93
|
}
|
|
61
94
|
}
|
|
95
|
+
let emailSection;
|
|
96
|
+
if (appId && appName) {
|
|
97
|
+
const emailInfo = await getEmailIntegrationInfo(appId);
|
|
98
|
+
if (emailInfo) {
|
|
99
|
+
const enabled = emailInfo.integration?.enabled ?? false;
|
|
100
|
+
emailSection = {
|
|
101
|
+
description: 'Send transactional emails via AWS SES',
|
|
102
|
+
status: enabled ? 'enabled' : 'disabled',
|
|
103
|
+
endpoint: `POST ${config.adminApiUrl}/admin/apps/${appId}/integrations/email/send`,
|
|
104
|
+
authentication: 'Include admin API token: Authorization: Bearer <token>',
|
|
105
|
+
fromAddress: emailInfo.fromAddress,
|
|
106
|
+
rateLimit: `1 email per ${emailInfo.rateLimitSeconds} seconds per app`,
|
|
107
|
+
requestBody: {
|
|
108
|
+
to: 'string (required) - recipient email address',
|
|
109
|
+
subject: 'string (required) - email subject line',
|
|
110
|
+
body: 'string (required) - plain text email body',
|
|
111
|
+
html: 'string (optional) - HTML email body',
|
|
112
|
+
},
|
|
113
|
+
example: `fetch('${config.adminApiUrl}/admin/apps/${appId}/integrations/email/send', {
|
|
114
|
+
method: 'POST',
|
|
115
|
+
headers: {
|
|
116
|
+
'Content-Type': 'application/json',
|
|
117
|
+
'Authorization': 'Bearer <your-api-token>'
|
|
118
|
+
},
|
|
119
|
+
body: JSON.stringify({
|
|
120
|
+
to: 'user@example.com',
|
|
121
|
+
subject: 'Welcome to ${appName}!',
|
|
122
|
+
body: 'Thanks for signing up.',
|
|
123
|
+
html: '<h1>Welcome!</h1><p>Thanks for signing up.</p>'
|
|
124
|
+
})
|
|
125
|
+
})`,
|
|
126
|
+
importantNotes: [
|
|
127
|
+
enabled
|
|
128
|
+
? 'Email integration is ENABLED for this app'
|
|
129
|
+
: 'Email integration is DISABLED - enable it in the Admin UI before sending',
|
|
130
|
+
'Rate limited to 1 email per minute to prevent spam',
|
|
131
|
+
'Returns HTTP 429 with retryAfter seconds if rate limit exceeded',
|
|
132
|
+
'Returns HTTP 403 if email integration is not enabled',
|
|
133
|
+
`Emails are sent from: ${emailInfo.fromAddress}`,
|
|
134
|
+
],
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
62
138
|
const endpoints = {
|
|
63
139
|
auth: {
|
|
64
140
|
baseUrl: config.authApiUrl,
|
|
@@ -72,6 +148,7 @@ export async function handleAuthTool(name, args) {
|
|
|
72
148
|
importantNotes: [
|
|
73
149
|
'After login, store the accessToken and user object (contains user.id)',
|
|
74
150
|
'Include accessToken in all API requests: Authorization: Bearer <accessToken>',
|
|
151
|
+
'Persist accessToken to localStorage to keep users logged in across page refreshes',
|
|
75
152
|
'Use GET /auth/me to fetch current user if needed (returns { user: { id, email, name } })',
|
|
76
153
|
],
|
|
77
154
|
},
|
|
@@ -120,6 +197,9 @@ export async function handleAuthTool(name, args) {
|
|
|
120
197
|
recommendation: 'Add to your global error handler (window.onerror, React error boundaries) during development to catch unhandled errors.',
|
|
121
198
|
},
|
|
122
199
|
};
|
|
200
|
+
if (emailSection) {
|
|
201
|
+
endpoints.email = emailSection;
|
|
202
|
+
}
|
|
123
203
|
return {
|
|
124
204
|
content: [
|
|
125
205
|
{
|