minimax-llm-mcp
by webski101
README.md
<div align="center">
# minimax-llm-mcp
**An [MCP](https://modelcontextprotocol.org) server that exposes the MiniMax M3 LLM API to MCP-compatible clients.**
[](https://www.npmjs.com/package/minimax-llm-mcp)
[](./LICENSE)
[](https://nodejs.org)
MCP server that exposes the MiniMax M3 LLM API to MCP-compatible clients over stdio and SSE.
</div>
---
## Overview
`minimax-llm-mcp` is a [Model Context Protocol](https://modelcontextprotocol.org) (MCP) server that exposes the [MiniMax M3 LLM API](https://api.minimax.io) to any MCP-compatible client — Claude Desktop, Cursor, CyOps, Windsurf, and others. The server speaks JSON-RPC over **stdio** (the default, suitable for child-process clients) and **HTTP + Server-Sent Events** (SSE, for browser- and network-based clients), and registers four tools that map cleanly onto the upstream's chat-completions and tool-use surface:
| Tool | Purpose |
| --- | --- |
| `minimax_chat` | Non-streaming chat completion. |
| `minimax_complete` | Single-turn text completion (prompt + optional system). |
| `minimax_tool_call` | M3-native tool-use passthrough. Forwards `tools` and `tool_choice` verbatim. |
| `minimax_count_tokens` | Local token count using `cl100k_base` (no upstream call). |
The MiniMax M3 endpoint is OpenAI-compatible; the server wraps a small, well-tested HTTP client that handles auth, timeouts, retry-on-429, error mapping, and request-secret redaction.
> **Status:** `0.1.0` — the binary, the four tools, and the stdio + SSE transports are wired up. The SSE transport is feature-complete but not exercised by the demo at this time.
---
## Features
- **MCP-native** — registers four tools with Zod-validated input schemas, conforming to the [MCP spec](https://modelcontextprotocol.org/specification).
- **Two transports** — stdio (default) for child-process clients, and HTTP+SSE for network clients.
- **OpenAI-compatible** — non-streaming and streaming chat completions, plus native tool-use passthrough.
- **Local token counting** — `minimax_count_tokens` runs entirely client-side via `gpt-tokenizer`'s `cl100k_base` encoding; no upstream call, deterministic, fast.
- **Production-grade HTTP** — `Authorization: Bearer …` on every request, configurable per-request timeout, one-shot retry on `429` with exponential backoff, and full HTTP-status → `McpError` mapping (401/403 → `AuthenticationRequired`, 429 → `RateLimited`, 5xx → `UpstreamError`, other 4xx → `InvalidRequest`).
- **Secret redaction** — error messages are scrubbed of `sk-…` API-key shapes before they leave the server.
- **TypeScript-native** — strict ES2022 / NodeNext / `tsup`-bundled CJS with declarations on the wire.
- **Tested** — `vitest` with `v8` coverage; 80%+ line coverage on the runtime modules.
---
## Installation
### From npm (recommended)
```bash
npm install -g minimax-llm-mcp
```
This installs the `minimax-llm-mcp` binary on your `PATH`, ready for any MCP client to spawn.
### From a local checkout
```bash
git clone https://github.com/your-org/minimax-llm-mcp.git
cd minimax-llm-mcp
npm install
npm run build
```
The compiled binary is then at `./dist/index.js`. Point your MCP client at it directly (see [Usage](#usage) below).
### Prerequisites
- **Node.js ≥ 18** (the `engines` field enforces this).
- A **MiniMax API key**. Sign up at the [MiniMax developer portal](https://api.minimax.io) and copy the bearer token from your dashboard.
---
## Configuration
The server reads its configuration from environment variables at startup. The schema is validated by Zod; missing or invalid values produce a `ConfigError` and exit 1 on stdio, or `500` on SSE.
| Variable | Required | Default | Description |
| --- | --- | --- | --- |
| `MINIMAX_API_KEY` | **yes** | _(none)_ | Bearer token for the MiniMax M3 LLM API. |
| `TRANSPORT` | no | `stdio` | Transport the server listens on. One of `stdio` or `sse`. |
| `REQUEST_TIMEOUT_MS` | no | `300000` | Per-request timeout when calling the upstream API (in milliseconds). |
| `RETRY_ON_429` | no | `true` | Whether to retry once on `429 Too Many Requests` with a short back-off. |
| `MINIMAX_EMBEDDING_ENABLED` | no | `false` | Reserved for a future `minimax_embed` tool (out of scope in `0.1.0`). |
The full set is also documented in [`.env.example`](./.env.example) — copy that file to `.env` and uncomment the lines you want to override:
```bash
cp .env.example .env
$EDITOR .env
```
---
## Usage
The server is consumed by an MCP client. Below are copy-pasteable configuration snippets for the four most common clients. Replace `<your-minimax-api-key>` with a real bearer token, or set `MINIMAX_API_KEY` in the client's environment.
### Claude Desktop
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
```json
{
"mcpServers": {
"minimax-llm-mcp": {
"command": "npx",
"args": ["-y", "minimax-llm-mcp"],
"env": {
"MINIMAX_API_KEY": "<your-minimax-api-key>"
}
}
}
}
```
Or, if you have a local build:
```json
{
"mcpServers": {
"minimax-llm-mcp": {
"command": "node",
"args": ["/absolute/path/to/minimax-llm-mcp/dist/index.js"],
"env": {
"MINIMAX_API_KEY": "<your-minimax-api-key>"
}
}
}
}
```
### Cursor
Edit `~/.cursor/mcp.json` (or use Settings → MCP → Add new global MCP server):
```json
{
"mcpServers": {
"minimax-llm-mcp": {
"command": "npx",
"args": ["-y", "minimax-llm-mcp"],
"env": {
"MINIMAX_API_KEY": "<your-minimax-api-key>"
}
}
}
}
```
### CyOps
CyOps reads MCP servers from its global config (`~/.cyops/mcp.json` or the in-app Settings → MCP panel):
```json
{
"mcpServers": {
"minimax-llm-mcp": {
"command": "npx",
"args": ["-y", "minimax-llm-mcp"],
"env": {
"MINIMAX_API_KEY": "<your-minimax-api-key>"
}
}
}
}
```
### Windsurf
Edit `~/.codeium/windsurf/mcp_config.json` (or the in-app Settings → Cascade → MCP Servers → Add server form):
```json
{
"mcpServers": {
"minimax-llm-mcp": {
"command": "npx",
"args": ["-y", "minimax-llm-mcp"],
"env": {
"MINIMAX_API_KEY": "<your-minimax-api-key>"
}
}
}
}
```
### Trying it without an MCP client
For a quick smoke test (no real API call required):
```bash
# In one terminal, run the server in stdio mode and pipe a JSON-RPC
# `tools/list` request through it:
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
| MINIMAX_API_KEY=demo-key npx minimax-llm-mcp
```
The server reads the request from stdin, dispatches it, and writes the JSON-RPC response to stdout. You should see the four tool names listed.
---
## Available Tools
Every tool's input is validated by a Zod schema; the SDK applies the schema before the handler runs.
### `minimax_chat`
Non-streaming chat completion. Returns the assistant content plus optional usage.
**Input:**
| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `model` | string | no (default: `MiniMax-M3`) | |
| `messages` | array | yes | At least one message. Each has `role` (`system`/`user`/`assistant`/`tool`/`function`), `content`, and optional `name` / `tool_call_id`. |
| `temperature` | number | no | `[0, 2]`. |
| `top_p` | number | no | `[0, 1]`. |
| `n` | integer | no | Number of completions. |
| `max_tokens` | integer | no | ≤ 1,000,000 (hard cap). |
| `stop` | string \| string[] | no | |
| `presence_penalty` | number | no | `[-2, 2]`. |
| `frequency_penalty` | number | no | `[-2, 2]`. |
| `user` | string | no | Upstream abuse-tracking identifier. |
**Output:** `{ content, finish_reason, model, usage? }`.
### `minimax_complete`
Single-turn text completion. Wraps `prompt` (plus optional `system`) into a one-message conversation.
**Input:**
| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `model` | string | no (default: `MiniMax-M3`) | |
| `prompt` | string | yes | Non-empty. |
| `system` | string | no | System message prepended before `prompt`. |
| _(rest)_ | _same as `minimax_chat`_ | | |
**Output:** `{ content, finish_reason, model, usage? }`.
### `minimax_tool_call`
M3-native tool-use passthrough. Forwards `tools` and `tool_choice` to the upstream **verbatim** — the server does not validate or transform the function definitions.
**Input:**
| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| _(same as `minimax_chat`)_ | | | |
| `tools` | array | **yes** | Non-empty. Each entry is the OpenAI tool object (e.g. `{ type: "function", function: { name, description, parameters } }`). |
| `tool_choice` | string \| object | no | Standard OpenAI forms: `"auto"`, `"none"`, `"required"`, or `{"type": "function", "function": {"name": "..."}}`. |
**Output:** `{ content, finish_reason, model, usage?, tool_calls?, tool_call_payload? }`. When `finish_reason === "tool_calls"`, `tool_call_payload` is a structured JSON block with the call list — it is the JSON-serialized text content the MCP client renders.
### `minimax_count_tokens`
Local token count using the `cl100k_base` BPE encoding (the same one OpenAI's `tiktoken` uses for GPT-3.5/4). Does **not** make an upstream call — entirely client-side.
**Input:**
| Field | Type | Required | Notes |
| --- | --- | --- | --- |
| `model` | string | no (default: `MiniMax-M3`) | Recorded in the result, not used for tokenization. |
| `messages` | array | yes | At least one message. |
**Output:** `{ total, model, encoding, per_message: [{ role, tokens }] }`. The counts are deterministic and match `gpt-tokenizer`'s `cl100k_base` encoding.
---
## Development
### Setup
```bash
git clone https://github.com/your-org/minimax-llm-mcp.git
cd minimax-llm-mcp
npm install
```
### Scripts
| Script | What it does |
| --- | --- |
| `npm run build` | Bundle `src/index.ts` to `dist/` via `tsup` (CJS + `.d.ts` + sourcemap). |
| `npm run dev` | Same as `build` but with `--watch`. |
| `npm run typecheck` | `tsc --noEmit` against `tsconfig.json`. |
| `npm test` | Run the `vitest` suite once. |
| `npm run test:watch` | `vitest --watch`. |
| `npm run coverage` | `vitest run --coverage` (v8 provider; writes HTML to `coverage/`). |
### Project layout
```
src/
├── index.ts # CLI entry point (stdio)
├── server.ts # MCP server: registers the four tools
├── client.ts # MiniMax M3 HTTP client (auth, retry, error mapping)
├── config.ts # Zod-validated env config
├── errors.ts # McpError factory + ErrorCategory
├── tools/
│ ├── chat.ts # minimax_chat
│ ├── complete.ts # minimax_complete
│ ├── tool-call.ts # minimax_tool_call
│ └── count-tokens.ts # minimax_count_tokens
└── transports/
├── stdio.ts # startStdioServer(config)
└── sse.ts # startSSEServer(config, options)
tests/ # Mirror of src/, plus a top-level suite
# for the server, the HTTP client, the
# SSE transport, and the error helpers.
```
### TDD workflow
The slices were added in this order: scaffold → config → errors → client → count-tokens → chat → complete → tool-call → server → stdio → SSE. Each slice added the source file(s), the matching `tests/.../*.test.ts`, and was verified with `npm test` + `npm run coverage` before moving on. When adding a new tool or transport, follow the same pattern: write a failing test, write the engine, run the suite.
### Adding a new tool
1. Create `src/tools/<name>.ts` with a Zod input schema, an `X_INPUT_SCHEMA` export, and a `handleX(client, input, signal?)` function. The handler returns a typed result object; the SDK wraps it in `{ content: [{ type: "text", text: ... }] }`.
2. Create `tests/tools/<name>.test.ts` with `vi.fn()`-based client stubs.
3. Register the tool in `src/server.ts` via `server.registerTool(name, { description, inputSchema: X_INPUT_SCHEMA.shape }, async (args) => { ... })`.
### Adding a new env var
1. Add a Zod schema entry to `CONFIG_SCHEMA` in `src/config.ts` (with a default if optional).
2. Add the uncommented placeholder to `.env.example`.
3. Add tests in `tests/config.test.ts` covering the validation paths.
---
## Publishing
The package is `npm publish`-ready out of the box (the `bin` entry, `files` whitelist of `["dist"]`, `engines`, `main`, `types`, and `license` are all wired up). A pre-publish checklist:
1. Bump `version` in `package.json`.
2. `npm run typecheck` — clean.
3. `npm test` — 100% green; coverage ≥ 80% on `src/`.
4. `npm run build` — `dist/index.js` has the shebang and is executable.
5. `npm pack` — inspect the tarball. The `package` field should include only `dist/`, `package.json`, and `README.md`.
6. `npm publish --dry-run` — confirm the publish plan.
7. `npm login` (one-time).
8. `npm publish` — tag with `latest` for production releases.
The pre-publish step in CI should also `npm install` in a clean checkout and `npm test` to catch any drift between the test environment and the publish artifact.
---
## License
[MIT](./LICENSE) — Copyright (c) 2026 minimax-llm-mcp contributors.
See [`LICENSE`](./LICENSE) for the full text.
TDQS
A3.5/5.0
Scored across 4 tools
Disambiguation5/5
Each tool has a distinct purpose: chat completion, single-turn completion, local token counting, and tool-use passthrough. No overlap or confusion.
Naming Consistency5/5
All tools follow the consistent pattern 'minimax_<action>' with snake_case, making them predictable and easy to navigate.
Tool Count5/5
4 tools is well-scoped for an LLM server, covering the essential interactions without unnecessary bloat.
Completeness4/5
Covers core LLM operations but lacks streaming support, which is a common expectation for chat completions. Otherwise sufficient.
Maintenance
ActivityInactive
ResponsivenessNo issues