Skip to main content
Glama
kadykov

OpenAPI Schema Explorer

path-item.ts6.22 kB
import { OpenAPIV3 } from 'openapi-types'; import { RenderableSpecObject, RenderContext, RenderResultItem } from './types.js'; // Add .js import { getOperationSummary, createErrorResult, generateListHint } from './utils.js'; // Add .js /** * Wraps an OpenAPIV3.PathItemObject to make it renderable. * Handles rendering the list of methods for a specific path and * the details of specific operations (methods). */ export class RenderablePathItem implements RenderableSpecObject { constructor( private pathItem: OpenAPIV3.PathItemObject | undefined, private path: string, // The raw, decoded path string e.g., "/users/{userId}" private pathUriSuffix: string // Built using buildPathItemUriSuffix(path) e.g., 'paths/users%7BuserId%7D' ) {} /** * Renders a token-efficient list of methods available for this path. * Corresponds to the `openapi://paths/{path}` URI. */ renderList(context: RenderContext): RenderResultItem[] { if (!this.pathItem) { return createErrorResult(this.pathUriSuffix, 'Path item not found.'); } // Correctly check if the lowercase key is one of the enum values const methods = Object.keys(this.pathItem).filter(key => Object.values(OpenAPIV3.HttpMethods).includes(key.toLowerCase() as OpenAPIV3.HttpMethods) ) as OpenAPIV3.HttpMethods[]; // Check if methods array is empty *after* filtering if (methods.length === 0) { // Return a specific non-error message indicating no methods were found return [ { uriSuffix: this.pathUriSuffix, data: `No standard HTTP methods found for path: ${decodeURIComponent( this.pathUriSuffix.substring('paths/'.length) // Get original path for display )}`, renderAsList: true, // isError is implicitly false here }, ]; } // Sort methods first to get the correct example methods.sort(); const firstMethodExample = methods.length > 0 ? methods[0] : undefined; // Generate hint using the new structure, providing the first *sorted* method as an example const hint = generateListHint(context, { itemType: 'pathMethod', parentPath: this.path, // Use the stored raw path firstItemExample: firstMethodExample, }); // Hint includes leading newline, so start output with it directly let outputLines: string[] = [hint.trim(), '']; // Trim leading newline from hint for first line // Iterate over the already sorted methods methods.forEach(method => { const operation = this.getOperation(method); // Use summary or operationId (via getOperationSummary) const summaryText = getOperationSummary(operation); // Format as METHOD: Summary or just METHOD if no summary/opId outputLines.push(`${method.toUpperCase()}${summaryText ? `: ${summaryText}` : ''}`); }); return [ { uriSuffix: this.pathUriSuffix, data: outputLines.join('\n'), // Join lines into a single string renderAsList: true, }, ]; } /** * Renders the detail view for one or more specific operations (methods) * Renders the detail view. For a PathItem, this usually means listing * the methods, similar to renderList. The handler should call * `renderOperationDetail` for specific method details. */ renderDetail(context: RenderContext): RenderResultItem[] { // Delegate to renderList as the primary view for a path item itself. return this.renderList(context); } /** * Renders the detail view for one or more specific operations (methods) * within this path item. * Corresponds to the `openapi://paths/{path}/{method*}` URI. * This is called by the handler after identifying the method(s). * * @param context - The rendering context. * @param methods - Array of method names (e.g., ['get', 'post']). * @returns An array of RenderResultItem representing the operation details. */ renderOperationDetail( _context: RenderContext, // Context might be needed later methods: string[] ): RenderResultItem[] { if (!this.pathItem) { // Create error results for all requested methods if path item is missing return methods.map(method => ({ uriSuffix: `${this.pathUriSuffix}/${method}`, data: null, isError: true, errorText: 'Path item not found.', renderAsList: true, })); } const results: RenderResultItem[] = []; for (const method of methods) { const operation = this.getOperation(method); const operationUriSuffix = `${this.pathUriSuffix}/${method}`; if (!operation) { results.push({ uriSuffix: operationUriSuffix, data: null, isError: true, errorText: `Method "${method.toUpperCase()}" not found for path.`, renderAsList: true, }); } else { // Return the raw operation object; handler will format it results.push({ uriSuffix: operationUriSuffix, data: operation, // isError: false (default) // renderAsList: false (default) }); } } return results; } /** * Gets the OperationObject for a specific HTTP method within this path item. * Performs case-insensitive lookup. * @param method - The HTTP method string (e.g., 'get', 'POST'). * @returns The OperationObject or undefined if not found. */ getOperation(method: string): OpenAPIV3.OperationObject | undefined { if (!this.pathItem) { return undefined; } const lowerMethod = method.toLowerCase(); // Check if the key is a standard HTTP method defined in the enum if (Object.values(OpenAPIV3.HttpMethods).includes(lowerMethod as OpenAPIV3.HttpMethods)) { const operation = this.pathItem[lowerMethod as keyof OpenAPIV3.PathItemObject]; // Basic check to ensure it looks like an operation object if (typeof operation === 'object' && operation !== null && 'responses' in operation) { // The check above narrows the type sufficiently, assertion is redundant return operation; } } return undefined; // Not a valid method or not an operation object } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kadykov/mcp-openapi-schema-explorer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server