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.
- package/.devcontainer/devcontainer.json +24 -0
- package/.github/dependabot.yml +13 -0
- package/.github/workflows/ci.yml +111 -0
- package/.husky/pre-commit +6 -0
- package/.prettierignore +3 -0
- package/.prettierrc.json +12 -0
- package/.releaserc.json +23 -0
- package/CHANGELOG.md +32 -0
- package/CONTRIBUTING.md +67 -0
- package/Dockerfile +3 -0
- package/LICENSE +21 -0
- package/README.md +127 -0
- package/dist/src/config.d.ts +15 -0
- package/dist/src/config.js +19 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/handlers/component-detail-handler.d.ts +14 -0
- package/dist/src/handlers/component-detail-handler.js +87 -0
- package/dist/src/handlers/component-detail-handler.js.map +1 -0
- package/dist/src/handlers/component-map-handler.d.ts +14 -0
- package/dist/src/handlers/component-map-handler.js +63 -0
- package/dist/src/handlers/component-map-handler.js.map +1 -0
- package/dist/src/handlers/handler-utils.d.ts +69 -0
- package/dist/src/handlers/handler-utils.js +180 -0
- package/dist/src/handlers/handler-utils.js.map +1 -0
- package/dist/src/handlers/operation-handler.d.ts +14 -0
- package/dist/src/handlers/operation-handler.js +86 -0
- package/dist/src/handlers/operation-handler.js.map +1 -0
- package/dist/src/handlers/path-item-handler.d.ts +14 -0
- package/dist/src/handlers/path-item-handler.js +66 -0
- package/dist/src/handlers/path-item-handler.js.map +1 -0
- package/dist/src/handlers/top-level-field-handler.d.ts +14 -0
- package/dist/src/handlers/top-level-field-handler.js +72 -0
- package/dist/src/handlers/top-level-field-handler.js.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +177 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/rendering/components.d.ts +67 -0
- package/dist/src/rendering/components.js +177 -0
- package/dist/src/rendering/components.js.map +1 -0
- package/dist/src/rendering/document.d.ts +36 -0
- package/dist/src/rendering/document.js +147 -0
- package/dist/src/rendering/document.js.map +1 -0
- package/dist/src/rendering/path-item.d.ts +45 -0
- package/dist/src/rendering/path-item.js +141 -0
- package/dist/src/rendering/path-item.js.map +1 -0
- package/dist/src/rendering/paths.d.ts +26 -0
- package/dist/src/rendering/paths.js +78 -0
- package/dist/src/rendering/paths.js.map +1 -0
- package/dist/src/rendering/types.d.ts +50 -0
- package/dist/src/rendering/types.js +12 -0
- package/dist/src/rendering/types.js.map +1 -0
- package/dist/src/rendering/utils.d.ts +31 -0
- package/dist/src/rendering/utils.js +79 -0
- package/dist/src/rendering/utils.js.map +1 -0
- package/dist/src/services/formatters.d.ts +36 -0
- package/dist/src/services/formatters.js +52 -0
- package/dist/src/services/formatters.js.map +1 -0
- package/dist/src/services/reference-transform.d.ts +27 -0
- package/dist/src/services/reference-transform.js +75 -0
- package/dist/src/services/reference-transform.js.map +1 -0
- package/dist/src/services/spec-loader.d.ts +27 -0
- package/dist/src/services/spec-loader.js +77 -0
- package/dist/src/services/spec-loader.js.map +1 -0
- package/dist/src/types.d.ts +11 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/uri-builder.d.ts +81 -0
- package/dist/src/utils/uri-builder.js +121 -0
- package/dist/src/utils/uri-builder.js.map +1 -0
- package/dist/src/version.d.ts +1 -0
- package/dist/src/version.js +4 -0
- package/dist/src/version.js.map +1 -0
- package/eslint.config.js +88 -0
- package/jest.config.js +32 -0
- package/justfile +66 -0
- package/memory-bank/activeContext.md +139 -0
- package/memory-bank/productContext.md +39 -0
- package/memory-bank/progress.md +141 -0
- package/memory-bank/projectbrief.md +50 -0
- package/memory-bank/systemPatterns.md +224 -0
- package/memory-bank/techContext.md +131 -0
- package/package.json +76 -0
- package/scripts/generate-version.js +49 -0
- package/src/config.ts +33 -0
- package/src/handlers/component-detail-handler.ts +121 -0
- package/src/handlers/component-map-handler.ts +92 -0
- package/src/handlers/handler-utils.ts +230 -0
- package/src/handlers/operation-handler.ts +114 -0
- package/src/handlers/path-item-handler.ts +88 -0
- package/src/handlers/top-level-field-handler.ts +92 -0
- package/src/index.ts +222 -0
- package/src/rendering/components.ts +228 -0
- package/src/rendering/document.ts +167 -0
- package/src/rendering/path-item.ts +157 -0
- package/src/rendering/paths.ts +87 -0
- package/src/rendering/types.ts +63 -0
- package/src/rendering/utils.ts +107 -0
- package/src/services/formatters.ts +71 -0
- package/src/services/reference-transform.ts +105 -0
- package/src/services/spec-loader.ts +88 -0
- package/src/types.ts +17 -0
- package/src/utils/uri-builder.ts +134 -0
- package/src/version.ts +4 -0
- package/test/__tests__/e2e/format.test.ts +224 -0
- package/test/__tests__/e2e/resources.test.ts +369 -0
- package/test/__tests__/e2e/spec-loading.test.ts +172 -0
- package/test/__tests__/unit/config.test.ts +39 -0
- package/test/__tests__/unit/handlers/component-detail-handler.test.ts +241 -0
- package/test/__tests__/unit/handlers/component-map-handler.test.ts +187 -0
- package/test/__tests__/unit/handlers/handler-utils.test.ts +255 -0
- package/test/__tests__/unit/handlers/operation-handler.test.ts +202 -0
- package/test/__tests__/unit/handlers/path-item-handler.test.ts +153 -0
- package/test/__tests__/unit/handlers/top-level-field-handler.test.ts +182 -0
- package/test/__tests__/unit/rendering/components.test.ts +269 -0
- package/test/__tests__/unit/rendering/document.test.ts +172 -0
- package/test/__tests__/unit/rendering/path-item.test.ts +197 -0
- package/test/__tests__/unit/rendering/paths.test.ts +115 -0
- package/test/__tests__/unit/services/formatters.test.ts +109 -0
- package/test/__tests__/unit/services/reference-transform.test.ts +320 -0
- package/test/__tests__/unit/services/spec-loader.test.ts +214 -0
- package/test/__tests__/unit/utils/uri-builder.test.ts +103 -0
- package/test/fixtures/complex-endpoint.json +146 -0
- package/test/fixtures/empty-api.json +8 -0
- package/test/fixtures/multi-component-types.json +55 -0
- package/test/fixtures/paths-test.json +61 -0
- package/test/fixtures/sample-api.json +68 -0
- package/test/fixtures/sample-v2-api.json +39 -0
- package/test/setup.ts +32 -0
- package/test/utils/console-helpers.ts +48 -0
- package/test/utils/mcp-test-helpers.ts +66 -0
- package/test/utils/test-types.ts +54 -0
- package/tsconfig.json +25 -0
- package/tsconfig.test.json +5 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"swagger": "2.0",
|
|
3
|
+
"info": {
|
|
4
|
+
"title": "Simple Swagger 2.0 API",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"description": "A simple API definition in Swagger 2.0 format for testing conversion."
|
|
7
|
+
},
|
|
8
|
+
"host": "localhost:3000",
|
|
9
|
+
"basePath": "/v2",
|
|
10
|
+
"schemes": ["http"],
|
|
11
|
+
"paths": {
|
|
12
|
+
"/ping": {
|
|
13
|
+
"get": {
|
|
14
|
+
"summary": "Check service health",
|
|
15
|
+
"description": "Returns a simple pong message.",
|
|
16
|
+
"produces": ["application/json"],
|
|
17
|
+
"responses": {
|
|
18
|
+
"200": {
|
|
19
|
+
"description": "Successful response",
|
|
20
|
+
"schema": {
|
|
21
|
+
"$ref": "#/definitions/Pong"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"definitions": {
|
|
29
|
+
"Pong": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"properties": {
|
|
32
|
+
"message": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"example": "pong"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
package/test/setup.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
// Extend timeout for E2E tests
|
|
5
|
+
jest.setTimeout(30000);
|
|
6
|
+
|
|
7
|
+
// Clean up any previous test artifacts
|
|
8
|
+
beforeAll(async () => {
|
|
9
|
+
// Create required directories if they don't exist
|
|
10
|
+
const dirs = ['dist', 'dist/src', 'test/fixtures'];
|
|
11
|
+
|
|
12
|
+
for (const dir of dirs) {
|
|
13
|
+
try {
|
|
14
|
+
await fs.mkdir(dir, { recursive: true });
|
|
15
|
+
} catch (error) {
|
|
16
|
+
// Ignore if directory already exists
|
|
17
|
+
if ((error as { code?: string }).code !== 'EEXIST') {
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Verify sample OpenAPI spec exists
|
|
24
|
+
const specPath = path.resolve(process.cwd(), 'test/fixtures/sample-api.json');
|
|
25
|
+
try {
|
|
26
|
+
await fs.access(specPath);
|
|
27
|
+
} catch {
|
|
28
|
+
throw new Error(`Sample OpenAPI spec not found at ${specPath}`);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Custom matchers could be added here if needed
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
|
|
3
|
+
// Define a type for the console.error function signature using unknown
|
|
4
|
+
type ConsoleErrorType = (message?: unknown, ...optionalParams: unknown[]) => void;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Temporarily suppresses console.error messages that match the expected pattern
|
|
8
|
+
* during the execution of a provided function (typically an async test operation).
|
|
9
|
+
* Unexpected console.error messages will still be logged.
|
|
10
|
+
*
|
|
11
|
+
* @param expectedMessage The exact string or a RegExp to match against the console.error message.
|
|
12
|
+
* @param fnToRun The async function to execute while suppression is active.
|
|
13
|
+
* @returns The result of the fnToRun function.
|
|
14
|
+
*/
|
|
15
|
+
export async function suppressExpectedConsoleError<T>(
|
|
16
|
+
expectedMessage: string | RegExp,
|
|
17
|
+
fnToRun: () => T | Promise<T>
|
|
18
|
+
): Promise<T> {
|
|
19
|
+
// Use the defined type for the original function
|
|
20
|
+
const originalConsoleError: ConsoleErrorType = console.error;
|
|
21
|
+
|
|
22
|
+
// Use unknown in the mock implementation signature
|
|
23
|
+
const consoleErrorSpy = jest
|
|
24
|
+
.spyOn(console, 'error')
|
|
25
|
+
.mockImplementation((message?: unknown, ...args: unknown[]) => {
|
|
26
|
+
const messageStr = String(message); // String conversion handles unknown
|
|
27
|
+
const shouldSuppress =
|
|
28
|
+
expectedMessage instanceof RegExp
|
|
29
|
+
? expectedMessage.test(messageStr)
|
|
30
|
+
: messageStr === expectedMessage;
|
|
31
|
+
|
|
32
|
+
if (!shouldSuppress) {
|
|
33
|
+
// Call the original implementation for unexpected errors
|
|
34
|
+
// We still need to handle the potential type mismatch for the spread
|
|
35
|
+
// Using Function.prototype.apply is a safer way to call with dynamic args
|
|
36
|
+
Function.prototype.apply.call(originalConsoleError, console, [message, ...args]);
|
|
37
|
+
}
|
|
38
|
+
// If it matches, do nothing (suppress)
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
// Execute the function that is expected to trigger the error log
|
|
43
|
+
return await fnToRun();
|
|
44
|
+
} finally {
|
|
45
|
+
// Restore the original console.error
|
|
46
|
+
consoleErrorSpy.mockRestore();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
3
|
+
// import path from 'path';
|
|
4
|
+
|
|
5
|
+
// Export the interface
|
|
6
|
+
export interface McpTestContext {
|
|
7
|
+
client: Client;
|
|
8
|
+
transport: StdioClientTransport;
|
|
9
|
+
cleanup: () => Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface StartServerOptions {
|
|
13
|
+
outputFormat?: 'json' | 'yaml' | 'json-minified';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Start MCP server with test configuration
|
|
18
|
+
*/
|
|
19
|
+
export async function startMcpServer(
|
|
20
|
+
specPath: string,
|
|
21
|
+
options: StartServerOptions = {}
|
|
22
|
+
): Promise<McpTestContext> {
|
|
23
|
+
let transport: StdioClientTransport | undefined;
|
|
24
|
+
let client: Client | undefined;
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// Initialize transport with spec path as argument
|
|
28
|
+
transport = new StdioClientTransport({
|
|
29
|
+
command: 'node',
|
|
30
|
+
args: [
|
|
31
|
+
'dist/src/index.js',
|
|
32
|
+
// path.resolve(specPath),
|
|
33
|
+
specPath,
|
|
34
|
+
...(options.outputFormat ? ['--output-format', options.outputFormat] : []),
|
|
35
|
+
],
|
|
36
|
+
stderr: 'inherit', // Pass through server errors normally - they're part of E2E testing
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Initialize client
|
|
40
|
+
client = new Client({
|
|
41
|
+
name: 'test-client',
|
|
42
|
+
version: '1.0.0',
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
await client.connect(transport);
|
|
46
|
+
|
|
47
|
+
// Create cleanup function
|
|
48
|
+
const cleanup = async (): Promise<void> => {
|
|
49
|
+
if (transport) {
|
|
50
|
+
await transport.close();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
client,
|
|
56
|
+
transport,
|
|
57
|
+
cleanup,
|
|
58
|
+
};
|
|
59
|
+
} catch (error) {
|
|
60
|
+
// Clean up on error
|
|
61
|
+
if (transport) {
|
|
62
|
+
await transport.close();
|
|
63
|
+
}
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { OpenAPIV3 } from 'openapi-types';
|
|
2
|
+
|
|
3
|
+
export interface EndpointSuccessResponse {
|
|
4
|
+
method: string;
|
|
5
|
+
path: string;
|
|
6
|
+
parameters?: OpenAPIV3.ParameterObject[];
|
|
7
|
+
requestBody?: OpenAPIV3.RequestBodyObject;
|
|
8
|
+
responses: { [key: string]: OpenAPIV3.ResponseObject };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface EndpointErrorResponse {
|
|
12
|
+
method: string;
|
|
13
|
+
path: string;
|
|
14
|
+
error: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type EndpointResponse = EndpointSuccessResponse | EndpointErrorResponse;
|
|
18
|
+
|
|
19
|
+
export function isEndpointErrorResponse(obj: unknown): obj is EndpointErrorResponse {
|
|
20
|
+
return (
|
|
21
|
+
typeof obj === 'object' &&
|
|
22
|
+
obj !== null &&
|
|
23
|
+
typeof (obj as EndpointErrorResponse).error === 'string'
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ResourceContent {
|
|
28
|
+
uri: string;
|
|
29
|
+
mimeType: string;
|
|
30
|
+
text: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ResourceResponse {
|
|
34
|
+
contents: ResourceContent[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Types for Schema Resource E2E tests
|
|
38
|
+
export type SchemaSuccessResponse = OpenAPIV3.SchemaObject; // Use type alias
|
|
39
|
+
|
|
40
|
+
export interface SchemaErrorResponse {
|
|
41
|
+
name: string;
|
|
42
|
+
error: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export type SchemaResponse = SchemaSuccessResponse | SchemaErrorResponse;
|
|
46
|
+
|
|
47
|
+
export function isSchemaErrorResponse(obj: unknown): obj is SchemaErrorResponse {
|
|
48
|
+
return (
|
|
49
|
+
typeof obj === 'object' &&
|
|
50
|
+
obj !== null &&
|
|
51
|
+
typeof (obj as SchemaErrorResponse).name === 'string' && // Check for name property
|
|
52
|
+
typeof (obj as SchemaErrorResponse).error === 'string'
|
|
53
|
+
);
|
|
54
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"outDir": "dist",
|
|
11
|
+
"declaration": true,
|
|
12
|
+
"sourceMap": true,
|
|
13
|
+
"rootDir": ".",
|
|
14
|
+
"lib": ["ES2020"],
|
|
15
|
+
"noImplicitAny": true,
|
|
16
|
+
"noImplicitThis": true,
|
|
17
|
+
"noUnusedLocals": true,
|
|
18
|
+
"noUnusedParameters": true,
|
|
19
|
+
"noImplicitReturns": true,
|
|
20
|
+
"noFallthroughCasesInSwitch": true,
|
|
21
|
+
"types": ["jest", "node"]
|
|
22
|
+
},
|
|
23
|
+
"include": ["src/**/*"],
|
|
24
|
+
"exclude": ["node_modules", "dist", "local-docs", "test"]
|
|
25
|
+
}
|