@robinmordasiewicz/f5xc-terraform-mcp 2.14.11 → 2.15.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.
package/dist/index.js CHANGED
@@ -2,12 +2,20 @@
2
2
  /**
3
3
  * F5 Distributed Cloud Terraform Provider MCP Server
4
4
  *
5
- * This MCP server provides AI assistants with access to:
6
- * - Terraform provider documentation (resources, data sources, functions, guides)
7
- * - F5 Distributed Cloud OpenAPI specifications (270+ specs)
8
- * - Search and query capabilities for both documentation and API specs
9
- * - Subscription tier information for resources and properties
10
- * - Addon service activation workflows
5
+ * Token-optimized MCP server with consolidated tools for AI assistants.
6
+ *
7
+ * This MCP server provides:
8
+ * - Tool discovery meta-tool for lazy loading
9
+ * - Consolidated documentation tool (search/get/list)
10
+ * - Consolidated API specification tool (search/get/endpoints/definitions)
11
+ * - Consolidated subscription tier tool
12
+ * - Consolidated addon service tool
13
+ * - Provider summary tool
14
+ *
15
+ * Token Optimization:
16
+ * - 14 tools consolidated to 6 tools (~77% token reduction)
17
+ * - Optimized descriptions using shared parameter descriptions
18
+ * - Discovery meta-tool enables lazy schema loading
11
19
  *
12
20
  * Version Synchronization:
13
21
  * The npm package version is automatically synced with GitHub releases via CI/CD.
@@ -24,10 +32,18 @@ const require = createRequire(import.meta.url);
24
32
  const packageJson = require('../package.json');
25
33
  const VERSION = packageJson.version;
26
34
  import { ResponseFormat } from './types.js';
27
- import { searchDocumentation, getDocumentation, listDocumentation, getDocumentationSummary, getResourceSubscriptionInfo, getAdvancedTierResources, getSubscriptionSummary, getPropertySubscriptionInfo, getResourceAdvancedProperties, } from './services/documentation.js';
28
- import { searchApiSpecs, getApiSpec, findEndpoints, getSchemaDefinition, listDefinitions, getApiSpecsSummary, } from './services/api-specs.js';
29
- import { listAddonServices, checkAddonActivation, getAddonWorkflow, } from './services/addons.js';
30
- import { SearchDocsSchema, GetDocSchema, ListDocsSchema, SearchApiSpecsSchema, GetApiSpecSchema, FindEndpointsSchema, GetSchemaDefSchema, ListDefinitionsSchema, GetSummarySchema, GetSubscriptionInfoSchema, GetPropertySubscriptionInfoSchema, ListAddonServicesSchema, CheckAddonActivationSchema, GetAddonWorkflowSchema, } from './schemas/index.js';
35
+ import { getDocumentationSummary } from './services/documentation.js';
36
+ import { getApiSpecsSummary } from './services/api-specs.js';
37
+ // Consolidated tool handlers
38
+ import { handleDiscover, DISCOVER_TOOL_DEFINITION } from './tools/discover.js';
39
+ import { handleDocs, DOCS_TOOL_DEFINITION } from './tools/docs.js';
40
+ import { handleApi, API_TOOL_DEFINITION } from './tools/api.js';
41
+ import { handleSubscription, SUBSCRIPTION_TOOL_DEFINITION } from './tools/subscription.js';
42
+ import { handleAddon, ADDON_TOOL_DEFINITION } from './tools/addon.js';
43
+ // Consolidated schemas
44
+ import { DiscoverSchema, DocsSchema, ApiSchema, SubscriptionSchema, AddonSchema, } from './schemas/common.js';
45
+ // Legacy schemas for get_summary tool
46
+ import { GetSummarySchema } from './schemas/index.js';
31
47
  // Constants
32
48
  const CHARACTER_LIMIT = 50000; // Maximum response size
33
49
  // Create MCP server instance
@@ -36,29 +52,12 @@ const server = new McpServer({
36
52
  version: VERSION,
37
53
  });
38
54
  // =============================================================================
39
- // DOCUMENTATION TOOLS
55
+ // TOOL 1: DISCOVERY META-TOOL (NEW)
40
56
  // =============================================================================
41
- server.registerTool('f5xc_terraform_search_docs', {
42
- title: 'Search F5XC Documentation',
43
- description: `Search the F5 Distributed Cloud Terraform provider documentation.
44
-
45
- Searches across all resource documentation, data sources, functions, and guides
46
- to find relevant information about configuring F5XC resources with Terraform.
47
-
48
- Args:
49
- - query (string): Search terms (resource names, attributes, descriptions)
50
- - type (optional): Filter by 'resource', 'data-source', 'function', or 'guide'
51
- - limit (number): Maximum results (default: 20, max: 50)
52
- - response_format: 'markdown' or 'json'
53
-
54
- Returns:
55
- List of matching documentation with relevance scores and snippets.
56
-
57
- Examples:
58
- - "http_loadbalancer" -> Find HTTP load balancer documentation
59
- - "origin pool" -> Find origin pool related docs
60
- - "waf" -> Find Web Application Firewall docs`,
61
- inputSchema: SearchDocsSchema,
57
+ server.registerTool(DISCOVER_TOOL_DEFINITION.name, {
58
+ title: 'Discover F5XC Terraform Tools',
59
+ description: DISCOVER_TOOL_DEFINITION.description,
60
+ inputSchema: DiscoverSchema,
62
61
  annotations: {
63
62
  readOnlyHint: true,
64
63
  destructiveHint: false,
@@ -66,73 +65,18 @@ Examples:
66
65
  openWorldHint: false,
67
66
  },
68
67
  }, async (params) => {
69
- const results = searchDocumentation(params.query, params.type, params.limit);
70
- if (results.length === 0) {
71
- return {
72
- content: [{
73
- type: 'text',
74
- text: `No documentation found matching "${params.query}"`,
75
- }],
76
- };
77
- }
78
- const output = {
79
- query: params.query,
80
- total: results.length,
81
- results: results.map(r => ({
82
- name: r.name,
83
- type: r.type,
84
- score: r.score,
85
- snippet: r.snippet,
86
- })),
87
- };
88
- let textContent;
89
- if (params.response_format === ResponseFormat.MARKDOWN) {
90
- const lines = [
91
- `# Documentation Search Results: "${params.query}"`,
92
- '',
93
- `Found ${results.length} results:`,
94
- '',
95
- ];
96
- for (const result of results) {
97
- lines.push(`## ${result.name} (${result.type})`);
98
- lines.push(`Score: ${result.score}`);
99
- lines.push(result.snippet);
100
- lines.push('');
101
- }
102
- textContent = lines.join('\n');
103
- }
104
- else {
105
- textContent = JSON.stringify(output, null, 2);
106
- }
68
+ const result = await handleDiscover(params);
107
69
  return {
108
- content: [{ type: 'text', text: textContent }],
109
- structuredContent: output,
70
+ content: [{ type: 'text', text: result }],
110
71
  };
111
72
  });
112
- server.registerTool('f5xc_terraform_get_doc', {
113
- title: 'Get F5XC Resource Documentation',
114
- description: `Get complete documentation for a specific F5XC Terraform resource.
115
-
116
- Retrieves the full markdown documentation including:
117
- - Resource description and usage
118
- - Argument reference (all configurable attributes)
119
- - Attribute reference (computed/read-only attributes)
120
- - Import instructions
121
- - Example configurations
122
-
123
- Args:
124
- - name (string): Resource name (e.g., "http_loadbalancer", "namespace", "origin_pool")
125
- - type (optional): 'resource', 'data-source', 'function', or 'guide'
126
- - response_format: 'markdown' or 'json'
127
-
128
- Returns:
129
- Complete documentation content.
130
-
131
- Examples:
132
- - name="http_loadbalancer" -> HTTP Load Balancer resource docs
133
- - name="app_firewall" -> Application Firewall (WAF) docs
134
- - name="blindfold", type="function" -> Blindfold encryption function docs`,
135
- inputSchema: GetDocSchema,
73
+ // =============================================================================
74
+ // TOOL 2: CONSOLIDATED DOCUMENTATION TOOL
75
+ // =============================================================================
76
+ server.registerTool(DOCS_TOOL_DEFINITION.name, {
77
+ title: 'F5XC Terraform Documentation',
78
+ description: DOCS_TOOL_DEFINITION.description,
79
+ inputSchema: DocsSchema,
136
80
  annotations: {
137
81
  readOnlyHint: true,
138
82
  destructiveHint: false,
@@ -140,119 +84,23 @@ Examples:
140
84
  openWorldHint: false,
141
85
  },
142
86
  }, async (params) => {
143
- const doc = getDocumentation(params.name, params.type);
144
- if (!doc) {
145
- return {
146
- content: [{
147
- type: 'text',
148
- text: `Documentation not found for "${params.name}"${params.type ? ` (type: ${params.type})` : ''}. Use f5xc_terraform_search_docs to find available documentation.`,
149
- }],
150
- };
151
- }
152
- const output = {
153
- name: doc.name,
154
- type: doc.type,
155
- path: doc.path,
156
- content: doc.content,
157
- };
158
- let textContent;
159
- if (params.response_format === ResponseFormat.MARKDOWN) {
160
- textContent = doc.content || `# ${doc.name}\n\nNo content available.`;
161
- }
162
- else {
163
- textContent = JSON.stringify(output, null, 2);
164
- }
87
+ const result = await handleDocs(params);
165
88
  // Truncate if too long
89
+ let textContent = result;
166
90
  if (textContent.length > CHARACTER_LIMIT) {
167
91
  textContent = textContent.slice(0, CHARACTER_LIMIT) + '\n\n... (truncated)';
168
92
  }
169
93
  return {
170
94
  content: [{ type: 'text', text: textContent }],
171
- structuredContent: output,
172
- };
173
- });
174
- server.registerTool('f5xc_terraform_list_docs', {
175
- title: 'List F5XC Documentation',
176
- description: `List all available F5 Distributed Cloud Terraform provider documentation.
177
-
178
- Lists all resources, data sources, functions, and guides available in the provider.
179
-
180
- Args:
181
- - type (optional): Filter by 'resource', 'data-source', 'function', or 'guide'
182
- - response_format: 'markdown' or 'json'
183
-
184
- Returns:
185
- List of all available documentation items.`,
186
- inputSchema: ListDocsSchema,
187
- annotations: {
188
- readOnlyHint: true,
189
- destructiveHint: false,
190
- idempotentHint: true,
191
- openWorldHint: false,
192
- },
193
- }, async (params) => {
194
- const docs = listDocumentation(params.type);
195
- const output = {
196
- total: docs.length,
197
- type_filter: params.type || 'all',
198
- items: docs.map(d => ({
199
- name: d.name,
200
- type: d.type,
201
- })),
202
- };
203
- let textContent;
204
- if (params.response_format === ResponseFormat.MARKDOWN) {
205
- const lines = [
206
- `# F5XC Terraform Documentation`,
207
- '',
208
- `Total: ${docs.length} items${params.type ? ` (filtered: ${params.type})` : ''}`,
209
- '',
210
- ];
211
- // Group by type
212
- const byType = {};
213
- for (const doc of docs) {
214
- if (!byType[doc.type])
215
- byType[doc.type] = [];
216
- byType[doc.type].push(doc.name);
217
- }
218
- for (const [type, names] of Object.entries(byType)) {
219
- lines.push(`## ${type}s (${names.length})`);
220
- lines.push(names.sort().join(', '));
221
- lines.push('');
222
- }
223
- textContent = lines.join('\n');
224
- }
225
- else {
226
- textContent = JSON.stringify(output, null, 2);
227
- }
228
- return {
229
- content: [{ type: 'text', text: textContent }],
230
- structuredContent: output,
231
95
  };
232
96
  });
233
97
  // =============================================================================
234
- // API SPECIFICATION TOOLS
98
+ // TOOL 3: CONSOLIDATED API TOOL
235
99
  // =============================================================================
236
- server.registerTool('f5xc_terraform_search_api_specs', {
237
- title: 'Search F5XC API Specifications',
238
- description: `Search the F5 Distributed Cloud OpenAPI specifications.
239
-
240
- The provider includes 270+ OpenAPI specs covering all F5XC API endpoints.
241
- Use this to find API specifications for specific services or operations.
242
-
243
- Args:
244
- - query (string): Search terms (schema names, service names)
245
- - limit (number): Maximum results (default: 20, max: 50)
246
- - response_format: 'markdown' or 'json'
247
-
248
- Returns:
249
- List of matching API specifications with titles and descriptions.
250
-
251
- Examples:
252
- - "http_loadbalancer" -> Find HTTP LB API spec
253
- - "namespace" -> Find namespace management API
254
- - "waf" or "app_firewall" -> Find WAF API specs`,
255
- inputSchema: SearchApiSpecsSchema,
100
+ server.registerTool(API_TOOL_DEFINITION.name, {
101
+ title: 'F5XC OpenAPI Specifications',
102
+ description: API_TOOL_DEFINITION.description,
103
+ inputSchema: ApiSchema,
256
104
  annotations: {
257
105
  readOnlyHint: true,
258
106
  destructiveHint: false,
@@ -260,170 +108,25 @@ Examples:
260
108
  openWorldHint: false,
261
109
  },
262
110
  }, async (params) => {
263
- const results = searchApiSpecs(params.query, params.limit);
264
- if (results.length === 0) {
265
- return {
266
- content: [{
267
- type: 'text',
268
- text: `No API specifications found matching "${params.query}"`,
269
- }],
270
- };
271
- }
272
- const output = {
273
- query: params.query,
274
- total: results.length,
275
- results: results.map(r => ({
276
- name: r.name,
277
- snippet: r.snippet,
278
- score: r.score,
279
- })),
280
- };
281
- let textContent;
282
- if (params.response_format === ResponseFormat.MARKDOWN) {
283
- const lines = [
284
- `# API Specification Search: "${params.query}"`,
285
- '',
286
- `Found ${results.length} specifications:`,
287
- '',
288
- ];
289
- for (const result of results) {
290
- lines.push(`## ${result.name}`);
291
- lines.push(result.snippet);
292
- lines.push('');
293
- }
294
- textContent = lines.join('\n');
295
- }
296
- else {
297
- textContent = JSON.stringify(output, null, 2);
298
- }
299
- return {
300
- content: [{ type: 'text', text: textContent }],
301
- structuredContent: output,
302
- };
303
- });
304
- server.registerTool('f5xc_terraform_get_api_spec', {
305
- title: 'Get F5XC API Specification',
306
- description: `Get a specific F5 Distributed Cloud OpenAPI specification.
307
-
308
- Retrieves the complete OpenAPI spec including:
309
- - API info and description
310
- - Available endpoints and methods
311
- - Request/response schemas
312
- - Authentication requirements
313
-
314
- Args:
315
- - name (string): Spec name (e.g., "http_loadbalancer", "namespace", "app_firewall")
316
- - include_paths (boolean): Include endpoint paths (default: true)
317
- - include_definitions (boolean): Include schema definitions (default: false, can be large)
318
- - response_format: 'markdown' or 'json'
319
-
320
- Returns:
321
- OpenAPI specification content.`,
322
- inputSchema: GetApiSpecSchema,
323
- annotations: {
324
- readOnlyHint: true,
325
- destructiveHint: false,
326
- idempotentHint: true,
327
- openWorldHint: false,
328
- },
329
- }, async (params) => {
330
- const spec = getApiSpec(params.name);
331
- if (!spec?.content) {
332
- return {
333
- content: [{
334
- type: 'text',
335
- text: `API specification not found for "${params.name}". Use f5xc_terraform_search_api_specs to find available specs.`,
336
- }],
337
- };
338
- }
339
- const content = spec.content;
340
- const output = {
341
- name: spec.schemaName,
342
- info: content.info,
343
- };
344
- if (params.include_paths && content.paths) {
345
- output.paths = Object.keys(content.paths).map(path => {
346
- const pathItem = content.paths[path];
347
- const methods = [];
348
- if (pathItem.get)
349
- methods.push('GET');
350
- if (pathItem.post)
351
- methods.push('POST');
352
- if (pathItem.put)
353
- methods.push('PUT');
354
- if (pathItem.delete)
355
- methods.push('DELETE');
356
- if (pathItem.patch)
357
- methods.push('PATCH');
358
- return { path, methods };
359
- });
360
- }
361
- if (params.include_definitions) {
362
- output.definitions = content.definitions || content.components?.schemas || {};
363
- }
364
- let textContent;
365
- if (params.response_format === ResponseFormat.MARKDOWN) {
366
- const lines = [
367
- `# ${content.info?.title || spec.schemaName}`,
368
- '',
369
- content.info?.description || '',
370
- '',
371
- `**Version**: ${content.info?.version || 'N/A'}`,
372
- '',
373
- ];
374
- if (params.include_paths && content.paths) {
375
- lines.push('## Endpoints');
376
- lines.push('');
377
- for (const [path, pathItem] of Object.entries(content.paths)) {
378
- const methods = [];
379
- if (pathItem.get)
380
- methods.push('GET');
381
- if (pathItem.post)
382
- methods.push('POST');
383
- if (pathItem.put)
384
- methods.push('PUT');
385
- if (pathItem.delete)
386
- methods.push('DELETE');
387
- if (pathItem.patch)
388
- methods.push('PATCH');
389
- lines.push(`- \`${methods.join('|')} ${path}\``);
390
- }
391
- lines.push('');
392
- }
393
- textContent = lines.join('\n');
394
- }
395
- else {
396
- textContent = JSON.stringify(output, null, 2);
397
- }
111
+ const result = await handleApi(params);
398
112
  // Truncate if too long
113
+ let textContent = result;
399
114
  if (textContent.length > CHARACTER_LIMIT) {
400
- textContent = textContent.slice(0, CHARACTER_LIMIT) + '\n\n... (truncated, use include_definitions=false for smaller response)';
115
+ textContent =
116
+ textContent.slice(0, CHARACTER_LIMIT) +
117
+ '\n\n... (truncated, use include_definitions=false for smaller response)';
401
118
  }
402
119
  return {
403
120
  content: [{ type: 'text', text: textContent }],
404
- structuredContent: output,
405
121
  };
406
122
  });
407
- server.registerTool('f5xc_terraform_find_endpoints', {
408
- title: 'Find F5XC API Endpoints',
409
- description: `Find API endpoints across all F5XC OpenAPI specifications.
410
-
411
- Searches through 270+ API specs to find endpoints matching a pattern.
412
- Useful for discovering which APIs to use for specific operations.
413
-
414
- Args:
415
- - pattern (string): URL pattern to search (e.g., "/namespaces", "http_loadbalancer")
416
- - method (optional): Filter by HTTP method (GET, POST, PUT, DELETE, PATCH)
417
- - limit (number): Maximum results (default: 20, max: 100)
418
- - response_format: 'markdown' or 'json'
419
-
420
- Returns:
421
- List of matching endpoints with spec name, path, method, and description.
422
-
423
- Examples:
424
- - pattern="/namespaces" -> All namespace-related endpoints
425
- - pattern="config", method="POST" -> POST endpoints for configuration`,
426
- inputSchema: FindEndpointsSchema,
123
+ // =============================================================================
124
+ // TOOL 4: CONSOLIDATED SUBSCRIPTION TOOL
125
+ // =============================================================================
126
+ server.registerTool(SUBSCRIPTION_TOOL_DEFINITION.name, {
127
+ title: 'F5XC Subscription Tiers',
128
+ description: SUBSCRIPTION_TOOL_DEFINITION.description,
129
+ inputSchema: SubscriptionSchema,
427
130
  annotations: {
428
131
  readOnlyHint: true,
429
132
  destructiveHint: false,
@@ -431,63 +134,18 @@ Examples:
431
134
  openWorldHint: false,
432
135
  },
433
136
  }, async (params) => {
434
- const endpoints = findEndpoints(params.pattern, params.method, params.limit);
435
- if (endpoints.length === 0) {
436
- return {
437
- content: [{
438
- type: 'text',
439
- text: `No endpoints found matching pattern "${params.pattern}"${params.method ? ` with method ${params.method}` : ''}`,
440
- }],
441
- };
442
- }
443
- const output = {
444
- pattern: params.pattern,
445
- method_filter: params.method || 'all',
446
- total: endpoints.length,
447
- endpoints,
448
- };
449
- let textContent;
450
- if (params.response_format === ResponseFormat.MARKDOWN) {
451
- const lines = [
452
- `# API Endpoints: "${params.pattern}"`,
453
- '',
454
- `Found ${endpoints.length} endpoints:`,
455
- '',
456
- ];
457
- for (const ep of endpoints) {
458
- lines.push(`## ${ep.method} ${ep.path}`);
459
- lines.push(`**Spec**: ${ep.specName}`);
460
- if (ep.summary)
461
- lines.push(`**Summary**: ${ep.summary}`);
462
- if (ep.operationId)
463
- lines.push(`**Operation ID**: ${ep.operationId}`);
464
- lines.push('');
465
- }
466
- textContent = lines.join('\n');
467
- }
468
- else {
469
- textContent = JSON.stringify(output, null, 2);
470
- }
137
+ const result = await handleSubscription(params);
471
138
  return {
472
- content: [{ type: 'text', text: textContent }],
473
- structuredContent: output,
139
+ content: [{ type: 'text', text: result }],
474
140
  };
475
141
  });
476
- server.registerTool('f5xc_terraform_get_schema_definition', {
477
- title: 'Get API Schema Definition',
478
- description: `Get a specific schema definition from an F5XC OpenAPI spec.
479
-
480
- Retrieves the JSON schema for a specific type definition, useful for
481
- understanding the structure of API request/response objects.
482
-
483
- Args:
484
- - spec_name (string): Name of the API spec
485
- - definition_name (string): Name of the schema definition
486
- - response_format: 'markdown' or 'json'
487
-
488
- Returns:
489
- JSON schema definition with properties, types, and descriptions.`,
490
- inputSchema: GetSchemaDefSchema,
142
+ // =============================================================================
143
+ // TOOL 5: CONSOLIDATED ADDON TOOL
144
+ // =============================================================================
145
+ server.registerTool(ADDON_TOOL_DEFINITION.name, {
146
+ title: 'F5XC Addon Services',
147
+ description: ADDON_TOOL_DEFINITION.description,
148
+ inputSchema: AddonSchema,
491
149
  annotations: {
492
150
  readOnlyHint: true,
493
151
  destructiveHint: false,
@@ -495,118 +153,22 @@ Returns:
495
153
  openWorldHint: false,
496
154
  },
497
155
  }, async (params) => {
498
- const definition = getSchemaDefinition(params.spec_name, params.definition_name);
499
- if (!definition) {
500
- return {
501
- content: [{
502
- type: 'text',
503
- text: `Schema definition "${params.definition_name}" not found in spec "${params.spec_name}". Use f5xc_terraform_list_definitions to see available definitions.`,
504
- }],
505
- };
506
- }
507
- const output = {
508
- spec_name: params.spec_name,
509
- definition_name: params.definition_name,
510
- schema: definition,
511
- };
512
- let textContent;
513
- if (params.response_format === ResponseFormat.MARKDOWN) {
514
- const lines = [
515
- `# Schema: ${params.definition_name}`,
516
- '',
517
- `**Spec**: ${params.spec_name}`,
518
- '',
519
- '```json',
520
- JSON.stringify(definition, null, 2),
521
- '```',
522
- ];
523
- textContent = lines.join('\n');
524
- }
525
- else {
526
- textContent = JSON.stringify(output, null, 2);
527
- }
156
+ const result = await handleAddon(params);
528
157
  // Truncate if too long
158
+ let textContent = result;
529
159
  if (textContent.length > CHARACTER_LIMIT) {
530
160
  textContent = textContent.slice(0, CHARACTER_LIMIT) + '\n\n... (truncated)';
531
161
  }
532
162
  return {
533
163
  content: [{ type: 'text', text: textContent }],
534
- structuredContent: output,
535
- };
536
- });
537
- server.registerTool('f5xc_terraform_list_definitions', {
538
- title: 'List API Schema Definitions',
539
- description: `List all schema definitions in an F5XC OpenAPI spec.
540
-
541
- Lists the names of all type definitions available in a specification,
542
- useful for discovering what schemas are available to query.
543
-
544
- Args:
545
- - spec_name (string): Name of the API spec
546
- - response_format: 'markdown' or 'json'
547
-
548
- Returns:
549
- List of definition names in the spec.`,
550
- inputSchema: ListDefinitionsSchema,
551
- annotations: {
552
- readOnlyHint: true,
553
- destructiveHint: false,
554
- idempotentHint: true,
555
- openWorldHint: false,
556
- },
557
- }, async (params) => {
558
- const definitions = listDefinitions(params.spec_name);
559
- if (definitions.length === 0) {
560
- return {
561
- content: [{
562
- type: 'text',
563
- text: `No definitions found in spec "${params.spec_name}". Use f5xc_terraform_search_api_specs to find available specs.`,
564
- }],
565
- };
566
- }
567
- const output = {
568
- spec_name: params.spec_name,
569
- total: definitions.length,
570
- definitions,
571
- };
572
- let textContent;
573
- if (params.response_format === ResponseFormat.MARKDOWN) {
574
- const lines = [
575
- `# Schema Definitions: ${params.spec_name}`,
576
- '',
577
- `Total: ${definitions.length} definitions`,
578
- '',
579
- definitions.sort().join(', '),
580
- ];
581
- textContent = lines.join('\n');
582
- }
583
- else {
584
- textContent = JSON.stringify(output, null, 2);
585
- }
586
- return {
587
- content: [{ type: 'text', text: textContent }],
588
- structuredContent: output,
589
164
  };
590
165
  });
591
166
  // =============================================================================
592
- // SUMMARY TOOL
167
+ // TOOL 6: SUMMARY TOOL (KEEP AS-IS)
593
168
  // =============================================================================
594
169
  server.registerTool('f5xc_terraform_get_summary', {
595
- title: 'Get F5XC Provider Summary',
596
- description: `Get a summary of all available F5 Distributed Cloud Terraform provider documentation and API specifications.
597
-
598
- Provides an overview of:
599
- - Total number of resources, data sources, functions, and guides
600
- - Total number of API specifications
601
- - Categories and counts
602
-
603
- Useful as a starting point to understand what's available.
604
-
605
- Args:
606
- - response_format: 'markdown' or 'json'
607
-
608
- Returns:
609
- Summary statistics of all documentation and API specs.`,
170
+ title: 'F5XC Provider Summary',
171
+ description: 'Get provider documentation and API specs summary',
610
172
  inputSchema: GetSummarySchema,
611
173
  annotations: {
612
174
  readOnlyHint: true,
@@ -645,448 +207,16 @@ Returns:
645
207
  lines.push('');
646
208
  lines.push(`Total: ${apiSummary.total} OpenAPI specs`);
647
209
  lines.push('');
648
- lines.push('### Available Tools');
210
+ lines.push('## Available Tools (Token-Optimized)');
649
211
  lines.push('');
650
- lines.push('- `f5xc_terraform_search_docs` - Search documentation');
651
- lines.push('- `f5xc_terraform_get_doc` - Get specific resource documentation');
652
- lines.push('- `f5xc_terraform_list_docs` - List all documentation');
653
- lines.push('- `f5xc_terraform_search_api_specs` - Search API specifications');
654
- lines.push('- `f5xc_terraform_get_api_spec` - Get specific API spec');
655
- lines.push('- `f5xc_terraform_find_endpoints` - Find API endpoints');
656
- lines.push('- `f5xc_terraform_get_schema_definition` - Get schema definition');
657
- lines.push('- `f5xc_terraform_list_definitions` - List definitions in a spec');
658
- lines.push('- `f5xc_terraform_get_subscription_info` - Check resource subscription tier requirements');
659
- lines.push('- `f5xc_terraform_get_property_subscription_info` - Check property-level subscription tier requirements');
660
- lines.push('- `f5xc_terraform_addon_list_services` - List all addon services with tier requirements');
661
- lines.push('- `f5xc_terraform_addon_check_activation` - Check activation requirements for an addon');
662
- lines.push('- `f5xc_terraform_addon_activation_workflow` - Get detailed activation workflow with Terraform config');
663
- textContent = lines.join('\n');
664
- }
665
- else {
666
- textContent = JSON.stringify(output, null, 2);
667
- }
668
- return {
669
- content: [{ type: 'text', text: textContent }],
670
- structuredContent: output,
671
- };
672
- });
673
- // =============================================================================
674
- // SUBSCRIPTION TIER TOOL
675
- // =============================================================================
676
- server.registerTool('f5xc_terraform_get_subscription_info', {
677
- title: 'Get F5XC Subscription Tier Info',
678
- description: `Get subscription tier requirements for F5 Distributed Cloud resources.
679
-
680
- Returns information about which resources require an Advanced subscription tier.
681
- Only Standard and Advanced subscription tiers are available.
682
- Resources not requiring Advanced are available with Standard subscription.
683
-
684
- Args:
685
- - resource (optional): Specific resource name to check
686
- - tier (optional): Filter by 'STANDARD' or 'ADVANCED'
687
- - response_format: 'markdown' or 'json'
688
-
689
- Returns:
690
- - For specific resource: Tier requirement and any Advanced-only features
691
- - For no resource specified: List of all resources requiring Advanced tier
692
-
693
- Examples:
694
- - resource="http_loadbalancer" -> Shows features requiring Advanced
695
- - tier="ADVANCED" -> Lists all resources requiring Advanced subscription
696
- - No args -> Summary of Advanced tier requirements`,
697
- inputSchema: GetSubscriptionInfoSchema,
698
- annotations: {
699
- readOnlyHint: true,
700
- destructiveHint: false,
701
- idempotentHint: true,
702
- openWorldHint: false,
703
- },
704
- }, async (params) => {
705
- // Query for specific resource
706
- if (params.resource) {
707
- const info = getResourceSubscriptionInfo(params.resource);
708
- if (!info) {
709
- return {
710
- content: [{
711
- type: 'text',
712
- text: `No subscription information found for resource "${params.resource}". The resource may not exist or may not have tier metadata.`,
713
- }],
714
- };
715
- }
716
- const output = {
717
- resource: params.resource,
718
- tier: info.tier,
719
- service: info.service,
720
- requires_advanced: info.requiresAdvanced,
721
- advanced_features: info.advancedFeatures || [],
722
- };
723
- let textContent;
724
- if (params.response_format === ResponseFormat.MARKDOWN) {
725
- const lines = [
726
- `# Subscription Info: ${params.resource}`,
727
- '',
728
- `**Minimum Tier**: ${info.tier}`,
729
- `**Service**: ${info.service}`,
730
- `**Requires Advanced**: ${info.requiresAdvanced ? 'Yes' : 'No'}`,
731
- '',
732
- ];
733
- if (info.advancedFeatures && info.advancedFeatures.length > 0) {
734
- lines.push('## Features Requiring Advanced Subscription');
735
- lines.push('');
736
- for (const feature of info.advancedFeatures) {
737
- lines.push(`- \`${feature}\``);
738
- }
739
- }
740
- else if (!info.requiresAdvanced) {
741
- lines.push('This resource is fully available with a Standard subscription.');
742
- }
743
- textContent = lines.join('\n');
744
- }
745
- else {
746
- textContent = JSON.stringify(output, null, 2);
747
- }
748
- return {
749
- content: [{ type: 'text', text: textContent }],
750
- structuredContent: output,
751
- };
752
- }
753
- // List all Advanced tier resources
754
- const advancedResources = getAdvancedTierResources();
755
- const summary = getSubscriptionSummary();
756
- // Filter by tier if specified
757
- let filteredResources = advancedResources;
758
- if (params.tier === 'ADVANCED') {
759
- filteredResources = advancedResources.filter(r => r.subscriptionTier === 'ADVANCED');
760
- }
761
- const output = {
762
- summary: {
763
- total_resources: summary.totalResources,
764
- advanced_only_resources: summary.advancedOnlyResources,
765
- resources_with_advanced_features: summary.resourcesWithAdvancedFeatures,
766
- advanced_features: summary.advancedFeaturesList,
767
- },
768
- resources: filteredResources.map(r => ({
769
- name: r.name,
770
- type: r.type,
771
- tier: r.subscriptionTier,
772
- service: r.addonService,
773
- advanced_features: r.advancedFeatures,
774
- })),
775
- };
776
- let textContent;
777
- if (params.response_format === ResponseFormat.MARKDOWN) {
778
- const lines = [
779
- '# F5XC Subscription Tier Requirements',
780
- '',
781
- '## Summary',
782
- '',
783
- `- **Total Resources**: ${summary.totalResources}`,
784
- `- **Resources Requiring Advanced**: ${summary.advancedOnlyResources}`,
785
- `- **Resources with Advanced Features**: ${summary.resourcesWithAdvancedFeatures}`,
786
- '',
787
- ];
788
- if (filteredResources.length > 0) {
789
- lines.push('## Resources Requiring Advanced Subscription');
790
- lines.push('');
791
- for (const res of filteredResources) {
792
- lines.push(`### ${res.name} (${res.type})`);
793
- if (res.subscriptionTier === 'ADVANCED') {
794
- lines.push('**Requires Advanced subscription**');
795
- }
796
- if (res.advancedFeatures && res.advancedFeatures.length > 0) {
797
- lines.push('');
798
- lines.push('Advanced-only features:');
799
- for (const feat of res.advancedFeatures) {
800
- lines.push(`- \`${feat}\``);
801
- }
802
- }
803
- lines.push('');
804
- }
805
- }
806
- if (summary.advancedFeaturesList.length > 0) {
807
- lines.push('## All Advanced Features');
808
- lines.push('');
809
- lines.push(summary.advancedFeaturesList.map(f => `\`${f}\``).join(', '));
810
- }
811
- textContent = lines.join('\n');
812
- }
813
- else {
814
- textContent = JSON.stringify(output, null, 2);
815
- }
816
- return {
817
- content: [{ type: 'text', text: textContent }],
818
- structuredContent: output,
819
- };
820
- });
821
- // =============================================================================
822
- // PROPERTY SUBSCRIPTION TIER TOOL
823
- // =============================================================================
824
- server.registerTool('f5xc_terraform_get_property_subscription_info', {
825
- title: 'Get Property Subscription Tier Info',
826
- description: `Get subscription tier requirements for a specific property within an F5XC resource.
827
-
828
- Checks if a specific property/attribute requires the Advanced subscription tier.
829
- This is useful for determining if a particular configuration option is available
830
- with your current subscription level.
831
-
832
- Args:
833
- - resource (string): Resource name (e.g., "http_loadbalancer", "app_firewall")
834
- - property (optional): Property name to check (e.g., "enable_malicious_user_detection")
835
- If omitted, returns all advanced features for the resource
836
- - response_format: 'markdown' or 'json'
837
-
838
- Returns:
839
- - For specific property: Whether it requires Advanced subscription and matched feature
840
- - For no property: List of all Advanced-only features for the resource
841
-
842
- Examples:
843
- - resource="http_loadbalancer", property="enable_malicious_user_detection" -> Advanced required
844
- - resource="http_loadbalancer", property="domains" -> Standard (no advanced requirement)
845
- - resource="http_loadbalancer" -> Lists all advanced features for HTTP LB`,
846
- inputSchema: GetPropertySubscriptionInfoSchema,
847
- annotations: {
848
- readOnlyHint: true,
849
- destructiveHint: false,
850
- idempotentHint: true,
851
- openWorldHint: false,
852
- },
853
- }, async (params) => {
854
- // Query for specific property
855
- if (params.property) {
856
- const info = getPropertySubscriptionInfo(params.resource, params.property);
857
- if (!info) {
858
- return {
859
- content: [{
860
- type: 'text',
861
- text: `No subscription information found for resource "${params.resource}". The resource may not exist or may not have tier metadata.`,
862
- }],
863
- };
864
- }
865
- const output = {
866
- resource: info.resourceName,
867
- property: info.propertyName,
868
- requires_advanced: info.requiresAdvanced,
869
- matched_feature: info.matchedFeature || null,
870
- resource_tier: info.resourceTier,
871
- service: info.service,
872
- };
873
- let textContent;
874
- if (params.response_format === ResponseFormat.MARKDOWN) {
875
- const lines = [
876
- `# Property Subscription Info`,
877
- '',
878
- `**Resource**: \`${info.resourceName}\``,
879
- `**Property**: \`${info.propertyName}\``,
880
- '',
881
- `**Requires Advanced Subscription**: ${info.requiresAdvanced ? '**Yes**' : 'No'}`,
882
- ];
883
- if (info.matchedFeature) {
884
- lines.push(`**Matched Feature**: \`${info.matchedFeature}\``);
885
- lines.push('');
886
- lines.push('> ⚠️ This property requires an Advanced subscription to use.');
887
- }
888
- else {
889
- lines.push('');
890
- lines.push('✓ This property is available with a Standard subscription.');
891
- }
892
- lines.push('');
893
- lines.push(`**Resource Tier**: ${info.resourceTier}`);
894
- lines.push(`**Service**: ${info.service}`);
895
- textContent = lines.join('\n');
896
- }
897
- else {
898
- textContent = JSON.stringify(output, null, 2);
899
- }
900
- return {
901
- content: [{ type: 'text', text: textContent }],
902
- structuredContent: output,
903
- };
904
- }
905
- // List all advanced features for the resource
906
- const resourceInfo = getResourceAdvancedProperties(params.resource);
907
- if (!resourceInfo) {
908
- return {
909
- content: [{
910
- type: 'text',
911
- text: `No subscription information found for resource "${params.resource}". The resource may not exist or may not have tier metadata.`,
912
- }],
913
- };
914
- }
915
- const output = {
916
- resource: resourceInfo.resourceName,
917
- resource_tier: resourceInfo.resourceTier,
918
- service: resourceInfo.service,
919
- advanced_features: resourceInfo.advancedFeatures,
920
- has_advanced_features: resourceInfo.advancedFeatures.length > 0,
921
- };
922
- let textContent;
923
- if (params.response_format === ResponseFormat.MARKDOWN) {
924
- const lines = [
925
- `# Advanced Features: ${resourceInfo.resourceName}`,
926
- '',
927
- `**Resource Tier**: ${resourceInfo.resourceTier}`,
928
- `**Service**: ${resourceInfo.service}`,
929
- '',
930
- ];
931
- if (resourceInfo.advancedFeatures.length > 0) {
932
- lines.push('## Properties Requiring Advanced Subscription');
933
- lines.push('');
934
- lines.push('The following features/properties require an Advanced subscription:');
935
- lines.push('');
936
- for (const feature of resourceInfo.advancedFeatures) {
937
- lines.push(`- \`${feature}\``);
938
- }
939
- lines.push('');
940
- lines.push('> Note: Property names may vary (e.g., `enable_malicious_user_detection` for feature `malicious_user_detection`)');
941
- }
942
- else {
943
- lines.push('✓ All properties of this resource are available with a Standard subscription.');
944
- }
945
- textContent = lines.join('\n');
946
- }
947
- else {
948
- textContent = JSON.stringify(output, null, 2);
949
- }
950
- return {
951
- content: [{ type: 'text', text: textContent }],
952
- structuredContent: output,
953
- };
954
- });
955
- // =============================================================================
956
- // ADDON SERVICE TOOLS
957
- // =============================================================================
958
- server.registerTool('f5xc_terraform_addon_list_services', {
959
- title: 'List F5XC Addon Services',
960
- description: `List all available F5 Distributed Cloud addon services with their activation types and tier requirements.
961
-
962
- Addon services are additional features that can be activated for your F5XC tenant,
963
- such as Bot Defense, Client Side Defense, API Discovery, and more.
964
-
965
- Args:
966
- - tier (optional): Filter by 'STANDARD', 'ADVANCED', or 'PREMIUM'
967
- - activation_type (optional): Filter by 'self' (user-activated) or 'managed' (requires sales contact)
968
- - response_format: 'markdown' or 'json'
969
-
970
- Returns:
971
- List of addon services with name, tier requirement, activation type, and description.
972
-
973
- Examples:
974
- - tier="ADVANCED" -> List all Advanced tier addon services
975
- - activation_type="self" -> List all self-activatable services`,
976
- inputSchema: ListAddonServicesSchema,
977
- annotations: {
978
- readOnlyHint: true,
979
- destructiveHint: false,
980
- idempotentHint: true,
981
- openWorldHint: false,
982
- },
983
- }, async (params) => {
984
- const result = listAddonServices(params.tier, params.activation_type);
985
- const output = {
986
- total: result.total,
987
- filters: {
988
- tier: params.tier || 'all',
989
- activation_type: params.activation_type || 'all',
990
- },
991
- services: result.services,
992
- };
993
- let textContent;
994
- if (params.response_format === ResponseFormat.MARKDOWN) {
995
- const lines = [
996
- '# F5XC Addon Services',
997
- '',
998
- `Total: ${result.total} services`,
999
- '',
1000
- ];
1001
- for (const service of result.services) {
1002
- lines.push(`## ${service.displayName} (\`${service.name}\`)`);
1003
- lines.push('');
1004
- lines.push(service.description);
1005
- lines.push('');
1006
- lines.push(`- **Tier**: ${service.tier}`);
1007
- lines.push(`- **Activation Type**: ${service.activationType}`);
1008
- lines.push(`- **Category**: ${service.category}`);
1009
- lines.push('');
1010
- }
1011
- textContent = lines.join('\n');
1012
- }
1013
- else {
1014
- textContent = JSON.stringify(output, null, 2);
1015
- }
1016
- return {
1017
- content: [{ type: 'text', text: textContent }],
1018
- structuredContent: output,
1019
- };
1020
- });
1021
- server.registerTool('f5xc_terraform_addon_check_activation', {
1022
- title: 'Check Addon Activation Requirements',
1023
- description: `Check activation requirements for a specific F5XC addon service.
1024
-
1025
- Provides information about whether an addon service can be activated,
1026
- the required subscription tier, and step-by-step activation guidance.
1027
-
1028
- Args:
1029
- - addon_service (string): Name of the addon service (e.g., "bot_defense", "client_side_defense")
1030
- - response_format: 'markdown' or 'json'
1031
-
1032
- Returns:
1033
- - Activation status (can activate, requirements)
1034
- - Required subscription tier
1035
- - Activation type (self, partial, managed)
1036
- - Step-by-step activation process
1037
- - Terraform example configuration
1038
-
1039
- Examples:
1040
- - addon_service="bot_defense" -> Bot Defense activation requirements
1041
- - addon_service="api_discovery" -> API Discovery activation info`,
1042
- inputSchema: CheckAddonActivationSchema,
1043
- annotations: {
1044
- readOnlyHint: true,
1045
- destructiveHint: false,
1046
- idempotentHint: true,
1047
- openWorldHint: false,
1048
- },
1049
- }, async (params) => {
1050
- const result = checkAddonActivation(params.addon_service);
1051
- if (!result) {
1052
- return {
1053
- content: [{
1054
- type: 'text',
1055
- text: `Addon service "${params.addon_service}" not found. Use f5xc_terraform_addon_list_services to see available services.`,
1056
- }],
1057
- };
1058
- }
1059
- const output = {
1060
- addon_service: result.addonService,
1061
- display_name: result.displayName,
1062
- tier: result.tier,
1063
- activation_type: result.activationType,
1064
- can_activate: result.canActivate,
1065
- steps: result.steps,
1066
- terraform_example: result.terraformExample,
1067
- };
1068
- let textContent;
1069
- if (params.response_format === ResponseFormat.MARKDOWN) {
1070
- const lines = [
1071
- `# Activation Requirements: ${result.displayName}`,
1072
- '',
1073
- `**Addon Service**: \`${result.addonService}\``,
1074
- `**Required Tier**: ${result.tier}`,
1075
- `**Activation Type**: ${result.activationType}`,
1076
- `**Can Activate Directly**: ${result.canActivate ? 'Yes' : 'No (requires sales contact)'}`,
1077
- '',
1078
- '## Activation Steps',
1079
- '',
1080
- ];
1081
- result.steps.forEach((step, index) => {
1082
- lines.push(`${index + 1}. ${step}`);
1083
- });
212
+ lines.push('- `f5xc_terraform_discover` - Discover available tools with optional schema details');
213
+ lines.push('- `f5xc_terraform_docs` - Search, get, or list documentation (operations: search, get, list)');
214
+ lines.push('- `f5xc_terraform_api` - Query API specs (operations: search, get, find_endpoints, get_definition, list_definitions)');
215
+ lines.push('- `f5xc_terraform_subscription` - Check subscription tiers (operations: resource, property)');
216
+ lines.push('- `f5xc_terraform_addon` - Addon services (operations: list, check, workflow)');
217
+ lines.push('- `f5xc_terraform_get_summary` - This summary');
1084
218
  lines.push('');
1085
- lines.push('## Terraform Configuration');
1086
- lines.push('');
1087
- lines.push('```hcl');
1088
- lines.push(result.terraformExample);
1089
- lines.push('```');
219
+ lines.push('> **Token Optimization**: 14 tools consolidated to 6 tools (~77% reduction)');
1090
220
  textContent = lines.join('\n');
1091
221
  }
1092
222
  else {
@@ -1097,120 +227,15 @@ Examples:
1097
227
  structuredContent: output,
1098
228
  };
1099
229
  });
1100
- server.registerTool('f5xc_terraform_addon_activation_workflow', {
1101
- title: 'Get Addon Activation Workflow',
1102
- description: `Get a detailed step-by-step activation workflow for an F5XC addon service.
1103
-
1104
- Provides comprehensive guidance for activating an addon service including:
1105
- - Prerequisites and requirements
1106
- - Step-by-step instructions with Terraform snippets
1107
- - Complete Terraform configuration
1108
- - Estimated activation time
1109
- - Important notes and considerations
1110
-
1111
- Args:
1112
- - addon_service (string): Name of the addon service
1113
- - activation_type (optional): Override workflow type ('self', 'partial', 'managed')
1114
- - response_format: 'markdown' or 'json'
1115
-
1116
- Returns:
1117
- Complete activation workflow with Terraform configuration examples.
1118
-
1119
- Examples:
1120
- - addon_service="bot_defense" -> Self-activation workflow
1121
- - addon_service="bot_defense", activation_type="partial" -> Partial managed workflow`,
1122
- inputSchema: GetAddonWorkflowSchema,
1123
- annotations: {
1124
- readOnlyHint: true,
1125
- destructiveHint: false,
1126
- idempotentHint: true,
1127
- openWorldHint: false,
1128
- },
1129
- }, async (params) => {
1130
- const result = getAddonWorkflow(params.addon_service, params.activation_type);
1131
- if (!result) {
1132
- return {
1133
- content: [{
1134
- type: 'text',
1135
- text: `Addon service "${params.addon_service}" not found or invalid activation type. Use f5xc_terraform_addon_list_services to see available services.`,
1136
- }],
1137
- };
1138
- }
1139
- const output = {
1140
- addon_service: result.addonService,
1141
- activation_type: result.activationType,
1142
- description: result.description,
1143
- prerequisites: result.prerequisites,
1144
- steps: result.steps,
1145
- terraform_config: result.terraformConfig,
1146
- estimated_time: result.estimatedTime,
1147
- notes: result.notes,
1148
- };
1149
- let textContent;
1150
- if (params.response_format === ResponseFormat.MARKDOWN) {
1151
- const lines = [
1152
- `# Activation Workflow: ${result.addonService}`,
1153
- '',
1154
- result.description,
1155
- '',
1156
- `**Activation Type**: ${result.activationType}`,
1157
- `**Estimated Time**: ${result.estimatedTime}`,
1158
- '',
1159
- '## Prerequisites',
1160
- '',
1161
- ];
1162
- for (const prereq of result.prerequisites) {
1163
- lines.push(`- ${prereq}`);
1164
- }
1165
- lines.push('');
1166
- lines.push('## Step-by-Step Instructions');
1167
- lines.push('');
1168
- for (const step of result.steps) {
1169
- lines.push(`### Step ${step.step}: ${step.action}`);
1170
- lines.push('');
1171
- lines.push(step.description);
1172
- if (step.terraformSnippet) {
1173
- lines.push('');
1174
- lines.push('```hcl');
1175
- lines.push(step.terraformSnippet);
1176
- lines.push('```');
1177
- }
1178
- lines.push('');
1179
- }
1180
- lines.push('## Complete Terraform Configuration');
1181
- lines.push('');
1182
- lines.push('```hcl');
1183
- lines.push(result.terraformConfig);
1184
- lines.push('```');
1185
- lines.push('');
1186
- lines.push('## Notes');
1187
- lines.push('');
1188
- for (const note of result.notes) {
1189
- lines.push(`- ${note}`);
1190
- }
1191
- textContent = lines.join('\n');
1192
- }
1193
- else {
1194
- textContent = JSON.stringify(output, null, 2);
1195
- }
1196
- // Truncate if too long
1197
- if (textContent.length > CHARACTER_LIMIT) {
1198
- textContent = textContent.slice(0, CHARACTER_LIMIT) + '\n\n... (truncated)';
1199
- }
1200
- return {
1201
- content: [{ type: 'text', text: textContent }],
1202
- structuredContent: output,
1203
- };
1204
- });
1205
230
  // =============================================================================
1206
231
  // SERVER STARTUP
1207
232
  // =============================================================================
1208
233
  async function main() {
1209
234
  const transport = new StdioServerTransport();
1210
235
  await server.connect(transport);
1211
- console.error('F5XC Terraform MCP server running on stdio');
236
+ console.error('F5XC Terraform MCP server running on stdio (token-optimized)');
1212
237
  }
1213
- main().catch(error => {
238
+ main().catch((error) => {
1214
239
  console.error('Server error:', error);
1215
240
  process.exit(1);
1216
241
  });