These instructions are for AI assistants working in this project.
Always open @/openspec/AGENTS.md when the request:
- Mentions planning or proposals (words like proposal, spec, change, plan)
- Introduces new capabilities, breaking changes, architecture shifts, or big performance/security work
- Sounds ambiguous and you need the authoritative spec before coding
Use @/openspec/AGENTS.md to learn:
- How to create and apply change proposals
- Spec format and conventions
- Project structure and guidelines
Keep this managed block so 'openspec update' can refresh the instructions.
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is an MCP (Model Context Protocol) server that exposes OpenAPI endpoints as MCP tools. It allows LLMs to discover and interact with REST APIs defined by OpenAPI specifications through the MCP protocol.
npm run build # Build TypeScript using esbuild
npm test # Run all tests with Vitest
npm test -- api-client.test.ts # Run specific test file
npm run test:watch # Run tests in watch mode
npm run test:coverage # Generate coverage reportnpm run dev # Watch mode with auto-rebuild
npm run inspect-watch # Debug mode with auto-reload (starts MCP inspector)
npm run typecheck # TypeScript type checking only (no build)
npm run lint # Run ESLint
npm run lint:fix # Auto-fix linting issuesNever try to run test scripts from command line using node -e - this will always fail. Write a TypeScript test file, build it, and execute it, or use the test runner with npm test.
The codebase has a layered architecture:
OpenAPIServer (server.ts)
↓ uses
ToolsManager (tools-manager.ts) → manages tool filtering & lookup
↓ uses
OpenAPISpecLoader (openapi-loader.ts) → parses specs & creates tools
↓ uses
ApiClient (api-client.ts) → executes HTTP requests
↓ uses
AuthProvider (auth-provider.ts) → handles authentication
Tool IDs uniquely identify API endpoints with format: METHOD::pathPart
Critical implementation detail: Uses double underscores (__) to separate path segments:
/api/v1/users→GET::api__v1__users/api/resource-name/items→GET::api__resource-name__items- Single hyphens within segments are preserved as-is
- Path parameters have braces removed:
/users/{id}→GET::users__---id
Implementation in src/utils/tool-id.ts:
generateToolId()- Creates tool IDs from method + pathparseToolId()- Parses tool IDs back to method + pathsanitizeForToolId()- Ensures safe characters only
Critical for Issue #50 fix: The system tracks where parameters should be sent using x-parameter-location metadata:
path- URL path parameters (e.g.,/users/{id})query- Query string parameters (e.g.,?page=1)header- HTTP header parameters (e.g.,Authorization: Bearer token)cookie- Cookie parameters
This metadata is set during tool creation in openapi-loader.ts:546 and consumed during request execution in api-client.ts:142-170.
Three distinct modes controlled by toolsMode:
"all"(default): Load all endpoint-based tools from OpenAPI spec, applying filters"dynamic": Load only meta-tools (LIST-API-ENDPOINTS,GET-API-ENDPOINT-SCHEMA,INVOKE-API-ENDPOINT)"explicit": Load only tools specified inincludeTools, ignoring all other filters
Tool names must be ≤64 characters with format [a-z0-9-]+. The abbreviation system (in openapi-loader.ts and utils/abbreviations.ts) applies:
- Word splitting (camelCase, underscores, numbers)
- Common word removal ("controller", "api", "service", etc.)
- Standard abbreviations ("management" → "mgmt", "user" → "usr")
- Vowel removal for long words
- Hash suffix if needed
Disable with disableAbbreviation: true (may cause errors if names exceed 64 chars).
Two approaches:
Static headers (backward compatible):
const config = { headers: { Authorization: "Bearer token" } }
// Creates StaticAuthProvider internallyDynamic AuthProvider (for token refresh, expiration handling):
interface AuthProvider {
getAuthHeaders(): Promise<Record<string, string>> // Called before each request
handleAuthError(error: AxiosError): Promise<boolean> // Called on 401/403, return true to retry
}Key flow: Before each request → getAuthHeaders() → On 401/403 → handleAuthError() → If returns true, retry once with fresh headers.
Reference resolution: Handles $ref pointers to components (parameters, schemas), detects circular references.
Schema composition: Supports allOf (merges schemas), oneOf/anyOf (preserves composition), not.
Input schema generation: Merges path/query/header/cookie parameters + request body into unified schema. Handles naming conflicts by prefixing body properties with body_.
Parameter inheritance: Path-level parameters are inherited by all operations, operation-level parameters can override.
Tests use Vitest with comprehensive coverage across:
- Unit tests: Individual functions and classes
- Integration tests: Component interactions
- Edge case tests: Boundary conditions and errors
- Regression tests: Prevent known issues (e.g., Issue #33, Issue #50)
Key test files match source files: api-client.test.ts tests api-client.ts, etc.
Each major feature or bug fix should have dedicated test cases with descriptive names referencing the issue number (e.g., "Issue #50: Header Parameter Support").
Filters are applied in order with AND logic:
includeTools(highest priority) - If specified, overrides all othersincludeOperations- HTTP methods (get, post, etc.)includeResources- Resource names extracted from pathsincludeTags- OpenAPI tags
All filtering is case-insensitive.
Exception: In explicit mode, only includeTools is used, all others are ignored.
- Configuration:
src/config.ts- Loads from env vars and CLI args - CLI entry:
src/cli.ts- Command-line interface - Transport:
src/transport/StreamableHttpServerTransport.ts- HTTP transport with SSE - Examples:
examples/directory - Real-world usage patterns - Developer docs:
docs/developer-guide.md- Comprehensive architecture guide
When adding support for new parameter locations (like the header fix for Issue #50):
- Update
openapi-loader.tsto setx-parameter-locationmetadata - Update
api-client.tsto extract and handle the parameter type - Add comprehensive tests covering GET/POST requests, mixed parameters, and auth header merging
- Ensure parameters are removed from the main params object after extraction
When adding metadata to ExtendedTool interface:
- Define property in
openapi-loader.tsinterface - Populate during tool creation in
parseOpenAPISpec() - Use in filtering logic in
tools-manager.ts - Add tests for filter behavior
When processing $ref:
- Always handle both reference objects and schema objects
- Pass
componentsandvisitedset for cycle detection - Return empty schema for unresolvable references
- Use
inlineSchema()helper for recursive resolution
src/- TypeScript source filestest/- Test files (mirrorsrc/structure)dist/- Built output (created by build process)bin/mcp-server.js- CLI executable entry pointbuild.js- esbuild configuration for bundlingexamples/- Complete runnable examples for different use cases