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,63 @@
1
+ import { ResourceTemplate, } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { RenderableComponentMap, VALID_COMPONENT_TYPES, } from '../rendering/components.js';
3
+ import { createErrorResult } from '../rendering/utils.js';
4
+ // Import shared handler utils
5
+ import { formatResults, isOpenAPIV3, getValidatedComponentMap, // Import the helper
6
+ } from './handler-utils.js'; // Already has .js
7
+ const BASE_URI = 'openapi://';
8
+ // Removed duplicated FormattedResultItem type - now imported from handler-utils
9
+ // Removed duplicated formatResults function - now imported from handler-utils
10
+ // Removed duplicated isOpenAPIV3 function - now imported from handler-utils
11
+ /**
12
+ * Handles requests for listing component names of a specific type.
13
+ * Corresponds to the `openapi://components/{type}` template.
14
+ */
15
+ export class ComponentMapHandler {
16
+ constructor(specLoader, formatter // Needed for context
17
+ ) {
18
+ this.specLoader = specLoader;
19
+ this.formatter = formatter;
20
+ this.handleRequest = async (uri, variables) => {
21
+ const type = variables.type;
22
+ const mapUriSuffix = `components/${type}`;
23
+ const context = { formatter: this.formatter, baseUri: BASE_URI };
24
+ let resultItems;
25
+ try {
26
+ if (!VALID_COMPONENT_TYPES.includes(type)) {
27
+ throw new Error(`Invalid component type: ${type}`);
28
+ }
29
+ const componentType = type;
30
+ const spec = await this.specLoader.getTransformedSpec({
31
+ resourceType: 'schema', // Use 'schema' for now
32
+ format: 'openapi',
33
+ });
34
+ // Use imported type guard
35
+ if (!isOpenAPIV3(spec)) {
36
+ throw new Error('Only OpenAPI v3 specifications are supported');
37
+ }
38
+ // --- Use helper to get validated component map ---
39
+ const componentMapObj = getValidatedComponentMap(spec, componentType);
40
+ // Instantiate RenderableComponentMap with the validated map
41
+ const renderableMap = new RenderableComponentMap(componentMapObj, // componentMapObj retrieved safely via helper
42
+ componentType, mapUriSuffix);
43
+ resultItems = renderableMap.renderList(context);
44
+ }
45
+ catch (error) {
46
+ const message = error instanceof Error ? error.message : String(error);
47
+ console.error(`Error handling request ${uri.href}: ${message}`);
48
+ resultItems = createErrorResult(mapUriSuffix, message);
49
+ }
50
+ // Use imported formatResults
51
+ const contents = formatResults(context, resultItems);
52
+ return { contents };
53
+ };
54
+ }
55
+ getTemplate() {
56
+ // TODO: Add completion logic if needed
57
+ return new ResourceTemplate(`${BASE_URI}components/{type}`, {
58
+ list: undefined,
59
+ complete: undefined,
60
+ });
61
+ }
62
+ }
63
+ //# sourceMappingURL=component-map-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component-map-handler.js","sourceRoot":"","sources":["../../../src/handlers/component-map-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,gBAAgB,GACjB,MAAM,yCAAyC,CAAC;AAIjD,OAAO,EACL,sBAAsB,EAEtB,qBAAqB,GACtB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,8BAA8B;AAC9B,OAAO,EACL,aAAa,EACb,WAAW,EAEX,wBAAwB,EAAE,oBAAoB;EAC/C,MAAM,oBAAoB,CAAC,CAAC,kBAAkB;AAE/C,MAAM,QAAQ,GAAG,YAAY,CAAC;AAE9B,gFAAgF;AAChF,8EAA8E;AAC9E,4EAA4E;AAE5E;;;GAGG;AACH,MAAM,OAAO,mBAAmB;IAC9B,YACU,UAA6B,EAC7B,SAAqB,CAAC,qBAAqB;;QAD3C,eAAU,GAAV,UAAU,CAAmB;QAC7B,cAAS,GAAT,SAAS,CAAY;QAW/B,kBAAa,GAAiC,KAAK,EACjD,GAAQ,EACR,SAAoB,EAC0B,EAAE;YAChD,MAAM,IAAI,GAAG,SAAS,CAAC,IAAc,CAAC;YACtC,MAAM,YAAY,GAAG,cAAc,IAAI,EAAE,CAAC;YAC1C,MAAM,OAAO,GAAkB,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;YAChF,IAAI,WAA+B,CAAC;YAEpC,IAAI,CAAC;gBACH,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC,IAAqB,CAAC,EAAE,CAAC;oBAC3D,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,EAAE,CAAC,CAAC;gBACrD,CAAC;gBACD,MAAM,aAAa,GAAG,IAAqB,CAAC;gBAE5C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,kBAAkB,CAAC;oBACpD,YAAY,EAAE,QAAQ,EAAE,uBAAuB;oBAC/C,MAAM,EAAE,SAAS;iBAClB,CAAC,CAAC;gBAEH,0BAA0B;gBAC1B,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;oBACvB,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;gBAClE,CAAC;gBAED,oDAAoD;gBACpD,MAAM,eAAe,GAAG,wBAAwB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;gBAEtE,4DAA4D;gBAC5D,MAAM,aAAa,GAAG,IAAI,sBAAsB,CAC9C,eAAe,EAAE,8CAA8C;gBAC/D,aAAa,EACb,YAAY,CACb,CAAC;gBACF,WAAW,GAAG,aAAa,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAClD,CAAC;YAAC,OAAO,KAAc,EAAE,CAAC;gBACxB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACvE,OAAO,CAAC,KAAK,CAAC,0BAA0B,GAAG,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;gBAChE,WAAW,GAAG,iBAAiB,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;YACzD,CAAC;YAED,6BAA6B;YAC7B,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;YACrD,OAAO,EAAE,QAAQ,EAAE,CAAC;QACtB,CAAC,CAAC;IAtDC,CAAC;IAEJ,WAAW;QACT,uCAAuC;QACvC,OAAO,IAAI,gBAAgB,CAAC,GAAG,QAAQ,mBAAmB,EAAE;YAC1D,IAAI,EAAE,SAAS;YACf,QAAQ,EAAE,SAAS;SACpB,CAAC,CAAC;IACL,CAAC;CA+CF"}
@@ -0,0 +1,69 @@
1
+ import { OpenAPIV3 } from 'openapi-types';
2
+ import { RenderContext, RenderResultItem } from '../rendering/types.js';
3
+ export type FormattedResultItem = {
4
+ uri: string;
5
+ mimeType?: string;
6
+ text: string;
7
+ isError?: boolean;
8
+ };
9
+ /**
10
+ * Formats RenderResultItem array into an array compatible with the 'contents'
11
+ * property of ReadResourceResultSchema (specifically TextResourceContents).
12
+ */
13
+ export declare function formatResults(context: RenderContext, items: RenderResultItem[]): FormattedResultItem[];
14
+ /**
15
+ * Type guard to check if an object is an OpenAPIV3.Document.
16
+ */
17
+ export declare function isOpenAPIV3(spec: unknown): spec is OpenAPIV3.Document;
18
+ /**
19
+ * Safely retrieves a PathItemObject from the specification using a Map.
20
+ * Throws an McpError if the path is not found.
21
+ *
22
+ * @param spec The OpenAPIV3 Document.
23
+ * @param path The decoded path string (e.g., '/users/{id}').
24
+ * @returns The validated PathItemObject.
25
+ * @throws {McpError} If the path is not found in spec.paths.
26
+ */
27
+ export declare function getValidatedPathItem(spec: OpenAPIV3.Document, path: string): OpenAPIV3.PathItemObject;
28
+ /**
29
+ * Validates requested HTTP methods against a PathItemObject using a Map.
30
+ * Returns the list of valid requested methods.
31
+ * Throws an McpError if none of the requested methods are valid for the path item.
32
+ *
33
+ * @param pathItem The PathItemObject to check against.
34
+ * @param requestedMethods An array of lowercase HTTP methods requested by the user.
35
+ * @param pathForError The path string, used for creating informative error messages.
36
+ * @returns An array of the requested methods that are valid for this path item.
37
+ * @throws {McpError} If none of the requested methods are valid.
38
+ */
39
+ export declare function getValidatedOperations(pathItem: OpenAPIV3.PathItemObject, requestedMethods: string[], pathForError: string): string[];
40
+ /**
41
+ * Safely retrieves the component map for a specific type (e.g., schemas, responses)
42
+ * from the specification using a Map.
43
+ * Throws an McpError if spec.components or the specific type map is not found.
44
+ *
45
+ * @param spec The OpenAPIV3 Document.
46
+ * @param type The ComponentType string (e.g., 'schemas', 'responses').
47
+ * @returns The validated component map object (e.g., spec.components.schemas).
48
+ * @throws {McpError} If spec.components or the type map is not found.
49
+ */
50
+ export declare function getValidatedComponentMap(spec: OpenAPIV3.Document, type: string): NonNullable<OpenAPIV3.ComponentsObject[keyof OpenAPIV3.ComponentsObject]>;
51
+ /**
52
+ * Validates requested component names against a specific component map (e.g., schemas).
53
+ * Returns an array of objects containing the valid name and its corresponding detail object.
54
+ * Throws an McpError if none of the requested names are valid for the component map.
55
+ *
56
+ * @param componentMap The specific component map object (e.g., spec.components.schemas).
57
+ * @param requestedNames An array of component names requested by the user.
58
+ * @param componentTypeForError The component type string, used for creating informative error messages.
59
+ * @param detailsMap A Map created from the specific component map object (e.g., new Map(Object.entries(spec.components.schemas))).
60
+ * @param requestedNames An array of component names requested by the user.
61
+ * @param componentTypeForError The component type string, used for creating informative error messages.
62
+ * @returns An array of { name: string, detail: V } for valid requested names, where V is the value type of the Map.
63
+ * @throws {McpError} If none of the requested names are valid.
64
+ */
65
+ export declare function getValidatedComponentDetails<V extends OpenAPIV3.ReferenceObject | object>(detailsMap: Map<string, V>, // Accept Map<string, V>
66
+ requestedNames: string[], componentTypeForError: string): {
67
+ name: string;
68
+ detail: V;
69
+ }[];
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Formats RenderResultItem array into an array compatible with the 'contents'
3
+ * property of ReadResourceResultSchema (specifically TextResourceContents).
4
+ */
5
+ export function formatResults(context, items) {
6
+ // Add type check for formatter existence in context
7
+ if (!context.formatter) {
8
+ throw new Error('Formatter is missing in RenderContext for formatResults');
9
+ }
10
+ return items.map(item => {
11
+ const uri = `${context.baseUri}${item.uriSuffix}`;
12
+ let text;
13
+ let mimeType;
14
+ if (item.isError) {
15
+ text = item.errorText || 'An unknown error occurred.';
16
+ mimeType = 'text/plain';
17
+ }
18
+ else if (item.renderAsList) {
19
+ text = typeof item.data === 'string' ? item.data : 'Invalid list data';
20
+ mimeType = 'text/plain';
21
+ }
22
+ else {
23
+ // Detail view: format using the provided formatter
24
+ try {
25
+ text = context.formatter.format(item.data);
26
+ mimeType = context.formatter.getMimeType();
27
+ }
28
+ catch (formatError) {
29
+ text = `Error formatting data for ${uri}: ${formatError instanceof Error ? formatError.message : String(formatError)}`;
30
+ mimeType = 'text/plain';
31
+ // Ensure isError is true if formatting fails
32
+ item.isError = true;
33
+ item.errorText = text; // Store the formatting error message
34
+ }
35
+ }
36
+ // Construct the final object, prioritizing item.isError
37
+ const finalItem = {
38
+ uri: uri,
39
+ mimeType: mimeType,
40
+ text: item.isError ? item.errorText || 'An unknown error occurred.' : text,
41
+ isError: item.isError ?? false, // Default to false if not explicitly set
42
+ };
43
+ // Ensure mimeType is text/plain for errors
44
+ if (finalItem.isError) {
45
+ finalItem.mimeType = 'text/plain';
46
+ }
47
+ return finalItem;
48
+ });
49
+ }
50
+ /**
51
+ * Type guard to check if an object is an OpenAPIV3.Document.
52
+ */
53
+ export function isOpenAPIV3(spec) {
54
+ return (typeof spec === 'object' &&
55
+ spec !== null &&
56
+ 'openapi' in spec &&
57
+ typeof spec.openapi === 'string' &&
58
+ spec.openapi.startsWith('3.'));
59
+ }
60
+ /**
61
+ * Safely retrieves a PathItemObject from the specification using a Map.
62
+ * Throws an McpError if the path is not found.
63
+ *
64
+ * @param spec The OpenAPIV3 Document.
65
+ * @param path The decoded path string (e.g., '/users/{id}').
66
+ * @returns The validated PathItemObject.
67
+ * @throws {McpError} If the path is not found in spec.paths.
68
+ */
69
+ export function getValidatedPathItem(spec, path) {
70
+ if (!spec.paths) {
71
+ // Use standard Error
72
+ throw new Error('Specification does not contain any paths.');
73
+ }
74
+ const pathsMap = new Map(Object.entries(spec.paths));
75
+ const pathItem = pathsMap.get(path);
76
+ if (!pathItem) {
77
+ const errorMessage = `Path "${path}" not found in the specification.`;
78
+ // Use standard Error
79
+ throw new Error(errorMessage);
80
+ }
81
+ // We assume the spec structure is valid if the key exists
82
+ return pathItem;
83
+ }
84
+ /**
85
+ * Validates requested HTTP methods against a PathItemObject using a Map.
86
+ * Returns the list of valid requested methods.
87
+ * Throws an McpError if none of the requested methods are valid for the path item.
88
+ *
89
+ * @param pathItem The PathItemObject to check against.
90
+ * @param requestedMethods An array of lowercase HTTP methods requested by the user.
91
+ * @param pathForError The path string, used for creating informative error messages.
92
+ * @returns An array of the requested methods that are valid for this path item.
93
+ * @throws {McpError} If none of the requested methods are valid.
94
+ */
95
+ export function getValidatedOperations(pathItem, requestedMethods, pathForError) {
96
+ const operationsMap = new Map();
97
+ Object.entries(pathItem).forEach(([method, operation]) => {
98
+ // Check if the key is a standard HTTP method before adding
99
+ if (['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'].includes(method.toLowerCase())) {
100
+ operationsMap.set(method.toLowerCase(), operation);
101
+ }
102
+ });
103
+ // Validate using lowercase versions, but preserve original case for return
104
+ const requestedMethodsLower = requestedMethods.map(m => m.toLowerCase());
105
+ const validLowerMethods = requestedMethodsLower.filter(m => operationsMap.has(m));
106
+ if (validLowerMethods.length === 0) {
107
+ const availableMethods = Array.from(operationsMap.keys()).join(', ');
108
+ // Show original case in error message for clarity
109
+ const errorMessage = `None of the requested methods (${requestedMethods.join(', ')}) are valid for path "${pathForError}". Available methods: ${availableMethods}`;
110
+ // Use standard Error
111
+ throw new Error(errorMessage);
112
+ }
113
+ // Return the methods from the *original* requestedMethods array
114
+ // that correspond to the valid lowercase methods found.
115
+ return requestedMethods.filter(m => validLowerMethods.includes(m.toLowerCase()));
116
+ }
117
+ /**
118
+ * Safely retrieves the component map for a specific type (e.g., schemas, responses)
119
+ * from the specification using a Map.
120
+ * Throws an McpError if spec.components or the specific type map is not found.
121
+ *
122
+ * @param spec The OpenAPIV3 Document.
123
+ * @param type The ComponentType string (e.g., 'schemas', 'responses').
124
+ * @returns The validated component map object (e.g., spec.components.schemas).
125
+ * @throws {McpError} If spec.components or the type map is not found.
126
+ */
127
+ export function getValidatedComponentMap(spec, type // Keep as string for validation flexibility
128
+ ) {
129
+ if (!spec.components) {
130
+ // Use standard Error
131
+ throw new Error('Specification does not contain a components section.');
132
+ }
133
+ // Validate the requested type against the actual keys in spec.components
134
+ const componentsMap = new Map(Object.entries(spec.components));
135
+ // Add type assertion for clarity, although the check below handles undefined
136
+ const componentMapObj = componentsMap.get(type);
137
+ if (!componentMapObj) {
138
+ const availableTypes = Array.from(componentsMap.keys()).join(', ');
139
+ const errorMessage = `Component type "${type}" not found in the specification. Available types: ${availableTypes}`;
140
+ // Use standard Error
141
+ throw new Error(errorMessage);
142
+ }
143
+ // We assume the spec structure is valid if the key exists
144
+ return componentMapObj;
145
+ }
146
+ /**
147
+ * Validates requested component names against a specific component map (e.g., schemas).
148
+ * Returns an array of objects containing the valid name and its corresponding detail object.
149
+ * Throws an McpError if none of the requested names are valid for the component map.
150
+ *
151
+ * @param componentMap The specific component map object (e.g., spec.components.schemas).
152
+ * @param requestedNames An array of component names requested by the user.
153
+ * @param componentTypeForError The component type string, used for creating informative error messages.
154
+ * @param detailsMap A Map created from the specific component map object (e.g., new Map(Object.entries(spec.components.schemas))).
155
+ * @param requestedNames An array of component names requested by the user.
156
+ * @param componentTypeForError The component type string, used for creating informative error messages.
157
+ * @returns An array of { name: string, detail: V } for valid requested names, where V is the value type of the Map.
158
+ * @throws {McpError} If none of the requested names are valid.
159
+ */
160
+ // Modify to accept a Map directly
161
+ export function getValidatedComponentDetails(detailsMap, // Accept Map<string, V>
162
+ requestedNames, componentTypeForError) {
163
+ // No longer need to create the map inside the function
164
+ const validDetails = requestedNames
165
+ .map(name => {
166
+ const detail = detailsMap.get(name); // detail will be V | undefined
167
+ return detail ? { name, detail } : null;
168
+ })
169
+ // Type predicate ensures we filter out nulls and have the correct type
170
+ .filter((item) => item !== null);
171
+ if (validDetails.length === 0) {
172
+ // Sort available names for deterministic error messages
173
+ const availableNames = Array.from(detailsMap.keys()).sort().join(', ');
174
+ const errorMessage = `None of the requested names (${requestedNames.join(', ')}) are valid for component type "${componentTypeForError}". Available names: ${availableNames}`;
175
+ // Use standard Error
176
+ throw new Error(errorMessage);
177
+ }
178
+ return validDetails;
179
+ }
180
+ //# sourceMappingURL=handler-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler-utils.js","sourceRoot":"","sources":["../../../src/handlers/handler-utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { ReadResourceTemplateCallback, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { SpecLoaderService } from '../types.js';
3
+ import { IFormatter } from '../services/formatters.js';
4
+ /**
5
+ * Handles requests for specific operation details within a path.
6
+ * Corresponds to the `openapi://paths/{path}/{method*}` template.
7
+ */
8
+ export declare class OperationHandler {
9
+ private specLoader;
10
+ private formatter;
11
+ constructor(specLoader: SpecLoaderService, formatter: IFormatter);
12
+ getTemplate(): ResourceTemplate;
13
+ handleRequest: ReadResourceTemplateCallback;
14
+ }
@@ -0,0 +1,86 @@
1
+ import { ResourceTemplate, } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { RenderablePathItem } from '../rendering/path-item.js';
3
+ import { createErrorResult } from '../rendering/utils.js';
4
+ import { buildPathItemUriSuffix } from '../utils/uri-builder.js'; // Added .js extension
5
+ // Import shared handler utils
6
+ import { formatResults, isOpenAPIV3, getValidatedPathItem, // Import new helper
7
+ getValidatedOperations, // Import new helper
8
+ } from './handler-utils.js'; // Already has .js
9
+ const BASE_URI = 'openapi://';
10
+ // Removed duplicated FormattedResultItem type - now imported from handler-utils
11
+ // Removed duplicated formatResults function - now imported from handler-utils
12
+ // Removed duplicated isOpenAPIV3 function - now imported from handler-utils
13
+ /**
14
+ * Handles requests for specific operation details within a path.
15
+ * Corresponds to the `openapi://paths/{path}/{method*}` template.
16
+ */
17
+ export class OperationHandler {
18
+ constructor(specLoader, formatter) {
19
+ this.specLoader = specLoader;
20
+ this.formatter = formatter;
21
+ this.handleRequest = async (uri, variables) => {
22
+ const encodedPath = variables.path;
23
+ // Correct variable access key: 'method', not 'method*'
24
+ const methodVar = variables['method']; // Can be string or string[]
25
+ // Decode the path received from the URI variable
26
+ const decodedPath = decodeURIComponent(encodedPath || '');
27
+ // Use the builder to create the suffix, which will re-encode the path correctly
28
+ const pathUriSuffix = buildPathItemUriSuffix(decodedPath);
29
+ const context = { formatter: this.formatter, baseUri: BASE_URI };
30
+ let resultItems;
31
+ try {
32
+ // Normalize methods: Handle string for single value, array for multiple.
33
+ let methods = [];
34
+ if (Array.isArray(methodVar)) {
35
+ methods = methodVar.map(m => String(m).trim().toLowerCase()); // Ensure elements are strings
36
+ }
37
+ else if (typeof methodVar === 'string') {
38
+ methods = [methodVar.trim().toLowerCase()]; // Treat as single item array
39
+ }
40
+ methods = methods.filter(m => m.length > 0); // Remove empty strings
41
+ if (methods.length === 0) {
42
+ throw new Error('No valid HTTP method specified.');
43
+ }
44
+ const spec = await this.specLoader.getTransformedSpec({
45
+ resourceType: 'schema', // Use 'schema' for now
46
+ format: 'openapi',
47
+ });
48
+ // Use imported type guard
49
+ if (!isOpenAPIV3(spec)) {
50
+ throw new Error('Only OpenAPI v3 specifications are supported');
51
+ }
52
+ // --- Use helper to get validated path item ---
53
+ const lookupPath = decodedPath.startsWith('/') ? decodedPath : `/${decodedPath}`;
54
+ const pathItemObj = getValidatedPathItem(spec, lookupPath);
55
+ // --- Use helper to get validated requested methods ---
56
+ const validMethods = getValidatedOperations(pathItemObj, methods, lookupPath);
57
+ // Instantiate RenderablePathItem with the validated pathItemObj
58
+ const renderablePathItem = new RenderablePathItem(pathItemObj, // pathItemObj retrieved safely via helper
59
+ lookupPath, // Pass the raw, decoded path
60
+ pathUriSuffix // Pass the correctly built suffix
61
+ );
62
+ // Use the validated methods returned by the helper
63
+ resultItems = renderablePathItem.renderOperationDetail(context, validMethods);
64
+ }
65
+ catch (error) {
66
+ // Catch errors from helpers (e.g., path/method not found) or rendering
67
+ const message = error instanceof Error ? error.message : String(error);
68
+ console.error(`Error handling request ${uri.href}: ${message}`);
69
+ // Create a single error item representing the overall request failure
70
+ resultItems = createErrorResult(uri.href.substring(BASE_URI.length), // Use request URI suffix
71
+ message);
72
+ }
73
+ // Use imported formatResults
74
+ const contents = formatResults(context, resultItems);
75
+ return { contents };
76
+ };
77
+ }
78
+ getTemplate() {
79
+ // TODO: Add completion logic if needed
80
+ return new ResourceTemplate(`${BASE_URI}paths/{path}/{method*}`, {
81
+ list: undefined,
82
+ complete: undefined,
83
+ });
84
+ }
85
+ }
86
+ //# sourceMappingURL=operation-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation-handler.js","sourceRoot":"","sources":["../../../src/handlers/operation-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { ReadResourceTemplateCallback, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { SpecLoaderService } from '../types.js';
3
+ import { IFormatter } from '../services/formatters.js';
4
+ /**
5
+ * Handles requests for listing methods for a specific path.
6
+ * Corresponds to the `openapi://paths/{path}` template.
7
+ */
8
+ export declare class PathItemHandler {
9
+ private specLoader;
10
+ private formatter;
11
+ constructor(specLoader: SpecLoaderService, formatter: IFormatter);
12
+ getTemplate(): ResourceTemplate;
13
+ handleRequest: ReadResourceTemplateCallback;
14
+ }
@@ -0,0 +1,66 @@
1
+ import { ResourceTemplate, } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { RenderablePathItem } from '../rendering/path-item.js';
3
+ import { createErrorResult } from '../rendering/utils.js';
4
+ import { buildPathItemUriSuffix } from '../utils/uri-builder.js'; // Added .js extension
5
+ // Import shared handler utils
6
+ import { formatResults, isOpenAPIV3, getValidatedPathItem, // Import the helper
7
+ } from './handler-utils.js'; // Already has .js
8
+ const BASE_URI = 'openapi://';
9
+ // Removed duplicated FormattedResultItem type - now imported from handler-utils
10
+ // Removed duplicated formatResults function - now imported from handler-utils
11
+ // Removed duplicated isOpenAPIV3 function - now imported from handler-utils
12
+ /**
13
+ * Handles requests for listing methods for a specific path.
14
+ * Corresponds to the `openapi://paths/{path}` template.
15
+ */
16
+ export class PathItemHandler {
17
+ constructor(specLoader, formatter // Although unused in list view, needed for context
18
+ ) {
19
+ this.specLoader = specLoader;
20
+ this.formatter = formatter;
21
+ this.handleRequest = async (uri, variables) => {
22
+ const encodedPath = variables.path;
23
+ // Decode the path received from the URI variable
24
+ const decodedPath = decodeURIComponent(encodedPath || '');
25
+ // Use the builder to create the suffix, which will re-encode the path correctly
26
+ const pathUriSuffix = buildPathItemUriSuffix(decodedPath);
27
+ const context = { formatter: this.formatter, baseUri: BASE_URI };
28
+ let resultItems;
29
+ try {
30
+ const spec = await this.specLoader.getTransformedSpec({
31
+ resourceType: 'schema', // Use 'schema' for now
32
+ format: 'openapi',
33
+ });
34
+ // Use imported type guard
35
+ if (!isOpenAPIV3(spec)) {
36
+ throw new Error('Only OpenAPI v3 specifications are supported');
37
+ }
38
+ // --- Use helper to get validated path item ---
39
+ const lookupPath = decodedPath.startsWith('/') ? decodedPath : `/${decodedPath}`;
40
+ const pathItemObj = getValidatedPathItem(spec, lookupPath);
41
+ // Instantiate RenderablePathItem with the validated pathItemObj
42
+ const renderablePathItem = new RenderablePathItem(pathItemObj, // pathItemObj retrieved safely via helper
43
+ lookupPath, // Pass the raw, decoded path
44
+ pathUriSuffix // Pass the correctly built suffix
45
+ );
46
+ resultItems = renderablePathItem.renderList(context);
47
+ }
48
+ catch (error) {
49
+ const message = error instanceof Error ? error.message : String(error);
50
+ console.error(`Error handling request ${uri.href}: ${message}`);
51
+ resultItems = createErrorResult(pathUriSuffix, message);
52
+ }
53
+ // Use imported formatResults
54
+ const contents = formatResults(context, resultItems);
55
+ return { contents };
56
+ };
57
+ }
58
+ getTemplate() {
59
+ // TODO: Add completion logic if needed
60
+ return new ResourceTemplate(`${BASE_URI}paths/{path}`, {
61
+ list: undefined,
62
+ complete: undefined,
63
+ });
64
+ }
65
+ }
66
+ //# sourceMappingURL=path-item-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-item-handler.js","sourceRoot":"","sources":["../../../src/handlers/path-item-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { ReadResourceTemplateCallback, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { SpecLoaderService } from '../types.js';
3
+ import { IFormatter } from '../services/formatters.js';
4
+ /**
5
+ * Handles requests for top-level OpenAPI fields (info, servers, paths list, components list).
6
+ * Corresponds to the `openapi://{field}` template.
7
+ */
8
+ export declare class TopLevelFieldHandler {
9
+ private specLoader;
10
+ private formatter;
11
+ constructor(specLoader: SpecLoaderService, formatter: IFormatter);
12
+ getTemplate(): ResourceTemplate;
13
+ handleRequest: ReadResourceTemplateCallback;
14
+ }
@@ -0,0 +1,72 @@
1
+ import { ResourceTemplate, } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { RenderableDocument } from '../rendering/document.js';
3
+ import { RenderablePaths } from '../rendering/paths.js';
4
+ import { RenderableComponents } from '../rendering/components.js';
5
+ import { createErrorResult } from '../rendering/utils.js';
6
+ // Import shared handler utils
7
+ import { formatResults, isOpenAPIV3 } from './handler-utils.js'; // Already has .js
8
+ const BASE_URI = 'openapi://';
9
+ // Removed duplicated FormattedResultItem type - now imported from handler-utils
10
+ // Removed duplicated formatResults function - now imported from handler-utils
11
+ /**
12
+ * Handles requests for top-level OpenAPI fields (info, servers, paths list, components list).
13
+ * Corresponds to the `openapi://{field}` template.
14
+ */
15
+ export class TopLevelFieldHandler {
16
+ constructor(specLoader, formatter) {
17
+ this.specLoader = specLoader;
18
+ this.formatter = formatter;
19
+ this.handleRequest = async (uri, variables
20
+ // matchedTemplate is not needed if we only handle one template
21
+ ) => {
22
+ // Return type uses the defined array structure
23
+ const field = variables.field;
24
+ const context = { formatter: this.formatter, baseUri: BASE_URI };
25
+ let resultItems;
26
+ try {
27
+ const spec = await this.specLoader.getTransformedSpec({
28
+ // Use 'schema' as placeholder resourceType for transformation context
29
+ resourceType: 'schema',
30
+ format: 'openapi',
31
+ });
32
+ // Use imported type guard
33
+ if (!isOpenAPIV3(spec)) {
34
+ throw new Error('Only OpenAPI v3 specifications are supported');
35
+ }
36
+ const renderableDoc = new RenderableDocument(spec);
37
+ // Route based on the field name
38
+ if (field === 'paths') {
39
+ const pathsObj = renderableDoc.getPathsObject();
40
+ resultItems = new RenderablePaths(pathsObj).renderList(context);
41
+ }
42
+ else if (field === 'components') {
43
+ const componentsObj = renderableDoc.getComponentsObject();
44
+ resultItems = new RenderableComponents(componentsObj).renderList(context);
45
+ }
46
+ else {
47
+ // Handle other top-level fields (info, servers, tags, etc.)
48
+ const fieldObject = renderableDoc.getTopLevelField(field);
49
+ resultItems = renderableDoc.renderTopLevelFieldDetail(context, fieldObject, field);
50
+ }
51
+ }
52
+ catch (error) {
53
+ const message = error instanceof Error ? error.message : String(error);
54
+ console.error(`Error handling request ${uri.href}: ${message}`);
55
+ resultItems = createErrorResult(field, message); // Use field as uriSuffix for error
56
+ }
57
+ // Format results into the final structure
58
+ const contents = formatResults(context, resultItems);
59
+ // Return the object with the correctly typed contents array
60
+ // Use imported formatResults
61
+ return { contents };
62
+ };
63
+ }
64
+ getTemplate() {
65
+ // TODO: Add completion logic if needed
66
+ return new ResourceTemplate(`${BASE_URI}{field}`, {
67
+ list: undefined,
68
+ complete: undefined,
69
+ });
70
+ }
71
+ } // Ensure class closing brace is present
72
+ //# sourceMappingURL=top-level-field-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"top-level-field-handler.js","sourceRoot":"","sources":["../../../src/handlers/top-level-field-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};