multisite-cms-mcp 1.0.13 → 1.0.16
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 +165 -9
- package/dist/index.js +31 -0
- package/dist/lib/api-client.d.ts +65 -0
- package/dist/lib/api-client.d.ts.map +1 -0
- package/dist/lib/api-client.js +157 -0
- package/dist/lib/credentials.d.ts +52 -0
- package/dist/lib/credentials.d.ts.map +1 -0
- package/dist/lib/credentials.js +196 -0
- package/dist/lib/device-flow.d.ts +14 -0
- package/dist/lib/device-flow.d.ts.map +1 -0
- package/dist/lib/device-flow.js +235 -0
- package/dist/tools/get-conversion-guide.d.ts.map +1 -1
- package/dist/tools/get-conversion-guide.js +55 -2
- package/dist/tools/get-example.d.ts.map +1 -1
- package/dist/tools/get-example.js +36 -14
- package/dist/tools/get-schema.d.ts.map +1 -1
- package/dist/tools/get-schema.js +148 -8
- package/dist/tools/get-tenant-schema.d.ts +12 -0
- package/dist/tools/get-tenant-schema.d.ts.map +1 -0
- package/dist/tools/get-tenant-schema.js +146 -0
- package/dist/tools/list-projects.d.ts +5 -0
- package/dist/tools/list-projects.d.ts.map +1 -0
- package/dist/tools/list-projects.js +101 -0
- package/package.json +1 -1
package/dist/tools/get-schema.js
CHANGED
|
@@ -84,6 +84,94 @@ Downloadable files and documents.
|
|
|
84
84
|
|
|
85
85
|
---
|
|
86
86
|
|
|
87
|
+
## Custom Collections
|
|
88
|
+
|
|
89
|
+
Custom collections are tenant-defined content types created in the CMS dashboard. Each custom collection has its own slug and user-defined fields.
|
|
90
|
+
|
|
91
|
+
**Token syntax:** Same as built-in collections
|
|
92
|
+
- \`{{fieldSlug}}\` for text, number, boolean, image, url, date, select fields
|
|
93
|
+
- \`{{{fieldSlug}}}\` for richText fields (MUST use triple braces)
|
|
94
|
+
|
|
95
|
+
**Built-in tokens for all custom collections:**
|
|
96
|
+
- \`{{slug}}\` - Item URL slug (if collection has slugs enabled)
|
|
97
|
+
- \`{{url}}\` - Full item URL (e.g., /services/my-service)
|
|
98
|
+
- \`{{publishedAt}}\` - Publish date (if collection has publish dates enabled)
|
|
99
|
+
|
|
100
|
+
**Example - A "Services" collection with fields: title, image, description (richText), price:**
|
|
101
|
+
\`\`\`html
|
|
102
|
+
<section class="services">
|
|
103
|
+
{{#each services}}
|
|
104
|
+
<div class="service-card">
|
|
105
|
+
<img src="{{image}}" alt="{{title}}">
|
|
106
|
+
<h2><a href="{{url}}">{{title}}</a></h2>
|
|
107
|
+
{{{description}}}
|
|
108
|
+
<p class="price">\${{price}}</p>
|
|
109
|
+
</div>
|
|
110
|
+
{{/each}}
|
|
111
|
+
</section>
|
|
112
|
+
\`\`\`
|
|
113
|
+
|
|
114
|
+
**Example - A "Testimonials" collection with fields: quote (richText), author, company, rating:**
|
|
115
|
+
\`\`\`html
|
|
116
|
+
<section class="testimonials">
|
|
117
|
+
{{#each testimonials limit=6}}
|
|
118
|
+
<blockquote class="testimonial">
|
|
119
|
+
{{{quote}}}
|
|
120
|
+
<cite>
|
|
121
|
+
<strong>{{author}}</strong>
|
|
122
|
+
{{#if company}}<span>{{company}}</span>{{/if}}
|
|
123
|
+
</cite>
|
|
124
|
+
</blockquote>
|
|
125
|
+
{{/each}}
|
|
126
|
+
</section>
|
|
127
|
+
\`\`\`
|
|
128
|
+
|
|
129
|
+
**To see exact fields for a tenant's custom collections, use the \`get_tenant_schema\` tool.**
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Custom Fields on Built-in Collections
|
|
134
|
+
|
|
135
|
+
Tenants can add custom fields to Blog Posts, Authors, Team Members, and Downloads. These custom fields work exactly like built-in fields.
|
|
136
|
+
|
|
137
|
+
**Token syntax:**
|
|
138
|
+
- \`{{customFieldSlug}}\` for text, number, boolean, image, url, date, select
|
|
139
|
+
- \`{{{customFieldSlug}}}\` for richText fields
|
|
140
|
+
|
|
141
|
+
**Example - "category" field added to blogs:**
|
|
142
|
+
\`\`\`html
|
|
143
|
+
{{#each blogs}}
|
|
144
|
+
<article class="post-card">
|
|
145
|
+
<h2>{{name}}</h2>
|
|
146
|
+
{{#if category}}
|
|
147
|
+
<span class="category-badge">{{category}}</span>
|
|
148
|
+
{{/if}}
|
|
149
|
+
<p>{{postSummary}}</p>
|
|
150
|
+
</article>
|
|
151
|
+
{{/each}}
|
|
152
|
+
\`\`\`
|
|
153
|
+
|
|
154
|
+
**Example - "department" and "linkedinUrl" fields added to team:**
|
|
155
|
+
\`\`\`html
|
|
156
|
+
{{#each team sort="order" order="asc"}}
|
|
157
|
+
<div class="team-member">
|
|
158
|
+
<img src="{{photo}}" alt="{{name}}">
|
|
159
|
+
<h3>{{name}}</h3>
|
|
160
|
+
<p class="role">{{role}}</p>
|
|
161
|
+
{{#if department}}
|
|
162
|
+
<p class="department">{{department}}</p>
|
|
163
|
+
{{/if}}
|
|
164
|
+
{{#if linkedinUrl}}
|
|
165
|
+
<a href="{{linkedinUrl}}">LinkedIn</a>
|
|
166
|
+
{{/if}}
|
|
167
|
+
</div>
|
|
168
|
+
{{/each}}
|
|
169
|
+
\`\`\`
|
|
170
|
+
|
|
171
|
+
**To see exact custom fields for a tenant, use the \`get_tenant_schema\` tool.**
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
87
175
|
## Template Syntax Reference
|
|
88
176
|
|
|
89
177
|
### Loops
|
|
@@ -103,6 +191,7 @@ Downloadable files and documents.
|
|
|
103
191
|
|
|
104
192
|
### Conditionals
|
|
105
193
|
\`\`\`html
|
|
194
|
+
<!-- Check if a field has a value -->
|
|
106
195
|
{{#if mainImage}}
|
|
107
196
|
<img src="{{mainImage}}" alt="{{name}}">
|
|
108
197
|
{{else}}
|
|
@@ -114,6 +203,50 @@ Downloadable files and documents.
|
|
|
114
203
|
{{/unless}}
|
|
115
204
|
\`\`\`
|
|
116
205
|
|
|
206
|
+
### Collection Empty Checks (Page Level)
|
|
207
|
+
Check if a collection has items - useful for "No posts yet" fallbacks:
|
|
208
|
+
|
|
209
|
+
\`\`\`html
|
|
210
|
+
{{#each blogs}}
|
|
211
|
+
<article>{{name}}</article>
|
|
212
|
+
{{/each}}
|
|
213
|
+
|
|
214
|
+
{{#unless blogs}}
|
|
215
|
+
<p>No posts yet. Check back soon!</p>
|
|
216
|
+
{{/unless}}
|
|
217
|
+
\`\`\`
|
|
218
|
+
|
|
219
|
+
**Important:** The \`{{#if blogs}}\` and \`{{#unless blogs}}\` syntax checks if the collection has any published items. Use this OUTSIDE of \`{{#each}}\` loops for empty state handling.
|
|
220
|
+
|
|
221
|
+
### Equality Comparisons in Conditionals
|
|
222
|
+
Compare two field values using the \`(eq field1 field2)\` helper:
|
|
223
|
+
|
|
224
|
+
\`\`\`html
|
|
225
|
+
<!-- Show content when fields ARE equal -->
|
|
226
|
+
{{#if (eq author.slug ../slug)}}
|
|
227
|
+
<span>This is your post!</span>
|
|
228
|
+
{{/if}}
|
|
229
|
+
|
|
230
|
+
<!-- Show content when fields are NOT equal -->
|
|
231
|
+
{{#unless (eq slug ../slug)}}
|
|
232
|
+
<a href="{{url}}">{{name}}</a>
|
|
233
|
+
{{/unless}}
|
|
234
|
+
\`\`\`
|
|
235
|
+
|
|
236
|
+
**Common use case - Related Posts (exclude current):**
|
|
237
|
+
\`\`\`html
|
|
238
|
+
<h3>Other Posts</h3>
|
|
239
|
+
{{#each blogs limit=3}}
|
|
240
|
+
{{#unless (eq slug ../slug)}}
|
|
241
|
+
<article>
|
|
242
|
+
<a href="{{url}}">{{name}}</a>
|
|
243
|
+
</article>
|
|
244
|
+
{{/unless}}
|
|
245
|
+
{{/each}}
|
|
246
|
+
\`\`\`
|
|
247
|
+
|
|
248
|
+
This pattern is essential for "Related Posts" sections where you want to show other posts but NOT the current one.
|
|
249
|
+
|
|
117
250
|
### Loop Variables
|
|
118
251
|
Inside \`{{#each}}\` blocks:
|
|
119
252
|
- \`{{@first}}\` - true for first item
|
|
@@ -149,17 +282,24 @@ Inside loops, access the parent scope (page's current item) using \`../\`:
|
|
|
149
282
|
- Category pages: filter items by current category
|
|
150
283
|
- Related items: match based on current detail page
|
|
151
284
|
|
|
152
|
-
### Equality
|
|
153
|
-
Compare two values in conditionals:
|
|
285
|
+
### Equality Helpers Summary
|
|
154
286
|
|
|
287
|
+
| Syntax | When to use | Shows content when... |
|
|
288
|
+
|--------|-------------|----------------------|
|
|
289
|
+
| \`{{#if (eq field1 field2)}}\` | Compare two fields | Fields ARE equal |
|
|
290
|
+
| \`{{#unless (eq field1 field2)}}\` | Exclude matches | Fields are NOT equal |
|
|
291
|
+
| \`{{#eq field "value"}}\` | Compare to string | Field equals string |
|
|
292
|
+
|
|
293
|
+
**Examples:**
|
|
155
294
|
\`\`\`html
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
295
|
+
<!-- Show only when author matches current page -->
|
|
296
|
+
{{#if (eq author.slug ../slug)}}...{{/if}}
|
|
297
|
+
|
|
298
|
+
<!-- Show all EXCEPT current item (Related Posts pattern) -->
|
|
299
|
+
{{#unless (eq slug ../slug)}}...{{/unless}}
|
|
159
300
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
{{/eq}}
|
|
301
|
+
<!-- Compare field to a literal value -->
|
|
302
|
+
{{#eq status "published"}}...{{/eq}}
|
|
163
303
|
\`\`\`
|
|
164
304
|
|
|
165
305
|
### Rich Text (Triple Braces)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetches the complete schema for a specific project including:
|
|
3
|
+
* - Built-in collections with their standard fields
|
|
4
|
+
* - Custom fields added to built-in collections
|
|
5
|
+
* - Custom collections with all their fields
|
|
6
|
+
*
|
|
7
|
+
* Uses stored credentials or triggers device flow for authentication.
|
|
8
|
+
*
|
|
9
|
+
* @param projectId - Project ID (UUID) or project name
|
|
10
|
+
*/
|
|
11
|
+
export declare function getTenantSchema(projectId: string): Promise<string>;
|
|
12
|
+
//# sourceMappingURL=get-tenant-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-tenant-schema.d.ts","sourceRoot":"","sources":["../../src/tools/get-tenant-schema.ts"],"names":[],"mappings":"AA2EA;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAmFxE"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getTenantSchema = getTenantSchema;
|
|
4
|
+
const api_client_1 = require("../lib/api-client");
|
|
5
|
+
const device_flow_1 = require("../lib/device-flow");
|
|
6
|
+
/**
|
|
7
|
+
* Resolve a project identifier to a tenant ID
|
|
8
|
+
* Accepts either a UUID or a project name
|
|
9
|
+
*/
|
|
10
|
+
async function resolveProjectId(projectIdentifier) {
|
|
11
|
+
// Check if it looks like a UUID
|
|
12
|
+
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
13
|
+
if (uuidPattern.test(projectIdentifier)) {
|
|
14
|
+
// It's already a UUID, use it directly
|
|
15
|
+
return { tenantId: projectIdentifier };
|
|
16
|
+
}
|
|
17
|
+
// Otherwise, look up by name using /api/tenants
|
|
18
|
+
const response = await (0, api_client_1.apiRequest)('/api/tenants');
|
|
19
|
+
if ((0, api_client_1.isApiError)(response)) {
|
|
20
|
+
return { error: `Failed to look up project: ${response.error}` };
|
|
21
|
+
}
|
|
22
|
+
const projects = response.data;
|
|
23
|
+
// Find by exact name match (case-insensitive)
|
|
24
|
+
const match = projects.find(p => p.name.toLowerCase() === projectIdentifier.toLowerCase());
|
|
25
|
+
if (match) {
|
|
26
|
+
return { tenantId: match.id };
|
|
27
|
+
}
|
|
28
|
+
// Try partial match
|
|
29
|
+
const partialMatch = projects.find(p => p.name.toLowerCase().includes(projectIdentifier.toLowerCase()));
|
|
30
|
+
if (partialMatch) {
|
|
31
|
+
return { tenantId: partialMatch.id };
|
|
32
|
+
}
|
|
33
|
+
// List available projects for the user
|
|
34
|
+
const availableProjects = projects.map(p => `- ${p.name} (${p.id})`).join('\n');
|
|
35
|
+
return {
|
|
36
|
+
error: `Project "${projectIdentifier}" not found.\n\nAvailable projects:\n${availableProjects || 'None'}\n\nUse list_projects to see all your projects.`
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Fetches the complete schema for a specific project including:
|
|
41
|
+
* - Built-in collections with their standard fields
|
|
42
|
+
* - Custom fields added to built-in collections
|
|
43
|
+
* - Custom collections with all their fields
|
|
44
|
+
*
|
|
45
|
+
* Uses stored credentials or triggers device flow for authentication.
|
|
46
|
+
*
|
|
47
|
+
* @param projectId - Project ID (UUID) or project name
|
|
48
|
+
*/
|
|
49
|
+
async function getTenantSchema(projectId) {
|
|
50
|
+
// Check if we need to authenticate
|
|
51
|
+
if (await (0, api_client_1.needsAuthentication)()) {
|
|
52
|
+
const authResult = await (0, device_flow_1.ensureAuthenticated)();
|
|
53
|
+
if (!authResult.authenticated) {
|
|
54
|
+
return authResult.message;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Resolve project identifier to tenant ID
|
|
58
|
+
const resolved = await resolveProjectId(projectId);
|
|
59
|
+
if ('error' in resolved) {
|
|
60
|
+
return `# Project Not Found
|
|
61
|
+
|
|
62
|
+
${resolved.error}
|
|
63
|
+
`;
|
|
64
|
+
}
|
|
65
|
+
const { tenantId } = resolved;
|
|
66
|
+
const apiUrl = (0, api_client_1.getApiUrl)();
|
|
67
|
+
// Fetch the AI prompt for this tenant
|
|
68
|
+
const response = await (0, api_client_1.apiRequest)('/api/cms/ai-prompt', {
|
|
69
|
+
tenantId,
|
|
70
|
+
});
|
|
71
|
+
if ((0, api_client_1.isApiError)(response)) {
|
|
72
|
+
// If auth error, try to re-authenticate
|
|
73
|
+
if ((0, api_client_1.needsAuthError)(response)) {
|
|
74
|
+
const authResult = await (0, device_flow_1.ensureAuthenticated)();
|
|
75
|
+
if (!authResult.authenticated) {
|
|
76
|
+
return authResult.message;
|
|
77
|
+
}
|
|
78
|
+
// Retry the request
|
|
79
|
+
const retryResponse = await (0, api_client_1.apiRequest)('/api/cms/ai-prompt', {
|
|
80
|
+
tenantId,
|
|
81
|
+
});
|
|
82
|
+
if ((0, api_client_1.isApiError)(retryResponse)) {
|
|
83
|
+
return `# API Error
|
|
84
|
+
|
|
85
|
+
Failed to fetch project schema: ${retryResponse.error}
|
|
86
|
+
|
|
87
|
+
**Project ID:** ${tenantId}
|
|
88
|
+
**Status:** ${retryResponse.statusCode}
|
|
89
|
+
|
|
90
|
+
Please try authenticating again.
|
|
91
|
+
`;
|
|
92
|
+
}
|
|
93
|
+
return formatSchemaResponse(retryResponse.data, tenantId);
|
|
94
|
+
}
|
|
95
|
+
if (response.statusCode === 403) {
|
|
96
|
+
return `# Authorization Error
|
|
97
|
+
|
|
98
|
+
You don't have permission to access this project's schema.
|
|
99
|
+
|
|
100
|
+
**Project ID:** ${tenantId}
|
|
101
|
+
|
|
102
|
+
Make sure:
|
|
103
|
+
1. You have membership in this project
|
|
104
|
+
2. Try running \`list_projects\` to see your accessible projects
|
|
105
|
+
`;
|
|
106
|
+
}
|
|
107
|
+
return `# API Error
|
|
108
|
+
|
|
109
|
+
Failed to fetch project schema: ${response.error}
|
|
110
|
+
|
|
111
|
+
**Project ID:** ${tenantId}
|
|
112
|
+
**API URL:** ${apiUrl}
|
|
113
|
+
**Status:** ${response.statusCode}
|
|
114
|
+
|
|
115
|
+
Please verify:
|
|
116
|
+
1. The project ID is correct
|
|
117
|
+
2. You have network connectivity
|
|
118
|
+
`;
|
|
119
|
+
}
|
|
120
|
+
return formatSchemaResponse(response.data, tenantId);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Format the schema response for display
|
|
124
|
+
*/
|
|
125
|
+
function formatSchemaResponse(data, tenantId) {
|
|
126
|
+
if (!data?.prompt) {
|
|
127
|
+
return `# Unexpected Response
|
|
128
|
+
|
|
129
|
+
The API returned a successful response but the prompt data was missing.
|
|
130
|
+
|
|
131
|
+
**Project ID:** ${tenantId}
|
|
132
|
+
**Response:** ${JSON.stringify(data, null, 2).slice(0, 500)}
|
|
133
|
+
`;
|
|
134
|
+
}
|
|
135
|
+
// Return the full AI prompt which includes all schema information
|
|
136
|
+
return `# Project Schema
|
|
137
|
+
|
|
138
|
+
**Project ID:** \`${tenantId}\`
|
|
139
|
+
|
|
140
|
+
The following schema is specific to this project and includes all custom collections and custom fields.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
${data.prompt}
|
|
145
|
+
`;
|
|
146
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-projects.d.ts","sourceRoot":"","sources":["../../src/tools/list-projects.ts"],"names":[],"mappings":"AAmBA;;GAEG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAkDpD"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.listProjects = listProjects;
|
|
4
|
+
const api_client_1 = require("../lib/api-client");
|
|
5
|
+
const device_flow_1 = require("../lib/device-flow");
|
|
6
|
+
/**
|
|
7
|
+
* List all FastMode projects the authenticated user has access to
|
|
8
|
+
*/
|
|
9
|
+
async function listProjects() {
|
|
10
|
+
// Check if we need to authenticate
|
|
11
|
+
if (await (0, api_client_1.needsAuthentication)()) {
|
|
12
|
+
const authResult = await (0, device_flow_1.ensureAuthenticated)();
|
|
13
|
+
if (!authResult.authenticated) {
|
|
14
|
+
return authResult.message;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
// Fetch user's tenants/projects
|
|
18
|
+
const response = await (0, api_client_1.apiRequest)('/api/tenants');
|
|
19
|
+
if ((0, api_client_1.isApiError)(response)) {
|
|
20
|
+
// If auth error, try to re-authenticate
|
|
21
|
+
if ((0, api_client_1.needsAuthError)(response)) {
|
|
22
|
+
const authResult = await (0, device_flow_1.ensureAuthenticated)();
|
|
23
|
+
if (!authResult.authenticated) {
|
|
24
|
+
return authResult.message;
|
|
25
|
+
}
|
|
26
|
+
// Retry the request
|
|
27
|
+
const retryResponse = await (0, api_client_1.apiRequest)('/api/tenants');
|
|
28
|
+
if ((0, api_client_1.isApiError)(retryResponse)) {
|
|
29
|
+
return `# API Error
|
|
30
|
+
|
|
31
|
+
Failed to fetch projects: ${retryResponse.error}
|
|
32
|
+
|
|
33
|
+
**Status:** ${retryResponse.statusCode}
|
|
34
|
+
|
|
35
|
+
Please try authenticating again.
|
|
36
|
+
`;
|
|
37
|
+
}
|
|
38
|
+
return formatProjectList(retryResponse.data);
|
|
39
|
+
}
|
|
40
|
+
return `# API Error
|
|
41
|
+
|
|
42
|
+
Failed to fetch projects: ${response.error}
|
|
43
|
+
|
|
44
|
+
**Status:** ${response.statusCode}
|
|
45
|
+
|
|
46
|
+
Please check:
|
|
47
|
+
1. Your authentication is valid
|
|
48
|
+
2. The API URL is correct
|
|
49
|
+
3. You have network connectivity
|
|
50
|
+
`;
|
|
51
|
+
}
|
|
52
|
+
return formatProjectList(response.data);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Format the project list for display
|
|
56
|
+
*/
|
|
57
|
+
function formatProjectList(projects) {
|
|
58
|
+
if (!projects || projects.length === 0) {
|
|
59
|
+
return `# No Projects Found
|
|
60
|
+
|
|
61
|
+
You don't have access to any FastMode projects.
|
|
62
|
+
|
|
63
|
+
**To create a project:**
|
|
64
|
+
1. Go to app.fastmode.ai
|
|
65
|
+
2. Click "Create New Project"
|
|
66
|
+
3. Run \`list_projects\` again to see it here
|
|
67
|
+
`;
|
|
68
|
+
}
|
|
69
|
+
// Format the project list
|
|
70
|
+
let output = `# Your FastMode Projects
|
|
71
|
+
|
|
72
|
+
Found ${projects.length} project${projects.length > 1 ? 's' : ''}:
|
|
73
|
+
|
|
74
|
+
`;
|
|
75
|
+
projects.forEach((project, index) => {
|
|
76
|
+
const siteStatus = project.site?.status || 'pending';
|
|
77
|
+
output += `## ${index + 1}. ${project.name}
|
|
78
|
+
|
|
79
|
+
- **Project ID:** \`${project.id}\`
|
|
80
|
+
- **Subdomain:** ${project.subdomain}.fastmode.ai
|
|
81
|
+
${project.customDomain ? `- **Custom Domain:** ${project.customDomain}\n` : ''}- **Your Role:** ${project.role}
|
|
82
|
+
- **Site Status:** ${siteStatus}
|
|
83
|
+
|
|
84
|
+
`;
|
|
85
|
+
});
|
|
86
|
+
output += `---
|
|
87
|
+
|
|
88
|
+
## How to Use
|
|
89
|
+
|
|
90
|
+
To get the schema for a project, use:
|
|
91
|
+
\`\`\`
|
|
92
|
+
get_tenant_schema(projectId: "${projects[0]?.id || 'project-id-here'}")
|
|
93
|
+
\`\`\`
|
|
94
|
+
|
|
95
|
+
Or use the project name:
|
|
96
|
+
\`\`\`
|
|
97
|
+
get_tenant_schema(projectId: "${projects[0]?.name || 'Project Name'}")
|
|
98
|
+
\`\`\`
|
|
99
|
+
`;
|
|
100
|
+
return output;
|
|
101
|
+
}
|
package/package.json
CHANGED