npm package: @millionfor/nestjs-mcp-server
Description: A mcp service encapsulation based on nestjs framework
Author: QuanQuan
Latest version: 1.0.0-alpha.3

# nestjs-mcp-server

> A mcp service encapsulation based on nestjs framework

## Install

```bash
# npm
npm install @millionfor/nestjs-mcp-server @modelcontextprotocol/sdk zod

# yarn
yarn add @millionfor/nestjs-mcp-server @modelcontextprotocol/sdk zod

# pnpm
pnpm add @millionfor/nestjs-mcp-server @modelcontextprotocol/sdk zod
```

## Usage

### 1. Import module

Import `McpModule` in your module

```typescript
import { Module } from "@nestjs/common";
import { McpModule } from "@millionfor/nestjs-mcp-server";

@Module({
  imports: [
    McpModule.register({
      controllerBaseUrl: "api/mcp",
      mcpServerConfigs: [
        {
          serverId: "my-mcp-server",
          serverInfo: {
            name: "my-mcp-server",
            version: "1.0.0",
          },
        },
      ],
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
```

In addition, you can also import modules through `forRoot` and `forFeature`, the differences are as follows:

- forRoot: Usually registered in the root module
- forFeature: Share Service instance with forRoot registered module
- register: Register once and do not share with any module

### 2. Operation McpServer

After creating MCP Server, you can obtain the official sdk's native MCP Server instance through the `getServer` method. This instance has no encapsulation and is completely native.

For example, you can register a tool

```ts
import { Controller } from "@nestjs/common";
import { McpServerService } from "@millionfor/nestjs-mcp-server";
import { z } from "zod";

@Controller()
export class AppController {
  constructor(private readonly mcpServerService: McpServerService) {
    this.mcpServerService.getServer("server-id")?.tool(
      "get-current-time",
      {
        format: z
          .enum(["iso", "locale", "unix"])
          .optional()
          .default("iso")
          .describe("时间格式: iso, locale, 或 unix"),
      },
      async ({ format }: { format: "iso" | "locale" | "unix" }) => {
        const now = new Date();
        let timeValue: string | number;

        switch (format) {
          case "iso":
            timeValue = now.toISOString();
            break;
          case "locale":
            timeValue = now.toLocaleString();
            break;
          case "unix":
            timeValue = now.getTime();
            break;
          default:
            timeValue = now.toISOString();
        }

        return {
          content: [{ type: "text", text: String(timeValue) }],
        };
      }
    );
  }
}
```

### 3. Manually register the server instance

The above example directly passes in `mcpServerConfigs` in `McpModule.register`, which creates an MCP Server when the module is initialized.

Of course this is not necessary. You can manually register the MCP Server in the Controller or Service without passing in `mcpServerConfigs`:

```typescript
import { Controller } from "@nestjs/common";
import { McpServerService } from "@millionfor/nestjs-mcp-server";
import { z } from "zod";

@Controller()
export class AppController {
  constructor(private readonly mcpServerService: McpServerService) {
    // 手动注册一个MCP Server实例
    this.mcpServerService.registerServer({
      serverId: "another-server",
      serverInfo: {
        name: "another-mcp-server",
        version: "1.0.0",
      },
    });
  }
}
```

## Server access endpoint

After registering the module, the following endpoints are automatically created:

- **SSE connection endpoint**: `/{controllerBaseUrl}/{serverId}/sse`
- **Message processing endpoint**: `/{controllerBaseUrl}/{serverId}/messages`

For example, if you use `controllerBaseUrl: 'api/mcp'` and `serverId: 'my-mcp-server'`, the access URL is:

- `http://localhost:3000/api/mcp/my-mcp-server/sse` - for SSE connections
- `http://localhost:3000/api/mcp/my-mcp-server/messages` - for message processing

## API

### McpModule.register

- **controllerBaseUrl**: Basic URL path for MCP Controller
- **sseEndpoint**: SSE connection endpoint name, default is `sse`
- **messagesEndpoint**: Message processing endpoint name, default is `messages`
- **mcpServerConfigs**:
- serverId: McpServer id, use id to manage multiple instances
- serverInfo: As the first parameter, pass it directly into the native [MCP TS-SDK McpServer class](https://github.com/modelcontextprotocol/typescript-sdk)
- serverOptions: As the second parameter, pass it directly into the native [MCP TS-SDK McpServer class](https://github.com/modelcontextprotocol/typescript-sdk)

forRoot same as register, forFeature only receives mcpServerConfigs

### McpServerService

McpModule export service to manage multiple McpServer instances.

You can inject it into your Controller or Service using the following methods:

#### registerServer

- Function: Register a McpServer instance
- Parameters: `mcpServerConfigs` same as `McpModule.forRoot`
- Return value: Registered McpServer instance

#### getServer

- Function: Get the specified McpServer instance
- Parameters: serverId
- Return value: McpServer instance

#### getServerIds

- Function: Get all registered McpServer instance ids
- Return value: `string[]` All registered McpServer instance id arrays

#### hasServer

- Function: determine whether the specified McpServer instance has been registered
- Parameters: serverId
- Return value: boolean

#### connect

- Function: Connecting McpServer to the transport layer generally does not need to be used unless you want to manually manage the connection.
- Parameters:
- serverId
- transport, Transport instance
- Return value: void

### McpTransportService

McpModule export service to manage multiple SSEServerTransport instances.

It is usually not necessary to use unless you want to manually manage the connection.

The following only lists the possible methods. For more methods, please refer to the source code.

#### getTransport

- Function: Get a specific transmission connection
- Parameters: serverId, sessionId
- Return value: SSEServerTransport instance

#### getActiveSessionIds

- Function: Get all active session IDs of McpServer
- Parameters: serverId
- Return value: `string[]` All active session ID array

## Advanced Configuration

### Custom endpoint name

You can customize the SSE and message endpoint names when module registration:

```typescript
McpModule.register({
  controllerBaseUrl: "api/mcp",
  sseEndpoint: "connect", // Default is 'sse'
  messagesEndpoint: "rpc", // Default is 'messages'
  mcpServerConfigs: [
    /* ... */
  ],
});
```

## License

[MIT](https://github.com/millionfor)