s3db.js 11.3.2 → 12.0.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.
- package/README.md +102 -8
- package/dist/s3db.cjs.js +36664 -15480
- package/dist/s3db.cjs.js.map +1 -1
- package/dist/s3db.d.ts +57 -0
- package/dist/s3db.es.js +36661 -15531
- package/dist/s3db.es.js.map +1 -1
- package/mcp/entrypoint.js +58 -0
- package/mcp/tools/documentation.js +434 -0
- package/mcp/tools/index.js +4 -0
- package/package.json +27 -6
- package/src/behaviors/user-managed.js +13 -6
- package/src/client.class.js +41 -46
- package/src/concerns/base62.js +85 -0
- package/src/concerns/dictionary-encoding.js +294 -0
- package/src/concerns/geo-encoding.js +256 -0
- package/src/concerns/high-performance-inserter.js +34 -30
- package/src/concerns/ip.js +325 -0
- package/src/concerns/metadata-encoding.js +345 -66
- package/src/concerns/money.js +193 -0
- package/src/concerns/partition-queue.js +7 -4
- package/src/concerns/plugin-storage.js +39 -19
- package/src/database.class.js +76 -74
- package/src/errors.js +0 -4
- package/src/plugins/api/auth/api-key-auth.js +88 -0
- package/src/plugins/api/auth/basic-auth.js +154 -0
- package/src/plugins/api/auth/index.js +112 -0
- package/src/plugins/api/auth/jwt-auth.js +169 -0
- package/src/plugins/api/index.js +539 -0
- package/src/plugins/api/middlewares/index.js +15 -0
- package/src/plugins/api/middlewares/validator.js +185 -0
- package/src/plugins/api/routes/auth-routes.js +241 -0
- package/src/plugins/api/routes/resource-routes.js +304 -0
- package/src/plugins/api/server.js +350 -0
- package/src/plugins/api/utils/error-handler.js +147 -0
- package/src/plugins/api/utils/openapi-generator.js +1240 -0
- package/src/plugins/api/utils/response-formatter.js +218 -0
- package/src/plugins/backup/streaming-exporter.js +132 -0
- package/src/plugins/backup.plugin.js +103 -50
- package/src/plugins/cache/s3-cache.class.js +95 -47
- package/src/plugins/cache.plugin.js +107 -9
- package/src/plugins/concerns/plugin-dependencies.js +313 -0
- package/src/plugins/concerns/prometheus-formatter.js +255 -0
- package/src/plugins/consumers/rabbitmq-consumer.js +4 -0
- package/src/plugins/consumers/sqs-consumer.js +4 -0
- package/src/plugins/costs.plugin.js +255 -39
- package/src/plugins/eventual-consistency/helpers.js +15 -1
- package/src/plugins/geo.plugin.js +873 -0
- package/src/plugins/importer/index.js +1020 -0
- package/src/plugins/index.js +11 -0
- package/src/plugins/metrics.plugin.js +163 -4
- package/src/plugins/queue-consumer.plugin.js +6 -27
- package/src/plugins/relation.errors.js +139 -0
- package/src/plugins/relation.plugin.js +1242 -0
- package/src/plugins/replicators/bigquery-replicator.class.js +180 -8
- package/src/plugins/replicators/dynamodb-replicator.class.js +383 -0
- package/src/plugins/replicators/index.js +28 -3
- package/src/plugins/replicators/mongodb-replicator.class.js +391 -0
- package/src/plugins/replicators/mysql-replicator.class.js +558 -0
- package/src/plugins/replicators/planetscale-replicator.class.js +409 -0
- package/src/plugins/replicators/postgres-replicator.class.js +182 -7
- package/src/plugins/replicators/s3db-replicator.class.js +1 -12
- package/src/plugins/replicators/schema-sync.helper.js +601 -0
- package/src/plugins/replicators/sqs-replicator.class.js +11 -9
- package/src/plugins/replicators/turso-replicator.class.js +416 -0
- package/src/plugins/replicators/webhook-replicator.class.js +612 -0
- package/src/plugins/state-machine.plugin.js +122 -68
- package/src/plugins/tfstate/README.md +745 -0
- package/src/plugins/tfstate/base-driver.js +80 -0
- package/src/plugins/tfstate/errors.js +112 -0
- package/src/plugins/tfstate/filesystem-driver.js +129 -0
- package/src/plugins/tfstate/index.js +2660 -0
- package/src/plugins/tfstate/s3-driver.js +192 -0
- package/src/plugins/ttl.plugin.js +536 -0
- package/src/resource.class.js +14 -10
- package/src/s3db.d.ts +57 -0
- package/src/schema.class.js +366 -32
- package/SECURITY.md +0 -76
- package/src/partition-drivers/base-partition-driver.js +0 -106
- package/src/partition-drivers/index.js +0 -66
- package/src/partition-drivers/memory-partition-driver.js +0 -289
- package/src/partition-drivers/sqs-partition-driver.js +0 -337
- package/src/partition-drivers/sync-partition-driver.js +0 -38
package/mcp/entrypoint.js
CHANGED
|
@@ -47,6 +47,35 @@ class S3dbMCPServer {
|
|
|
47
47
|
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
48
48
|
return {
|
|
49
49
|
tools: [
|
|
50
|
+
// 📖 DOCUMENTATION TOOLS (for AI agents)
|
|
51
|
+
{
|
|
52
|
+
name: 's3dbQueryDocs',
|
|
53
|
+
description: 'Search s3db.js documentation to answer questions about features, plugins, best practices, and usage. Use this tool to help AI agents understand how to use s3db.js effectively.',
|
|
54
|
+
inputSchema: {
|
|
55
|
+
type: 'object',
|
|
56
|
+
properties: {
|
|
57
|
+
query: {
|
|
58
|
+
type: 'string',
|
|
59
|
+
description: 'Natural language question about s3db.js (e.g., "How do I use GeoPlugin?", "What is the best caching strategy?", "How do partitions work?")'
|
|
60
|
+
},
|
|
61
|
+
maxResults: {
|
|
62
|
+
type: 'number',
|
|
63
|
+
description: 'Maximum number of documentation files to return',
|
|
64
|
+
default: 5
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
required: ['query']
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 's3dbListTopics',
|
|
72
|
+
description: 'List all available documentation topics and their categories',
|
|
73
|
+
inputSchema: {
|
|
74
|
+
type: 'object',
|
|
75
|
+
properties: {},
|
|
76
|
+
required: []
|
|
77
|
+
}
|
|
78
|
+
},
|
|
50
79
|
{
|
|
51
80
|
name: 'dbConnect',
|
|
52
81
|
description: 'Connect to an S3DB database with automatic costs tracking and configurable cache (memory or filesystem)',
|
|
@@ -877,6 +906,15 @@ class S3dbMCPServer {
|
|
|
877
906
|
let result;
|
|
878
907
|
|
|
879
908
|
switch (name) {
|
|
909
|
+
// Documentation tools
|
|
910
|
+
case 's3dbQueryDocs':
|
|
911
|
+
result = await this.handleS3dbQueryDocs(args);
|
|
912
|
+
break;
|
|
913
|
+
|
|
914
|
+
case 's3dbListTopics':
|
|
915
|
+
result = await this.handleS3dbListTopics(args);
|
|
916
|
+
break;
|
|
917
|
+
|
|
880
918
|
case 'dbConnect':
|
|
881
919
|
result = await this.handleDbConnect(args);
|
|
882
920
|
break;
|
|
@@ -1135,6 +1173,26 @@ class S3dbMCPServer {
|
|
|
1135
1173
|
});
|
|
1136
1174
|
}
|
|
1137
1175
|
|
|
1176
|
+
// 📖 DOCUMENTATION TOOLS HANDLERS
|
|
1177
|
+
|
|
1178
|
+
async handleS3dbQueryDocs(args) {
|
|
1179
|
+
const { query, maxResults = 5 } = args;
|
|
1180
|
+
|
|
1181
|
+
// Import the documentation handler dynamically
|
|
1182
|
+
const { createDocumentationHandlers } = await import('./tools/documentation.js');
|
|
1183
|
+
const handlers = createDocumentationHandlers(this);
|
|
1184
|
+
|
|
1185
|
+
return await handlers.s3dbQueryDocs(args);
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
async handleS3dbListTopics(args) {
|
|
1189
|
+
// Import the documentation handler dynamically
|
|
1190
|
+
const { createDocumentationHandlers } = await import('./tools/documentation.js');
|
|
1191
|
+
const handlers = createDocumentationHandlers(this);
|
|
1192
|
+
|
|
1193
|
+
return await handlers.s3dbListTopics(args);
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1138
1196
|
// Database connection handlers
|
|
1139
1197
|
async handleDbConnect(args) {
|
|
1140
1198
|
const {
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Documentation Tools
|
|
3
|
+
* Provides intelligent documentation retrieval for AI agents
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFileSync, readdirSync, statSync } from 'fs';
|
|
7
|
+
import { join, dirname } from 'path';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = dirname(__filename);
|
|
12
|
+
const PROJECT_ROOT = join(__dirname, '../..');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Documentation index - maps topics to file paths and sections
|
|
16
|
+
*/
|
|
17
|
+
const DOCUMENTATION_INDEX = {
|
|
18
|
+
// Core concepts
|
|
19
|
+
'getting started': ['README.md', 'docs/schema.md'],
|
|
20
|
+
'installation': ['README.md'],
|
|
21
|
+
'connection': ['docs/client.md', 'CLAUDE.md'],
|
|
22
|
+
'database': ['CLAUDE.md', 'docs/schema.md'],
|
|
23
|
+
'resource': ['CLAUDE.md', 'docs/schema.md'],
|
|
24
|
+
'schema': ['docs/schema.md', 'CLAUDE.md'],
|
|
25
|
+
'validation': ['docs/schema.md', 'CLAUDE.md'],
|
|
26
|
+
|
|
27
|
+
// Data operations
|
|
28
|
+
'insert': ['CLAUDE.md'],
|
|
29
|
+
'update': ['CLAUDE.md'],
|
|
30
|
+
'delete': ['CLAUDE.md'],
|
|
31
|
+
'query': ['CLAUDE.md'],
|
|
32
|
+
'list': ['CLAUDE.md'],
|
|
33
|
+
'get': ['CLAUDE.md'],
|
|
34
|
+
'count': ['CLAUDE.md'],
|
|
35
|
+
'crud': ['CLAUDE.md'],
|
|
36
|
+
|
|
37
|
+
// Partitioning
|
|
38
|
+
'partition': ['CLAUDE.md', 'docs/benchmarks/partitions.md'],
|
|
39
|
+
'partitioning': ['CLAUDE.md', 'docs/benchmarks/partitions.md'],
|
|
40
|
+
'orphaned partition': ['CLAUDE.md'],
|
|
41
|
+
'partition migration': ['CLAUDE.md', 'docs/mcp.md'],
|
|
42
|
+
|
|
43
|
+
// Plugins
|
|
44
|
+
'plugin': ['docs/plugins/README.md', 'CLAUDE.md'],
|
|
45
|
+
'cache': ['docs/plugins/cache.md', 'CLAUDE.md'],
|
|
46
|
+
'caching': ['docs/plugins/cache.md', 'CLAUDE.md'],
|
|
47
|
+
'audit': ['docs/plugins/audit.md', 'CLAUDE.md'],
|
|
48
|
+
'replicator': ['docs/plugins/replicator.md', 'CLAUDE.md'],
|
|
49
|
+
'backup': ['docs/plugins/backup.md', 'CLAUDE.md'],
|
|
50
|
+
'geo': ['docs/plugins/geo.md', 'CLAUDE.md'],
|
|
51
|
+
'geospatial': ['docs/plugins/geo.md', 'CLAUDE.md'],
|
|
52
|
+
'location': ['docs/plugins/geo.md'],
|
|
53
|
+
'metrics': ['docs/plugins/metrics.md', 'CLAUDE.md'],
|
|
54
|
+
'costs': ['docs/plugins/costs.md', 'CLAUDE.md'],
|
|
55
|
+
'eventual consistency': ['docs/plugins/eventual-consistency.md', 'CLAUDE.md'],
|
|
56
|
+
'fulltext': ['docs/plugins/fulltext.md'],
|
|
57
|
+
'search': ['docs/plugins/fulltext.md'],
|
|
58
|
+
'queue': ['docs/plugins/queue-consumer.md', 'docs/plugins/s3-queue.md'],
|
|
59
|
+
|
|
60
|
+
// Performance & Optimization
|
|
61
|
+
'performance': ['CLAUDE.md', 'docs/benchmarks/README.md'],
|
|
62
|
+
'optimization': ['CLAUDE.md', 'docs/benchmarks/README.md'],
|
|
63
|
+
'benchmark': ['docs/benchmarks/README.md'],
|
|
64
|
+
'compression': ['CLAUDE.md'],
|
|
65
|
+
'encoding': ['CLAUDE.md', 'docs/benchmarks/smart-encoding.md'],
|
|
66
|
+
|
|
67
|
+
// MCP specific
|
|
68
|
+
'mcp': ['docs/mcp.md'],
|
|
69
|
+
'model context protocol': ['docs/mcp.md'],
|
|
70
|
+
'ai agent': ['docs/mcp.md'],
|
|
71
|
+
'claude desktop': ['docs/mcp.md'],
|
|
72
|
+
|
|
73
|
+
// Advanced features
|
|
74
|
+
'encryption': ['CLAUDE.md'],
|
|
75
|
+
'secret': ['CLAUDE.md'],
|
|
76
|
+
'vector': ['docs/plugins/vector.md'],
|
|
77
|
+
'embedding': ['CLAUDE.md', 'docs/plugins/vector.md'],
|
|
78
|
+
'versioning': ['CLAUDE.md'],
|
|
79
|
+
'hooks': ['CLAUDE.md'],
|
|
80
|
+
'behavior': ['CLAUDE.md'],
|
|
81
|
+
'metadata': ['CLAUDE.md'],
|
|
82
|
+
|
|
83
|
+
// Troubleshooting
|
|
84
|
+
'error': ['CLAUDE.md', 'docs/mcp.md'],
|
|
85
|
+
'troubleshooting': ['docs/mcp.md'],
|
|
86
|
+
'recovery': ['CLAUDE.md']
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get all markdown files in docs directory
|
|
91
|
+
*/
|
|
92
|
+
function getAllDocFiles() {
|
|
93
|
+
const files = [];
|
|
94
|
+
|
|
95
|
+
function walkDir(dir) {
|
|
96
|
+
try {
|
|
97
|
+
const items = readdirSync(dir);
|
|
98
|
+
for (const item of items) {
|
|
99
|
+
const fullPath = join(dir, item);
|
|
100
|
+
try {
|
|
101
|
+
const stat = statSync(fullPath);
|
|
102
|
+
if (stat.isDirectory()) {
|
|
103
|
+
walkDir(fullPath);
|
|
104
|
+
} else if (item.endsWith('.md')) {
|
|
105
|
+
const relativePath = fullPath.replace(PROJECT_ROOT + '/', '');
|
|
106
|
+
files.push(relativePath);
|
|
107
|
+
}
|
|
108
|
+
} catch (err) {
|
|
109
|
+
// Skip files we can't access
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} catch (err) {
|
|
114
|
+
// Skip directories we can't access
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
walkDir(join(PROJECT_ROOT, 'docs'));
|
|
120
|
+
|
|
121
|
+
// Add root-level important docs
|
|
122
|
+
try {
|
|
123
|
+
['README.md', 'CLAUDE.md'].forEach(file => {
|
|
124
|
+
const fullPath = join(PROJECT_ROOT, file);
|
|
125
|
+
try {
|
|
126
|
+
if (statSync(fullPath).isFile()) {
|
|
127
|
+
files.push(file);
|
|
128
|
+
}
|
|
129
|
+
} catch (err) {
|
|
130
|
+
// Skip if file doesn't exist
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
} catch (err) {
|
|
134
|
+
// Skip if error accessing root files
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return files;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Search for query terms in documentation
|
|
142
|
+
*/
|
|
143
|
+
function searchDocumentation(query) {
|
|
144
|
+
const queryLower = query.toLowerCase();
|
|
145
|
+
const queryTerms = queryLower.split(/\s+/).filter(t => t.length > 2);
|
|
146
|
+
|
|
147
|
+
// Find relevant files from index
|
|
148
|
+
const relevantFiles = new Set();
|
|
149
|
+
|
|
150
|
+
// Check index first
|
|
151
|
+
for (const [topic, files] of Object.entries(DOCUMENTATION_INDEX)) {
|
|
152
|
+
if (queryLower.includes(topic) || queryTerms.some(term => topic.includes(term))) {
|
|
153
|
+
files.forEach(f => relevantFiles.add(f));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// If no matches in index, search all docs
|
|
158
|
+
if (relevantFiles.size === 0) {
|
|
159
|
+
getAllDocFiles().forEach(f => relevantFiles.add(f));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const results = [];
|
|
163
|
+
|
|
164
|
+
for (const filePath of relevantFiles) {
|
|
165
|
+
try {
|
|
166
|
+
const fullPath = join(PROJECT_ROOT, filePath);
|
|
167
|
+
const content = readFileSync(fullPath, 'utf-8');
|
|
168
|
+
|
|
169
|
+
// Calculate relevance score
|
|
170
|
+
let score = 0;
|
|
171
|
+
const contentLower = content.toLowerCase();
|
|
172
|
+
|
|
173
|
+
// Exact query match
|
|
174
|
+
if (contentLower.includes(queryLower)) {
|
|
175
|
+
score += 100;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Individual term matches
|
|
179
|
+
queryTerms.forEach(term => {
|
|
180
|
+
const regex = new RegExp(`\\b${term}\\b`, 'gi');
|
|
181
|
+
const matches = content.match(regex);
|
|
182
|
+
if (matches) {
|
|
183
|
+
score += matches.length * 10;
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Boost for certain file types
|
|
188
|
+
if (filePath === 'CLAUDE.md') score *= 1.5;
|
|
189
|
+
if (filePath.includes('plugins/')) score *= 1.2;
|
|
190
|
+
|
|
191
|
+
if (score > 0) {
|
|
192
|
+
// Extract relevant sections
|
|
193
|
+
const sections = extractRelevantSections(content, queryTerms, filePath);
|
|
194
|
+
|
|
195
|
+
results.push({
|
|
196
|
+
file: filePath,
|
|
197
|
+
score,
|
|
198
|
+
sections
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
} catch (error) {
|
|
202
|
+
// Skip files that can't be read
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Sort by relevance
|
|
208
|
+
results.sort((a, b) => b.score - a.score);
|
|
209
|
+
|
|
210
|
+
return results;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Extract relevant sections from markdown content
|
|
215
|
+
*/
|
|
216
|
+
function extractRelevantSections(content, queryTerms, filePath) {
|
|
217
|
+
const lines = content.split('\n');
|
|
218
|
+
const sections = [];
|
|
219
|
+
let currentSection = null;
|
|
220
|
+
let currentContent = [];
|
|
221
|
+
let sectionScore = 0;
|
|
222
|
+
|
|
223
|
+
for (let i = 0; i < lines.length; i++) {
|
|
224
|
+
const line = lines[i];
|
|
225
|
+
|
|
226
|
+
// Detect section headers
|
|
227
|
+
if (line.match(/^#{1,4}\s/)) {
|
|
228
|
+
// Save previous section if it has content
|
|
229
|
+
if (currentSection && currentContent.length > 0 && sectionScore > 0) {
|
|
230
|
+
sections.push({
|
|
231
|
+
header: currentSection,
|
|
232
|
+
content: currentContent.join('\n').trim(),
|
|
233
|
+
score: sectionScore
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Start new section
|
|
238
|
+
currentSection = line;
|
|
239
|
+
currentContent = [line];
|
|
240
|
+
sectionScore = 0;
|
|
241
|
+
|
|
242
|
+
// Score the header
|
|
243
|
+
const headerLower = line.toLowerCase();
|
|
244
|
+
queryTerms.forEach(term => {
|
|
245
|
+
if (headerLower.includes(term)) {
|
|
246
|
+
sectionScore += 50;
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
} else if (currentSection) {
|
|
250
|
+
currentContent.push(line);
|
|
251
|
+
|
|
252
|
+
// Score the content
|
|
253
|
+
const lineLower = line.toLowerCase();
|
|
254
|
+
queryTerms.forEach(term => {
|
|
255
|
+
if (lineLower.includes(term)) {
|
|
256
|
+
sectionScore += 5;
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Save last section
|
|
263
|
+
if (currentSection && currentContent.length > 0 && sectionScore > 0) {
|
|
264
|
+
sections.push({
|
|
265
|
+
header: currentSection,
|
|
266
|
+
content: currentContent.join('\n').trim(),
|
|
267
|
+
score: sectionScore
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Sort sections by score and limit
|
|
272
|
+
sections.sort((a, b) => b.score - a.score);
|
|
273
|
+
|
|
274
|
+
// Return top sections (limit to avoid huge responses)
|
|
275
|
+
return sections.slice(0, 3);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Format search results for display
|
|
280
|
+
*/
|
|
281
|
+
function formatResults(results, query, maxResults = 5) {
|
|
282
|
+
if (results.length === 0) {
|
|
283
|
+
return {
|
|
284
|
+
query,
|
|
285
|
+
found: false,
|
|
286
|
+
message: 'No documentation found for this query. Try rephrasing or asking about: plugins, cache, partitions, schema, CRUD operations, or MCP integration.',
|
|
287
|
+
suggestions: [
|
|
288
|
+
'How do I use the CachePlugin?',
|
|
289
|
+
'What are partitions and when should I use them?',
|
|
290
|
+
'How do I create a resource with schema validation?',
|
|
291
|
+
'How does the MCP server work?',
|
|
292
|
+
'What plugins are available?'
|
|
293
|
+
]
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const topResults = results.slice(0, maxResults);
|
|
298
|
+
|
|
299
|
+
const formatted = {
|
|
300
|
+
query,
|
|
301
|
+
found: true,
|
|
302
|
+
resultCount: results.length,
|
|
303
|
+
showing: topResults.length,
|
|
304
|
+
results: []
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
for (const result of topResults) {
|
|
308
|
+
const formattedResult = {
|
|
309
|
+
file: result.file,
|
|
310
|
+
relevanceScore: result.score,
|
|
311
|
+
sections: []
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
for (const section of result.sections) {
|
|
315
|
+
// Limit section content length
|
|
316
|
+
let content = section.content;
|
|
317
|
+
if (content.length > 1500) {
|
|
318
|
+
content = content.substring(0, 1500) + '\n\n... (truncated)';
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
formattedResult.sections.push({
|
|
322
|
+
header: section.header,
|
|
323
|
+
content: content
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
formatted.results.push(formattedResult);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return formatted;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export const documentationTools = [
|
|
334
|
+
{
|
|
335
|
+
name: 's3dbQueryDocs',
|
|
336
|
+
description: 'Search s3db.js documentation to answer questions about features, plugins, best practices, and usage. Use this tool to help AI agents understand how to use s3db.js effectively.',
|
|
337
|
+
inputSchema: {
|
|
338
|
+
type: 'object',
|
|
339
|
+
properties: {
|
|
340
|
+
query: {
|
|
341
|
+
type: 'string',
|
|
342
|
+
description: 'Natural language question about s3db.js (e.g., "How do I use GeoPlugin?", "What is the best caching strategy?", "How do partitions work?")'
|
|
343
|
+
},
|
|
344
|
+
maxResults: {
|
|
345
|
+
type: 'number',
|
|
346
|
+
description: 'Maximum number of documentation files to return',
|
|
347
|
+
default: 5
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
required: ['query']
|
|
351
|
+
}
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
name: 's3dbListTopics',
|
|
355
|
+
description: 'List all available documentation topics and their categories',
|
|
356
|
+
inputSchema: {
|
|
357
|
+
type: 'object',
|
|
358
|
+
properties: {},
|
|
359
|
+
required: []
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
];
|
|
363
|
+
|
|
364
|
+
export function createDocumentationHandlers(server) {
|
|
365
|
+
return {
|
|
366
|
+
async s3dbQueryDocs(args) {
|
|
367
|
+
const { query, maxResults = 5 } = args;
|
|
368
|
+
|
|
369
|
+
try {
|
|
370
|
+
const results = searchDocumentation(query);
|
|
371
|
+
const formatted = formatResults(results, query, maxResults);
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
success: true,
|
|
375
|
+
...formatted
|
|
376
|
+
};
|
|
377
|
+
} catch (error) {
|
|
378
|
+
return {
|
|
379
|
+
success: false,
|
|
380
|
+
query,
|
|
381
|
+
error: error.message,
|
|
382
|
+
suggestion: 'Try rephrasing your question or use s3dbListTopics to see available topics'
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
},
|
|
386
|
+
|
|
387
|
+
async s3dbListTopics(args) {
|
|
388
|
+
const topics = {};
|
|
389
|
+
|
|
390
|
+
// Organize by category
|
|
391
|
+
topics.core = ['getting started', 'installation', 'connection', 'database', 'resource', 'schema', 'validation'];
|
|
392
|
+
topics.operations = ['insert', 'update', 'delete', 'query', 'list', 'get', 'count', 'crud'];
|
|
393
|
+
topics.partitioning = ['partition', 'partitioning', 'orphaned partition', 'partition migration'];
|
|
394
|
+
topics.plugins = ['plugin', 'cache', 'audit', 'replicator', 'backup', 'geo', 'metrics', 'costs', 'eventual consistency', 'fulltext', 'queue'];
|
|
395
|
+
topics.performance = ['performance', 'optimization', 'benchmark', 'compression', 'encoding'];
|
|
396
|
+
topics.mcp = ['mcp', 'model context protocol', 'ai agent', 'claude desktop'];
|
|
397
|
+
topics.advanced = ['encryption', 'secret', 'vector', 'embedding', 'versioning', 'hooks', 'behavior', 'metadata'];
|
|
398
|
+
topics.troubleshooting = ['error', 'troubleshooting', 'recovery'];
|
|
399
|
+
|
|
400
|
+
const allFiles = getAllDocFiles();
|
|
401
|
+
|
|
402
|
+
return {
|
|
403
|
+
success: true,
|
|
404
|
+
message: 'Use s3dbQueryDocs with any of these topics to get detailed documentation',
|
|
405
|
+
categories: topics,
|
|
406
|
+
availableFiles: allFiles,
|
|
407
|
+
totalTopics: Object.keys(DOCUMENTATION_INDEX).length,
|
|
408
|
+
totalFiles: allFiles.length,
|
|
409
|
+
examples: [
|
|
410
|
+
{
|
|
411
|
+
query: 'How do I use the CachePlugin?',
|
|
412
|
+
description: 'Learn about caching strategies and configuration'
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
query: 'What are partitions?',
|
|
416
|
+
description: 'Understand partitioning for performance optimization'
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
query: 'How do I handle orphaned partitions?',
|
|
420
|
+
description: 'Recovery workflow for partition issues'
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
query: 'What plugins are available?',
|
|
424
|
+
description: 'Complete list of available plugins and their purposes'
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
query: 'How does MCP integration work?',
|
|
428
|
+
description: 'Setting up and using the MCP server with AI agents'
|
|
429
|
+
}
|
|
430
|
+
]
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
}
|
package/mcp/tools/index.js
CHANGED
|
@@ -12,12 +12,14 @@ import { partitionTools, createPartitionHandlers } from './partitions.js';
|
|
|
12
12
|
import { bulkTools, createBulkHandlers } from './bulk.js';
|
|
13
13
|
import { exportImportTools, createExportImportHandlers } from './export-import.js';
|
|
14
14
|
import { statsTools, createStatsHandlers } from './stats.js';
|
|
15
|
+
import { documentationTools, createDocumentationHandlers } from './documentation.js';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Get all tool definitions
|
|
18
19
|
*/
|
|
19
20
|
export function getAllTools() {
|
|
20
21
|
return [
|
|
22
|
+
...documentationTools,
|
|
21
23
|
...connectionTools,
|
|
22
24
|
...resourceManagementTools,
|
|
23
25
|
...crudTools,
|
|
@@ -37,6 +39,7 @@ export function getAllTools() {
|
|
|
37
39
|
*/
|
|
38
40
|
export function createAllHandlers(server) {
|
|
39
41
|
return {
|
|
42
|
+
...createDocumentationHandlers(server),
|
|
40
43
|
...createConnectionHandlers(server),
|
|
41
44
|
...createResourceManagementHandlers(server),
|
|
42
45
|
...createCrudHandlers(server),
|
|
@@ -54,6 +57,7 @@ export function createAllHandlers(server) {
|
|
|
54
57
|
*/
|
|
55
58
|
export function getToolsByCategory() {
|
|
56
59
|
return {
|
|
60
|
+
documentation: documentationTools,
|
|
57
61
|
connection: connectionTools,
|
|
58
62
|
resources: resourceManagementTools,
|
|
59
63
|
crud: crudTools,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "s3db.js",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "12.0.0",
|
|
4
4
|
"description": "Use AWS S3, the world's most reliable document storage, as a database with this ORM.",
|
|
5
5
|
"main": "dist/s3db.cjs.js",
|
|
6
6
|
"module": "dist/s3db.es.js",
|
|
@@ -72,6 +72,7 @@
|
|
|
72
72
|
"dotenv": "^17.2.3",
|
|
73
73
|
"fastest-validator": "^1.19.1",
|
|
74
74
|
"flat": "^6.0.1",
|
|
75
|
+
"glob": "^11.0.3",
|
|
75
76
|
"json-stable-stringify": "^1.3.0",
|
|
76
77
|
"lodash-es": "^4.17.21",
|
|
77
78
|
"nanoid": "5.1.6"
|
|
@@ -79,9 +80,12 @@
|
|
|
79
80
|
"peerDependencies": {
|
|
80
81
|
"@aws-sdk/client-sqs": "^3.0.0",
|
|
81
82
|
"@google-cloud/bigquery": "^7.0.0",
|
|
83
|
+
"@hono/node-server": "^1.0.0",
|
|
84
|
+
"@hono/swagger-ui": "^0.5.0",
|
|
82
85
|
"amqplib": "^0.10.8",
|
|
83
|
-
"
|
|
84
|
-
"
|
|
86
|
+
"hono": "^4.0.0",
|
|
87
|
+
"node-cron": "^4.0.0",
|
|
88
|
+
"pg": "^8.0.0"
|
|
85
89
|
},
|
|
86
90
|
"peerDependenciesMeta": {
|
|
87
91
|
"@aws-sdk/client-sqs": {
|
|
@@ -90,19 +94,30 @@
|
|
|
90
94
|
"@google-cloud/bigquery": {
|
|
91
95
|
"optional": true
|
|
92
96
|
},
|
|
93
|
-
"
|
|
97
|
+
"@hono/node-server": {
|
|
94
98
|
"optional": true
|
|
95
99
|
},
|
|
96
|
-
"
|
|
100
|
+
"@hono/swagger-ui": {
|
|
101
|
+
"optional": true
|
|
102
|
+
},
|
|
103
|
+
"pg": {
|
|
97
104
|
"optional": true
|
|
98
105
|
},
|
|
99
106
|
"amqplib": {
|
|
100
107
|
"optional": true
|
|
108
|
+
},
|
|
109
|
+
"hono": {
|
|
110
|
+
"optional": true
|
|
111
|
+
},
|
|
112
|
+
"node-cron": {
|
|
113
|
+
"optional": true
|
|
101
114
|
}
|
|
102
115
|
},
|
|
103
116
|
"devDependencies": {
|
|
117
|
+
"@aws-sdk/client-sqs": "^3.0.0",
|
|
104
118
|
"@babel/core": "^7.28.4",
|
|
105
119
|
"@babel/preset-env": "^7.28.3",
|
|
120
|
+
"@google-cloud/bigquery": "^7.0.0",
|
|
106
121
|
"@rollup/plugin-commonjs": "^28.0.6",
|
|
107
122
|
"@rollup/plugin-json": "^6.1.0",
|
|
108
123
|
"@rollup/plugin-node-resolve": "^16.0.2",
|
|
@@ -110,6 +125,7 @@
|
|
|
110
125
|
"@rollup/plugin-terser": "^0.4.4",
|
|
111
126
|
"@types/node": "24.7.0",
|
|
112
127
|
"@xenova/transformers": "^2.17.2",
|
|
128
|
+
"amqplib": "^0.10.8",
|
|
113
129
|
"babel-loader": "^10.0.0",
|
|
114
130
|
"chalk": "^5.6.2",
|
|
115
131
|
"cli-table3": "^0.6.5",
|
|
@@ -117,8 +133,10 @@
|
|
|
117
133
|
"esbuild": "^0.25.10",
|
|
118
134
|
"inquirer": "^12.9.6",
|
|
119
135
|
"jest": "^30.2.0",
|
|
136
|
+
"node-cron": "^4.0.0",
|
|
120
137
|
"node-loader": "^2.1.0",
|
|
121
138
|
"ora": "^9.0.0",
|
|
139
|
+
"pg": "^8.0.0",
|
|
122
140
|
"pkg": "^5.8.1",
|
|
123
141
|
"rollup": "^4.52.4",
|
|
124
142
|
"rollup-plugin-copy": "^3.5.0",
|
|
@@ -128,6 +146,7 @@
|
|
|
128
146
|
"rollup-plugin-terser": "^7.0.2",
|
|
129
147
|
"tsx": "^4.20.6",
|
|
130
148
|
"typescript": "5.9.3",
|
|
149
|
+
"uuid": "^13.0.0",
|
|
131
150
|
"webpack": "^5.102.1",
|
|
132
151
|
"webpack-cli": "^6.0.1"
|
|
133
152
|
},
|
|
@@ -155,6 +174,8 @@
|
|
|
155
174
|
"release:check": "./scripts/pre-release-check.sh",
|
|
156
175
|
"validate:types": "pnpm run test:ts && echo 'TypeScript definitions are valid!'",
|
|
157
176
|
"test:ts:runtime": "tsx tests/typescript/types-runtime-simple.ts",
|
|
158
|
-
"test:mcp": "node mcp/entrypoint.js --help"
|
|
177
|
+
"test:mcp": "node mcp/entrypoint.js --help",
|
|
178
|
+
"install:peers": "pnpm add -D @aws-sdk/client-sqs @google-cloud/bigquery amqplib node-cron pg",
|
|
179
|
+
"install:peers:script": "./scripts/install-peer-deps.sh"
|
|
159
180
|
}
|
|
160
181
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { calculateTotalSize } from '../concerns/calculator.js';
|
|
2
2
|
import { calculateEffectiveLimit } from '../concerns/calculator.js';
|
|
3
3
|
import { S3_METADATA_LIMIT_BYTES } from './enforce-limits.js';
|
|
4
|
+
import tryFn from '../concerns/try-fn.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* User Managed Behavior Configuration Documentation
|
|
@@ -149,15 +150,21 @@ export async function handleUpsert({ resource, id, data, mappedData, originalDat
|
|
|
149
150
|
export async function handleGet({ resource, metadata, body }) {
|
|
150
151
|
// If body contains data, parse it and merge with metadata
|
|
151
152
|
if (body && body.trim() !== '') {
|
|
152
|
-
|
|
153
|
+
const [ok, error, result] = tryFn(() => {
|
|
153
154
|
const bodyData = JSON.parse(body);
|
|
154
155
|
// Merge body data with metadata, with metadata taking precedence
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
156
|
+
return {
|
|
157
|
+
metadata: {
|
|
158
|
+
...bodyData,
|
|
159
|
+
...metadata
|
|
160
|
+
},
|
|
161
|
+
body
|
|
158
162
|
};
|
|
159
|
-
|
|
160
|
-
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
if (ok) {
|
|
166
|
+
return result;
|
|
167
|
+
} else {
|
|
161
168
|
// If parsing fails, return original metadata and body
|
|
162
169
|
return { metadata, body };
|
|
163
170
|
}
|