Skip to main content
Glama
README.md
# mcp-honojs

Hono.js middleware for building [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers. Create MCP-compatible APIs with a simple, fluent interface — no decorators required.

## Features

- **Simple fluent API** — `server.tool()`, `server.resource()`, `server.prompt()`
- **Multiple transports** — SSE and HTTP (JSON-RPC 2.0)
- **Session management** — Built-in session handling with automatic cleanup
- **Type-safe** — Full TypeScript support with comprehensive types
- **Zero dependencies** — Only peer dependency is Hono
- **Lightweight** — Minimal footprint, perfect for edge deployments

## Installation

```bash
npm install mcp-honojs hono
```

## Quick Start

```typescript
import { Hono } from 'hono';
import { McpServer } from 'mcp-honojs';

// Create MCP server
const mcp = new McpServer({
  name: 'my-mcp-server',
  version: '1.0.0',
});

// Register a tool
mcp.tool(
  {
    name: 'greet',
    description: 'Greet someone by name',
    inputSchema: {
      type: 'object',
      properties: {
        name: { type: 'string', description: 'Name to greet' },
      },
      required: ['name'],
    },
  },
  async (args) => {
    return {
      content: [{ type: 'text', text: `Hello, ${args.name}!` }],
    };
  }
);

// Create Hono app and attach MCP
const app = new Hono();
mcp.attach(app);

export default app;
```

Visit your endpoints:
- SSE: `GET /sse`
- HTTP: `POST /mcp`

## API Reference

### McpServer

#### Constructor

```typescript
const mcp = new McpServer(options: McpServerOptions);
```

**Options:**
```typescript
interface McpServerOptions {
  name: string;              // Server name
  version: string;           // Server version
  session?: {
    timeout?: number;        // Session timeout in ms (default: 30min)
    cleanupInterval?: number; // Cleanup interval in ms (default: 5min)
    maxSessions?: number;     // Max concurrent sessions (default: 1000)
  };
}
```

#### Methods

**`tool(options, handler)`** — Register a tool

```typescript
mcp.tool(
  {
    name: 'add',
    description: 'Add two numbers',
    inputSchema: {
      type: 'object',
      properties: {
        a: { type: 'number' },
        b: { type: 'number' },
      },
      required: ['a', 'b'],
    },
  },
  async (args, context) => {
    return {
      content: [
        { type: 'text', text: `Result: ${args.a + args.b}` }
      ],
    };
  }
);
```

**`resource(options, handler)`** — Register a resource

```typescript
mcp.resource(
  {
    uri: 'example://data',
    name: 'example-data',
    description: 'Example data resource',
    mimeType: 'application/json',
  },
  async (uri, context) => {
    return {
      contents: [
        {
          uri,
          mimeType: 'application/json',
          text: JSON.stringify({ data: 'example' }),
        },
      ],
    };
  }
);
```

**`prompt(options, handler)`** — Register a prompt

```typescript
mcp.prompt(
  {
    name: 'welcome',
    description: 'Welcome prompt',
    arguments: [
      { name: 'username', description: 'User name', required: true }
    ],
  },
  async (args, context) => {
    return {
      messages: [
        {
          role: 'user',
          content: {
            type: 'text',
            text: `Welcome, ${args.username}!`,
          },
        },
      ],
    };
  }
);
```

**`attach(app, transportOptions?)`** — Attach transports to Hono app

```typescript
mcp.attach(app, {
  ssePath: '/sse',    // SSE endpoint (default: /sse)
  httpPath: '/mcp',   // HTTP endpoint (default: /mcp)
});
```

**`sseMiddleware(path?)`** — Get SSE middleware

```typescript
app.use(mcp.sseMiddleware('/custom-sse'));
```

**`httpMiddleware(path?)`** — Get HTTP middleware

```typescript
app.use(mcp.httpMiddleware('/custom-mcp'));
```

### Context

Both tool and resource handlers receive a `McpContext` object:

```typescript
interface McpContext {
  sessionId?: string;        // Current session ID
  honoContext?: Context;     // Hono request context
  metadata?: Record<string, any>; // Custom metadata
}
```

Access the Hono context for request details:

```typescript
mcp.tool({ /* ... */ }, async (args, context) => {
  const userAgent = context.honoContext?.req.header('user-agent');
  // ...
});
```

## Type Definitions

### Tool Types

```typescript
interface ToolResult {
  content: ToolResultContent[];
  isError?: boolean;
}

interface ToolResultContent {
  type: 'text' | 'image' | 'resource';
  text?: string;
  data?: string;          // Base64 for images
  mimeType?: string;
  uri?: string;           // For resource references
}
```

### Resource Types

```typescript
interface ResourceResult {
  contents: ResourceContent[];
}

interface ResourceContent {
  uri: string;
  mimeType?: string;
  text?: string;
  blob?: string;          // Base64 encoded binary
}
```

### Prompt Types

```typescript
interface PromptResult {
  description?: string;
  messages: PromptMessage[];
}

interface PromptMessage {
  role: 'user' | 'assistant';
  content: PromptMessageContent;
}
```

## JSON-RPC Protocol

The server implements [MCP's JSON-RPC 2.0 protocol](https://spec.modelcontextprotocol.io/specification/architecture/#message-types).

### Supported Methods

- `initialize` — Initialize connection
- `tools/list` — List available tools
- `tools/call` — Execute a tool
- `resources/list` — List resources
- `resources/templates/list` — List resource templates
- `resources/read` — Read a resource
- `prompts/list` — List prompts
- `prompts/get` — Get a prompt
- `ping` — Health check

### Example Requests

**Initialize:**
```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize"
}
```

**Call a tool:**
```json
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "greet",
    "arguments": { "name": "Alice" }
  }
}
```

## Session Management

Sessions are automatically managed:
- Created on first request
- Returned in `x-session-id` header (HTTP) or endpoint event (SSE)
- Auto-expire after timeout (default: 30 minutes)
- Cleaned up periodically (default: every 5 minutes)

**HTTP:** Pass session ID in `x-session-id` header
**SSE:** Session is bound to the connection

## Examples

See the [`examples/`](./examples) directory for complete examples:

- **basic.ts** — Simple calculator and greeting tools
- More examples coming soon!

Run examples:
```bash
cd examples
npm install
npm run dev
```

## Comparison with mcp-nestjs

| Feature | mcp-honojs | mcp-nestjs |
|---------|-----------|-----------|
| Framework | Hono.js | NestJS |
| API Style | Fluent/functional | Decorator-based |
| DI Container | ❌ (not needed) | ✅ (NestJS) |
| Auto-discovery | ❌ | ✅ |
| Edge-ready | ✅ | Depends |
| Bundle size | Tiny | Larger |
| Use case | APIs, edge, serverless | Enterprise apps |

## Deployment

### Node.js

```typescript
import { serve } from '@hono/node-server';
import app from './app';

serve({ fetch: app.fetch, port: 3000 });
```

### Cloudflare Workers

```typescript
export default app;
```

### Deno

```typescript
import { serve } from 'https://deno.land/std/http/server.ts';
serve(app.fetch);
```

### Bun

```typescript
export default {
  fetch: app.fetch,
  port: 3000,
};
```

## Contributing

Contributions welcome! Please open an issue or PR.

## License

MIT © zahidhasanaunto

## Resources

- [Model Context Protocol Spec](https://spec.modelcontextprotocol.io/)
- [Hono Documentation](https://hono.dev/)
- [mcp-nestjs](https://github.com/zahidhasanaunto/mcp-nestjs) (NestJS version)

Maintenance

ActivityInactive
ResponsivenessNo issues