refactor: consolidate shared utilities and reduce code duplication
- Extract HTTP_METHODS constant to types.ts (eliminates duplication in 3 files) - Add DEFAULT_CACHE_MAX_SIZE and DEFAULT_CACHE_TTL_MINUTES constants to cache.ts - Create schema-utils.ts with getSchemas, findSchema, getSchemaNames, getSchemaCount - Create spec-guards.ts with isOpenAPIV3, isSwaggerV2, getSpecVersion type guards - Create tool-response.ts with successResponse, errorResponse helpers - Update all tool handlers to use response helpers (~50 lines reduced) - Update parser.ts to use type guards for version detection Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
cae5f7fce1
commit
70fada22d6
12 changed files with 212 additions and 180 deletions
|
|
@ -2,6 +2,12 @@ import * as fs from 'fs';
|
|||
import * as path from 'path';
|
||||
import type { OpenAPISpec, ParsedSpec } from './types.js';
|
||||
|
||||
/** Default maximum number of cached specs */
|
||||
export const DEFAULT_CACHE_MAX_SIZE = 10;
|
||||
|
||||
/** Default cache TTL in minutes */
|
||||
export const DEFAULT_CACHE_TTL_MINUTES = 15;
|
||||
|
||||
export interface CacheEntry {
|
||||
spec: OpenAPISpec;
|
||||
metadata: ParsedSpec;
|
||||
|
|
@ -14,7 +20,7 @@ export class SpecCache {
|
|||
private maxSize: number;
|
||||
private ttlMs: number;
|
||||
|
||||
constructor(maxSize = 10, ttlMinutes = 15) {
|
||||
constructor(maxSize = DEFAULT_CACHE_MAX_SIZE, ttlMinutes = DEFAULT_CACHE_TTL_MINUTES) {
|
||||
this.cache = new Map();
|
||||
this.maxSize = maxSize;
|
||||
this.ttlMs = ttlMinutes * 60 * 1000;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import SwaggerParser from '@apidevtools/swagger-parser';
|
||||
import type { OpenAPI, OpenAPIV3, OpenAPIV3_1 } from 'openapi-types';
|
||||
import type { OpenAPIV3 } from 'openapi-types';
|
||||
import { HTTP_METHODS } from './types.js';
|
||||
import type { ParsedSpec, OpenAPISpec } from './types.js';
|
||||
import { specCache, getCacheKey } from './cache.js';
|
||||
|
||||
const HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] as const;
|
||||
import { getSchemaCount } from './schema-utils.js';
|
||||
import { isOpenAPIV3, isSwaggerV2, getSpecVersion as getVersion } from './spec-guards.js';
|
||||
|
||||
export interface ParseOptions {
|
||||
dereference?: boolean;
|
||||
|
|
@ -66,7 +67,7 @@ export async function bundleSpec(specPath: string): Promise<OpenAPISpec> {
|
|||
}
|
||||
|
||||
function extractMetadata(spec: OpenAPISpec): ParsedSpec {
|
||||
const version = getSpecVersion(spec);
|
||||
const version = getSpecVersionString(spec);
|
||||
const info = spec.info;
|
||||
|
||||
const paths = spec.paths || {};
|
||||
|
|
@ -103,35 +104,25 @@ function extractMetadata(spec: OpenAPISpec): ParsedSpec {
|
|||
};
|
||||
}
|
||||
|
||||
function getSpecVersion(spec: OpenAPISpec): string {
|
||||
if ('openapi' in spec) {
|
||||
return `OpenAPI ${spec.openapi}`;
|
||||
function getSpecVersionString(spec: OpenAPISpec): string {
|
||||
const version = getVersion(spec);
|
||||
if (isOpenAPIV3(spec)) {
|
||||
return `OpenAPI ${version}`;
|
||||
}
|
||||
if ('swagger' in spec) {
|
||||
return `Swagger ${spec.swagger}`;
|
||||
if (isSwaggerV2(spec)) {
|
||||
return `Swagger ${version}`;
|
||||
}
|
||||
return 'Unknown';
|
||||
}
|
||||
|
||||
function getSchemaCount(spec: OpenAPISpec): number {
|
||||
if ('components' in spec && spec.components?.schemas) {
|
||||
return Object.keys(spec.components.schemas).length;
|
||||
}
|
||||
if ('definitions' in spec && spec.definitions) {
|
||||
return Object.keys(spec.definitions).length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getServers(spec: OpenAPISpec): string[] {
|
||||
if ('servers' in spec && spec.servers) {
|
||||
if (isOpenAPIV3(spec) && spec.servers) {
|
||||
return spec.servers.map(s => s.url);
|
||||
}
|
||||
if ('host' in spec) {
|
||||
const swagger2 = spec as { host?: string; basePath?: string; schemes?: string[] };
|
||||
const scheme = swagger2.schemes?.[0] || 'https';
|
||||
const host = swagger2.host || 'localhost';
|
||||
const basePath = swagger2.basePath || '';
|
||||
if (isSwaggerV2(spec)) {
|
||||
const scheme = spec.schemes?.[0] || 'https';
|
||||
const host = spec.host || 'localhost';
|
||||
const basePath = spec.basePath || '';
|
||||
return [`${scheme}://${host}${basePath}`];
|
||||
}
|
||||
return [];
|
||||
|
|
|
|||
45
src/lib/schema-utils.ts
Normal file
45
src/lib/schema-utils.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* Consolidated schema access utilities for OpenAPI/Swagger specs.
|
||||
* Handles both OpenAPI 3.x (components.schemas) and Swagger 2.0 (definitions).
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get all schemas from a spec, handling both OpenAPI 3.x and Swagger 2.0 formats.
|
||||
*/
|
||||
export function getSchemas(spec: object): Record<string, object> {
|
||||
// OpenAPI 3.x: components.schemas
|
||||
const spec3 = spec as { components?: { schemas?: Record<string, object> } };
|
||||
if (spec3.components?.schemas) {
|
||||
return spec3.components.schemas;
|
||||
}
|
||||
|
||||
// Swagger 2.0: definitions
|
||||
const spec2 = spec as { definitions?: Record<string, object> };
|
||||
if (spec2.definitions) {
|
||||
return spec2.definitions;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a specific schema by name.
|
||||
*/
|
||||
export function findSchema(spec: object, schemaName: string): object | null {
|
||||
const schemas = getSchemas(spec);
|
||||
return schemas[schemaName] || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of all schema names.
|
||||
*/
|
||||
export function getSchemaNames(spec: object): string[] {
|
||||
return Object.keys(getSchemas(spec));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get count of schemas in the spec.
|
||||
*/
|
||||
export function getSchemaCount(spec: object): number {
|
||||
return Object.keys(getSchemas(spec)).length;
|
||||
}
|
||||
70
src/lib/spec-guards.ts
Normal file
70
src/lib/spec-guards.ts
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* Type guards for OpenAPI specification version detection.
|
||||
* Provides type-safe access to spec properties based on version.
|
||||
*/
|
||||
|
||||
import type { OpenAPI, OpenAPIV2, OpenAPIV3, OpenAPIV3_1 } from 'openapi-types';
|
||||
|
||||
/** OpenAPI 3.x specification (v3.0 or v3.1) */
|
||||
export type OpenAPIV3Spec = OpenAPIV3.Document | OpenAPIV3_1.Document;
|
||||
|
||||
/** Swagger 2.0 specification */
|
||||
export type SwaggerV2Spec = OpenAPIV2.Document;
|
||||
|
||||
/**
|
||||
* Check if spec is OpenAPI 3.x (has 'openapi' property starting with '3.')
|
||||
*/
|
||||
export function isOpenAPIV3(spec: OpenAPI.Document): spec is OpenAPIV3Spec {
|
||||
const openapi = (spec as { openapi?: string }).openapi;
|
||||
return typeof openapi === 'string' && openapi.startsWith('3.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if spec is Swagger 2.0 (has 'swagger' property equal to '2.0')
|
||||
*/
|
||||
export function isSwaggerV2(spec: OpenAPI.Document): spec is SwaggerV2Spec {
|
||||
const swagger = (spec as { swagger?: string }).swagger;
|
||||
return swagger === '2.0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if spec is OpenAPI 3.1 specifically (has 'openapi' starting with '3.1')
|
||||
*/
|
||||
export function isOpenAPIV31(spec: OpenAPI.Document): spec is OpenAPIV3_1.Document {
|
||||
const openapi = (spec as { openapi?: string }).openapi;
|
||||
return typeof openapi === 'string' && openapi.startsWith('3.1');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the spec version string (e.g., "3.0.0", "3.1.0", "2.0")
|
||||
*/
|
||||
export function getSpecVersion(spec: OpenAPI.Document): string {
|
||||
if (isOpenAPIV3(spec)) {
|
||||
return (spec as { openapi: string }).openapi;
|
||||
}
|
||||
if (isSwaggerV2(spec)) {
|
||||
return (spec as { swagger: string }).swagger;
|
||||
}
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get schemas from spec with proper type narrowing
|
||||
* Returns components.schemas for v3, definitions for v2
|
||||
*/
|
||||
export function getSchemasTyped(spec: OpenAPI.Document): Record<string, object> {
|
||||
if (isOpenAPIV3(spec)) {
|
||||
return spec.components?.schemas as Record<string, object> ?? {};
|
||||
}
|
||||
if (isSwaggerV2(spec)) {
|
||||
return spec.definitions ?? {};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get paths object from spec
|
||||
*/
|
||||
export function getPathsTyped(spec: OpenAPI.Document): Record<string, object> {
|
||||
return (spec as { paths?: Record<string, object> }).paths ?? {};
|
||||
}
|
||||
33
src/lib/tool-response.ts
Normal file
33
src/lib/tool-response.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* Tool response helpers for consistent MCP tool responses.
|
||||
*/
|
||||
|
||||
/** Standard MCP tool response structure - compatible with MCP SDK index signature */
|
||||
export interface ToolResponse {
|
||||
[x: string]: unknown;
|
||||
content: Array<{ type: 'text'; text: string }>;
|
||||
structuredContent: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a success response with text content and structured data.
|
||||
*/
|
||||
export function successResponse(text: string, data: Record<string, unknown>): ToolResponse {
|
||||
return {
|
||||
content: [{ type: 'text', text }],
|
||||
structuredContent: { success: true, ...data },
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an error response with error message.
|
||||
* @param message - Error message
|
||||
* @param context - Optional context prefix (e.g., "parsing spec", "querying endpoints")
|
||||
*/
|
||||
export function errorResponse(message: string, context?: string): ToolResponse {
|
||||
const text = context ? `Error ${context}: ${message}` : `Error: ${message}`;
|
||||
return {
|
||||
content: [{ type: 'text', text }],
|
||||
structuredContent: { success: false, error: message },
|
||||
};
|
||||
}
|
||||
|
|
@ -3,6 +3,12 @@ import type { OpenAPI, OpenAPIV3, OpenAPIV3_1 } from 'openapi-types';
|
|||
export type OpenAPISpec = OpenAPI.Document;
|
||||
export type OpenAPIV3Spec = OpenAPIV3.Document | OpenAPIV3_1.Document;
|
||||
|
||||
/** Standard HTTP methods supported in OpenAPI specifications */
|
||||
export const HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] as const;
|
||||
|
||||
/** Type derived from HTTP_METHODS constant */
|
||||
export type HttpMethod = typeof HTTP_METHODS[number];
|
||||
|
||||
export interface ParsedSpec {
|
||||
version: string;
|
||||
title: string;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import SwaggerParser from '@apidevtools/swagger-parser';
|
||||
import { HTTP_METHODS } from './types.js';
|
||||
import type { ValidationResult, ValidationError } from './types.js';
|
||||
|
||||
export async function validateSpec(specPath: string): Promise<ValidationResult> {
|
||||
|
|
@ -54,7 +55,7 @@ export async function validateWithWarnings(specPath: string): Promise<Validation
|
|||
if (spec.paths) {
|
||||
for (const [path, pathItem] of Object.entries(spec.paths)) {
|
||||
if (!pathItem) continue;
|
||||
for (const method of ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']) {
|
||||
for (const method of HTTP_METHODS) {
|
||||
const operation = (pathItem as Record<string, unknown>)[method] as { operationId?: string } | undefined;
|
||||
if (operation && !operation.operationId) {
|
||||
result.warnings.push({
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import { z } from 'zod';
|
||||
import { parseSpec } from '../lib/parser.js';
|
||||
import { formatTypes } from '../utils/format.js';
|
||||
import { getSchemas } from '../lib/schema-utils.js';
|
||||
import { successResponse, errorResponse } from '../lib/tool-response.js';
|
||||
import type { ToolResponse } from '../lib/tool-response.js';
|
||||
import type { TypeScriptOptions } from '../lib/types.js';
|
||||
|
||||
export const generateToolName = 'generate-types';
|
||||
|
|
@ -31,10 +34,7 @@ export async function generateToolHandler({ path, schemas, noCache, options }: {
|
|||
schemas?: string[];
|
||||
noCache?: boolean;
|
||||
options?: TypeScriptOptions;
|
||||
}): Promise<{
|
||||
content: Array<{ type: 'text'; text: string }>;
|
||||
structuredContent: Record<string, unknown>;
|
||||
}> {
|
||||
}): Promise<ToolResponse> {
|
||||
try {
|
||||
// Validate mutually exclusive options
|
||||
if (options?.enumAsUnion && options?.enumAsEnum) {
|
||||
|
|
@ -71,43 +71,16 @@ export async function generateToolHandler({ path, schemas, noCache, options }: {
|
|||
const types = generateTypeScript(schemasToGenerate, options);
|
||||
const text = formatTypes(types);
|
||||
|
||||
return {
|
||||
content: [{ type: 'text', text }],
|
||||
structuredContent: {
|
||||
success: true,
|
||||
types,
|
||||
generatedCount: Object.keys(schemasToGenerate).length,
|
||||
options: options || {},
|
||||
},
|
||||
};
|
||||
return successResponse(text, {
|
||||
types,
|
||||
generatedCount: Object.keys(schemasToGenerate).length,
|
||||
options: options || {},
|
||||
});
|
||||
} catch (err) {
|
||||
const error = err as Error;
|
||||
return {
|
||||
content: [{ type: 'text', text: `Error generating types: ${error.message}` }],
|
||||
structuredContent: {
|
||||
success: false,
|
||||
error: error.message,
|
||||
},
|
||||
};
|
||||
return errorResponse((err as Error).message, 'generating types');
|
||||
}
|
||||
}
|
||||
|
||||
function getSchemas(spec: object): Record<string, object> {
|
||||
// OpenAPI 3.x
|
||||
const spec3 = spec as { components?: { schemas?: Record<string, object> } };
|
||||
if (spec3.components?.schemas) {
|
||||
return spec3.components.schemas;
|
||||
}
|
||||
|
||||
// Swagger 2.0
|
||||
const spec2 = spec as { definitions?: Record<string, object> };
|
||||
if (spec2.definitions) {
|
||||
return spec2.definitions;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/** Get indentation string based on options */
|
||||
function getIndent(options?: TypeScriptOptions): string {
|
||||
const style = options?.indentation ?? '2';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { z } from 'zod';
|
||||
import { parseSpec } from '../lib/parser.js';
|
||||
import { formatMetadata } from '../utils/format.js';
|
||||
import { successResponse, errorResponse } from '../lib/tool-response.js';
|
||||
import type { ToolResponse } from '../lib/tool-response.js';
|
||||
|
||||
export const parseToolName = 'parse-spec';
|
||||
|
||||
|
|
@ -14,10 +16,7 @@ export const parseToolSchema = {
|
|||
export async function parseToolHandler({ path, noCache }: {
|
||||
path: string;
|
||||
noCache?: boolean;
|
||||
}): Promise<{
|
||||
content: Array<{ type: 'text'; text: string }>;
|
||||
structuredContent: Record<string, unknown>;
|
||||
}> {
|
||||
}): Promise<ToolResponse> {
|
||||
try {
|
||||
const { spec, metadata, dereferenced, cached } = await parseSpec(path, { noCache });
|
||||
|
||||
|
|
@ -29,24 +28,8 @@ export async function parseToolHandler({ path, noCache }: {
|
|||
text += '\n\n*Served from cache*';
|
||||
}
|
||||
|
||||
return {
|
||||
content: [{ type: 'text', text }],
|
||||
structuredContent: {
|
||||
success: true,
|
||||
metadata,
|
||||
dereferenced,
|
||||
cached,
|
||||
spec,
|
||||
},
|
||||
};
|
||||
return successResponse(text, { metadata, dereferenced, cached, spec });
|
||||
} catch (err) {
|
||||
const error = err as Error;
|
||||
return {
|
||||
content: [{ type: 'text', text: `Error parsing spec: ${error.message}` }],
|
||||
structuredContent: {
|
||||
success: false,
|
||||
error: error.message,
|
||||
},
|
||||
};
|
||||
return errorResponse((err as Error).message, 'parsing spec');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import { z } from 'zod';
|
||||
import { parseSpec } from '../lib/parser.js';
|
||||
import { formatEndpoints } from '../utils/format.js';
|
||||
import { HTTP_METHODS } from '../lib/types.js';
|
||||
import { successResponse, errorResponse } from '../lib/tool-response.js';
|
||||
import type { ToolResponse } from '../lib/tool-response.js';
|
||||
import type { EndpointInfo, EndpointFilter, ParameterInfo, ResponseInfo } from '../lib/types.js';
|
||||
import type { OpenAPIV3 } from 'openapi-types';
|
||||
|
||||
|
|
@ -17,8 +20,6 @@ export const queryToolSchema = {
|
|||
noCache: z.boolean().optional().describe('Bypass cache and parse fresh'),
|
||||
};
|
||||
|
||||
const HTTP_METHODS = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] as const;
|
||||
|
||||
export async function queryToolHandler(args: {
|
||||
path: string;
|
||||
method?: string;
|
||||
|
|
@ -26,10 +27,7 @@ export async function queryToolHandler(args: {
|
|||
tag?: string;
|
||||
operationId?: string;
|
||||
noCache?: boolean;
|
||||
}): Promise<{
|
||||
content: Array<{ type: 'text'; text: string }>;
|
||||
structuredContent: Record<string, unknown>;
|
||||
}> {
|
||||
}): Promise<ToolResponse> {
|
||||
try {
|
||||
const { spec } = await parseSpec(args.path, { noCache: args.noCache });
|
||||
|
||||
|
|
@ -43,23 +41,9 @@ export async function queryToolHandler(args: {
|
|||
const endpoints = extractEndpoints(spec, filter);
|
||||
const text = formatEndpoints(endpoints);
|
||||
|
||||
return {
|
||||
content: [{ type: 'text', text }],
|
||||
structuredContent: {
|
||||
success: true,
|
||||
count: endpoints.length,
|
||||
endpoints,
|
||||
},
|
||||
};
|
||||
return successResponse(text, { count: endpoints.length, endpoints });
|
||||
} catch (err) {
|
||||
const error = err as Error;
|
||||
return {
|
||||
content: [{ type: 'text', text: `Error querying endpoints: ${error.message}` }],
|
||||
structuredContent: {
|
||||
success: false,
|
||||
error: error.message,
|
||||
},
|
||||
};
|
||||
return errorResponse((err as Error).message, 'querying endpoints');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import { z } from 'zod';
|
||||
import { parseSpec } from '../lib/parser.js';
|
||||
import { formatSchema } from '../utils/format.js';
|
||||
import { findSchema, getSchemaNames } from '../lib/schema-utils.js';
|
||||
import { successResponse, errorResponse } from '../lib/tool-response.js';
|
||||
import type { ToolResponse } from '../lib/tool-response.js';
|
||||
import type { SchemaInfo } from '../lib/types.js';
|
||||
|
||||
export const schemaToolName = 'get-schema';
|
||||
|
|
@ -17,17 +20,14 @@ export async function schemaToolHandler({ path, schemaName, noCache }: {
|
|||
path: string;
|
||||
schemaName: string;
|
||||
noCache?: boolean;
|
||||
}): Promise<{
|
||||
content: Array<{ type: 'text'; text: string }>;
|
||||
structuredContent: Record<string, unknown>;
|
||||
}> {
|
||||
}): Promise<ToolResponse> {
|
||||
try {
|
||||
const { spec } = await parseSpec(path, { noCache });
|
||||
|
||||
const schema = findSchema(spec, schemaName);
|
||||
|
||||
if (!schema) {
|
||||
const available = getAvailableSchemas(spec);
|
||||
const available = getSchemaNames(spec);
|
||||
return {
|
||||
content: [{ type: 'text', text: `Schema '${schemaName}' not found. Available schemas: ${available.join(', ')}` }],
|
||||
structuredContent: {
|
||||
|
|
@ -49,53 +49,8 @@ export async function schemaToolHandler({ path, schemaName, noCache }: {
|
|||
|
||||
const text = formatSchema(schemaInfo);
|
||||
|
||||
return {
|
||||
content: [{ type: 'text', text }],
|
||||
structuredContent: {
|
||||
success: true,
|
||||
schema: schemaInfo,
|
||||
},
|
||||
};
|
||||
return successResponse(text, { schema: schemaInfo });
|
||||
} catch (err) {
|
||||
const error = err as Error;
|
||||
return {
|
||||
content: [{ type: 'text', text: `Error getting schema: ${error.message}` }],
|
||||
structuredContent: {
|
||||
success: false,
|
||||
error: error.message,
|
||||
},
|
||||
};
|
||||
return errorResponse((err as Error).message, 'getting schema');
|
||||
}
|
||||
}
|
||||
|
||||
function findSchema(spec: object, schemaName: string): object | null {
|
||||
// OpenAPI 3.x: components.schemas
|
||||
const spec3 = spec as { components?: { schemas?: Record<string, object> } };
|
||||
if (spec3.components?.schemas?.[schemaName]) {
|
||||
return spec3.components.schemas[schemaName];
|
||||
}
|
||||
|
||||
// Swagger 2.0: definitions
|
||||
const spec2 = spec as { definitions?: Record<string, object> };
|
||||
if (spec2.definitions?.[schemaName]) {
|
||||
return spec2.definitions[schemaName];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getAvailableSchemas(spec: object): string[] {
|
||||
// OpenAPI 3.x
|
||||
const spec3 = spec as { components?: { schemas?: Record<string, object> } };
|
||||
if (spec3.components?.schemas) {
|
||||
return Object.keys(spec3.components.schemas);
|
||||
}
|
||||
|
||||
// Swagger 2.0
|
||||
const spec2 = spec as { definitions?: Record<string, object> };
|
||||
if (spec2.definitions) {
|
||||
return Object.keys(spec2.definitions);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { z } from 'zod';
|
||||
import { validateWithWarnings } from '../lib/validator.js';
|
||||
import { formatValidation } from '../utils/format.js';
|
||||
import { successResponse, errorResponse } from '../lib/tool-response.js';
|
||||
import type { ToolResponse } from '../lib/tool-response.js';
|
||||
|
||||
export const validateToolName = 'validate-spec';
|
||||
|
||||
|
|
@ -10,29 +12,12 @@ export const validateToolSchema = {
|
|||
path: z.string().describe('Path to the OpenAPI/Swagger spec file (YAML or JSON)'),
|
||||
};
|
||||
|
||||
export async function validateToolHandler({ path }: { path: string }): Promise<{
|
||||
content: Array<{ type: 'text'; text: string }>;
|
||||
structuredContent: Record<string, unknown>;
|
||||
}> {
|
||||
export async function validateToolHandler({ path }: { path: string }): Promise<ToolResponse> {
|
||||
try {
|
||||
const result = await validateWithWarnings(path);
|
||||
const text = formatValidation(result);
|
||||
|
||||
return {
|
||||
content: [{ type: 'text', text }],
|
||||
structuredContent: {
|
||||
success: true,
|
||||
...result,
|
||||
},
|
||||
};
|
||||
return successResponse(text, { ...result });
|
||||
} catch (err) {
|
||||
const error = err as Error;
|
||||
return {
|
||||
content: [{ type: 'text', text: `Error validating spec: ${error.message}` }],
|
||||
structuredContent: {
|
||||
success: false,
|
||||
error: error.message,
|
||||
},
|
||||
};
|
||||
return errorResponse((err as Error).message, 'validating spec');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue