mcp-openapi-schema-explorer 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 (133) hide show
  1. package/.devcontainer/devcontainer.json +24 -0
  2. package/.github/dependabot.yml +13 -0
  3. package/.github/workflows/ci.yml +111 -0
  4. package/.husky/pre-commit +6 -0
  5. package/.prettierignore +3 -0
  6. package/.prettierrc.json +12 -0
  7. package/.releaserc.json +23 -0
  8. package/CHANGELOG.md +32 -0
  9. package/CONTRIBUTING.md +67 -0
  10. package/Dockerfile +3 -0
  11. package/LICENSE +21 -0
  12. package/README.md +127 -0
  13. package/dist/src/config.d.ts +15 -0
  14. package/dist/src/config.js +19 -0
  15. package/dist/src/config.js.map +1 -0
  16. package/dist/src/handlers/component-detail-handler.d.ts +14 -0
  17. package/dist/src/handlers/component-detail-handler.js +87 -0
  18. package/dist/src/handlers/component-detail-handler.js.map +1 -0
  19. package/dist/src/handlers/component-map-handler.d.ts +14 -0
  20. package/dist/src/handlers/component-map-handler.js +63 -0
  21. package/dist/src/handlers/component-map-handler.js.map +1 -0
  22. package/dist/src/handlers/handler-utils.d.ts +69 -0
  23. package/dist/src/handlers/handler-utils.js +180 -0
  24. package/dist/src/handlers/handler-utils.js.map +1 -0
  25. package/dist/src/handlers/operation-handler.d.ts +14 -0
  26. package/dist/src/handlers/operation-handler.js +86 -0
  27. package/dist/src/handlers/operation-handler.js.map +1 -0
  28. package/dist/src/handlers/path-item-handler.d.ts +14 -0
  29. package/dist/src/handlers/path-item-handler.js +66 -0
  30. package/dist/src/handlers/path-item-handler.js.map +1 -0
  31. package/dist/src/handlers/top-level-field-handler.d.ts +14 -0
  32. package/dist/src/handlers/top-level-field-handler.js +72 -0
  33. package/dist/src/handlers/top-level-field-handler.js.map +1 -0
  34. package/dist/src/index.d.ts +2 -0
  35. package/dist/src/index.js +177 -0
  36. package/dist/src/index.js.map +1 -0
  37. package/dist/src/rendering/components.d.ts +67 -0
  38. package/dist/src/rendering/components.js +177 -0
  39. package/dist/src/rendering/components.js.map +1 -0
  40. package/dist/src/rendering/document.d.ts +36 -0
  41. package/dist/src/rendering/document.js +147 -0
  42. package/dist/src/rendering/document.js.map +1 -0
  43. package/dist/src/rendering/path-item.d.ts +45 -0
  44. package/dist/src/rendering/path-item.js +141 -0
  45. package/dist/src/rendering/path-item.js.map +1 -0
  46. package/dist/src/rendering/paths.d.ts +26 -0
  47. package/dist/src/rendering/paths.js +78 -0
  48. package/dist/src/rendering/paths.js.map +1 -0
  49. package/dist/src/rendering/types.d.ts +50 -0
  50. package/dist/src/rendering/types.js +12 -0
  51. package/dist/src/rendering/types.js.map +1 -0
  52. package/dist/src/rendering/utils.d.ts +31 -0
  53. package/dist/src/rendering/utils.js +79 -0
  54. package/dist/src/rendering/utils.js.map +1 -0
  55. package/dist/src/services/formatters.d.ts +36 -0
  56. package/dist/src/services/formatters.js +52 -0
  57. package/dist/src/services/formatters.js.map +1 -0
  58. package/dist/src/services/reference-transform.d.ts +27 -0
  59. package/dist/src/services/reference-transform.js +75 -0
  60. package/dist/src/services/reference-transform.js.map +1 -0
  61. package/dist/src/services/spec-loader.d.ts +27 -0
  62. package/dist/src/services/spec-loader.js +77 -0
  63. package/dist/src/services/spec-loader.js.map +1 -0
  64. package/dist/src/types.d.ts +11 -0
  65. package/dist/src/types.js +2 -0
  66. package/dist/src/types.js.map +1 -0
  67. package/dist/src/utils/uri-builder.d.ts +81 -0
  68. package/dist/src/utils/uri-builder.js +121 -0
  69. package/dist/src/utils/uri-builder.js.map +1 -0
  70. package/dist/src/version.d.ts +1 -0
  71. package/dist/src/version.js +4 -0
  72. package/dist/src/version.js.map +1 -0
  73. package/eslint.config.js +88 -0
  74. package/jest.config.js +32 -0
  75. package/justfile +66 -0
  76. package/memory-bank/activeContext.md +139 -0
  77. package/memory-bank/productContext.md +39 -0
  78. package/memory-bank/progress.md +141 -0
  79. package/memory-bank/projectbrief.md +50 -0
  80. package/memory-bank/systemPatterns.md +224 -0
  81. package/memory-bank/techContext.md +131 -0
  82. package/package.json +76 -0
  83. package/scripts/generate-version.js +49 -0
  84. package/src/config.ts +33 -0
  85. package/src/handlers/component-detail-handler.ts +121 -0
  86. package/src/handlers/component-map-handler.ts +92 -0
  87. package/src/handlers/handler-utils.ts +230 -0
  88. package/src/handlers/operation-handler.ts +114 -0
  89. package/src/handlers/path-item-handler.ts +88 -0
  90. package/src/handlers/top-level-field-handler.ts +92 -0
  91. package/src/index.ts +222 -0
  92. package/src/rendering/components.ts +228 -0
  93. package/src/rendering/document.ts +167 -0
  94. package/src/rendering/path-item.ts +157 -0
  95. package/src/rendering/paths.ts +87 -0
  96. package/src/rendering/types.ts +63 -0
  97. package/src/rendering/utils.ts +107 -0
  98. package/src/services/formatters.ts +71 -0
  99. package/src/services/reference-transform.ts +105 -0
  100. package/src/services/spec-loader.ts +88 -0
  101. package/src/types.ts +17 -0
  102. package/src/utils/uri-builder.ts +134 -0
  103. package/src/version.ts +4 -0
  104. package/test/__tests__/e2e/format.test.ts +224 -0
  105. package/test/__tests__/e2e/resources.test.ts +369 -0
  106. package/test/__tests__/e2e/spec-loading.test.ts +172 -0
  107. package/test/__tests__/unit/config.test.ts +39 -0
  108. package/test/__tests__/unit/handlers/component-detail-handler.test.ts +241 -0
  109. package/test/__tests__/unit/handlers/component-map-handler.test.ts +187 -0
  110. package/test/__tests__/unit/handlers/handler-utils.test.ts +255 -0
  111. package/test/__tests__/unit/handlers/operation-handler.test.ts +202 -0
  112. package/test/__tests__/unit/handlers/path-item-handler.test.ts +153 -0
  113. package/test/__tests__/unit/handlers/top-level-field-handler.test.ts +182 -0
  114. package/test/__tests__/unit/rendering/components.test.ts +269 -0
  115. package/test/__tests__/unit/rendering/document.test.ts +172 -0
  116. package/test/__tests__/unit/rendering/path-item.test.ts +197 -0
  117. package/test/__tests__/unit/rendering/paths.test.ts +115 -0
  118. package/test/__tests__/unit/services/formatters.test.ts +109 -0
  119. package/test/__tests__/unit/services/reference-transform.test.ts +320 -0
  120. package/test/__tests__/unit/services/spec-loader.test.ts +214 -0
  121. package/test/__tests__/unit/utils/uri-builder.test.ts +103 -0
  122. package/test/fixtures/complex-endpoint.json +146 -0
  123. package/test/fixtures/empty-api.json +8 -0
  124. package/test/fixtures/multi-component-types.json +55 -0
  125. package/test/fixtures/paths-test.json +61 -0
  126. package/test/fixtures/sample-api.json +68 -0
  127. package/test/fixtures/sample-v2-api.json +39 -0
  128. package/test/setup.ts +32 -0
  129. package/test/utils/console-helpers.ts +48 -0
  130. package/test/utils/mcp-test-helpers.ts +66 -0
  131. package/test/utils/test-types.ts +54 -0
  132. package/tsconfig.json +25 -0
  133. package/tsconfig.test.json +5 -0
@@ -0,0 +1,131 @@
1
+ # Technical Context
2
+
3
+ ## Development Stack
4
+
5
+ - TypeScript for implementation
6
+ - MCP SDK for server functionality
7
+ - Jest for testing
8
+ - npm for package distribution
9
+
10
+ ## Key Dependencies
11
+
12
+ - `@modelcontextprotocol/sdk`: Core MCP functionality
13
+ - `swagger2openapi`: OpenAPI/Swagger spec loading, parsing, and v2->v3 conversion (Runtime dependency)
14
+ - `js-yaml`: YAML parsing (Runtime dependency)
15
+ - `zod`: Schema validation (Runtime dependency)
16
+ - `openapi-types`: OpenAPI type definitions (devDependency)
17
+ - `typescript`: TypeScript compiler (devDependency)
18
+ - `@types/*`: Various type definitions (devDependencies)
19
+ - `jest`: Testing framework (devDependency)
20
+ - `eslint`: Code linting (devDependency)
21
+ - `prettier`: Code formatting (devDependency)
22
+ - `semantic-release` & plugins (`@semantic-release/*`): Automated releases (devDependencies)
23
+ - `just`: Task runner (Used locally, installed via action in CI)
24
+
25
+ ## Technical Requirements
26
+
27
+ 1. Must follow MCP protocol specifications.
28
+ 2. Must handle large OpenAPI/Swagger specs efficiently.
29
+ 3. Must provide type-safe reference handling (transforming internal refs to MCP URIs).
30
+ 4. Must support loading specs from local file paths and remote HTTP/HTTPS URLs.
31
+ 5. Must support OpenAPI v3.0 and Swagger v2.0 formats (with v2.0 being converted to v3.0).
32
+ 6. Must be easily testable and maintainable.
33
+
34
+ ## Development Environment
35
+
36
+ - TypeScript setup with strict type checking
37
+ - Jest testing framework with coverage
38
+ - ESLint for code quality
39
+ - Prettier for code formatting
40
+ - `just` task runner (`justfile`) for common development tasks (build, test, lint, etc.)
41
+ - Conventional Commits standard for commit messages (required for `semantic-release`)
42
+ - Test fixtures and helpers
43
+
44
+ ## Code Organization
45
+
46
+ - Services layer:
47
+ - `SpecLoaderService`: Uses `swagger2openapi` to load specs from files/URLs and handle v2->v3 conversion.
48
+ - `ReferenceTransformService`: Transforms internal `#/components/...` refs to MCP URIs.
49
+ - `Formatters`: Handle JSON/YAML output.
50
+ - Handlers layer for resource endpoints.
51
+ - Rendering layer for generating resource content.
52
+ - Utilities (e.g., URI builder).
53
+ - Strong typing with generics.
54
+ - Comprehensive test coverage.
55
+
56
+ ## Testing Infrastructure
57
+
58
+ - Unit tests:
59
+ - `SpecLoaderService` (mocking `swagger2openapi`).
60
+ - `ReferenceTransformService`.
61
+ - Rendering classes.
62
+ - Handlers (mocking services).
63
+ - End-to-end tests:
64
+ - Verify resource access for local v3, local v2, and remote v3 specs.
65
+ - Test multi-value parameters.
66
+ - Cover success and error scenarios.
67
+ - Verify resource completion logic using `client.complete()`.
68
+ - Type-safe test utilities (`mcp-test-helpers`).
69
+ - Test fixtures (including v2.0 and v3.0 examples).
70
+ - Coverage reporting via Jest and upload to Codecov via GitHub Actions.
71
+ - CI Integration (`.github/workflows/ci.yml`):
72
+ - Runs checks (`just all`, `just security`, CodeQL) on pushes/PRs to `main`.
73
+ - Uses Node 22 environment.
74
+
75
+ ## Response Formats
76
+
77
+ 1. Base Formats
78
+
79
+ - JSON format (default format)
80
+ - YAML format support
81
+ - URI-based reference links
82
+ - Token-efficient structure
83
+ - OpenAPI v3 type compliance
84
+
85
+ 2. Format Service
86
+
87
+ - Pluggable formatter architecture
88
+ - Format-specific MIME types (`application/json`, `text/yaml`)
89
+ - Type-safe formatter interface (`IFormatter`)
90
+ - Consistent error formatting (`text/plain`)
91
+ - CLI-configurable output format (`--output-format`)
92
+
93
+ 3. Implementation
94
+ - Format-specific serialization
95
+ - Shared type system
96
+ - Error response handling
97
+ - Multiple operation support
98
+ - Reference transformation
99
+
100
+ ## Deployment / Release Process
101
+
102
+ - Automated publishing to npm via `semantic-release` triggered by pushes to `main` branch in GitHub Actions.
103
+ - Relies on Conventional Commits to determine version bumps.
104
+ - Creates version tags (e.g., `v1.2.3`) and GitHub Releases automatically.
105
+ - Requires `NPM_TOKEN` secret configured in GitHub repository for publishing.
106
+ - `CHANGELOG.md` is automatically generated and updated.
107
+ - Server version is dynamically set at runtime based on the release version.
108
+
109
+ ## Configuration
110
+
111
+ - Command-line argument based configuration (`src/config.ts`).
112
+ - Single required argument: `<path-or-url-to-spec>`.
113
+ - Optional argument: `--output-format <json|yaml|json-minified>`.
114
+ - Required argument validation.
115
+ - TypeScript type safety (`ServerConfig` interface).
116
+ - Error handling for missing/invalid arguments.
117
+
118
+ ## Error Handling
119
+
120
+ - Descriptive error messages
121
+ - Type-safe error handling
122
+ - Consistent error format
123
+ - Proper error propagation
124
+
125
+ ## Future Extensions
126
+
127
+ - AsyncAPI format support
128
+ - GraphQL schema support
129
+ - External reference resolution
130
+ - Enhanced schema resources
131
+ - Reference validation
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "mcp-openapi-schema-explorer",
3
+ "version": "1.0.0",
4
+ "description": "MCP OpenAPI schema explorer",
5
+ "type": "module",
6
+ "main": "dist/src/index.js",
7
+ "types": "dist/src/index.d.ts",
8
+ "bin": {
9
+ "mcp-openapi-schema-explorer": "dist/src/index.js"
10
+ },
11
+ "scripts": {
12
+ "build": "rm -rf dist && mkdir -p dist && npx tsc && chmod +x dist/src/index.js",
13
+ "test": "jest",
14
+ "test:watch": "jest --watch",
15
+ "test:coverage": "jest --coverage",
16
+ "lint": "eslint . --ext .ts",
17
+ "lint:fix": "eslint . --ext .ts --fix",
18
+ "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
19
+ "format:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\"",
20
+ "type-check": "tsc --noEmit",
21
+ "prepare": "husky",
22
+ "prepublishOnly": "npm run build"
23
+ },
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/kadykov/mcp-openapi-schema-explorer.git"
27
+ },
28
+ "keywords": [
29
+ "mcp",
30
+ "openapi"
31
+ ],
32
+ "author": "",
33
+ "license": "MIT",
34
+ "bugs": {
35
+ "url": "https://github.com/kadykov/mcp-openapi-schema-explorer/issues"
36
+ },
37
+ "homepage": "https://github.com/kadykov/mcp-openapi-schema-explorer#readme",
38
+ "dependencies": {
39
+ "@modelcontextprotocol/sdk": "^1.8.0",
40
+ "js-yaml": "^4.1.0",
41
+ "swagger2openapi": "7.0.8",
42
+ "zod": "^3.24.2"
43
+ },
44
+ "devDependencies": {
45
+ "@eslint/js": "^9.24.0",
46
+ "@semantic-release/changelog": "^6.0.3",
47
+ "@semantic-release/commit-analyzer": "^13.0.1",
48
+ "@semantic-release/exec": "^7.0.3",
49
+ "@semantic-release/git": "^10.0.1",
50
+ "@semantic-release/github": "^11.0.1",
51
+ "@semantic-release/npm": "^12.0.1",
52
+ "@semantic-release/release-notes-generator": "^14.0.3",
53
+ "@types/jest": "^29.5.14",
54
+ "@types/js-yaml": "^4.0.9",
55
+ "@types/node": "^22.14.1",
56
+ "@types/node-fetch": "^2.6.12",
57
+ "@types/swagger2openapi": "^7.0.4",
58
+ "@typescript-eslint/eslint-plugin": "^8.29.0",
59
+ "@typescript-eslint/parser": "^8.29.0",
60
+ "axios": "^1.8.4",
61
+ "eslint": "^9.24.0",
62
+ "eslint-plugin-security": "^3.0.1",
63
+ "globals": "^16.0.0",
64
+ "husky": "^9.1.7",
65
+ "jest": "^29.7.0",
66
+ "jest-silent-reporter": "^0.6.0",
67
+ "license-checker": "^25.0.1",
68
+ "msw": "^2.7.4",
69
+ "openapi-types": "^12.1.3",
70
+ "openapi-typescript": "^7.6.1",
71
+ "prettier": "^3.5.3",
72
+ "semantic-release": "^24.2.3",
73
+ "ts-jest": "^29.3.1",
74
+ "typescript": "^5.8.3"
75
+ }
76
+ }
@@ -0,0 +1,49 @@
1
+ // scripts/generate-version.js
2
+ // Purpose: Writes the release version provided by semantic-release to src/version.ts
3
+ // Called by: @semantic-release/exec during the 'prepare' step
4
+
5
+ import fs from 'fs'; // Use import
6
+ import path from 'path'; // Use import
7
+ import { fileURLToPath } from 'url'; // Needed to convert import.meta.url
8
+
9
+ // Get version from the command line argument passed by semantic-release exec
10
+ // process is globally available in ESM
11
+ const version = process.argv[2];
12
+
13
+ if (!version) {
14
+ console.error('Error: No version argument provided to generate-version.js!');
15
+ process.exit(1);
16
+ }
17
+
18
+ // Basic check for semantic version format (adjust regex if needed)
19
+ if (!/^\d+\.\d+\.\d+(-[\w.-]+)?(\+[\w.-]+)?$/.test(version)) {
20
+ console.error(`Error: Invalid version format received: "${version}"`);
21
+ process.exit(1);
22
+ }
23
+
24
+ const content = `// Auto-generated by scripts/generate-version.js during semantic-release prepare step
25
+ // Do not edit this file manually.
26
+
27
+ export const VERSION = '${version}';
28
+ `;
29
+
30
+ // Derive the directory path in ESM
31
+ const __filename = fileURLToPath(import.meta.url);
32
+ const __dirname = path.dirname(__filename);
33
+
34
+ // Construct the absolute path to src/version.ts
35
+ const filePath = path.join(__dirname, '..', 'src', 'version.ts');
36
+ const fileDir = path.dirname(filePath);
37
+
38
+ try {
39
+ // Ensure the src directory exists (though it should)
40
+ if (!fs.existsSync(fileDir)) {
41
+ fs.mkdirSync(fileDir, { recursive: true });
42
+ }
43
+ // Write the version file
44
+ fs.writeFileSync(filePath, content, { encoding: 'utf-8' });
45
+ console.log(`Successfully wrote version ${version} to ${filePath}`);
46
+ } catch (error) {
47
+ console.error(`Error writing version file to ${filePath}:`, error);
48
+ process.exit(1);
49
+ }
package/src/config.ts ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Configuration management for the OpenAPI Explorer MCP server
3
+ */
4
+
5
+ import { OutputFormat } from './services/formatters.js';
6
+
7
+ /** Server configuration */
8
+ export interface ServerConfig {
9
+ /** Path to OpenAPI specification file */
10
+ specPath: string;
11
+ /** Output format for responses */
12
+ outputFormat: OutputFormat;
13
+ }
14
+
15
+ /** Load server configuration from command line arguments */
16
+ export function loadConfig(specPath?: string, options?: { outputFormat?: string }): ServerConfig {
17
+ if (!specPath) {
18
+ throw new Error(
19
+ 'OpenAPI spec path is required. Usage: npx mcp-openapi-schema-explorer <path-to-spec> [--output-format json|yaml]'
20
+ );
21
+ }
22
+
23
+ const format = options?.outputFormat || 'json';
24
+ if (format !== 'json' && format !== 'yaml' && format !== 'json-minified') {
25
+ throw new Error('Invalid output format. Supported formats: json, yaml, json-minified');
26
+ }
27
+
28
+ return {
29
+ specPath,
30
+ // Cast is safe here due to the validation above
31
+ outputFormat: format as OutputFormat,
32
+ };
33
+ }
@@ -0,0 +1,121 @@
1
+ import {
2
+ ReadResourceTemplateCallback,
3
+ ResourceTemplate,
4
+ } from '@modelcontextprotocol/sdk/server/mcp.js';
5
+ import { Variables } from '@modelcontextprotocol/sdk/shared/uriTemplate.js';
6
+ import { SpecLoaderService } from '../types.js';
7
+ import { IFormatter } from '../services/formatters.js';
8
+ import {
9
+ RenderableComponentMap,
10
+ ComponentType,
11
+ VALID_COMPONENT_TYPES,
12
+ } from '../rendering/components.js';
13
+ import { RenderContext, RenderResultItem } from '../rendering/types.js';
14
+ import { createErrorResult } from '../rendering/utils.js';
15
+ // Import shared handler utils
16
+ import {
17
+ formatResults,
18
+ isOpenAPIV3,
19
+ FormattedResultItem,
20
+ getValidatedComponentMap, // Import helper
21
+ getValidatedComponentDetails, // Import helper
22
+ } from './handler-utils.js'; // Already has .js
23
+
24
+ const BASE_URI = 'openapi://';
25
+
26
+ // Removed duplicated FormattedResultItem type - now imported from handler-utils
27
+ // Removed duplicated formatResults function - now imported from handler-utils
28
+ // Removed duplicated isOpenAPIV3 function - now imported from handler-utils
29
+
30
+ /**
31
+ * Handles requests for specific component details.
32
+ * Corresponds to the `openapi://components/{type}/{name*}` template.
33
+ */
34
+ export class ComponentDetailHandler {
35
+ constructor(
36
+ private specLoader: SpecLoaderService,
37
+ private formatter: IFormatter
38
+ ) {}
39
+
40
+ getTemplate(): ResourceTemplate {
41
+ // TODO: Add completion logic if needed
42
+ return new ResourceTemplate(`${BASE_URI}components/{type}/{name*}`, {
43
+ list: undefined,
44
+ complete: undefined,
45
+ });
46
+ }
47
+
48
+ handleRequest: ReadResourceTemplateCallback = async (
49
+ uri: URL,
50
+ variables: Variables
51
+ ): Promise<{ contents: FormattedResultItem[] }> => {
52
+ const type = variables.type as string;
53
+ // Correct variable access key: 'name', not 'name*'
54
+ const nameVar = variables['name']; // Can be string or string[]
55
+ const mapUriSuffix = `components/${type}`;
56
+ const context: RenderContext = { formatter: this.formatter, baseUri: BASE_URI };
57
+ let resultItems: RenderResultItem[];
58
+
59
+ try {
60
+ if (!VALID_COMPONENT_TYPES.includes(type as ComponentType)) {
61
+ throw new Error(`Invalid component type: ${type}`);
62
+ }
63
+ const componentType = type as ComponentType;
64
+
65
+ // Normalize names: Handle string for single value, array for multiple.
66
+ let names: string[] = [];
67
+ if (Array.isArray(nameVar)) {
68
+ names = nameVar.map(n => String(n).trim()); // Ensure elements are strings
69
+ } else if (typeof nameVar === 'string') {
70
+ names = [nameVar.trim()]; // Treat as single item array
71
+ }
72
+ names = names.filter(n => n.length > 0); // Remove empty strings
73
+
74
+ if (names.length === 0) {
75
+ throw new Error('No valid component name specified.');
76
+ }
77
+
78
+ const spec = await this.specLoader.getTransformedSpec({
79
+ resourceType: 'schema', // Use 'schema' for now
80
+ format: 'openapi',
81
+ });
82
+
83
+ // Use imported type guard
84
+ if (!isOpenAPIV3(spec)) {
85
+ throw new Error('Only OpenAPI v3 specifications are supported');
86
+ }
87
+
88
+ // --- Use helper to get validated component map ---
89
+ const componentMapObj = getValidatedComponentMap(spec, componentType);
90
+
91
+ // --- Create Map and use helper to get validated component names/details ---
92
+ // Create the Map from the validated object
93
+ const detailsMap = new Map(Object.entries(componentMapObj));
94
+ // Pass the Map to the helper
95
+ const validDetails = getValidatedComponentDetails(detailsMap, names, componentType);
96
+ const validNames = validDetails.map(detail => detail.name); // Extract names
97
+
98
+ // Instantiate RenderableComponentMap with the validated map object
99
+ const renderableMap = new RenderableComponentMap(
100
+ componentMapObj, // componentMapObj retrieved safely via helper
101
+ componentType,
102
+ mapUriSuffix
103
+ );
104
+ // Pass the validated names to the rendering function
105
+ resultItems = renderableMap.renderComponentDetail(context, validNames);
106
+ } catch (error: unknown) {
107
+ // Catch errors from helpers (e.g., type/name not found) or rendering
108
+ const message = error instanceof Error ? error.message : String(error);
109
+ console.error(`Error handling request ${uri.href}: ${message}`);
110
+ // Create a single error item representing the overall request failure
111
+ resultItems = createErrorResult(
112
+ uri.href.substring(BASE_URI.length), // Use request URI suffix
113
+ message
114
+ );
115
+ }
116
+
117
+ // Use imported formatResults
118
+ const contents = formatResults(context, resultItems);
119
+ return { contents };
120
+ };
121
+ }
@@ -0,0 +1,92 @@
1
+ import {
2
+ ReadResourceTemplateCallback,
3
+ ResourceTemplate,
4
+ } from '@modelcontextprotocol/sdk/server/mcp.js';
5
+ import { Variables } from '@modelcontextprotocol/sdk/shared/uriTemplate.js';
6
+ import { SpecLoaderService } from '../types.js';
7
+ import { IFormatter } from '../services/formatters.js';
8
+ import {
9
+ RenderableComponentMap,
10
+ ComponentType,
11
+ VALID_COMPONENT_TYPES,
12
+ } from '../rendering/components.js';
13
+ import { RenderContext, RenderResultItem } from '../rendering/types.js';
14
+ import { createErrorResult } from '../rendering/utils.js';
15
+ // Import shared handler utils
16
+ import {
17
+ formatResults,
18
+ isOpenAPIV3,
19
+ FormattedResultItem,
20
+ getValidatedComponentMap, // Import the helper
21
+ } from './handler-utils.js'; // Already has .js
22
+
23
+ const BASE_URI = 'openapi://';
24
+
25
+ // Removed duplicated FormattedResultItem type - now imported from handler-utils
26
+ // Removed duplicated formatResults function - now imported from handler-utils
27
+ // Removed duplicated isOpenAPIV3 function - now imported from handler-utils
28
+
29
+ /**
30
+ * Handles requests for listing component names of a specific type.
31
+ * Corresponds to the `openapi://components/{type}` template.
32
+ */
33
+ export class ComponentMapHandler {
34
+ constructor(
35
+ private specLoader: SpecLoaderService,
36
+ private formatter: IFormatter // Needed for context
37
+ ) {}
38
+
39
+ getTemplate(): ResourceTemplate {
40
+ // TODO: Add completion logic if needed
41
+ return new ResourceTemplate(`${BASE_URI}components/{type}`, {
42
+ list: undefined,
43
+ complete: undefined,
44
+ });
45
+ }
46
+
47
+ handleRequest: ReadResourceTemplateCallback = async (
48
+ uri: URL,
49
+ variables: Variables
50
+ ): Promise<{ contents: FormattedResultItem[] }> => {
51
+ const type = variables.type as string;
52
+ const mapUriSuffix = `components/${type}`;
53
+ const context: RenderContext = { formatter: this.formatter, baseUri: BASE_URI };
54
+ let resultItems: RenderResultItem[];
55
+
56
+ try {
57
+ if (!VALID_COMPONENT_TYPES.includes(type as ComponentType)) {
58
+ throw new Error(`Invalid component type: ${type}`);
59
+ }
60
+ const componentType = type as ComponentType;
61
+
62
+ const spec = await this.specLoader.getTransformedSpec({
63
+ resourceType: 'schema', // Use 'schema' for now
64
+ format: 'openapi',
65
+ });
66
+
67
+ // Use imported type guard
68
+ if (!isOpenAPIV3(spec)) {
69
+ throw new Error('Only OpenAPI v3 specifications are supported');
70
+ }
71
+
72
+ // --- Use helper to get validated component map ---
73
+ const componentMapObj = getValidatedComponentMap(spec, componentType);
74
+
75
+ // Instantiate RenderableComponentMap with the validated map
76
+ const renderableMap = new RenderableComponentMap(
77
+ componentMapObj, // componentMapObj retrieved safely via helper
78
+ componentType,
79
+ mapUriSuffix
80
+ );
81
+ resultItems = renderableMap.renderList(context);
82
+ } catch (error: unknown) {
83
+ const message = error instanceof Error ? error.message : String(error);
84
+ console.error(`Error handling request ${uri.href}: ${message}`);
85
+ resultItems = createErrorResult(mapUriSuffix, message);
86
+ }
87
+
88
+ // Use imported formatResults
89
+ const contents = formatResults(context, resultItems);
90
+ return { contents };
91
+ };
92
+ }