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/README.md
CHANGED
|
@@ -13,7 +13,10 @@ Add to your MCP configuration (e.g., `.cursor/mcp.json`):
|
|
|
13
13
|
"mcpServers": {
|
|
14
14
|
"multisite-cms": {
|
|
15
15
|
"command": "npx",
|
|
16
|
-
"args": ["-y", "multisite-cms-mcp"]
|
|
16
|
+
"args": ["-y", "multisite-cms-mcp"],
|
|
17
|
+
"env": {
|
|
18
|
+
"FASTMODE_API_URL": "https://api.fastmode.ai"
|
|
19
|
+
}
|
|
17
20
|
}
|
|
18
21
|
}
|
|
19
22
|
}
|
|
@@ -31,7 +34,63 @@ Then configure:
|
|
|
31
34
|
{
|
|
32
35
|
"mcpServers": {
|
|
33
36
|
"multisite-cms": {
|
|
34
|
-
"command": "multisite-cms-mcp"
|
|
37
|
+
"command": "multisite-cms-mcp",
|
|
38
|
+
"env": {
|
|
39
|
+
"FASTMODE_API_URL": "https://api.fastmode.ai"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Authentication
|
|
47
|
+
|
|
48
|
+
The MCP server uses **automatic browser-based authentication**. When you use a tool that requires authentication (`list_projects`, `get_tenant_schema`), the server will:
|
|
49
|
+
|
|
50
|
+
1. **Open your browser** to app.fastmode.ai/device
|
|
51
|
+
2. **Display a code** in your terminal (e.g., "ABCD-1234")
|
|
52
|
+
3. You **log in** (if not already) and **click Authorize**
|
|
53
|
+
4. **Done!** Credentials are saved and auto-refresh
|
|
54
|
+
|
|
55
|
+
### How It Works
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
AI: Let me list your projects...
|
|
59
|
+
[calls list_projects]
|
|
60
|
+
|
|
61
|
+
MCP: Opening browser for authentication...
|
|
62
|
+
Visit: https://app.fastmode.ai/device
|
|
63
|
+
Code: ABCD-1234
|
|
64
|
+
|
|
65
|
+
Waiting for authorization...
|
|
66
|
+
|
|
67
|
+
[Browser opens, you click "Authorize"]
|
|
68
|
+
|
|
69
|
+
MCP: Authenticated as you@email.com
|
|
70
|
+
Credentials saved to ~/.fastmode/credentials.json
|
|
71
|
+
|
|
72
|
+
[Returns project list]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Credentials
|
|
76
|
+
|
|
77
|
+
- Stored in `~/.fastmode/credentials.json`
|
|
78
|
+
- Access tokens auto-refresh (90-day refresh tokens)
|
|
79
|
+
- No need to manually copy/paste tokens!
|
|
80
|
+
|
|
81
|
+
### Manual Token (Optional)
|
|
82
|
+
|
|
83
|
+
If you prefer manual setup, you can still set `FASTMODE_AUTH_TOKEN`:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"mcpServers": {
|
|
88
|
+
"multisite-cms": {
|
|
89
|
+
"command": "npx",
|
|
90
|
+
"args": ["-y", "multisite-cms-mcp"],
|
|
91
|
+
"env": {
|
|
92
|
+
"FASTMODE_AUTH_TOKEN": "your-token-here"
|
|
93
|
+
}
|
|
35
94
|
}
|
|
36
95
|
}
|
|
37
96
|
}
|
|
@@ -47,6 +106,49 @@ AI: What fields are available for blog posts?
|
|
|
47
106
|
[calls get_schema]
|
|
48
107
|
```
|
|
49
108
|
|
|
109
|
+
### `list_projects`
|
|
110
|
+
List all FastMode projects you have access to. Use this to discover project IDs for `get_tenant_schema`.
|
|
111
|
+
|
|
112
|
+
**Triggers authentication if needed.**
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
AI: Let me see what projects you have access to...
|
|
116
|
+
[calls list_projects]
|
|
117
|
+
|
|
118
|
+
Response:
|
|
119
|
+
# Your FastMode Projects
|
|
120
|
+
|
|
121
|
+
Found 2 projects:
|
|
122
|
+
|
|
123
|
+
## 1. My Business Site
|
|
124
|
+
- Project ID: abc-123-def-456
|
|
125
|
+
- Subdomain: mybusiness.fastmode.ai
|
|
126
|
+
- Your Role: owner
|
|
127
|
+
|
|
128
|
+
## 2. Portfolio
|
|
129
|
+
- Project ID: xyz-789-uvw-012
|
|
130
|
+
- Subdomain: portfolio.fastmode.ai
|
|
131
|
+
- Your Role: editor
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### `get_tenant_schema`
|
|
135
|
+
Fetch the complete schema for a specific project, including custom collections and custom fields on built-in collections.
|
|
136
|
+
|
|
137
|
+
**Triggers authentication if needed.**
|
|
138
|
+
|
|
139
|
+
**Parameters:**
|
|
140
|
+
- `projectId` (string, required): The project ID (UUID from `list_projects`) or project name
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
AI: Let me get the schema for your "My Business Site" project...
|
|
144
|
+
[calls get_tenant_schema with projectId: "My Business Site"]
|
|
145
|
+
|
|
146
|
+
Response: Returns full schema including:
|
|
147
|
+
- Built-in collections with any custom fields added
|
|
148
|
+
- All custom collections with their fields
|
|
149
|
+
- Correct token syntax for each field
|
|
150
|
+
```
|
|
151
|
+
|
|
50
152
|
### `validate_manifest`
|
|
51
153
|
Validate a manifest.json file structure.
|
|
52
154
|
|
|
@@ -103,6 +205,16 @@ Get the complete conversion documentation.
|
|
|
103
205
|
**Parameters:**
|
|
104
206
|
- `section` (string, optional): One of: `full`, `analysis`, `structure`, `manifest`, `templates`, `tokens`, `forms`, `assets`, `checklist`
|
|
105
207
|
|
|
208
|
+
## Custom Collections & Custom Fields
|
|
209
|
+
|
|
210
|
+
The CMS supports:
|
|
211
|
+
|
|
212
|
+
1. **Custom Collections** - Tenant-defined content types (e.g., "services", "testimonials")
|
|
213
|
+
2. **Custom Fields on Built-in Collections** - Additional fields added to Blog Posts, Authors, Team, Downloads
|
|
214
|
+
|
|
215
|
+
Use `get_schema` for static documentation about how these work.
|
|
216
|
+
Use `list_projects` + `get_tenant_schema` to fetch the actual custom collections and fields for a specific project.
|
|
217
|
+
|
|
106
218
|
## CMS Template Configuration
|
|
107
219
|
|
|
108
220
|
Templates can be configured two ways:
|
|
@@ -132,20 +244,64 @@ This allows for a faster workflow: upload pages first, then configure templates
|
|
|
132
244
|
When converting a website, an AI agent can:
|
|
133
245
|
|
|
134
246
|
1. **Start** → Call `get_conversion_guide` for requirements
|
|
135
|
-
2. **Check fields** → Call `get_schema` to see
|
|
136
|
-
3. **
|
|
137
|
-
4. **
|
|
138
|
-
5. **
|
|
139
|
-
6. **
|
|
247
|
+
2. **Check generic fields** → Call `get_schema` to see built-in token names
|
|
248
|
+
3. **Discover projects** → Call `list_projects` (triggers auth if needed)
|
|
249
|
+
4. **Get project schema** → Call `get_tenant_schema` with project ID/name for custom collections & fields
|
|
250
|
+
5. **Need example?** → Call `get_example` for specific pattern
|
|
251
|
+
6. **Create manifest** → Call `validate_manifest` to check it
|
|
252
|
+
7. **Create template** → Call `validate_template` to verify tokens
|
|
253
|
+
8. **Final check** → Call `validate_package` for full validation
|
|
254
|
+
|
|
255
|
+
### Example Conversion Flow
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
User: Convert example.com for the "My Business" project
|
|
259
|
+
|
|
260
|
+
AI: Let me first check what projects you have access to...
|
|
261
|
+
[calls list_projects]
|
|
262
|
+
|
|
263
|
+
[Browser opens for auth if needed]
|
|
264
|
+
[User clicks Authorize]
|
|
265
|
+
|
|
266
|
+
→ Found "My Business" with ID: abc-123
|
|
267
|
+
|
|
268
|
+
AI: Now let me get the custom schema for this project...
|
|
269
|
+
[calls get_tenant_schema with projectId: "abc-123"]
|
|
270
|
+
→ Found custom collection "Services" with fields: title, description, icon
|
|
271
|
+
→ Found custom field "category" on Blog Posts
|
|
272
|
+
|
|
273
|
+
AI: I'll create templates using these tokens...
|
|
274
|
+
- {{#each services}} {{title}} {{description}} {{/each}}
|
|
275
|
+
- {{#each blogs}} {{name}} {{category}} {{/each}}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Managing Device Sessions
|
|
279
|
+
|
|
280
|
+
You can manage your authorized devices from the FastMode dashboard:
|
|
281
|
+
|
|
282
|
+
1. Go to **app.fastmode.ai → Settings → Security**
|
|
283
|
+
2. View all authorized MCP/CLI sessions
|
|
284
|
+
3. Revoke access to any device
|
|
285
|
+
|
|
286
|
+
Credentials are stored locally at `~/.fastmode/credentials.json`.
|
|
140
287
|
|
|
141
288
|
## Key Benefits
|
|
142
289
|
|
|
290
|
+
- **One-click authentication** - No manual token copying
|
|
291
|
+
- **Auto-refresh** - Tokens refresh automatically (90-day sessions)
|
|
292
|
+
- **Project discovery** - `list_projects` shows all accessible projects
|
|
293
|
+
- **Tenant-specific schemas** - `get_tenant_schema` fetches exact custom fields
|
|
143
294
|
- **Validates as you work** - Catch errors during conversion, not after
|
|
144
295
|
- **Correct field names** - No more guessing `{{title}}` vs `{{name}}`
|
|
145
296
|
- **Triple brace reminders** - Ensures rich text uses `{{{...}}}`
|
|
146
297
|
- **Structure validation** - Confirms package layout is correct
|
|
147
|
-
|
|
148
|
-
|
|
298
|
+
|
|
299
|
+
## Environment Variables
|
|
300
|
+
|
|
301
|
+
| Variable | Required | Default | Description |
|
|
302
|
+
|----------|----------|---------|-------------|
|
|
303
|
+
| `FASTMODE_API_URL` | No | `https://api.fastmode.ai` | API base URL |
|
|
304
|
+
| `FASTMODE_AUTH_TOKEN` | No | - | Manual auth token (optional, device flow is preferred) |
|
|
149
305
|
|
|
150
306
|
## License
|
|
151
307
|
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,8 @@ const validate_template_1 = require("./tools/validate-template");
|
|
|
10
10
|
const validate_package_1 = require("./tools/validate-package");
|
|
11
11
|
const get_example_1 = require("./tools/get-example");
|
|
12
12
|
const get_conversion_guide_1 = require("./tools/get-conversion-guide");
|
|
13
|
+
const get_tenant_schema_1 = require("./tools/get-tenant-schema");
|
|
14
|
+
const list_projects_1 = require("./tools/list-projects");
|
|
13
15
|
const server = new index_js_1.Server({
|
|
14
16
|
name: 'multisite-cms',
|
|
15
17
|
version: '1.0.0',
|
|
@@ -139,6 +141,29 @@ const TOOLS = [
|
|
|
139
141
|
required: [],
|
|
140
142
|
},
|
|
141
143
|
},
|
|
144
|
+
{
|
|
145
|
+
name: 'list_projects',
|
|
146
|
+
description: 'List all FastMode projects you have access to. Use this to discover project IDs and names for use with get_tenant_schema. Requires FASTMODE_AUTH_TOKEN environment variable to be set.',
|
|
147
|
+
inputSchema: {
|
|
148
|
+
type: 'object',
|
|
149
|
+
properties: {},
|
|
150
|
+
required: [],
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: 'get_tenant_schema',
|
|
155
|
+
description: 'Fetch the complete schema for a specific project including custom collections and custom fields on built-in collections. Use this when converting a website for an existing project to see their specific CMS configuration. Requires FASTMODE_AUTH_TOKEN environment variable.',
|
|
156
|
+
inputSchema: {
|
|
157
|
+
type: 'object',
|
|
158
|
+
properties: {
|
|
159
|
+
projectId: {
|
|
160
|
+
type: 'string',
|
|
161
|
+
description: 'The project ID (UUID from list_projects) or project name',
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
required: ['projectId'],
|
|
165
|
+
},
|
|
166
|
+
},
|
|
142
167
|
];
|
|
143
168
|
// Handle list tools request
|
|
144
169
|
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
|
|
@@ -179,6 +204,12 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
179
204
|
case 'get_conversion_guide':
|
|
180
205
|
result = await (0, get_conversion_guide_1.getConversionGuide)(params.section || 'full');
|
|
181
206
|
break;
|
|
207
|
+
case 'list_projects':
|
|
208
|
+
result = await (0, list_projects_1.listProjects)();
|
|
209
|
+
break;
|
|
210
|
+
case 'get_tenant_schema':
|
|
211
|
+
result = await (0, get_tenant_schema_1.getTenantSchema)(params.projectId);
|
|
212
|
+
break;
|
|
182
213
|
default:
|
|
183
214
|
return {
|
|
184
215
|
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
|
|
@@ -0,0 +1,65 @@
|
|
|
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
|
+
//# 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,CAO5D;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"}
|
|
@@ -0,0 +1,157 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.getApiUrl = getApiUrl;
|
|
8
|
+
exports.getApiConfigAsync = getApiConfigAsync;
|
|
9
|
+
exports.getApiConfig = getApiConfig;
|
|
10
|
+
exports.isAuthConfigured = isAuthConfigured;
|
|
11
|
+
exports.needsAuthentication = needsAuthentication;
|
|
12
|
+
exports.getAuthRequiredMessage = getAuthRequiredMessage;
|
|
13
|
+
exports.apiRequest = apiRequest;
|
|
14
|
+
exports.isApiError = isApiError;
|
|
15
|
+
exports.needsAuthError = needsAuthError;
|
|
16
|
+
const credentials_1 = require("./credentials");
|
|
17
|
+
/**
|
|
18
|
+
* Get API URL from environment or default
|
|
19
|
+
*/
|
|
20
|
+
function getApiUrl() {
|
|
21
|
+
return process.env.FASTMODE_API_URL || 'https://api.fastmode.ai';
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Get API configuration - tries stored credentials first, then env var
|
|
25
|
+
*/
|
|
26
|
+
async function getApiConfigAsync() {
|
|
27
|
+
// First, try to get valid stored credentials
|
|
28
|
+
const credentials = await (0, credentials_1.getValidCredentials)();
|
|
29
|
+
if (credentials) {
|
|
30
|
+
return {
|
|
31
|
+
apiUrl: getApiUrl(),
|
|
32
|
+
authToken: credentials.accessToken,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
// Fall back to environment variable (for backward compatibility)
|
|
36
|
+
return {
|
|
37
|
+
apiUrl: getApiUrl(),
|
|
38
|
+
authToken: process.env.FASTMODE_AUTH_TOKEN || null,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Synchronous version - only checks env var (for quick checks)
|
|
43
|
+
*/
|
|
44
|
+
function getApiConfig() {
|
|
45
|
+
return {
|
|
46
|
+
apiUrl: getApiUrl(),
|
|
47
|
+
authToken: process.env.FASTMODE_AUTH_TOKEN || null,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Check if authentication is configured (either stored credentials or env var)
|
|
52
|
+
*/
|
|
53
|
+
function isAuthConfigured() {
|
|
54
|
+
return (0, credentials_1.hasCredentials)() || !!process.env.FASTMODE_AUTH_TOKEN;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Check if we need to trigger the device flow
|
|
58
|
+
*/
|
|
59
|
+
async function needsAuthentication() {
|
|
60
|
+
const credentials = await (0, credentials_1.getValidCredentials)();
|
|
61
|
+
if (credentials)
|
|
62
|
+
return false;
|
|
63
|
+
if (process.env.FASTMODE_AUTH_TOKEN)
|
|
64
|
+
return false;
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get a helpful message for authentication
|
|
69
|
+
*/
|
|
70
|
+
function getAuthRequiredMessage() {
|
|
71
|
+
return `# Authentication Required
|
|
72
|
+
|
|
73
|
+
You need to authenticate to use this tool.
|
|
74
|
+
|
|
75
|
+
**Starting authentication flow...**
|
|
76
|
+
|
|
77
|
+
A browser window will open for you to log in. If it doesn't open automatically, you'll see a URL to visit.
|
|
78
|
+
`;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Make an authenticated API request
|
|
82
|
+
* Uses stored credentials with auto-refresh, or falls back to env var
|
|
83
|
+
*/
|
|
84
|
+
async function apiRequest(endpoint, options = {}) {
|
|
85
|
+
const config = await getApiConfigAsync();
|
|
86
|
+
if (!config.authToken) {
|
|
87
|
+
return {
|
|
88
|
+
success: false,
|
|
89
|
+
error: 'Not authenticated',
|
|
90
|
+
statusCode: 401,
|
|
91
|
+
needsAuth: true,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const url = `${config.apiUrl}${endpoint.startsWith('/') ? endpoint : '/' + endpoint}`;
|
|
95
|
+
const headers = {
|
|
96
|
+
'Authorization': `Bearer ${config.authToken}`,
|
|
97
|
+
'Content-Type': 'application/json',
|
|
98
|
+
};
|
|
99
|
+
if (options.tenantId) {
|
|
100
|
+
headers['X-Tenant-Id'] = options.tenantId;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const response = await fetch(url, {
|
|
104
|
+
method: options.method || 'GET',
|
|
105
|
+
headers,
|
|
106
|
+
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
107
|
+
});
|
|
108
|
+
const data = await response.json();
|
|
109
|
+
if (!response.ok) {
|
|
110
|
+
// Check if this is an auth error
|
|
111
|
+
if (response.status === 401) {
|
|
112
|
+
return {
|
|
113
|
+
success: false,
|
|
114
|
+
error: 'Authentication expired or invalid',
|
|
115
|
+
statusCode: 401,
|
|
116
|
+
needsAuth: true,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
success: false,
|
|
121
|
+
error: data.error || `API request failed with status ${response.status}`,
|
|
122
|
+
statusCode: response.status,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return data;
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
129
|
+
if (message.includes('fetch') || message.includes('network') || message.includes('ECONNREFUSED')) {
|
|
130
|
+
return {
|
|
131
|
+
success: false,
|
|
132
|
+
error: `Network error: Unable to connect to ${config.apiUrl}`,
|
|
133
|
+
statusCode: 0,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
success: false,
|
|
138
|
+
error: message,
|
|
139
|
+
statusCode: 0,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Check if a response is an error
|
|
145
|
+
*/
|
|
146
|
+
function isApiError(response) {
|
|
147
|
+
return (typeof response === 'object' &&
|
|
148
|
+
response !== null &&
|
|
149
|
+
'success' in response &&
|
|
150
|
+
response.success === false);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Check if error requires authentication
|
|
154
|
+
*/
|
|
155
|
+
function needsAuthError(error) {
|
|
156
|
+
return error.needsAuth === true || error.statusCode === 401;
|
|
157
|
+
}
|
|
@@ -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"}
|