@cli4ai/mongodb 1.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.
Files changed (3) hide show
  1. package/c4ai.json +32 -0
  2. package/package.json +39 -0
  3. package/run.ts +216 -0
package/c4ai.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "mongodb",
3
+ "version": "1.0.0",
4
+ "description": "MongoDB queries",
5
+ "author": "cliforai",
6
+ "license": "MIT",
7
+ "entry": "run.ts",
8
+ "runtime": "bun",
9
+ "keywords": ["mongodb", "database", "nosql", "query"],
10
+ "commands": {
11
+ "databases": { "description": "List all databases" },
12
+ "collections": { "description": "List collections", "args": [{ "name": "db", "required": true }] },
13
+ "schema": { "description": "Infer schema", "args": [{ "name": "db", "required": true }, { "name": "collection", "required": true }, { "name": "samples", "required": false }] },
14
+ "find": { "description": "Find documents", "args": [{ "name": "db", "required": true }, { "name": "collection", "required": true }, { "name": "query", "required": false }, { "name": "limit", "required": false }] },
15
+ "one": { "description": "Single document", "args": [{ "name": "db", "required": true }, { "name": "collection", "required": true }, { "name": "query", "required": false }] },
16
+ "aggregate": { "description": "Run aggregation pipeline", "args": [{ "name": "db", "required": true }, { "name": "collection", "required": true }, { "name": "pipeline", "required": true }] },
17
+ "count": { "description": "Count documents", "args": [{ "name": "db", "required": true }, { "name": "collection", "required": true }, { "name": "query", "required": false }] },
18
+ "indexes": { "description": "List indexes", "args": [{ "name": "db", "required": true }, { "name": "collection", "required": true }] },
19
+ "stats": { "description": "Collection statistics", "args": [{ "name": "db", "required": true }, { "name": "collection", "required": true }] },
20
+ "distinct": { "description": "Distinct values", "args": [{ "name": "db", "required": true }, { "name": "collection", "required": true }, { "name": "field", "required": true }, { "name": "query", "required": false }] },
21
+ "sample": { "description": "Random sample", "args": [{ "name": "db", "required": true }, { "name": "collection", "required": true }, { "name": "size", "required": false }] },
22
+ "explain": { "description": "Query explanation", "args": [{ "name": "db", "required": true }, { "name": "collection", "required": true }, { "name": "query", "required": false }] }
23
+ },
24
+ "env": {
25
+ "MONGO_URI": { "required": false, "description": "MongoDB connection URI (default: localhost:27017)" }
26
+ },
27
+ "dependencies": {
28
+ "mongodb": "^6.0.0",
29
+ "commander": "^14.0.0"
30
+ },
31
+ "mcp": { "enabled": true, "transport": "stdio" }
32
+ }
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@cli4ai/mongodb",
3
+ "version": "1.0.0",
4
+ "description": "MongoDB queries",
5
+ "author": "cliforai",
6
+ "license": "MIT",
7
+ "main": "run.ts",
8
+ "bin": {
9
+ "mongodb": "./run.ts"
10
+ },
11
+ "type": "module",
12
+ "keywords": [
13
+ "c4ai",
14
+ "cli",
15
+ "ai-tools",
16
+ "mongodb",
17
+ "database",
18
+ "nosql",
19
+ "query"
20
+ ],
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/cli4ai/packages",
24
+ "directory": "packages/mongodb"
25
+ },
26
+ "homepage": "https://github.com/cli4ai/packages/tree/main/packages/mongodb",
27
+ "bugs": {
28
+ "url": "https://github.com/cli4ai/packages/issues"
29
+ },
30
+ "files": [
31
+ "run.ts",
32
+ "c4ai.json",
33
+ "README.md",
34
+ "LICENSE"
35
+ ],
36
+ "publishConfig": {
37
+ "access": "public"
38
+ }
39
+ }
package/run.ts ADDED
@@ -0,0 +1,216 @@
1
+ #!/usr/bin/env bun
2
+ import { MongoClient, Document } from 'mongodb';
3
+ import { cli, loadEnv, output, outputError, withErrorHandling, parseJson } from '../lib/cli.ts';
4
+
5
+ loadEnv();
6
+
7
+ const MONGO_URI = process.env.MONGO_URI || 'mongodb://localhost:27017';
8
+
9
+ async function withClient<T>(fn: (client: MongoClient) => Promise<T>): Promise<T> {
10
+ const client = new MongoClient(MONGO_URI);
11
+ try {
12
+ await client.connect();
13
+ return await fn(client);
14
+ } finally {
15
+ await client.close();
16
+ }
17
+ }
18
+
19
+ // Infer schema from documents by analyzing field types
20
+ function inferSchema(docs: Document[]): Document {
21
+ const schema: Record<string, { types: Set<string>; sample: unknown; count: number }> = {};
22
+
23
+ function getType(value: unknown): string {
24
+ if (value === null) return 'null';
25
+ if (Array.isArray(value)) return 'array';
26
+ if (value instanceof Date) return 'date';
27
+ if (typeof value === 'object' && (value as object).constructor?.name === 'ObjectId') return 'ObjectId';
28
+ if (typeof value === 'object' && (value as object).constructor?.name === 'Binary') return 'Binary';
29
+ return typeof value;
30
+ }
31
+
32
+ function processDoc(doc: Document, prefix = ''): void {
33
+ for (const [key, value] of Object.entries(doc)) {
34
+ const path = prefix ? `${prefix}.${key}` : key;
35
+ const type = getType(value);
36
+
37
+ if (!schema[path]) {
38
+ schema[path] = { types: new Set(), sample: value, count: 0 };
39
+ }
40
+ schema[path].types.add(type);
41
+ schema[path].count++;
42
+
43
+ // Recurse into objects (but not arrays or special types)
44
+ if (type === 'object' && value !== null) {
45
+ processDoc(value as Document, path);
46
+ }
47
+ }
48
+ }
49
+
50
+ docs.forEach(doc => processDoc(doc));
51
+
52
+ // Convert to output format
53
+ const result: Record<string, { types: string[]; sample: unknown; frequency: string }> = {};
54
+ for (const [path, info] of Object.entries(schema)) {
55
+ result[path] = {
56
+ types: Array.from(info.types),
57
+ sample: info.sample,
58
+ frequency: `${info.count}/${docs.length}`
59
+ };
60
+ }
61
+ return result;
62
+ }
63
+
64
+ const program = cli('mongodb', '1.0.0', 'MongoDB queries');
65
+
66
+ program
67
+ .command('databases')
68
+ .description('List all databases')
69
+ .action(withErrorHandling(async () => {
70
+ await withClient(async (client) => {
71
+ const admin = client.db().admin();
72
+ const result = await admin.listDatabases();
73
+ output(result.databases.map(db => ({
74
+ name: db.name,
75
+ sizeOnDisk: db.sizeOnDisk,
76
+ sizeMB: db.sizeOnDisk ? Math.round(db.sizeOnDisk / 1024 / 1024 * 100) / 100 : 0
77
+ })));
78
+ });
79
+ }));
80
+
81
+ program
82
+ .command('collections <db>')
83
+ .description('List collections')
84
+ .action(withErrorHandling(async (dbName: string) => {
85
+ await withClient(async (client) => {
86
+ const db = client.db(dbName);
87
+ const collections = await db.listCollections().toArray();
88
+ output(collections.map(c => ({ name: c.name, type: c.type })));
89
+ });
90
+ }));
91
+
92
+ program
93
+ .command('schema <db> <collection> [samples]')
94
+ .description('Infer schema (default: 100 samples)')
95
+ .action(withErrorHandling(async (dbName: string, collName: string, sampleSize = '100') => {
96
+ await withClient(async (client) => {
97
+ const collection = client.db(dbName).collection(collName);
98
+ const docs = await collection.find({}).limit(parseInt(sampleSize)).toArray();
99
+ const schema = inferSchema(docs);
100
+ output({
101
+ collection: `${dbName}.${collName}`,
102
+ sampledDocs: docs.length,
103
+ fields: schema
104
+ });
105
+ });
106
+ }));
107
+
108
+ program
109
+ .command('find <db> <collection> [query] [limit]')
110
+ .description('Find documents (default: {}, 10)')
111
+ .action(withErrorHandling(async (dbName: string, collName: string, query = '{}', limit = '10') => {
112
+ await withClient(async (client) => {
113
+ const collection = client.db(dbName).collection(collName);
114
+ const docs = await collection.find(parseJson(query, 'query')).limit(parseInt(limit)).toArray();
115
+ output(docs);
116
+ });
117
+ }));
118
+
119
+ program
120
+ .command('one <db> <collection> [query]')
121
+ .description('Single document')
122
+ .action(withErrorHandling(async (dbName: string, collName: string, query = '{}') => {
123
+ await withClient(async (client) => {
124
+ const collection = client.db(dbName).collection(collName);
125
+ const doc = await collection.findOne(parseJson(query, 'query'));
126
+ output(doc);
127
+ });
128
+ }));
129
+
130
+ program
131
+ .command('aggregate <db> <collection> <pipeline>')
132
+ .description('Run aggregation pipeline')
133
+ .action(withErrorHandling(async (dbName: string, collName: string, pipeline: string) => {
134
+ await withClient(async (client) => {
135
+ const collection = client.db(dbName).collection(collName);
136
+ const docs = await collection.aggregate(parseJson(pipeline, 'pipeline')).toArray();
137
+ output(docs);
138
+ });
139
+ }));
140
+
141
+ program
142
+ .command('count <db> <collection> [query]')
143
+ .description('Count documents')
144
+ .action(withErrorHandling(async (dbName: string, collName: string, query = '{}') => {
145
+ await withClient(async (client) => {
146
+ const collection = client.db(dbName).collection(collName);
147
+ const count = await collection.countDocuments(parseJson(query, 'query'));
148
+ output({ collection: `${dbName}.${collName}`, count });
149
+ });
150
+ }));
151
+
152
+ program
153
+ .command('indexes <db> <collection>')
154
+ .description('List indexes')
155
+ .action(withErrorHandling(async (dbName: string, collName: string) => {
156
+ await withClient(async (client) => {
157
+ const collection = client.db(dbName).collection(collName);
158
+ const indexes = await collection.indexes();
159
+ output(indexes);
160
+ });
161
+ }));
162
+
163
+ program
164
+ .command('stats <db> <collection>')
165
+ .description('Collection statistics')
166
+ .action(withErrorHandling(async (dbName: string, collName: string) => {
167
+ await withClient(async (client) => {
168
+ const db = client.db(dbName);
169
+ const stats = await db.command({ collStats: collName });
170
+ output({
171
+ ns: stats.ns,
172
+ count: stats.count,
173
+ size: stats.size,
174
+ sizeMB: Math.round(stats.size / 1024 / 1024 * 100) / 100,
175
+ avgObjSize: stats.avgObjSize,
176
+ storageSize: stats.storageSize,
177
+ indexes: stats.nindexes,
178
+ indexSize: stats.totalIndexSize
179
+ });
180
+ });
181
+ }));
182
+
183
+ program
184
+ .command('distinct <db> <collection> <field> [query]')
185
+ .description('Distinct values')
186
+ .action(withErrorHandling(async (dbName: string, collName: string, field: string, query = '{}') => {
187
+ await withClient(async (client) => {
188
+ const collection = client.db(dbName).collection(collName);
189
+ const values = await collection.distinct(field, parseJson(query, 'query'));
190
+ output({ field, count: values.length, values });
191
+ });
192
+ }));
193
+
194
+ program
195
+ .command('sample <db> <collection> [size]')
196
+ .description('Random sample (default: 5)')
197
+ .action(withErrorHandling(async (dbName: string, collName: string, size = '5') => {
198
+ await withClient(async (client) => {
199
+ const collection = client.db(dbName).collection(collName);
200
+ const docs = await collection.aggregate([{ $sample: { size: parseInt(size) } }]).toArray();
201
+ output(docs);
202
+ });
203
+ }));
204
+
205
+ program
206
+ .command('explain <db> <collection> [query]')
207
+ .description('Query explanation')
208
+ .action(withErrorHandling(async (dbName: string, collName: string, query = '{}') => {
209
+ await withClient(async (client) => {
210
+ const collection = client.db(dbName).collection(collName);
211
+ const explanation = await collection.find(parseJson(query, 'query')).explain('executionStats');
212
+ output(explanation);
213
+ });
214
+ }));
215
+
216
+ program.parse();