fastmode-mcp 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 (67) hide show
  1. package/README.md +561 -0
  2. package/bin/run.js +50 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +802 -0
  6. package/dist/lib/api-client.d.ts +81 -0
  7. package/dist/lib/api-client.d.ts.map +1 -0
  8. package/dist/lib/api-client.js +237 -0
  9. package/dist/lib/auth-state.d.ts +13 -0
  10. package/dist/lib/auth-state.d.ts.map +1 -0
  11. package/dist/lib/auth-state.js +24 -0
  12. package/dist/lib/context-fetcher.d.ts +67 -0
  13. package/dist/lib/context-fetcher.d.ts.map +1 -0
  14. package/dist/lib/context-fetcher.js +190 -0
  15. package/dist/lib/credentials.d.ts +52 -0
  16. package/dist/lib/credentials.d.ts.map +1 -0
  17. package/dist/lib/credentials.js +196 -0
  18. package/dist/lib/device-flow.d.ts +14 -0
  19. package/dist/lib/device-flow.d.ts.map +1 -0
  20. package/dist/lib/device-flow.js +244 -0
  21. package/dist/tools/cms-items.d.ts +56 -0
  22. package/dist/tools/cms-items.d.ts.map +1 -0
  23. package/dist/tools/cms-items.js +376 -0
  24. package/dist/tools/create-site.d.ts +9 -0
  25. package/dist/tools/create-site.d.ts.map +1 -0
  26. package/dist/tools/create-site.js +202 -0
  27. package/dist/tools/deploy-package.d.ts +9 -0
  28. package/dist/tools/deploy-package.d.ts.map +1 -0
  29. package/dist/tools/deploy-package.js +434 -0
  30. package/dist/tools/generate-samples.d.ts +19 -0
  31. package/dist/tools/generate-samples.d.ts.map +1 -0
  32. package/dist/tools/generate-samples.js +272 -0
  33. package/dist/tools/get-conversion-guide.d.ts +7 -0
  34. package/dist/tools/get-conversion-guide.d.ts.map +1 -0
  35. package/dist/tools/get-conversion-guide.js +1323 -0
  36. package/dist/tools/get-example.d.ts +7 -0
  37. package/dist/tools/get-example.d.ts.map +1 -0
  38. package/dist/tools/get-example.js +1568 -0
  39. package/dist/tools/get-field-types.d.ts +30 -0
  40. package/dist/tools/get-field-types.d.ts.map +1 -0
  41. package/dist/tools/get-field-types.js +154 -0
  42. package/dist/tools/get-schema.d.ts +5 -0
  43. package/dist/tools/get-schema.d.ts.map +1 -0
  44. package/dist/tools/get-schema.js +320 -0
  45. package/dist/tools/get-started.d.ts +21 -0
  46. package/dist/tools/get-started.d.ts.map +1 -0
  47. package/dist/tools/get-started.js +624 -0
  48. package/dist/tools/get-tenant-schema.d.ts +18 -0
  49. package/dist/tools/get-tenant-schema.d.ts.map +1 -0
  50. package/dist/tools/get-tenant-schema.js +158 -0
  51. package/dist/tools/list-projects.d.ts +5 -0
  52. package/dist/tools/list-projects.d.ts.map +1 -0
  53. package/dist/tools/list-projects.js +101 -0
  54. package/dist/tools/sync-schema.d.ts +41 -0
  55. package/dist/tools/sync-schema.d.ts.map +1 -0
  56. package/dist/tools/sync-schema.js +483 -0
  57. package/dist/tools/validate-manifest.d.ts +5 -0
  58. package/dist/tools/validate-manifest.d.ts.map +1 -0
  59. package/dist/tools/validate-manifest.js +311 -0
  60. package/dist/tools/validate-package.d.ts +5 -0
  61. package/dist/tools/validate-package.d.ts.map +1 -0
  62. package/dist/tools/validate-package.js +337 -0
  63. package/dist/tools/validate-template.d.ts +12 -0
  64. package/dist/tools/validate-template.d.ts.map +1 -0
  65. package/dist/tools/validate-template.js +790 -0
  66. package/package.json +54 -0
  67. package/scripts/postinstall.js +129 -0
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Centralized API client for FastMode API requests
3
+ * Uses stored credentials with automatic refresh, or falls back to env var
4
+ */
5
+ export interface ApiConfig {
6
+ apiUrl: string;
7
+ authToken: string | null;
8
+ }
9
+ /**
10
+ * Get API URL from environment or default
11
+ */
12
+ export declare function getApiUrl(): string;
13
+ /**
14
+ * Get API configuration - tries stored credentials first, then env var
15
+ */
16
+ export declare function getApiConfigAsync(): Promise<ApiConfig>;
17
+ /**
18
+ * Synchronous version - only checks env var (for quick checks)
19
+ */
20
+ export declare function getApiConfig(): ApiConfig;
21
+ /**
22
+ * Check if authentication is configured (either stored credentials or env var)
23
+ */
24
+ export declare function isAuthConfigured(): boolean;
25
+ /**
26
+ * Check if we need to trigger the device flow
27
+ */
28
+ export declare function needsAuthentication(): Promise<boolean>;
29
+ /**
30
+ * Get a helpful message for authentication
31
+ */
32
+ export declare function getAuthRequiredMessage(): string;
33
+ /**
34
+ * API request options
35
+ */
36
+ export interface ApiRequestOptions {
37
+ tenantId?: string;
38
+ method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
39
+ body?: unknown;
40
+ }
41
+ /**
42
+ * API error response
43
+ */
44
+ export interface ApiError {
45
+ success: false;
46
+ error: string;
47
+ statusCode: number;
48
+ needsAuth?: boolean;
49
+ }
50
+ /**
51
+ * Make an authenticated API request
52
+ * Uses stored credentials with auto-refresh, or falls back to env var
53
+ */
54
+ export declare function apiRequest<T>(endpoint: string, options?: ApiRequestOptions): Promise<{
55
+ data: T;
56
+ } | ApiError>;
57
+ /**
58
+ * Check if a response is an error
59
+ */
60
+ export declare function isApiError(response: unknown): response is ApiError;
61
+ /**
62
+ * Check if error requires authentication
63
+ */
64
+ export declare function needsAuthError(error: ApiError): boolean;
65
+ /**
66
+ * Resolve a project identifier to a tenant ID
67
+ * Accepts either a UUID or a project name
68
+ */
69
+ export declare function resolveProjectId(projectIdentifier: string): Promise<{
70
+ tenantId: string;
71
+ } | {
72
+ error: string;
73
+ }>;
74
+ /**
75
+ * Make an authenticated external API request
76
+ * Uses the /external/* endpoints with tenant context
77
+ */
78
+ export declare function externalApiRequest<T>(tenantId: string, endpoint: string, options?: Omit<ApiRequestOptions, 'tenantId'>): Promise<{
79
+ data: T;
80
+ } | ApiError>;
81
+ //# sourceMappingURL=api-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/lib/api-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,SAAS,CAAC,CAgB5D;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,SAAS,CAKxC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED;;GAEG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,CAAC,CAW5D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAS/C;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAC3C,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAChC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,GAAG,QAAQ,CAAC,CAoEjC;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,OAAO,GAAG,QAAQ,IAAI,QAAQ,CAOlE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAEvD;AAYD;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAwCnH;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EACxC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAM,GAChD,OAAO,CAAC;IAAE,IAAI,EAAE,CAAC,CAAA;CAAE,GAAG,QAAQ,CAAC,CAOjC"}
@@ -0,0 +1,237 @@
1
+ "use strict";
2
+ /**
3
+ * Centralized API client for FastMode API requests
4
+ * Uses stored credentials with automatic refresh, or falls back to env var
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.getApiUrl = getApiUrl;
41
+ exports.getApiConfigAsync = getApiConfigAsync;
42
+ exports.getApiConfig = getApiConfig;
43
+ exports.isAuthConfigured = isAuthConfigured;
44
+ exports.needsAuthentication = needsAuthentication;
45
+ exports.getAuthRequiredMessage = getAuthRequiredMessage;
46
+ exports.apiRequest = apiRequest;
47
+ exports.isApiError = isApiError;
48
+ exports.needsAuthError = needsAuthError;
49
+ exports.resolveProjectId = resolveProjectId;
50
+ exports.externalApiRequest = externalApiRequest;
51
+ const credentials_1 = require("./credentials");
52
+ /**
53
+ * Get API URL from environment or default
54
+ */
55
+ function getApiUrl() {
56
+ return process.env.FASTMODE_API_URL || 'https://api.fastmode.ai';
57
+ }
58
+ /**
59
+ * Get API configuration - tries stored credentials first, then env var
60
+ */
61
+ async function getApiConfigAsync() {
62
+ // First, try to get valid stored credentials
63
+ const credentials = await (0, credentials_1.getValidCredentials)();
64
+ if (credentials) {
65
+ return {
66
+ apiUrl: getApiUrl(),
67
+ authToken: credentials.accessToken,
68
+ };
69
+ }
70
+ // Fall back to environment variable (for backward compatibility)
71
+ return {
72
+ apiUrl: getApiUrl(),
73
+ authToken: process.env.FASTMODE_AUTH_TOKEN || null,
74
+ };
75
+ }
76
+ /**
77
+ * Synchronous version - only checks env var (for quick checks)
78
+ */
79
+ function getApiConfig() {
80
+ return {
81
+ apiUrl: getApiUrl(),
82
+ authToken: process.env.FASTMODE_AUTH_TOKEN || null,
83
+ };
84
+ }
85
+ /**
86
+ * Check if authentication is configured (either stored credentials or env var)
87
+ */
88
+ function isAuthConfigured() {
89
+ return (0, credentials_1.hasCredentials)() || !!process.env.FASTMODE_AUTH_TOKEN;
90
+ }
91
+ /**
92
+ * Check if we need to trigger the device flow
93
+ */
94
+ async function needsAuthentication() {
95
+ // Wait for any startup auth that might be in progress
96
+ const { waitForAuth } = await Promise.resolve().then(() => __importStar(require('./auth-state')));
97
+ await waitForAuth();
98
+ const credentials = await (0, credentials_1.getValidCredentials)();
99
+ if (credentials)
100
+ return false;
101
+ if (process.env.FASTMODE_AUTH_TOKEN)
102
+ return false;
103
+ return true;
104
+ }
105
+ /**
106
+ * Get a helpful message for authentication
107
+ */
108
+ function getAuthRequiredMessage() {
109
+ return `# Authentication Required
110
+
111
+ You need to authenticate to use this tool.
112
+
113
+ **Starting authentication flow...**
114
+
115
+ A browser window will open for you to log in. If it doesn't open automatically, you'll see a URL to visit.
116
+ `;
117
+ }
118
+ /**
119
+ * Make an authenticated API request
120
+ * Uses stored credentials with auto-refresh, or falls back to env var
121
+ */
122
+ async function apiRequest(endpoint, options = {}) {
123
+ const config = await getApiConfigAsync();
124
+ if (!config.authToken) {
125
+ return {
126
+ success: false,
127
+ error: 'Not authenticated',
128
+ statusCode: 401,
129
+ needsAuth: true,
130
+ };
131
+ }
132
+ const url = `${config.apiUrl}${endpoint.startsWith('/') ? endpoint : '/' + endpoint}`;
133
+ const headers = {
134
+ 'Authorization': `Bearer ${config.authToken}`,
135
+ 'Content-Type': 'application/json',
136
+ };
137
+ if (options.tenantId) {
138
+ headers['X-Tenant-Id'] = options.tenantId;
139
+ }
140
+ try {
141
+ const response = await fetch(url, {
142
+ method: options.method || 'GET',
143
+ headers,
144
+ body: options.body ? JSON.stringify(options.body) : undefined,
145
+ });
146
+ const data = await response.json();
147
+ if (!response.ok) {
148
+ // Check if this is an auth error
149
+ if (response.status === 401) {
150
+ return {
151
+ success: false,
152
+ error: 'Authentication expired or invalid',
153
+ statusCode: 401,
154
+ needsAuth: true,
155
+ };
156
+ }
157
+ return {
158
+ success: false,
159
+ error: data.error || `API request failed with status ${response.status}`,
160
+ statusCode: response.status,
161
+ };
162
+ }
163
+ return data;
164
+ }
165
+ catch (error) {
166
+ const message = error instanceof Error ? error.message : String(error);
167
+ if (message.includes('fetch') || message.includes('network') || message.includes('ECONNREFUSED')) {
168
+ return {
169
+ success: false,
170
+ error: `Network error: Unable to connect to ${config.apiUrl}`,
171
+ statusCode: 0,
172
+ };
173
+ }
174
+ return {
175
+ success: false,
176
+ error: message,
177
+ statusCode: 0,
178
+ };
179
+ }
180
+ }
181
+ /**
182
+ * Check if a response is an error
183
+ */
184
+ function isApiError(response) {
185
+ return (typeof response === 'object' &&
186
+ response !== null &&
187
+ 'success' in response &&
188
+ response.success === false);
189
+ }
190
+ /**
191
+ * Check if error requires authentication
192
+ */
193
+ function needsAuthError(error) {
194
+ return error.needsAuth === true || error.statusCode === 401;
195
+ }
196
+ /**
197
+ * Resolve a project identifier to a tenant ID
198
+ * Accepts either a UUID or a project name
199
+ */
200
+ async function resolveProjectId(projectIdentifier) {
201
+ // Check if it looks like a UUID
202
+ const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
203
+ if (uuidPattern.test(projectIdentifier)) {
204
+ return { tenantId: projectIdentifier };
205
+ }
206
+ // Look up by name using /api/tenants
207
+ const response = await apiRequest('/api/tenants');
208
+ if (isApiError(response)) {
209
+ return { error: `Failed to look up project: ${response.error}` };
210
+ }
211
+ const projects = response.data;
212
+ // Find by exact name match (case-insensitive)
213
+ const match = projects.find(p => p.name.toLowerCase() === projectIdentifier.toLowerCase());
214
+ if (match) {
215
+ return { tenantId: match.id };
216
+ }
217
+ // Try partial match
218
+ const partialMatch = projects.find(p => p.name.toLowerCase().includes(projectIdentifier.toLowerCase()));
219
+ if (partialMatch) {
220
+ return { tenantId: partialMatch.id };
221
+ }
222
+ const availableProjects = projects.map(p => `- ${p.name} (${p.id})`).join('\n');
223
+ return {
224
+ error: `Project "${projectIdentifier}" not found.\n\nAvailable projects:\n${availableProjects || 'None'}`
225
+ };
226
+ }
227
+ /**
228
+ * Make an authenticated external API request
229
+ * Uses the /external/* endpoints with tenant context
230
+ */
231
+ async function externalApiRequest(tenantId, endpoint, options = {}) {
232
+ // External API endpoints are at /external/...
233
+ const fullEndpoint = endpoint.startsWith('/external')
234
+ ? endpoint
235
+ : `/external${endpoint.startsWith('/') ? endpoint : '/' + endpoint}`;
236
+ return apiRequest(fullEndpoint, { ...options, tenantId });
237
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Shared authentication state
3
+ * Separate file to avoid circular dependencies
4
+ */
5
+ /**
6
+ * Set the in-progress auth promise
7
+ */
8
+ export declare function setAuthInProgress(promise: Promise<string> | null): void;
9
+ /**
10
+ * Wait for any in-progress authentication to complete
11
+ */
12
+ export declare function waitForAuth(): Promise<void>;
13
+ //# sourceMappingURL=auth-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-state.d.ts","sourceRoot":"","sources":["../../src/lib/auth-state.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,GAAG,IAAI,CAEvE;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAIjD"}
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ /**
3
+ * Shared authentication state
4
+ * Separate file to avoid circular dependencies
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.setAuthInProgress = setAuthInProgress;
8
+ exports.waitForAuth = waitForAuth;
9
+ // Track in-progress authentication
10
+ let authInProgress = null;
11
+ /**
12
+ * Set the in-progress auth promise
13
+ */
14
+ function setAuthInProgress(promise) {
15
+ authInProgress = promise;
16
+ }
17
+ /**
18
+ * Wait for any in-progress authentication to complete
19
+ */
20
+ async function waitForAuth() {
21
+ if (authInProgress) {
22
+ await authInProgress;
23
+ }
24
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Context Fetcher
3
+ *
4
+ * Centralizes logic for fetching project state, collections, and content.
5
+ * Used by get_started and other tools that need project context.
6
+ */
7
+ export interface ProjectSummary {
8
+ id: string;
9
+ name: string;
10
+ subdomain: string;
11
+ customDomain: string | null;
12
+ status: 'pending' | 'building' | 'published' | 'failed';
13
+ hasCollections: boolean;
14
+ collectionCount: number;
15
+ hasContent: boolean;
16
+ totalItems: number;
17
+ hasPendingChanges: boolean;
18
+ lastDeployedAt: string | null;
19
+ githubConnected: boolean;
20
+ githubRepo: string | null;
21
+ }
22
+ export interface CollectionField {
23
+ slug: string;
24
+ name: string;
25
+ type: string;
26
+ isRequired: boolean;
27
+ referenceCollection?: string;
28
+ }
29
+ export interface CollectionSummary {
30
+ slug: string;
31
+ name: string;
32
+ nameSingular: string;
33
+ fieldCount: number;
34
+ itemCount: number;
35
+ fields: CollectionField[];
36
+ }
37
+ export interface ProjectDetail {
38
+ id: string;
39
+ name: string;
40
+ subdomain: string;
41
+ customDomain: string | null;
42
+ status: 'pending' | 'building' | 'published' | 'failed';
43
+ collections: CollectionSummary[];
44
+ hasPendingChanges: boolean;
45
+ lastDeployedAt: string | null;
46
+ githubConnected: boolean;
47
+ }
48
+ export interface ProjectContext {
49
+ isAuthenticated: boolean;
50
+ projects: ProjectSummary[];
51
+ selectedProject?: ProjectDetail;
52
+ }
53
+ /**
54
+ * Fetch complete project context
55
+ *
56
+ * @param projectId - Optional specific project to get detailed info for
57
+ */
58
+ export declare function fetchProjectContext(projectId?: string): Promise<ProjectContext>;
59
+ /**
60
+ * Resolve a project identifier to a tenant ID
61
+ */
62
+ export declare function resolveProjectId(projectIdentifier: string): Promise<{
63
+ tenantId: string;
64
+ name: string;
65
+ subdomain: string;
66
+ } | null>;
67
+ //# sourceMappingURL=context-fetcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-fetcher.d.ts","sourceRoot":"","sources":["../../src/lib/context-fetcher.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,GAAG,QAAQ,CAAC;IACxD,cAAc,EAAE,OAAO,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,OAAO,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,GAAG,QAAQ,CAAC;IACxD,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,eAAe,EAAE,OAAO,CAAC;IACzB,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,eAAe,CAAC,EAAE,aAAa,CAAC;CACjC;AAgHD;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CA6GrF;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAsCvI"}
@@ -0,0 +1,190 @@
1
+ "use strict";
2
+ /**
3
+ * Context Fetcher
4
+ *
5
+ * Centralizes logic for fetching project state, collections, and content.
6
+ * Used by get_started and other tools that need project context.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.fetchProjectContext = fetchProjectContext;
10
+ exports.resolveProjectId = resolveProjectId;
11
+ const api_client_1 = require("./api-client");
12
+ // ============ Helper Functions ============
13
+ /**
14
+ * Fetch all projects for the authenticated user
15
+ */
16
+ async function fetchProjects() {
17
+ const response = await (0, api_client_1.apiRequest)('/api/tenants');
18
+ if ((0, api_client_1.isApiError)(response)) {
19
+ return [];
20
+ }
21
+ return response.data;
22
+ }
23
+ /**
24
+ * Fetch collections for a specific project
25
+ */
26
+ async function fetchCollections(tenantId) {
27
+ const response = await (0, api_client_1.apiRequest)('/api/collections', { tenantId });
28
+ if ((0, api_client_1.isApiError)(response)) {
29
+ return [];
30
+ }
31
+ return response.data;
32
+ }
33
+ /**
34
+ * Fetch GitHub connection status for a project
35
+ */
36
+ async function fetchGitHubStatus(tenantId) {
37
+ const response = await (0, api_client_1.apiRequest)('/api/github/site-connection', { tenantId });
38
+ if ((0, api_client_1.isApiError)(response)) {
39
+ return null;
40
+ }
41
+ return response.data;
42
+ }
43
+ // Note: fetchLatestDeploy could be used for detailed deploy state in the future
44
+ // async function fetchLatestDeploy(tenantId: string): Promise<DeployLog | null> {
45
+ // const response = await apiRequest<DeployLog[]>('/api/deploy/history?limit=1', { tenantId });
46
+ // if (isApiError(response) || !response.data || response.data.length === 0) {
47
+ // return null;
48
+ // }
49
+ // return response.data[0];
50
+ // }
51
+ /**
52
+ * Fetch item counts for collections
53
+ */
54
+ async function fetchCollectionItemCounts(tenantId, collectionSlugs) {
55
+ const counts = new Map();
56
+ // Fetch counts in parallel (limit concurrency)
57
+ const promises = collectionSlugs.slice(0, 10).map(async (slug) => {
58
+ const response = await (0, api_client_1.apiRequest)(`/api/collections/${slug}/items?limit=0`, { tenantId });
59
+ if (!(0, api_client_1.isApiError)(response) && response.data) {
60
+ counts.set(slug, response.data.total || 0);
61
+ }
62
+ });
63
+ await Promise.all(promises);
64
+ return counts;
65
+ }
66
+ // ============ Main Functions ============
67
+ /**
68
+ * Fetch complete project context
69
+ *
70
+ * @param projectId - Optional specific project to get detailed info for
71
+ */
72
+ async function fetchProjectContext(projectId) {
73
+ // Check authentication
74
+ const isAuthenticated = !(await (0, api_client_1.needsAuthentication)());
75
+ if (!isAuthenticated) {
76
+ return {
77
+ isAuthenticated: false,
78
+ projects: [],
79
+ };
80
+ }
81
+ // Fetch all projects
82
+ const tenants = await fetchProjects();
83
+ // Build project summaries
84
+ const projects = await Promise.all(tenants.map(async (tenant) => {
85
+ // Fetch collections for this project (for count)
86
+ const collections = await fetchCollections(tenant.id);
87
+ const collectionCount = collections.length;
88
+ // Calculate total items
89
+ let totalItems = 0;
90
+ for (const col of collections) {
91
+ if (col._count?.items) {
92
+ totalItems += col._count.items;
93
+ }
94
+ }
95
+ // Fetch GitHub status
96
+ const github = await fetchGitHubStatus(tenant.id);
97
+ return {
98
+ id: tenant.id,
99
+ name: tenant.name,
100
+ subdomain: tenant.subdomain,
101
+ customDomain: tenant.customDomain || null,
102
+ status: (tenant.site?.status || 'pending'),
103
+ hasCollections: collectionCount > 0,
104
+ collectionCount,
105
+ hasContent: totalItems > 0,
106
+ totalItems,
107
+ hasPendingChanges: tenant.site?.hasPendingChanges || false,
108
+ lastDeployedAt: tenant.site?.updatedAt || null,
109
+ githubConnected: github?.connected || false,
110
+ githubRepo: github?.repo || null,
111
+ };
112
+ }));
113
+ // If a specific project is requested, get detailed info
114
+ let selectedProject;
115
+ if (projectId) {
116
+ // Find the project
117
+ const tenant = tenants.find(t => t.id === projectId ||
118
+ t.name.toLowerCase() === projectId.toLowerCase() ||
119
+ t.subdomain.toLowerCase() === projectId.toLowerCase());
120
+ if (tenant) {
121
+ // Fetch collections with full field details
122
+ const collections = await fetchCollections(tenant.id);
123
+ // Fetch item counts
124
+ const itemCounts = await fetchCollectionItemCounts(tenant.id, collections.map(c => c.slug));
125
+ // Fetch GitHub status
126
+ const github = await fetchGitHubStatus(tenant.id);
127
+ // Build collection summaries
128
+ const collectionSummaries = collections.map(col => ({
129
+ slug: col.slug,
130
+ name: col.name,
131
+ nameSingular: col.nameSingular,
132
+ fieldCount: col.fields.length,
133
+ itemCount: itemCounts.get(col.slug) || col._count?.items || 0,
134
+ fields: col.fields.map(f => ({
135
+ slug: f.slug,
136
+ name: f.name,
137
+ type: f.type,
138
+ isRequired: f.isRequired || false,
139
+ referenceCollection: f.referenceCollection,
140
+ })),
141
+ }));
142
+ selectedProject = {
143
+ id: tenant.id,
144
+ name: tenant.name,
145
+ subdomain: tenant.subdomain,
146
+ customDomain: tenant.customDomain || null,
147
+ status: (tenant.site?.status || 'pending'),
148
+ collections: collectionSummaries,
149
+ hasPendingChanges: tenant.site?.hasPendingChanges || false,
150
+ lastDeployedAt: tenant.site?.updatedAt || null,
151
+ githubConnected: github?.connected || false,
152
+ };
153
+ }
154
+ }
155
+ return {
156
+ isAuthenticated: true,
157
+ projects,
158
+ selectedProject,
159
+ };
160
+ }
161
+ /**
162
+ * Resolve a project identifier to a tenant ID
163
+ */
164
+ async function resolveProjectId(projectIdentifier) {
165
+ const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
166
+ const tenants = await fetchProjects();
167
+ // Try exact UUID match
168
+ if (uuidPattern.test(projectIdentifier)) {
169
+ const tenant = tenants.find(t => t.id === projectIdentifier);
170
+ if (tenant) {
171
+ return { tenantId: tenant.id, name: tenant.name, subdomain: tenant.subdomain };
172
+ }
173
+ }
174
+ // Try exact name match
175
+ const exactMatch = tenants.find(t => t.name.toLowerCase() === projectIdentifier.toLowerCase());
176
+ if (exactMatch) {
177
+ return { tenantId: exactMatch.id, name: exactMatch.name, subdomain: exactMatch.subdomain };
178
+ }
179
+ // Try subdomain match
180
+ const subdomainMatch = tenants.find(t => t.subdomain.toLowerCase() === projectIdentifier.toLowerCase());
181
+ if (subdomainMatch) {
182
+ return { tenantId: subdomainMatch.id, name: subdomainMatch.name, subdomain: subdomainMatch.subdomain };
183
+ }
184
+ // Try partial match
185
+ const partialMatch = tenants.find(t => t.name.toLowerCase().includes(projectIdentifier.toLowerCase()));
186
+ if (partialMatch) {
187
+ return { tenantId: partialMatch.id, name: partialMatch.name, subdomain: partialMatch.subdomain };
188
+ }
189
+ return null;
190
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Stored credentials for the MCP server
3
+ */
4
+ export interface StoredCredentials {
5
+ accessToken: string;
6
+ refreshToken: string;
7
+ expiresAt: string;
8
+ email: string;
9
+ name?: string;
10
+ }
11
+ /**
12
+ * Get the path to the credentials file
13
+ */
14
+ export declare function getCredentialsPath(): string;
15
+ /**
16
+ * Load stored credentials from disk
17
+ */
18
+ export declare function loadCredentials(): StoredCredentials | null;
19
+ /**
20
+ * Save credentials to disk
21
+ */
22
+ export declare function saveCredentials(credentials: StoredCredentials): void;
23
+ /**
24
+ * Delete stored credentials
25
+ */
26
+ export declare function deleteCredentials(): void;
27
+ /**
28
+ * Check if credentials exist
29
+ */
30
+ export declare function hasCredentials(): boolean;
31
+ /**
32
+ * Check if the access token is expired or about to expire
33
+ */
34
+ export declare function isTokenExpired(credentials: StoredCredentials, bufferMinutes?: number): boolean;
35
+ /**
36
+ * Get the API URL from environment or default
37
+ */
38
+ export declare function getApiUrl(): string;
39
+ /**
40
+ * Refresh the access token using the refresh token
41
+ */
42
+ export declare function refreshAccessToken(credentials: StoredCredentials): Promise<StoredCredentials | null>;
43
+ /**
44
+ * Get valid credentials, refreshing if needed
45
+ * Returns null if no credentials or refresh fails
46
+ */
47
+ export declare function getValidCredentials(): Promise<StoredCredentials | null>;
48
+ /**
49
+ * Get the current auth token (access token) if available and valid
50
+ */
51
+ export declare function getAuthToken(): Promise<string | null>;
52
+ //# sourceMappingURL=credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/lib/credentials.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAG3C;AAYD;;GAEG;AACH,wBAAgB,eAAe,IAAI,iBAAiB,GAAG,IAAI,CAoB1D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,iBAAiB,GAAG,IAAI,CAUpE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAUxC;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,iBAAiB,EAAE,aAAa,GAAE,MAAU,GAAG,OAAO,CAIjG;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,WAAW,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAyC1G;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAoB7E;AAED;;GAEG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAG3D"}