@jsondb-cloud/mcp 1.0.8 → 1.0.11
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 +158 -0
- package/dist/index.js +46 -64
- package/dist/index.mjs +46 -64
- package/package.json +14 -5
package/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# @jsondb-cloud/mcp
|
|
2
|
+
|
|
3
|
+
The official MCP (Model Context Protocol) server for [jsondb.cloud](https://jsondb.cloud) — a hosted JSON document database. Lets AI agents create, query, and manage documents through natural language.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@jsondb-cloud/mcp)
|
|
6
|
+
[](https://www.npmjs.com/package/@jsondb-cloud/mcp)
|
|
7
|
+
[](https://github.com/JsonDBCloud/mcp/actions)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
[](https://nodejs.org)
|
|
10
|
+
[](https://bundlephobia.com/package/@jsondb-cloud/mcp)
|
|
11
|
+
[](https://github.com/JsonDBCloud/mcp)
|
|
12
|
+
[](https://github.com/JsonDBCloud/mcp)
|
|
13
|
+
[](https://opensource.org/licenses/MIT)
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @jsondb-cloud/mcp
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
Once configured, ask your AI assistant:
|
|
24
|
+
|
|
25
|
+
> List all collections in my database
|
|
26
|
+
|
|
27
|
+
> Create a document in "users" with name "Alice" and role "admin"
|
|
28
|
+
|
|
29
|
+
> Find all users where role is "admin"
|
|
30
|
+
|
|
31
|
+
> Show me version history for document abc123 in "orders"
|
|
32
|
+
|
|
33
|
+
> Set up a webhook on "orders" that fires on document.created
|
|
34
|
+
|
|
35
|
+
## Configuration
|
|
36
|
+
|
|
37
|
+
You need a jsondb.cloud API key. Get one from your [dashboard](https://jsondb.cloud).
|
|
38
|
+
|
|
39
|
+
### Claude Desktop
|
|
40
|
+
|
|
41
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"mcpServers": {
|
|
46
|
+
"jsondb": {
|
|
47
|
+
"command": "npx",
|
|
48
|
+
"args": ["-y", "@jsondb-cloud/mcp"],
|
|
49
|
+
"env": {
|
|
50
|
+
"JSONDB_API_KEY": "jdb_sk_live_..."
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Claude Code
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
claude mcp add jsondb -- npx -y @jsondb-cloud/mcp \
|
|
61
|
+
--env JSONDB_API_KEY=jdb_sk_live_...
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Cursor
|
|
65
|
+
|
|
66
|
+
Add to `.cursor/mcp.json`:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"mcpServers": {
|
|
71
|
+
"jsondb": {
|
|
72
|
+
"command": "npx",
|
|
73
|
+
"args": ["-y", "@jsondb-cloud/mcp"],
|
|
74
|
+
"env": {
|
|
75
|
+
"JSONDB_API_KEY": "jdb_sk_live_..."
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Environment Variables
|
|
83
|
+
|
|
84
|
+
| Variable | Required | Default | Description |
|
|
85
|
+
| ----------------- | -------- | -------------------------- | ------------------------------------------------ |
|
|
86
|
+
| `JSONDB_API_KEY` | Yes | — | API key (`jdb_sk_live_...` or `jdb_sk_test_...`) |
|
|
87
|
+
| `JSONDB_PROJECT` | No | `v1` | Project namespace |
|
|
88
|
+
| `JSONDB_BASE_URL` | No | `https://api.jsondb.cloud` | API base URL |
|
|
89
|
+
|
|
90
|
+
## Tools
|
|
91
|
+
|
|
92
|
+
### Documents
|
|
93
|
+
|
|
94
|
+
| Tool | Description |
|
|
95
|
+
| --------------------- | ------------------------------------------------------ |
|
|
96
|
+
| `create_document` | Create a new document in a collection |
|
|
97
|
+
| `get_document` | Read a single document by ID |
|
|
98
|
+
| `list_documents` | List documents with filtering, sorting, and pagination |
|
|
99
|
+
| `update_document` | Replace a document entirely |
|
|
100
|
+
| `patch_document` | Partially update a document (merge patch) |
|
|
101
|
+
| `delete_document` | Delete a document by ID |
|
|
102
|
+
| `count_documents` | Count documents matching an optional filter |
|
|
103
|
+
| `json_patch_document` | Apply RFC 6902 JSON Patch operations |
|
|
104
|
+
|
|
105
|
+
### Collections
|
|
106
|
+
|
|
107
|
+
| Tool | Description |
|
|
108
|
+
| ------------------- | ------------------------------------------------------------------ |
|
|
109
|
+
| `list_collections` | List all collections in the current project |
|
|
110
|
+
| `search_documents` | Search with advanced filters (`eq`, `gt`, `contains`, `in`, etc.) |
|
|
111
|
+
| `import_documents` | Bulk import with conflict resolution (`fail`, `skip`, `overwrite`) |
|
|
112
|
+
| `export_collection` | Export all documents as JSON |
|
|
113
|
+
|
|
114
|
+
### Schemas
|
|
115
|
+
|
|
116
|
+
| Tool | Description |
|
|
117
|
+
| ------------------- | ----------------------------------------------- |
|
|
118
|
+
| `get_schema` | Get the JSON Schema for a collection |
|
|
119
|
+
| `set_schema` | Set a JSON Schema to enforce document structure |
|
|
120
|
+
| `remove_schema` | Remove schema validation from a collection |
|
|
121
|
+
| `validate_document` | Dry-run validate a document against the schema |
|
|
122
|
+
|
|
123
|
+
### Versions
|
|
124
|
+
|
|
125
|
+
| Tool | Description |
|
|
126
|
+
| ----------------- | ------------------------------------------- |
|
|
127
|
+
| `list_versions` | List all stored versions of a document |
|
|
128
|
+
| `get_version` | Retrieve a specific version snapshot |
|
|
129
|
+
| `restore_version` | Restore a document to a previous version |
|
|
130
|
+
| `diff_versions` | Compare two versions with a structured diff |
|
|
131
|
+
|
|
132
|
+
### Webhooks
|
|
133
|
+
|
|
134
|
+
| Tool | Description |
|
|
135
|
+
| ---------------- | ----------------------------------------------- |
|
|
136
|
+
| `create_webhook` | Register a webhook for collection events |
|
|
137
|
+
| `list_webhooks` | List all webhooks for a collection |
|
|
138
|
+
| `get_webhook` | Get webhook details and recent delivery history |
|
|
139
|
+
| `update_webhook` | Update webhook URL, events, or status |
|
|
140
|
+
| `delete_webhook` | Delete a webhook |
|
|
141
|
+
| `test_webhook` | Send a test event to verify delivery |
|
|
142
|
+
|
|
143
|
+
## Documentation
|
|
144
|
+
|
|
145
|
+
Full documentation at [jsondb.cloud/docs](https://jsondb.cloud/docs).
|
|
146
|
+
|
|
147
|
+
## Related Packages
|
|
148
|
+
|
|
149
|
+
| Package | Description |
|
|
150
|
+
| ------------------------------------------------------------ | ------------------------- |
|
|
151
|
+
| [@jsondb-cloud/client](https://github.com/JsonDBCloud/node) | JavaScript/TypeScript SDK |
|
|
152
|
+
| [@jsondb-cloud/mcp](https://github.com/JsonDBCloud/mcp) | MCP server for AI agents |
|
|
153
|
+
| [@jsondb-cloud/cli](https://github.com/JsonDBCloud/cli) | CLI tool |
|
|
154
|
+
| [jsondb-cloud](https://github.com/JsonDBCloud/python) (PyPI) | Python SDK |
|
|
155
|
+
|
|
156
|
+
## License
|
|
157
|
+
|
|
158
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
var
|
|
4
|
+
var import_mcp2 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
5
5
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
6
6
|
var import_client = require("@jsondb-cloud/client");
|
|
7
7
|
|
|
@@ -30,9 +30,7 @@ function registerDocumentTools(server, db) {
|
|
|
30
30
|
{
|
|
31
31
|
collection: import_zod.z.string().describe("The collection name (e.g., 'users', 'posts', 'settings')"),
|
|
32
32
|
data: import_zod.z.record(import_zod.z.string(), import_zod.z.any()).describe("The JSON document to store. Can contain any valid JSON."),
|
|
33
|
-
id: import_zod.z.string().optional().describe(
|
|
34
|
-
"Optional custom document ID. If not provided, an ID is auto-generated."
|
|
35
|
-
)
|
|
33
|
+
id: import_zod.z.string().optional().describe("Optional custom document ID. If not provided, an ID is auto-generated.")
|
|
36
34
|
},
|
|
37
35
|
async ({ collection, data, id }) => {
|
|
38
36
|
try {
|
|
@@ -107,9 +105,7 @@ function registerDocumentTools(server, db) {
|
|
|
107
105
|
filter: import_zod.z.record(import_zod.z.string(), import_zod.z.any()).optional().describe(
|
|
108
106
|
"Filter criteria. Keys are field names, values are match values. Use {field: {$gt: N}} for comparisons."
|
|
109
107
|
),
|
|
110
|
-
sort: import_zod.z.string().optional().describe(
|
|
111
|
-
"Field to sort by. Prefix with '-' for descending (e.g., '-$createdAt')"
|
|
112
|
-
),
|
|
108
|
+
sort: import_zod.z.string().optional().describe("Field to sort by. Prefix with '-' for descending (e.g., '-$createdAt')"),
|
|
113
109
|
limit: import_zod.z.number().optional().describe("Max documents to return (default: 20, max: 100)"),
|
|
114
110
|
offset: import_zod.z.number().optional().describe("Number of documents to skip for pagination"),
|
|
115
111
|
select: import_zod.z.array(import_zod.z.string()).optional().describe(
|
|
@@ -143,9 +139,7 @@ function registerDocumentTools(server, db) {
|
|
|
143
139
|
{
|
|
144
140
|
collection: import_zod.z.string().describe("The collection name"),
|
|
145
141
|
id: import_zod.z.string().describe("The document ID to replace"),
|
|
146
|
-
data: import_zod.z.record(import_zod.z.string(), import_zod.z.any()).describe(
|
|
147
|
-
"The complete new document data. This replaces all existing fields."
|
|
148
|
-
)
|
|
142
|
+
data: import_zod.z.record(import_zod.z.string(), import_zod.z.any()).describe("The complete new document data. This replaces all existing fields.")
|
|
149
143
|
},
|
|
150
144
|
async ({ collection, id, data }) => {
|
|
151
145
|
try {
|
|
@@ -379,7 +373,10 @@ function registerCollectionTools(server, db) {
|
|
|
379
373
|
try {
|
|
380
374
|
const apiKey = process.env.JSONDB_API_KEY || "";
|
|
381
375
|
const project = process.env.JSONDB_PROJECT || process.env.JSONDB_NAMESPACE || "v1";
|
|
382
|
-
const baseUrl = (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(
|
|
376
|
+
const baseUrl = (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(
|
|
377
|
+
/\/$/,
|
|
378
|
+
""
|
|
379
|
+
);
|
|
383
380
|
const res = await fetch(`${baseUrl}/${project}`, {
|
|
384
381
|
headers: {
|
|
385
382
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -388,7 +385,10 @@ function registerCollectionTools(server, db) {
|
|
|
388
385
|
});
|
|
389
386
|
if (!res.ok) {
|
|
390
387
|
const body = await res.json().catch(() => ({}));
|
|
391
|
-
throw {
|
|
388
|
+
throw {
|
|
389
|
+
status: res.status,
|
|
390
|
+
message: body.error || res.statusText
|
|
391
|
+
};
|
|
392
392
|
}
|
|
393
393
|
const data = await res.json();
|
|
394
394
|
return success2(data);
|
|
@@ -409,26 +409,12 @@ function registerCollectionTools(server, db) {
|
|
|
409
409
|
collection: import_zod2.z.string().describe("The collection name"),
|
|
410
410
|
filters: import_zod2.z.array(
|
|
411
411
|
import_zod2.z.object({
|
|
412
|
-
field: import_zod2.z.string().describe(
|
|
413
|
-
|
|
414
|
-
),
|
|
415
|
-
operator: import_zod2.z.enum([
|
|
416
|
-
"eq",
|
|
417
|
-
"neq",
|
|
418
|
-
"gt",
|
|
419
|
-
"gte",
|
|
420
|
-
"lt",
|
|
421
|
-
"lte",
|
|
422
|
-
"contains",
|
|
423
|
-
"in",
|
|
424
|
-
"exists"
|
|
425
|
-
]).describe("Comparison operator"),
|
|
412
|
+
field: import_zod2.z.string().describe("Field path (supports dot notation for nested fields)"),
|
|
413
|
+
operator: import_zod2.z.enum(["eq", "neq", "gt", "gte", "lt", "lte", "contains", "in", "exists"]).describe("Comparison operator"),
|
|
426
414
|
value: import_zod2.z.any().describe("Value to compare against")
|
|
427
415
|
})
|
|
428
416
|
).describe("Array of filter conditions (combined with AND logic)"),
|
|
429
|
-
sort: import_zod2.z.string().optional().describe(
|
|
430
|
-
"Field to sort by. Prefix with '-' for descending (e.g., '-$createdAt')"
|
|
431
|
-
),
|
|
417
|
+
sort: import_zod2.z.string().optional().describe("Field to sort by. Prefix with '-' for descending (e.g., '-$createdAt')"),
|
|
432
418
|
limit: import_zod2.z.number().optional().describe("Max documents to return (default: 20, max: 100)"),
|
|
433
419
|
offset: import_zod2.z.number().optional().describe("Number of documents to skip for pagination")
|
|
434
420
|
},
|
|
@@ -462,15 +448,16 @@ function registerCollectionTools(server, db) {
|
|
|
462
448
|
onConflict: import_zod2.z.enum(["fail", "skip", "overwrite"]).optional().describe(
|
|
463
449
|
"How to handle ID conflicts: 'fail' (default) rejects the batch, 'skip' ignores duplicates, 'overwrite' replaces existing documents"
|
|
464
450
|
),
|
|
465
|
-
idField: import_zod2.z.string().optional().describe(
|
|
466
|
-
"Field in each document to use as _id. If omitted, IDs are auto-generated."
|
|
467
|
-
)
|
|
451
|
+
idField: import_zod2.z.string().optional().describe("Field in each document to use as _id. If omitted, IDs are auto-generated.")
|
|
468
452
|
},
|
|
469
453
|
async ({ collection, documents, onConflict, idField }) => {
|
|
470
454
|
try {
|
|
471
455
|
const apiKey = process.env.JSONDB_API_KEY || "";
|
|
472
456
|
const project = process.env.JSONDB_PROJECT || process.env.JSONDB_NAMESPACE || "v1";
|
|
473
|
-
const baseUrl = (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(
|
|
457
|
+
const baseUrl = (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(
|
|
458
|
+
/\/$/,
|
|
459
|
+
""
|
|
460
|
+
);
|
|
474
461
|
const params = new URLSearchParams();
|
|
475
462
|
if (onConflict) params.set("onConflict", onConflict);
|
|
476
463
|
if (idField) params.set("idField", idField);
|
|
@@ -532,7 +519,10 @@ function registerCollectionTools(server, db) {
|
|
|
532
519
|
try {
|
|
533
520
|
const apiKey = process.env.JSONDB_API_KEY || "";
|
|
534
521
|
const project = process.env.JSONDB_PROJECT || process.env.JSONDB_NAMESPACE || "v1";
|
|
535
|
-
const baseUrl = (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(
|
|
522
|
+
const baseUrl = (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(
|
|
523
|
+
/\/$/,
|
|
524
|
+
""
|
|
525
|
+
);
|
|
536
526
|
const params = new URLSearchParams();
|
|
537
527
|
if (filter) {
|
|
538
528
|
for (const [field, value] of Object.entries(filter)) {
|
|
@@ -753,10 +743,7 @@ function resolveEnv() {
|
|
|
753
743
|
return {
|
|
754
744
|
apiKey: process.env.JSONDB_API_KEY || "",
|
|
755
745
|
project: process.env.JSONDB_PROJECT || process.env.JSONDB_NAMESPACE || "v1",
|
|
756
|
-
baseUrl: (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(
|
|
757
|
-
/\/$/,
|
|
758
|
-
""
|
|
759
|
-
)
|
|
746
|
+
baseUrl: (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(/\/$/, "")
|
|
760
747
|
};
|
|
761
748
|
}
|
|
762
749
|
async function versionFetch(url, apiKey, opts = {}) {
|
|
@@ -954,10 +941,7 @@ function resolveEnv2() {
|
|
|
954
941
|
return {
|
|
955
942
|
apiKey: process.env.JSONDB_API_KEY || "",
|
|
956
943
|
project: process.env.JSONDB_PROJECT || process.env.JSONDB_NAMESPACE || "v1",
|
|
957
|
-
baseUrl: (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(
|
|
958
|
-
/\/$/,
|
|
959
|
-
""
|
|
960
|
-
)
|
|
944
|
+
baseUrl: (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(/\/$/, "")
|
|
961
945
|
};
|
|
962
946
|
}
|
|
963
947
|
async function webhookFetch(url, apiKey, method = "GET", body) {
|
|
@@ -982,11 +966,7 @@ async function webhookFetch(url, apiKey, method = "GET", body) {
|
|
|
982
966
|
if (res.status === 204) return { ok: true };
|
|
983
967
|
return res.json();
|
|
984
968
|
}
|
|
985
|
-
var webhookEventEnum = import_zod5.z.enum([
|
|
986
|
-
"document.created",
|
|
987
|
-
"document.updated",
|
|
988
|
-
"document.deleted"
|
|
989
|
-
]);
|
|
969
|
+
var webhookEventEnum = import_zod5.z.enum(["document.created", "document.updated", "document.deleted"]);
|
|
990
970
|
function registerWebhookTools(server, _db) {
|
|
991
971
|
server.tool(
|
|
992
972
|
"create_webhook",
|
|
@@ -1035,10 +1015,7 @@ function registerWebhookTools(server, _db) {
|
|
|
1035
1015
|
async ({ collection }) => {
|
|
1036
1016
|
try {
|
|
1037
1017
|
const { apiKey, project, baseUrl } = resolveEnv2();
|
|
1038
|
-
const data = await webhookFetch(
|
|
1039
|
-
`${baseUrl}/${project}/${collection}/_webhooks`,
|
|
1040
|
-
apiKey
|
|
1041
|
-
);
|
|
1018
|
+
const data = await webhookFetch(`${baseUrl}/${project}/${collection}/_webhooks`, apiKey);
|
|
1042
1019
|
return success5(data);
|
|
1043
1020
|
} catch (err) {
|
|
1044
1021
|
const e = err;
|
|
@@ -1093,7 +1070,14 @@ function registerWebhookTools(server, _db) {
|
|
|
1093
1070
|
description: import_zod5.z.string().optional().describe("New description"),
|
|
1094
1071
|
status: import_zod5.z.enum(["active", "disabled"]).optional().describe("Set to 'disabled' to pause delivery, 'active' to resume")
|
|
1095
1072
|
},
|
|
1096
|
-
async ({
|
|
1073
|
+
async ({
|
|
1074
|
+
collection,
|
|
1075
|
+
webhookId,
|
|
1076
|
+
url: webhookUrl,
|
|
1077
|
+
events,
|
|
1078
|
+
description,
|
|
1079
|
+
status: webhookStatus
|
|
1080
|
+
}) => {
|
|
1097
1081
|
try {
|
|
1098
1082
|
const { apiKey, project, baseUrl } = resolveEnv2();
|
|
1099
1083
|
const body = {};
|
|
@@ -1194,12 +1178,12 @@ function registerWebhookTools(server, _db) {
|
|
|
1194
1178
|
}
|
|
1195
1179
|
|
|
1196
1180
|
// src/resources/collections.ts
|
|
1181
|
+
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
1197
1182
|
function registerCollectionResources(server, db) {
|
|
1198
1183
|
server.resource(
|
|
1199
1184
|
"collections-list",
|
|
1200
1185
|
"jsondb://collections",
|
|
1201
1186
|
{
|
|
1202
|
-
name: "jsondb.cloud Collections",
|
|
1203
1187
|
description: "List of all collections in the current project. Use this to discover what data is available.",
|
|
1204
1188
|
mimeType: "application/json"
|
|
1205
1189
|
},
|
|
@@ -1207,7 +1191,10 @@ function registerCollectionResources(server, db) {
|
|
|
1207
1191
|
try {
|
|
1208
1192
|
const apiKey = process.env.JSONDB_API_KEY || "";
|
|
1209
1193
|
const project = process.env.JSONDB_PROJECT || process.env.JSONDB_NAMESPACE || "v1";
|
|
1210
|
-
const baseUrl = (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(
|
|
1194
|
+
const baseUrl = (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(
|
|
1195
|
+
/\/$/,
|
|
1196
|
+
""
|
|
1197
|
+
);
|
|
1211
1198
|
const res = await fetch(`${baseUrl}/${project}`, {
|
|
1212
1199
|
headers: {
|
|
1213
1200
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -1246,11 +1233,7 @@ function registerCollectionResources(server, db) {
|
|
|
1246
1233
|
{
|
|
1247
1234
|
uri: "jsondb://collections",
|
|
1248
1235
|
mimeType: "application/json",
|
|
1249
|
-
text: JSON.stringify(
|
|
1250
|
-
{ error: e.message || "Failed to fetch collections" },
|
|
1251
|
-
null,
|
|
1252
|
-
2
|
|
1253
|
-
)
|
|
1236
|
+
text: JSON.stringify({ error: e.message || "Failed to fetch collections" }, null, 2)
|
|
1254
1237
|
}
|
|
1255
1238
|
]
|
|
1256
1239
|
};
|
|
@@ -1259,14 +1242,13 @@ function registerCollectionResources(server, db) {
|
|
|
1259
1242
|
);
|
|
1260
1243
|
server.resource(
|
|
1261
1244
|
"collection-schema",
|
|
1262
|
-
"jsondb://collections/{collection}/schema",
|
|
1245
|
+
new import_mcp.ResourceTemplate("jsondb://collections/{collection}/schema", { list: void 0 }),
|
|
1263
1246
|
{
|
|
1264
|
-
name: "Collection Schema",
|
|
1265
1247
|
description: "JSON Schema for a specific collection (if set). Helps AI agents understand the expected document structure.",
|
|
1266
1248
|
mimeType: "application/json"
|
|
1267
1249
|
},
|
|
1268
|
-
async (uri,
|
|
1269
|
-
const collection = typeof
|
|
1250
|
+
async (uri, variables) => {
|
|
1251
|
+
const collection = typeof variables.collection === "string" ? variables.collection : String(variables.collection);
|
|
1270
1252
|
try {
|
|
1271
1253
|
const coll = db.collection(collection);
|
|
1272
1254
|
const schema = await coll.getSchema();
|
|
@@ -1322,7 +1304,7 @@ async function main() {
|
|
|
1322
1304
|
project,
|
|
1323
1305
|
baseUrl
|
|
1324
1306
|
});
|
|
1325
|
-
const server = new
|
|
1307
|
+
const server = new import_mcp2.McpServer({
|
|
1326
1308
|
name: "jsondb-cloud",
|
|
1327
1309
|
version: "1.0.0"
|
|
1328
1310
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { JsonDB } from "@jsondb-cloud/client";
|
|
5
5
|
|
|
@@ -28,9 +28,7 @@ function registerDocumentTools(server, db) {
|
|
|
28
28
|
{
|
|
29
29
|
collection: z.string().describe("The collection name (e.g., 'users', 'posts', 'settings')"),
|
|
30
30
|
data: z.record(z.string(), z.any()).describe("The JSON document to store. Can contain any valid JSON."),
|
|
31
|
-
id: z.string().optional().describe(
|
|
32
|
-
"Optional custom document ID. If not provided, an ID is auto-generated."
|
|
33
|
-
)
|
|
31
|
+
id: z.string().optional().describe("Optional custom document ID. If not provided, an ID is auto-generated.")
|
|
34
32
|
},
|
|
35
33
|
async ({ collection, data, id }) => {
|
|
36
34
|
try {
|
|
@@ -105,9 +103,7 @@ function registerDocumentTools(server, db) {
|
|
|
105
103
|
filter: z.record(z.string(), z.any()).optional().describe(
|
|
106
104
|
"Filter criteria. Keys are field names, values are match values. Use {field: {$gt: N}} for comparisons."
|
|
107
105
|
),
|
|
108
|
-
sort: z.string().optional().describe(
|
|
109
|
-
"Field to sort by. Prefix with '-' for descending (e.g., '-$createdAt')"
|
|
110
|
-
),
|
|
106
|
+
sort: z.string().optional().describe("Field to sort by. Prefix with '-' for descending (e.g., '-$createdAt')"),
|
|
111
107
|
limit: z.number().optional().describe("Max documents to return (default: 20, max: 100)"),
|
|
112
108
|
offset: z.number().optional().describe("Number of documents to skip for pagination"),
|
|
113
109
|
select: z.array(z.string()).optional().describe(
|
|
@@ -141,9 +137,7 @@ function registerDocumentTools(server, db) {
|
|
|
141
137
|
{
|
|
142
138
|
collection: z.string().describe("The collection name"),
|
|
143
139
|
id: z.string().describe("The document ID to replace"),
|
|
144
|
-
data: z.record(z.string(), z.any()).describe(
|
|
145
|
-
"The complete new document data. This replaces all existing fields."
|
|
146
|
-
)
|
|
140
|
+
data: z.record(z.string(), z.any()).describe("The complete new document data. This replaces all existing fields.")
|
|
147
141
|
},
|
|
148
142
|
async ({ collection, id, data }) => {
|
|
149
143
|
try {
|
|
@@ -377,7 +371,10 @@ function registerCollectionTools(server, db) {
|
|
|
377
371
|
try {
|
|
378
372
|
const apiKey = process.env.JSONDB_API_KEY || "";
|
|
379
373
|
const project = process.env.JSONDB_PROJECT || process.env.JSONDB_NAMESPACE || "v1";
|
|
380
|
-
const baseUrl = (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(
|
|
374
|
+
const baseUrl = (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(
|
|
375
|
+
/\/$/,
|
|
376
|
+
""
|
|
377
|
+
);
|
|
381
378
|
const res = await fetch(`${baseUrl}/${project}`, {
|
|
382
379
|
headers: {
|
|
383
380
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -386,7 +383,10 @@ function registerCollectionTools(server, db) {
|
|
|
386
383
|
});
|
|
387
384
|
if (!res.ok) {
|
|
388
385
|
const body = await res.json().catch(() => ({}));
|
|
389
|
-
throw {
|
|
386
|
+
throw {
|
|
387
|
+
status: res.status,
|
|
388
|
+
message: body.error || res.statusText
|
|
389
|
+
};
|
|
390
390
|
}
|
|
391
391
|
const data = await res.json();
|
|
392
392
|
return success2(data);
|
|
@@ -407,26 +407,12 @@ function registerCollectionTools(server, db) {
|
|
|
407
407
|
collection: z2.string().describe("The collection name"),
|
|
408
408
|
filters: z2.array(
|
|
409
409
|
z2.object({
|
|
410
|
-
field: z2.string().describe(
|
|
411
|
-
|
|
412
|
-
),
|
|
413
|
-
operator: z2.enum([
|
|
414
|
-
"eq",
|
|
415
|
-
"neq",
|
|
416
|
-
"gt",
|
|
417
|
-
"gte",
|
|
418
|
-
"lt",
|
|
419
|
-
"lte",
|
|
420
|
-
"contains",
|
|
421
|
-
"in",
|
|
422
|
-
"exists"
|
|
423
|
-
]).describe("Comparison operator"),
|
|
410
|
+
field: z2.string().describe("Field path (supports dot notation for nested fields)"),
|
|
411
|
+
operator: z2.enum(["eq", "neq", "gt", "gte", "lt", "lte", "contains", "in", "exists"]).describe("Comparison operator"),
|
|
424
412
|
value: z2.any().describe("Value to compare against")
|
|
425
413
|
})
|
|
426
414
|
).describe("Array of filter conditions (combined with AND logic)"),
|
|
427
|
-
sort: z2.string().optional().describe(
|
|
428
|
-
"Field to sort by. Prefix with '-' for descending (e.g., '-$createdAt')"
|
|
429
|
-
),
|
|
415
|
+
sort: z2.string().optional().describe("Field to sort by. Prefix with '-' for descending (e.g., '-$createdAt')"),
|
|
430
416
|
limit: z2.number().optional().describe("Max documents to return (default: 20, max: 100)"),
|
|
431
417
|
offset: z2.number().optional().describe("Number of documents to skip for pagination")
|
|
432
418
|
},
|
|
@@ -460,15 +446,16 @@ function registerCollectionTools(server, db) {
|
|
|
460
446
|
onConflict: z2.enum(["fail", "skip", "overwrite"]).optional().describe(
|
|
461
447
|
"How to handle ID conflicts: 'fail' (default) rejects the batch, 'skip' ignores duplicates, 'overwrite' replaces existing documents"
|
|
462
448
|
),
|
|
463
|
-
idField: z2.string().optional().describe(
|
|
464
|
-
"Field in each document to use as _id. If omitted, IDs are auto-generated."
|
|
465
|
-
)
|
|
449
|
+
idField: z2.string().optional().describe("Field in each document to use as _id. If omitted, IDs are auto-generated.")
|
|
466
450
|
},
|
|
467
451
|
async ({ collection, documents, onConflict, idField }) => {
|
|
468
452
|
try {
|
|
469
453
|
const apiKey = process.env.JSONDB_API_KEY || "";
|
|
470
454
|
const project = process.env.JSONDB_PROJECT || process.env.JSONDB_NAMESPACE || "v1";
|
|
471
|
-
const baseUrl = (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(
|
|
455
|
+
const baseUrl = (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(
|
|
456
|
+
/\/$/,
|
|
457
|
+
""
|
|
458
|
+
);
|
|
472
459
|
const params = new URLSearchParams();
|
|
473
460
|
if (onConflict) params.set("onConflict", onConflict);
|
|
474
461
|
if (idField) params.set("idField", idField);
|
|
@@ -530,7 +517,10 @@ function registerCollectionTools(server, db) {
|
|
|
530
517
|
try {
|
|
531
518
|
const apiKey = process.env.JSONDB_API_KEY || "";
|
|
532
519
|
const project = process.env.JSONDB_PROJECT || process.env.JSONDB_NAMESPACE || "v1";
|
|
533
|
-
const baseUrl = (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(
|
|
520
|
+
const baseUrl = (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(
|
|
521
|
+
/\/$/,
|
|
522
|
+
""
|
|
523
|
+
);
|
|
534
524
|
const params = new URLSearchParams();
|
|
535
525
|
if (filter) {
|
|
536
526
|
for (const [field, value] of Object.entries(filter)) {
|
|
@@ -751,10 +741,7 @@ function resolveEnv() {
|
|
|
751
741
|
return {
|
|
752
742
|
apiKey: process.env.JSONDB_API_KEY || "",
|
|
753
743
|
project: process.env.JSONDB_PROJECT || process.env.JSONDB_NAMESPACE || "v1",
|
|
754
|
-
baseUrl: (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(
|
|
755
|
-
/\/$/,
|
|
756
|
-
""
|
|
757
|
-
)
|
|
744
|
+
baseUrl: (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(/\/$/, "")
|
|
758
745
|
};
|
|
759
746
|
}
|
|
760
747
|
async function versionFetch(url, apiKey, opts = {}) {
|
|
@@ -952,10 +939,7 @@ function resolveEnv2() {
|
|
|
952
939
|
return {
|
|
953
940
|
apiKey: process.env.JSONDB_API_KEY || "",
|
|
954
941
|
project: process.env.JSONDB_PROJECT || process.env.JSONDB_NAMESPACE || "v1",
|
|
955
|
-
baseUrl: (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(
|
|
956
|
-
/\/$/,
|
|
957
|
-
""
|
|
958
|
-
)
|
|
942
|
+
baseUrl: (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(/\/$/, "")
|
|
959
943
|
};
|
|
960
944
|
}
|
|
961
945
|
async function webhookFetch(url, apiKey, method = "GET", body) {
|
|
@@ -980,11 +964,7 @@ async function webhookFetch(url, apiKey, method = "GET", body) {
|
|
|
980
964
|
if (res.status === 204) return { ok: true };
|
|
981
965
|
return res.json();
|
|
982
966
|
}
|
|
983
|
-
var webhookEventEnum = z5.enum([
|
|
984
|
-
"document.created",
|
|
985
|
-
"document.updated",
|
|
986
|
-
"document.deleted"
|
|
987
|
-
]);
|
|
967
|
+
var webhookEventEnum = z5.enum(["document.created", "document.updated", "document.deleted"]);
|
|
988
968
|
function registerWebhookTools(server, _db) {
|
|
989
969
|
server.tool(
|
|
990
970
|
"create_webhook",
|
|
@@ -1033,10 +1013,7 @@ function registerWebhookTools(server, _db) {
|
|
|
1033
1013
|
async ({ collection }) => {
|
|
1034
1014
|
try {
|
|
1035
1015
|
const { apiKey, project, baseUrl } = resolveEnv2();
|
|
1036
|
-
const data = await webhookFetch(
|
|
1037
|
-
`${baseUrl}/${project}/${collection}/_webhooks`,
|
|
1038
|
-
apiKey
|
|
1039
|
-
);
|
|
1016
|
+
const data = await webhookFetch(`${baseUrl}/${project}/${collection}/_webhooks`, apiKey);
|
|
1040
1017
|
return success5(data);
|
|
1041
1018
|
} catch (err) {
|
|
1042
1019
|
const e = err;
|
|
@@ -1091,7 +1068,14 @@ function registerWebhookTools(server, _db) {
|
|
|
1091
1068
|
description: z5.string().optional().describe("New description"),
|
|
1092
1069
|
status: z5.enum(["active", "disabled"]).optional().describe("Set to 'disabled' to pause delivery, 'active' to resume")
|
|
1093
1070
|
},
|
|
1094
|
-
async ({
|
|
1071
|
+
async ({
|
|
1072
|
+
collection,
|
|
1073
|
+
webhookId,
|
|
1074
|
+
url: webhookUrl,
|
|
1075
|
+
events,
|
|
1076
|
+
description,
|
|
1077
|
+
status: webhookStatus
|
|
1078
|
+
}) => {
|
|
1095
1079
|
try {
|
|
1096
1080
|
const { apiKey, project, baseUrl } = resolveEnv2();
|
|
1097
1081
|
const body = {};
|
|
@@ -1192,12 +1176,12 @@ function registerWebhookTools(server, _db) {
|
|
|
1192
1176
|
}
|
|
1193
1177
|
|
|
1194
1178
|
// src/resources/collections.ts
|
|
1179
|
+
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
1195
1180
|
function registerCollectionResources(server, db) {
|
|
1196
1181
|
server.resource(
|
|
1197
1182
|
"collections-list",
|
|
1198
1183
|
"jsondb://collections",
|
|
1199
1184
|
{
|
|
1200
|
-
name: "jsondb.cloud Collections",
|
|
1201
1185
|
description: "List of all collections in the current project. Use this to discover what data is available.",
|
|
1202
1186
|
mimeType: "application/json"
|
|
1203
1187
|
},
|
|
@@ -1205,7 +1189,10 @@ function registerCollectionResources(server, db) {
|
|
|
1205
1189
|
try {
|
|
1206
1190
|
const apiKey = process.env.JSONDB_API_KEY || "";
|
|
1207
1191
|
const project = process.env.JSONDB_PROJECT || process.env.JSONDB_NAMESPACE || "v1";
|
|
1208
|
-
const baseUrl = (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(
|
|
1192
|
+
const baseUrl = (process.env.JSONDB_BASE_URL || "https://api.jsondb.cloud").replace(
|
|
1193
|
+
/\/$/,
|
|
1194
|
+
""
|
|
1195
|
+
);
|
|
1209
1196
|
const res = await fetch(`${baseUrl}/${project}`, {
|
|
1210
1197
|
headers: {
|
|
1211
1198
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -1244,11 +1231,7 @@ function registerCollectionResources(server, db) {
|
|
|
1244
1231
|
{
|
|
1245
1232
|
uri: "jsondb://collections",
|
|
1246
1233
|
mimeType: "application/json",
|
|
1247
|
-
text: JSON.stringify(
|
|
1248
|
-
{ error: e.message || "Failed to fetch collections" },
|
|
1249
|
-
null,
|
|
1250
|
-
2
|
|
1251
|
-
)
|
|
1234
|
+
text: JSON.stringify({ error: e.message || "Failed to fetch collections" }, null, 2)
|
|
1252
1235
|
}
|
|
1253
1236
|
]
|
|
1254
1237
|
};
|
|
@@ -1257,14 +1240,13 @@ function registerCollectionResources(server, db) {
|
|
|
1257
1240
|
);
|
|
1258
1241
|
server.resource(
|
|
1259
1242
|
"collection-schema",
|
|
1260
|
-
"jsondb://collections/{collection}/schema",
|
|
1243
|
+
new ResourceTemplate("jsondb://collections/{collection}/schema", { list: void 0 }),
|
|
1261
1244
|
{
|
|
1262
|
-
name: "Collection Schema",
|
|
1263
1245
|
description: "JSON Schema for a specific collection (if set). Helps AI agents understand the expected document structure.",
|
|
1264
1246
|
mimeType: "application/json"
|
|
1265
1247
|
},
|
|
1266
|
-
async (uri,
|
|
1267
|
-
const collection = typeof
|
|
1248
|
+
async (uri, variables) => {
|
|
1249
|
+
const collection = typeof variables.collection === "string" ? variables.collection : String(variables.collection);
|
|
1268
1250
|
try {
|
|
1269
1251
|
const coll = db.collection(collection);
|
|
1270
1252
|
const schema = await coll.getSchema();
|
|
@@ -1320,7 +1302,7 @@ async function main() {
|
|
|
1320
1302
|
project,
|
|
1321
1303
|
baseUrl
|
|
1322
1304
|
});
|
|
1323
|
-
const server = new
|
|
1305
|
+
const server = new McpServer2({
|
|
1324
1306
|
name: "jsondb-cloud",
|
|
1325
1307
|
version: "1.0.0"
|
|
1326
1308
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsondb-cloud/mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"description": "MCP (Model Context Protocol) server for jsondb.cloud — lets AI agents interact with your JSON database",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -20,17 +20,26 @@
|
|
|
20
20
|
],
|
|
21
21
|
"scripts": {
|
|
22
22
|
"build": "tsup src/index.ts --format cjs,esm --clean",
|
|
23
|
-
"dev": "tsup src/index.ts --format cjs,esm --watch"
|
|
23
|
+
"dev": "tsup src/index.ts --format cjs,esm --watch",
|
|
24
|
+
"lint": "eslint src/",
|
|
25
|
+
"format:check": "prettier --check .",
|
|
26
|
+
"test": "vitest run"
|
|
24
27
|
},
|
|
25
28
|
"dependencies": {
|
|
26
|
-
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
27
29
|
"@jsondb-cloud/client": "*",
|
|
30
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
28
31
|
"zod": "^3.23.0"
|
|
29
32
|
},
|
|
30
33
|
"devDependencies": {
|
|
34
|
+
"@types/node": "^20.0.0",
|
|
35
|
+
"@typescript-eslint/eslint-plugin": "^8.56.1",
|
|
36
|
+
"@typescript-eslint/parser": "^8.56.1",
|
|
37
|
+
"eslint": "^10.0.2",
|
|
38
|
+
"eslint-config-prettier": "^10.1.8",
|
|
39
|
+
"prettier": "^3.8.1",
|
|
31
40
|
"tsup": "^8.0.0",
|
|
32
41
|
"typescript": "^5.4.0",
|
|
33
|
-
"
|
|
42
|
+
"vitest": "^4.0.18"
|
|
34
43
|
},
|
|
35
44
|
"keywords": [
|
|
36
45
|
"jsondb",
|
|
@@ -48,6 +57,6 @@
|
|
|
48
57
|
"url": "git+https://github.com/JsonDBCloud/mcp.git"
|
|
49
58
|
},
|
|
50
59
|
"engines": {
|
|
51
|
-
"node": ">=
|
|
60
|
+
"node": ">=20.19.0"
|
|
52
61
|
}
|
|
53
62
|
}
|