How to Extend the API with New Modules in the MCP-OpenAPI Server: Equipment Example

You can extend the MCP-OpenAPI server with new modules by adding REST controllers to the dummy application and ensuring the OpenAPI specification reflects the new endpoints—the MCP server automatically converts these into callable tools without requiring additional MCP-specific code.

The castrozan/tcc repository implements a generic MCP (Model-Context-Protocol) server that transforms any OpenAPI 3.x specification into conversational tools. This guide demonstrates how to extend the API with new modules—using the existing Equipment domain as a working example—by leveraging the declarative architecture of the OpenAPI specification and the automated tool generation provided by OpenAPISpecLoader and ToolsManager.

Architecture Overview

The system follows a declarative pattern where the OpenAPI specification serves as the single source of truth. When you add new endpoints to the underlying REST API, the MCP layer automatically exposes them as tools.

Key Components

Component Role Source File
OpenAPISpecLoader Retrieves and parses the OpenAPI spec, handling $ref inlining and type inference. [mcp-openapi-server/src/openapi-loader.ts](https://github.com/castrozan/tcc/blob/master/mcp-openapi-server/src/openapi-loader.ts)
ToolsManager Converts each OpenAPI operation into an MCP Tool with proper input schemas. Supports filtering via tags or tool names. [mcp-openapi-server/src/tools-manager.ts](https://github.com/castrozan/tcc/blob/master/mcp-openapi-server/src/tools-manager.ts)
OpenAPIServer Core MCP server that registers handlers for listTools and callTool, delegating execution to the API client. [mcp-openapi-server/src/server.ts](https://github.com/castrozan/tcc/blob/master/mcp-openapi-server/src/server.ts)
Dummy Application Hono.js REST API providing the actual endpoints (Equipment CRUD). Auto-generates OpenAPI documentation via chanfana. [equipments-dummy-app/src/infrastructure/web/open-api/server.ts](https://github.com/castrozan/tcc/blob/master/equipments-dummy-app/src/infrastructure/web/open-api/server.ts)

Step-by-Step Guide to Adding a New Module

Follow these steps to extend the API with a new domain module. The Equipment implementation serves as the reference pattern.

Step 1: Create the REST Controller

Extend OpenAPIRoute from chanfana to define the endpoint schema and handler. The schema automatically generates the OpenAPI documentation.

// File: equipments-dummy-app/src/presentation/controllers/equipments/CreateEquipmentController.ts
import { CreateEquipmentUseCase } from 'application/use-cases/equipments/CreateEquipmentUseCase';
import { Bool, OpenAPIRoute } from 'chanfana';
import equipmentRepository from 'infrastructure/database/repositories/equipments';
import { withErrorHandling } from 'presentation/decorators';
import { z } from 'zod';

export class CreateEquipmentController extends OpenAPIRoute {
    schema = {
        tags: ['Equipments'],
        summary: 'Create a new Equipment',
        security: [{ bearerAuth: [] }],
        request: { 
            body: { 
                content: { 
                    'application/json': { 
                        schema: z.object({
                            name: z.string().min(1),
                            description: z.string().min(1),
                            imageUrl: z.string().url().optional(),
                            type: z.string().optional(),
                        })
                    }
                }
            }
        },
        responses: {
            '201': { 
                description: 'Equipment created', 
                content: { 
                    'application/json': { 
                        schema: z.object({
                            success: Bool(),
                            result: z.object({
                                id: z.number(),
                                name: z.string(),
                                description: z.string(),
                                imageUrl: z.string().nullable(),
                                createdAt: z.string(),
                                type: z.string().nullable(),
                            })
                        })
                    }
                }
            }
        }
    };

    @withErrorHandling
    async handle(): Promise<object> {
        const data = await this.getValidatedData<typeof this.schema>();
        const { name, description, imageUrl, type } = data.body;
        const useCase = new CreateEquipmentUseCase(equipmentRepository);
        const equipment = await useCase.execute({ name, description, imageUrl, type });
        return {
            success: true,
            result: {
                id: equipment.id,
                name: equipment.name,
                description: equipment.description,
                imageUrl: equipment.imageUrl,
                createdAt: equipment.createdAt.toISOString(),
                type: equipment.type,
            },
        };
    }
}

Create analogous controllers for Read (FindByIdEquipmentController), Update (UpdateEquipmentController), Delete (DeleteEquipmentController), and List (FindAllEquipmentController) following the same pattern.

Step 2: Implement Use Cases and Repository

The controller delegates business logic to use cases and persistence to repositories. For Equipment, the repository uses SQLite:

// File: equipments-dummy-app/src/infrastructure/database/repositories/equipments/SQLiteEquipmentRepository.ts
// Implements CRUD operations for Equipment entities

The use cases (CreateEquipmentUseCase, FindAllEquipmentUseCase, etc.) reside in equipments-dummy-app/src/application/use-cases/equipments/.

Step 3: Register Routes in the Dummy App

Bind the controllers to HTTP endpoints in the Hono server:

// File: equipments-dummy-app/src/infrastructure/web/open-api/server.ts
import { fromHono } from 'chanfana';
import { Hono } from 'hono';
import { config } from 'dotenv';
import {
    CreateEquipmentController,
    DeleteEquipmentController,
    FindAllEquipmentController,
    FindByIdEquipmentController,
    UpdateEquipmentController,
} from 'presentation/controllers/equipments';

const app = new Hono();
const openapi = fromHono(app, { docs_url: '/', schema: { security: [{ bearerAuth: [] }] } });

openapi.registry.registerComponent('securitySchemes', 'bearerAuth', {
    type: 'http',
    scheme: 'bearer',
});

config();

// Register CRUD routes
openapi.get('/public/equipment', FindAllEquipmentController);
openapi.get('/public/equipment/:id', FindByIdEquipmentController);
openapi.put('/equipment', UpdateEquipmentController);
openapi.post('/equipment', CreateEquipmentController);
openapi.delete('/equipment/:id', DeleteEquipmentController);

export default app;

Step 4: Expose the OpenAPI Specification

The chanfana library auto-generates the OpenAPI specification at the root path (/). Ensure the dummy app is running:

cd equipments-dummy-app
npm install
npm run dev

# OpenAPI JSON available at http://localhost:3000/openapi.json

Step 5: Restart the MCP Server

Point the MCP server to the new specification:

cd mcp-openapi-server
node src/index.js \
  --transport http \
  --port 4000 \
  --host 127.0.0.1 \
  --path /mcp \
  --api-base-url http://localhost:3000 \
  --openapi-spec http://localhost:3000/openapi.json

The OpenAPISpecLoader parses the spec, and ToolsManager automatically creates tools for each operation (e.g., POST-/equipment becomes tool ID POST-equipment).

Filtering and Customizing Tools

If you need to expose only specific tools from the new module, use the filtering options in ToolsManager:

node src/index.js \
  --openapi-spec http://localhost:3000/openapi.json \
  --tag Equipments \
  --tool create-equipment \
  --tool get-equipment

The includeTools and includeTags logic in ToolsManager.initialize() (lines 85-130) applies these filters when building the final Map<string, Tool>.

Summary

  • Extend the API with new modules by adding REST controllers to the dummy application using the chanfana library.
  • The OpenAPI specification serves as the single source of truth; the MCP server requires no code changes to support new endpoints.
  • Restart the MCP server with the updated --openapi-spec path to automatically generate tools for the new module.
  • Use filtering options (--tag, --tool) to control which endpoints are exposed as MCP tools.

Frequently Asked Questions

Do I need to modify the MCP server code to add new modules?

No. The MCP server in castrozan/tcc is designed to be generic. As long as your REST API exposes a valid OpenAPI 3.x specification, the OpenAPISpecLoader and ToolsManager will automatically convert new endpoints into MCP tools without requiring changes to mcp-openapi-server/src/server.ts or related files.

How does the MCP server handle authentication for new modules?

The MCP server forwards authentication headers transparently. In the dummy app, controllers specify security: [{ bearerAuth: [] }] in their schema, and the route registration in server.ts registers the bearerAuth component. When calling tools through the MCP server, include the authorization token in the request context; the ApiClient will pass it through to the underlying REST endpoints.

Can I use a YAML file instead of the auto-generated JSON specification?

Yes. The OpenAPISpecLoader supports multiple sources including local files (YAML or JSON), URLs, stdin, or inline strings. You can point the MCP server to a static YAML file:

node src/index.js --openapi-spec ./my-api-spec.yaml --api-base-url http://localhost:3000

The loader will parse the YAML and apply the same tool generation logic as with JSON specifications.

What if I only want to expose specific operations from a large module?

Use the filtering capabilities built into ToolsManager. You can include or exclude tools based on tags or specific operation IDs using CLI flags:

node src/index.js \
  --openapi-spec http://localhost:3000/openapi.json \
  --tag Equipments \
  --exclude-tool delete-equipment

This allows you to granularly control which CRUD operations are available to MCP clients without modifying the underlying REST API.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →