@firecms/mcp-server 3.3.0-canary.451aa49 → 3.3.0-canary.7e3431b
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 +86 -7
- package/dist/api-client.d.ts +70 -0
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +207 -3
- package/dist/api-client.js.map +1 -1
- package/dist/resources/project.d.ts +1 -0
- package/dist/resources/project.d.ts.map +1 -1
- package/dist/resources/project.js +55 -0
- package/dist/resources/project.js.map +1 -1
- package/dist/server.d.ts +12 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +23 -2
- package/dist/server.js.map +1 -1
- package/dist/tools/collection-schemas.d.ts +8 -0
- package/dist/tools/collection-schemas.d.ts.map +1 -0
- package/dist/tools/collection-schemas.js +278 -0
- package/dist/tools/collection-schemas.js.map +1 -0
- package/dist/tools/import.d.ts +8 -0
- package/dist/tools/import.d.ts.map +1 -0
- package/dist/tools/import.js +57 -0
- package/dist/tools/import.js.map +1 -0
- package/dist/tools/project-config.d.ts +8 -0
- package/dist/tools/project-config.d.ts.map +1 -0
- package/dist/tools/project-config.js +174 -0
- package/dist/tools/project-config.js.map +1 -0
- package/dist/tools/users.d.ts +2 -0
- package/dist/tools/users.d.ts.map +1 -1
- package/dist/tools/users.js +10 -3
- package/dist/tools/users.js.map +1 -1
- package/package.json +6 -6
- package/src/api-client.ts +242 -3
- package/src/resources/project.ts +69 -0
- package/src/server.ts +26 -2
- package/src/tools/collection-schemas.ts +316 -0
- package/src/tools/import.ts +65 -0
- package/src/tools/project-config.ts +202 -0
- package/src/tools/users.ts +10 -3
- package/LICENSE +0 -6
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# @firecms/mcp-server
|
|
2
2
|
|
|
3
|
-
MCP server for [FireCMS Cloud](https://firecms.co). Lets AI assistants manage your CMS — browse data, create/update/delete documents, manage users, and generate collection schemas with AI.
|
|
3
|
+
MCP server for [FireCMS Cloud](https://firecms.co). Lets AI assistants manage your CMS — browse data, create/update/delete documents, manage collection schemas and properties, configure project settings, manage users, and generate collection schemas with AI.
|
|
4
|
+
|
|
5
|
+
> **Admin-only**: All write operations require the authenticated user to have the `admin` role on the target project. Read operations are available to any authenticated project member.
|
|
4
6
|
|
|
5
7
|
## Setup
|
|
6
8
|
|
|
@@ -23,6 +25,19 @@ Add to `claude_desktop_config.json`:
|
|
|
23
25
|
}
|
|
24
26
|
```
|
|
25
27
|
|
|
28
|
+
Or use npx (no build required):
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"mcpServers": {
|
|
33
|
+
"firecms": {
|
|
34
|
+
"command": "npx",
|
|
35
|
+
"args": ["@firecms/mcp-server"]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
26
41
|
Use `firecms_login` to sign in when prompted. Tokens shared with FireCMS CLI (`~/.firecms/tokens.json`).
|
|
27
42
|
|
|
28
43
|
## Tools
|
|
@@ -34,16 +49,47 @@ Use `firecms_login` to sign in when prompted. Tokens shared with FireCMS CLI (`~
|
|
|
34
49
|
| `firecms_logout` | Sign out |
|
|
35
50
|
| `firecms_get_current_user` | Show current user |
|
|
36
51
|
|
|
37
|
-
### Projects &
|
|
52
|
+
### Projects & Root Collections
|
|
38
53
|
| Tool | Description |
|
|
39
54
|
|------|-------------|
|
|
40
55
|
| `list_projects` | List FireCMS Cloud projects |
|
|
41
56
|
| `get_root_collections` | List Firestore root collections |
|
|
57
|
+
|
|
58
|
+
### Project Configuration 🔒
|
|
59
|
+
| Tool | Description |
|
|
60
|
+
|------|-------------|
|
|
61
|
+
| `get_project_config` | Get full project config (name, colors, plan, features) |
|
|
62
|
+
| `update_project_name` | Rename a project |
|
|
63
|
+
| `update_project_colors` | Update primary/secondary brand colors |
|
|
64
|
+
| `update_default_locale` | Change the default locale |
|
|
65
|
+
| `toggle_text_search` | Enable/disable local text search |
|
|
66
|
+
| `toggle_entity_history` | Enable/disable entity history tracking |
|
|
67
|
+
|
|
68
|
+
### Users 🔒
|
|
69
|
+
| Tool | Description |
|
|
70
|
+
|------|-------------|
|
|
42
71
|
| `list_users` | List project users and roles |
|
|
43
72
|
| `add_user` | Invite a user |
|
|
44
73
|
| `update_user_roles` | Change user roles |
|
|
45
74
|
| `remove_user` | Remove a user |
|
|
46
75
|
|
|
76
|
+
### Collection Schemas 🔒
|
|
77
|
+
| Tool | Description |
|
|
78
|
+
|------|-------------|
|
|
79
|
+
| `list_collection_schemas` | List all persisted collection schemas |
|
|
80
|
+
| `get_collection_schema` | Get full schema for a collection |
|
|
81
|
+
| `save_collection_schema` | Create or replace a collection schema |
|
|
82
|
+
| `update_collection_schema` | Partially update a collection schema |
|
|
83
|
+
| `delete_collection_schema` | Delete a collection schema (data untouched) |
|
|
84
|
+
| `save_property` | Add or update a single property in a collection |
|
|
85
|
+
| `delete_property` | Remove a property from a collection schema |
|
|
86
|
+
|
|
87
|
+
### AI Schema Generation
|
|
88
|
+
| Tool | Description |
|
|
89
|
+
|------|-------------|
|
|
90
|
+
| `generate_collection` | AI-generate a collection schema from a prompt |
|
|
91
|
+
| `modify_collection` | AI-modify an existing schema from a prompt |
|
|
92
|
+
|
|
47
93
|
### Documents (Firestore CRUD)
|
|
48
94
|
| Tool | Description |
|
|
49
95
|
|------|-------------|
|
|
@@ -54,16 +100,49 @@ Use `firecms_login` to sign in when prompted. Tokens shared with FireCMS CLI (`~
|
|
|
54
100
|
| `delete_document` | Delete a document |
|
|
55
101
|
| `count_documents` | Count documents in a collection |
|
|
56
102
|
|
|
57
|
-
###
|
|
103
|
+
### Data Import & Export
|
|
58
104
|
| Tool | Description |
|
|
59
105
|
|------|-------------|
|
|
60
|
-
| `generate_collection` | AI-generate a collection schema |
|
|
61
|
-
| `modify_collection` | AI-modify an existing schema |
|
|
62
106
|
| `export_collection` | Export collection data as JSON |
|
|
107
|
+
| `import_documents` 🔒 | Bulk import documents (max 500/call) |
|
|
108
|
+
|
|
109
|
+
> 🔒 = Admin-only operation
|
|
63
110
|
|
|
64
111
|
## Resources
|
|
65
112
|
|
|
66
113
|
| URI | Description |
|
|
67
114
|
|-----|-------------|
|
|
68
|
-
| `firecms://projects/{id}/collections` |
|
|
69
|
-
| `firecms://projects/{id}/users` | Project users |
|
|
115
|
+
| `firecms://projects/{id}/collections` | Firestore root-level collections |
|
|
116
|
+
| `firecms://projects/{id}/users` | Project users and roles |
|
|
117
|
+
| `firecms://projects/{id}/schemas` | All collection schemas (full config tree) |
|
|
118
|
+
| `firecms://projects/{id}/config` | Project configuration snapshot |
|
|
119
|
+
|
|
120
|
+
## Example Workflows
|
|
121
|
+
|
|
122
|
+
### Create a collection from scratch
|
|
123
|
+
```
|
|
124
|
+
1. firecms_login → authenticate
|
|
125
|
+
2. list_projects → pick your project
|
|
126
|
+
3. save_collection_schema → define the schema
|
|
127
|
+
4. import_documents → seed with initial data
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### AI-assisted schema creation
|
|
131
|
+
```
|
|
132
|
+
1. generate_collection → describe what you want in natural language
|
|
133
|
+
2. save_collection_schema → persist the generated schema
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Modify an existing collection
|
|
137
|
+
```
|
|
138
|
+
1. get_collection_schema → read current schema
|
|
139
|
+
2. save_property → add/update individual fields
|
|
140
|
+
3. update_collection_schema → update display settings
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Security
|
|
144
|
+
|
|
145
|
+
- **Authentication**: Google OAuth via browser, same as `firecms login` CLI
|
|
146
|
+
- **Authorization**: Write operations enforce admin role check per project
|
|
147
|
+
- **Token storage**: `~/.firecms/tokens.json` (shared with CLI)
|
|
148
|
+
- **Admin cache**: Role checks are cached for 5 minutes per project
|
package/dist/api-client.d.ts
CHANGED
|
@@ -1,18 +1,71 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Typed HTTP client for the FireCMS Cloud backend REST API.
|
|
3
3
|
* Uses the same tokens as the FireCMS CLI (from ~/.firecms/tokens.json).
|
|
4
|
+
*
|
|
5
|
+
* Architecture notes:
|
|
6
|
+
* - Collection schemas are stored in the CLIENT's Firestore at
|
|
7
|
+
* `__FIRECMS/config/collections/{collectionId}`. We use the existing
|
|
8
|
+
* document CRUD proxy endpoints to read/write them.
|
|
9
|
+
* - Project config (name, colors, locale) is stored in the SaaS backend's
|
|
10
|
+
* Firestore at `projects/{projectId}`. Dedicated `/config` endpoints
|
|
11
|
+
* handle this.
|
|
12
|
+
* - Bulk import uses the admin `batch_write` endpoint.
|
|
4
13
|
*/
|
|
5
14
|
export declare class FireCMSApiClient {
|
|
6
15
|
private client;
|
|
16
|
+
private adminCache;
|
|
17
|
+
/** Cache admin checks for 5 minutes */
|
|
18
|
+
private static ADMIN_CACHE_TTL_MS;
|
|
7
19
|
constructor();
|
|
8
20
|
private authHeaders;
|
|
9
21
|
private request;
|
|
22
|
+
/**
|
|
23
|
+
* Verify that the current user is an admin of the given project.
|
|
24
|
+
* Results are cached for 5 minutes per project.
|
|
25
|
+
* @throws Error if the user is not an admin.
|
|
26
|
+
*/
|
|
27
|
+
assertAdmin(projectId: string): Promise<void>;
|
|
28
|
+
private notAdminError;
|
|
10
29
|
listProjects(): Promise<any>;
|
|
11
30
|
getRootCollections(projectId: string): Promise<any>;
|
|
31
|
+
getProjectConfig(projectId: string): Promise<any>;
|
|
32
|
+
updateProjectConfig(projectId: string, data: Record<string, any>): Promise<any>;
|
|
12
33
|
listUsers(projectId: string): Promise<any>;
|
|
13
34
|
createUser(projectId: string, email: string, roles: string[]): Promise<any>;
|
|
14
35
|
updateUser(projectId: string, userId: string, roles: string[]): Promise<any>;
|
|
15
36
|
deleteUser(projectId: string, userId: string): Promise<any>;
|
|
37
|
+
/**
|
|
38
|
+
* List all persisted collection schemas.
|
|
39
|
+
* Uses the document list endpoint to read from `__FIRECMS/config/collections`.
|
|
40
|
+
*/
|
|
41
|
+
listCollectionSchemas(projectId: string): Promise<any[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Get a single collection schema by its document ID.
|
|
44
|
+
*/
|
|
45
|
+
getCollectionSchema(projectId: string, collectionId: string): Promise<any>;
|
|
46
|
+
/**
|
|
47
|
+
* Create or fully replace a collection schema.
|
|
48
|
+
* Uses the document create/update endpoints to write to `__FIRECMS/config/collections/{id}`.
|
|
49
|
+
*/
|
|
50
|
+
saveCollectionSchema(projectId: string, collectionId: string, schema: Record<string, any>): Promise<any>;
|
|
51
|
+
/**
|
|
52
|
+
* Partially update an existing collection schema (merge).
|
|
53
|
+
*/
|
|
54
|
+
updateCollectionSchema(projectId: string, collectionId: string, data: Record<string, any>): Promise<any>;
|
|
55
|
+
/**
|
|
56
|
+
* Delete a collection schema.
|
|
57
|
+
*/
|
|
58
|
+
deleteCollectionSchema(projectId: string, collectionId: string): Promise<any>;
|
|
59
|
+
/**
|
|
60
|
+
* Add or update a single property within a collection schema.
|
|
61
|
+
* Reads the current schema, modifies the property, and writes back.
|
|
62
|
+
*/
|
|
63
|
+
saveProperty(projectId: string, collectionId: string, propertyKey: string, property: Record<string, any>, namespace?: string): Promise<any>;
|
|
64
|
+
/**
|
|
65
|
+
* Delete a property from a collection schema.
|
|
66
|
+
* Reads the current schema, removes the property, and writes back.
|
|
67
|
+
*/
|
|
68
|
+
deleteProperty(projectId: string, collectionId: string, propertyKey: string, namespace?: string): Promise<any>;
|
|
16
69
|
generateCollection(prompt: string, existingCollections?: any[], existingCollection?: any): Promise<any>;
|
|
17
70
|
listDocuments(projectId: string, body: {
|
|
18
71
|
path: string;
|
|
@@ -31,5 +84,22 @@ export declare class FireCMSApiClient {
|
|
|
31
84
|
updateDocument(projectId: string, path: string, documentId: string, data: Record<string, any>, databaseId?: string): Promise<any>;
|
|
32
85
|
deleteDocument(projectId: string, path: string, documentId: string, databaseId?: string): Promise<any>;
|
|
33
86
|
countDocuments(projectId: string, path: string, databaseId?: string): Promise<any>;
|
|
87
|
+
/**
|
|
88
|
+
* Bulk import documents into a collection using the admin batch_write endpoint.
|
|
89
|
+
* This writes directly to the client's Firestore via the delegated service account.
|
|
90
|
+
*/
|
|
91
|
+
importDocuments(projectId: string, body: {
|
|
92
|
+
path: string;
|
|
93
|
+
documents: Array<{
|
|
94
|
+
id?: string;
|
|
95
|
+
data: Record<string, any>;
|
|
96
|
+
}>;
|
|
97
|
+
merge?: boolean;
|
|
98
|
+
databaseId?: string;
|
|
99
|
+
}): Promise<any>;
|
|
100
|
+
/**
|
|
101
|
+
* Generate a random Firestore-style document ID.
|
|
102
|
+
*/
|
|
103
|
+
private generateId;
|
|
34
104
|
}
|
|
35
105
|
//# sourceMappingURL=api-client.d.ts.map
|
package/dist/api-client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
|
package/dist/api-client.js
CHANGED
|
@@ -1,12 +1,30 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
|
-
import { getValidTokens } from "./auth.js";
|
|
2
|
+
import { getValidTokens, getCurrentUserEmail } from "./auth.js";
|
|
3
3
|
const API_URL = "https://api.firecms.co";
|
|
4
|
+
/**
|
|
5
|
+
* The Firestore path where collection schemas are persisted in the CLIENT's
|
|
6
|
+
* Firestore (not the SaaS backend). The backend's document CRUD endpoints
|
|
7
|
+
* proxy operations into the client's project Firestore.
|
|
8
|
+
*/
|
|
9
|
+
const COLLECTIONS_CONFIG_PATH = "__FIRECMS/config/collections";
|
|
4
10
|
/**
|
|
5
11
|
* Typed HTTP client for the FireCMS Cloud backend REST API.
|
|
6
12
|
* Uses the same tokens as the FireCMS CLI (from ~/.firecms/tokens.json).
|
|
13
|
+
*
|
|
14
|
+
* Architecture notes:
|
|
15
|
+
* - Collection schemas are stored in the CLIENT's Firestore at
|
|
16
|
+
* `__FIRECMS/config/collections/{collectionId}`. We use the existing
|
|
17
|
+
* document CRUD proxy endpoints to read/write them.
|
|
18
|
+
* - Project config (name, colors, locale) is stored in the SaaS backend's
|
|
19
|
+
* Firestore at `projects/{projectId}`. Dedicated `/config` endpoints
|
|
20
|
+
* handle this.
|
|
21
|
+
* - Bulk import uses the admin `batch_write` endpoint.
|
|
7
22
|
*/
|
|
8
23
|
export class FireCMSApiClient {
|
|
9
24
|
client;
|
|
25
|
+
adminCache = new Map();
|
|
26
|
+
/** Cache admin checks for 5 minutes */
|
|
27
|
+
static ADMIN_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
10
28
|
constructor() {
|
|
11
29
|
this.client = axios.create({
|
|
12
30
|
baseURL: API_URL,
|
|
@@ -32,6 +50,34 @@ export class FireCMSApiClient {
|
|
|
32
50
|
});
|
|
33
51
|
return response.data;
|
|
34
52
|
}
|
|
53
|
+
// ─── Admin guard ──────────────────────────────────────────
|
|
54
|
+
/**
|
|
55
|
+
* Verify that the current user is an admin of the given project.
|
|
56
|
+
* Results are cached for 5 minutes per project.
|
|
57
|
+
* @throws Error if the user is not an admin.
|
|
58
|
+
*/
|
|
59
|
+
async assertAdmin(projectId) {
|
|
60
|
+
const cached = this.adminCache.get(projectId);
|
|
61
|
+
if (cached && (Date.now() - cached.checkedAt) < FireCMSApiClient.ADMIN_CACHE_TTL_MS) {
|
|
62
|
+
if (!cached.isAdmin) {
|
|
63
|
+
throw this.notAdminError(projectId);
|
|
64
|
+
}
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const users = await this.listUsers(projectId);
|
|
68
|
+
const currentEmail = getCurrentUserEmail();
|
|
69
|
+
const me = users.find((u) => u.email?.toLowerCase() === currentEmail?.toLowerCase());
|
|
70
|
+
const isAdmin = me?.roles?.includes("admin") ?? false;
|
|
71
|
+
this.adminCache.set(projectId, { isAdmin, checkedAt: Date.now() });
|
|
72
|
+
if (!isAdmin) {
|
|
73
|
+
throw this.notAdminError(projectId);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
notAdminError(projectId) {
|
|
77
|
+
const email = getCurrentUserEmail() ?? "unknown";
|
|
78
|
+
return new Error(`Access denied: ${email} is not an admin of project "${projectId}". ` +
|
|
79
|
+
`The FireCMS MCP server requires admin access for this operation.`);
|
|
80
|
+
}
|
|
35
81
|
// ─── Projects ──────────────────────────────────────────
|
|
36
82
|
async listProjects() {
|
|
37
83
|
return this.request({ method: "GET", url: "/projects" });
|
|
@@ -42,6 +88,20 @@ export class FireCMSApiClient {
|
|
|
42
88
|
url: `/projects/${projectId}/firestore_root_collections`,
|
|
43
89
|
});
|
|
44
90
|
}
|
|
91
|
+
// ─── Project Config (SaaS backend Firestore) ──────────
|
|
92
|
+
async getProjectConfig(projectId) {
|
|
93
|
+
return this.request({
|
|
94
|
+
method: "GET",
|
|
95
|
+
url: `/projects/${projectId}/config`,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
async updateProjectConfig(projectId, data) {
|
|
99
|
+
return this.request({
|
|
100
|
+
method: "PATCH",
|
|
101
|
+
url: `/projects/${projectId}/config`,
|
|
102
|
+
data,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
45
105
|
// ─── Users ─────────────────────────────────────────────
|
|
46
106
|
async listUsers(projectId) {
|
|
47
107
|
return this.request({ method: "GET", url: `/projects/${projectId}/users` });
|
|
@@ -63,7 +123,118 @@ export class FireCMSApiClient {
|
|
|
63
123
|
async deleteUser(projectId, userId) {
|
|
64
124
|
return this.request({ method: "DELETE", url: `/projects/${projectId}/users/${userId}` });
|
|
65
125
|
}
|
|
66
|
-
// ───
|
|
126
|
+
// ─── Collection Schemas (client Firestore via document proxy) ───────
|
|
127
|
+
/**
|
|
128
|
+
* List all persisted collection schemas.
|
|
129
|
+
* Uses the document list endpoint to read from `__FIRECMS/config/collections`.
|
|
130
|
+
*/
|
|
131
|
+
async listCollectionSchemas(projectId) {
|
|
132
|
+
const response = await this.request({
|
|
133
|
+
method: "POST",
|
|
134
|
+
url: `/projects/${projectId}/documents/list`,
|
|
135
|
+
data: { path: COLLECTIONS_CONFIG_PATH, limit: 500 },
|
|
136
|
+
});
|
|
137
|
+
// The document list endpoint returns { data: Document[] } or { documents: Document[] }
|
|
138
|
+
return response?.data ?? response?.documents ?? [];
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get a single collection schema by its document ID.
|
|
142
|
+
*/
|
|
143
|
+
async getCollectionSchema(projectId, collectionId) {
|
|
144
|
+
return this.request({
|
|
145
|
+
method: "POST",
|
|
146
|
+
url: `/projects/${projectId}/documents/get`,
|
|
147
|
+
data: { path: COLLECTIONS_CONFIG_PATH, documentId: collectionId },
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Create or fully replace a collection schema.
|
|
152
|
+
* Uses the document create/update endpoints to write to `__FIRECMS/config/collections/{id}`.
|
|
153
|
+
*/
|
|
154
|
+
async saveCollectionSchema(projectId, collectionId, schema) {
|
|
155
|
+
// Try update first (if doc exists), fall back to create
|
|
156
|
+
try {
|
|
157
|
+
return await this.request({
|
|
158
|
+
method: "POST",
|
|
159
|
+
url: `/projects/${projectId}/documents/update`,
|
|
160
|
+
data: {
|
|
161
|
+
path: COLLECTIONS_CONFIG_PATH,
|
|
162
|
+
documentId: collectionId,
|
|
163
|
+
data: { ...schema, id: collectionId },
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
// If the document doesn't exist, create it
|
|
169
|
+
if (error.response?.status === 404) {
|
|
170
|
+
return this.request({
|
|
171
|
+
method: "POST",
|
|
172
|
+
url: `/projects/${projectId}/documents/create`,
|
|
173
|
+
data: {
|
|
174
|
+
path: COLLECTIONS_CONFIG_PATH,
|
|
175
|
+
documentId: collectionId,
|
|
176
|
+
data: { ...schema, id: collectionId },
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
throw error;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Partially update an existing collection schema (merge).
|
|
185
|
+
*/
|
|
186
|
+
async updateCollectionSchema(projectId, collectionId, data) {
|
|
187
|
+
return this.request({
|
|
188
|
+
method: "POST",
|
|
189
|
+
url: `/projects/${projectId}/documents/update`,
|
|
190
|
+
data: {
|
|
191
|
+
path: COLLECTIONS_CONFIG_PATH,
|
|
192
|
+
documentId: collectionId,
|
|
193
|
+
data,
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Delete a collection schema.
|
|
199
|
+
*/
|
|
200
|
+
async deleteCollectionSchema(projectId, collectionId) {
|
|
201
|
+
return this.request({
|
|
202
|
+
method: "POST",
|
|
203
|
+
url: `/projects/${projectId}/documents/delete`,
|
|
204
|
+
data: {
|
|
205
|
+
path: COLLECTIONS_CONFIG_PATH,
|
|
206
|
+
documentId: collectionId,
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Add or update a single property within a collection schema.
|
|
212
|
+
* Reads the current schema, modifies the property, and writes back.
|
|
213
|
+
*/
|
|
214
|
+
async saveProperty(projectId, collectionId, propertyKey, property, namespace) {
|
|
215
|
+
// Get current schema
|
|
216
|
+
const current = await this.getCollectionSchema(projectId, collectionId);
|
|
217
|
+
const schemaData = current?.data ?? current ?? {};
|
|
218
|
+
// Build the properties map
|
|
219
|
+
const properties = schemaData.properties ?? {};
|
|
220
|
+
const key = namespace ? `${namespace}:${propertyKey}` : propertyKey;
|
|
221
|
+
properties[key] = property;
|
|
222
|
+
// Write back
|
|
223
|
+
return this.updateCollectionSchema(projectId, collectionId, { properties });
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Delete a property from a collection schema.
|
|
227
|
+
* Reads the current schema, removes the property, and writes back.
|
|
228
|
+
*/
|
|
229
|
+
async deleteProperty(projectId, collectionId, propertyKey, namespace) {
|
|
230
|
+
const current = await this.getCollectionSchema(projectId, collectionId);
|
|
231
|
+
const schemaData = current?.data ?? current ?? {};
|
|
232
|
+
const properties = schemaData.properties ?? {};
|
|
233
|
+
const key = namespace ? `${namespace}:${propertyKey}` : propertyKey;
|
|
234
|
+
delete properties[key];
|
|
235
|
+
return this.updateCollectionSchema(projectId, collectionId, { properties });
|
|
236
|
+
}
|
|
237
|
+
// ─── AI Collection Generation ──────────────────────────
|
|
67
238
|
async generateCollection(prompt, existingCollections = [], existingCollection) {
|
|
68
239
|
return this.request({
|
|
69
240
|
method: "POST",
|
|
@@ -71,7 +242,7 @@ export class FireCMSApiClient {
|
|
|
71
242
|
data: { prompt, existingCollections, existingCollection },
|
|
72
243
|
});
|
|
73
244
|
}
|
|
74
|
-
// ─── Documents (Firestore CRUD via backend)
|
|
245
|
+
// ─── Documents (Firestore CRUD via backend proxy) ──────
|
|
75
246
|
async listDocuments(projectId, body) {
|
|
76
247
|
return this.request({
|
|
77
248
|
method: "POST",
|
|
@@ -114,5 +285,38 @@ export class FireCMSApiClient {
|
|
|
114
285
|
data: { path, databaseId },
|
|
115
286
|
});
|
|
116
287
|
}
|
|
288
|
+
// ─── Data Import (admin batch_write) ────────────────────
|
|
289
|
+
/**
|
|
290
|
+
* Bulk import documents into a collection using the admin batch_write endpoint.
|
|
291
|
+
* This writes directly to the client's Firestore via the delegated service account.
|
|
292
|
+
*/
|
|
293
|
+
async importDocuments(projectId, body) {
|
|
294
|
+
// Transform documents into BatchOperation format expected by the backend
|
|
295
|
+
const operations = body.documents.map(doc => ({
|
|
296
|
+
type: (body.merge ? "update" : "set"),
|
|
297
|
+
path: body.path,
|
|
298
|
+
documentId: doc.id ?? this.generateId(),
|
|
299
|
+
data: doc.data,
|
|
300
|
+
}));
|
|
301
|
+
return this.request({
|
|
302
|
+
method: "POST",
|
|
303
|
+
url: `/projects/${projectId}/admin/documents/batch_write`,
|
|
304
|
+
data: {
|
|
305
|
+
operations,
|
|
306
|
+
databaseId: body.databaseId,
|
|
307
|
+
},
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Generate a random Firestore-style document ID.
|
|
312
|
+
*/
|
|
313
|
+
generateId() {
|
|
314
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
315
|
+
let id = "";
|
|
316
|
+
for (let i = 0; i < 20; i++) {
|
|
317
|
+
id += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
318
|
+
}
|
|
319
|
+
return id;
|
|
320
|
+
}
|
|
117
321
|
}
|
|
118
322
|
//# sourceMappingURL=api-client.js.map
|
package/dist/api-client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAA4C,MAAM,OAAO,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
import { FireCMSApiClient } from "../api-client.js";
|
|
3
3
|
/**
|
|
4
4
|
* Register MCP resources — read-only contextual data about FireCMS projects.
|
|
5
|
+
* Resources provide ambient context to the LLM without requiring explicit tool calls.
|
|
5
6
|
*/
|
|
6
7
|
export declare function registerProjectResources(server: McpServer, api: FireCMSApiClient): void;
|
|
7
8
|
//# sourceMappingURL=project.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../src/resources/project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAoB,MAAM,yCAAyC,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD
|
|
1
|
+
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../src/resources/project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAoB,MAAM,yCAAyC,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,gBAAgB,QAiIhF"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
/**
|
|
3
3
|
* Register MCP resources — read-only contextual data about FireCMS projects.
|
|
4
|
+
* Resources provide ambient context to the LLM without requiring explicit tool calls.
|
|
4
5
|
*/
|
|
5
6
|
export function registerProjectResources(server, api) {
|
|
7
|
+
// ─── Root collections ──────────────────────────────────
|
|
6
8
|
server.registerResource("project-collections", new ResourceTemplate("firecms://projects/{projectId}/collections", { list: undefined }), {
|
|
7
9
|
description: "Firestore root-level collections for a FireCMS project",
|
|
8
10
|
mimeType: "application/json",
|
|
@@ -28,6 +30,7 @@ export function registerProjectResources(server, api) {
|
|
|
28
30
|
};
|
|
29
31
|
}
|
|
30
32
|
});
|
|
33
|
+
// ─── Project users ─────────────────────────────────────
|
|
31
34
|
server.registerResource("project-users", new ResourceTemplate("firecms://projects/{projectId}/users", { list: undefined }), {
|
|
32
35
|
description: "Users with access to a FireCMS project",
|
|
33
36
|
mimeType: "application/json",
|
|
@@ -53,5 +56,57 @@ export function registerProjectResources(server, api) {
|
|
|
53
56
|
};
|
|
54
57
|
}
|
|
55
58
|
});
|
|
59
|
+
// ─── Collection schemas (full schema tree) ─────────────
|
|
60
|
+
server.registerResource("project-schemas", new ResourceTemplate("firecms://projects/{projectId}/schemas", { list: undefined }), {
|
|
61
|
+
description: "All persisted collection schemas for a FireCMS project — the full configuration tree including properties, validation rules, and display settings",
|
|
62
|
+
mimeType: "application/json",
|
|
63
|
+
}, async (uri, variables) => {
|
|
64
|
+
const projectId = variables.projectId;
|
|
65
|
+
try {
|
|
66
|
+
const schemas = await api.listCollectionSchemas(projectId);
|
|
67
|
+
return {
|
|
68
|
+
contents: [{
|
|
69
|
+
uri: uri.href,
|
|
70
|
+
mimeType: "application/json",
|
|
71
|
+
text: JSON.stringify(schemas, null, 2),
|
|
72
|
+
}],
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
return {
|
|
77
|
+
contents: [{
|
|
78
|
+
uri: uri.href,
|
|
79
|
+
mimeType: "application/json",
|
|
80
|
+
text: JSON.stringify({ error: error.message }),
|
|
81
|
+
}],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
// ─── Project configuration ─────────────────────────────
|
|
86
|
+
server.registerResource("project-config", new ResourceTemplate("firecms://projects/{projectId}/config", { list: undefined }), {
|
|
87
|
+
description: "Project configuration — name, colors, subscription plan, feature toggles, and locale settings",
|
|
88
|
+
mimeType: "application/json",
|
|
89
|
+
}, async (uri, variables) => {
|
|
90
|
+
const projectId = variables.projectId;
|
|
91
|
+
try {
|
|
92
|
+
const config = await api.getProjectConfig(projectId);
|
|
93
|
+
return {
|
|
94
|
+
contents: [{
|
|
95
|
+
uri: uri.href,
|
|
96
|
+
mimeType: "application/json",
|
|
97
|
+
text: JSON.stringify(config, null, 2),
|
|
98
|
+
}],
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
return {
|
|
103
|
+
contents: [{
|
|
104
|
+
uri: uri.href,
|
|
105
|
+
mimeType: "application/json",
|
|
106
|
+
text: JSON.stringify({ error: error.message }),
|
|
107
|
+
}],
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
});
|
|
56
111
|
}
|
|
57
112
|
//# sourceMappingURL=project.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/resources/project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAGtF
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/resources/project.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
/**
|
|
3
3
|
* Create and configure the FireCMS MCP server with all tools and resources.
|
|
4
|
+
*
|
|
5
|
+
* Tool categories:
|
|
6
|
+
* - Auth: Login/logout/current user
|
|
7
|
+
* - Projects: List projects, root collections
|
|
8
|
+
* - Project Config: Name, colors, locale, feature toggles (admin-only)
|
|
9
|
+
* - Users: User management (invite, roles, remove)
|
|
10
|
+
* - Collection Schemas: CRUD for collection configurations (admin-only)
|
|
11
|
+
* - AI Collections: AI-powered schema generation/modification
|
|
12
|
+
* - Documents: Firestore CRUD (list, get, create, update, delete, count)
|
|
13
|
+
* - Export: Data export as JSON
|
|
14
|
+
* - Import: Bulk data import (admin-only)
|
|
15
|
+
* - Resources: Read-only context (collections, users, schemas, config)
|
|
4
16
|
*/
|
|
5
17
|
export declare function createFireCMSMcpServer(): McpServer;
|
|
6
18
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAapE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,IAAI,SAAS,CAqClD"}
|