@fectivnfy/minimax-code-sdk
v0.3.1
Published
TypeScript SDK for the MiniMax Code CLI (mcode) — mirrors the skeleton of @anthropic-ai/claude-code while carrying mcode-native semantics.
Maintainers
Readme
@fectivnfy/minimax-code-sdk
The official TypeScript SDK for the MiniMax Code CLI (mcode).
The skeleton mirrors
@anthropic-ai/claude-code; the semantics are mcode-native. See docs/spec.md for the full design contract and docs/design-decisions.md for the rationale behind every choice.
Quickstart
import { query } from "@fectivnfy/minimax-code-sdk";
for await (const ev of query("Reply with the single word: ok")) {
if (ev.type === "delta" && ev.delta.content) {
process.stdout.write(ev.delta.content);
}
if (ev.type === "result") {
console.log(`\nstatus=${ev.result.status} duration=${ev.result.durationMs}ms`);
}
}Installation
npm install @fectivnfy/minimax-code-sdkPrerequisites: Node.js
>= 18, andmcodeonPATH(install vianpm i -g @minimax-ai/codeor the official installer). If anything is off, runnpx @fectivnfy/minimax-code-sdk doctor.
Usage
One-shot query
import { query } from "@fectivnfy/minimax-code-sdk";
const events = query("Explain stream-json in one sentence");
for await (const ev of events) {
// ev is one of 7 native mcode event types — see "Streaming" below.
}Streaming
The SDK passes the native mcode stream-json through unchanged.
Consumers see exactly the 7 event types that mcode exec --output-format stream-json emits:
| Event type | Carries |
|---|---|
| heartbeat | turnId — liveness signal |
| session-status | started \| finished, turnId |
| message | full message snapshot (role, content, thinking, usage, …) |
| delta | streaming chunk (content or thinking, chunkIndex, finish) |
| generic | forward-compat envelope for mcode internal events |
| done | turn finished |
| result | final aggregated exec.result (status, answer, durationMs, model, usage) |
Multi-turn sessions
By default each query() is a fresh session. To continue, pass either:
// continue the most recent session in this cwd
for await (const ev of query("Now add 7", { continue_conversation: true })) { /* … */ }
// or resume a specific one
for await (const ev of query("And divide by 2", { session_id: "mvs_…" })) { /* … */ }Permission policy
// Read-only run: agent can read but not write or run shell.
for await (const ev of query("List files in ./src", { permission: "off" })) { /* … */ }Values: "ask" | "smart" | "full" | "off" (mcode-native; not Claude's).
Attachments
for await (const ev of query("Summarise these", {
files: ["./src/index.ts", "./README.md"],
})) { /* … */ }Cancellation & timeout
const ac = new AbortController();
setTimeout(() => ac.abort(), 30_000);
try {
for await (const ev of query("…", { signal: ac.signal, timeout_ms: 25_000 })) { /* … */ }
} catch (e) {
if (e instanceof TimeoutError) console.error("timed out");
if (e instanceof AbortError) console.error("aborted");
}ACP — Agent Client Protocol
For richer bidirectional integrations (IDE plugins, agent-of-agents):
import { acp } from "@fectivnfy/minimax-code-sdk";
const conn = acp.start({ model: "minimax/MiniMax-M3", permission: "ask" });
const off = conn.onMessage((msg) => console.log("[in]", msg.method, msg.params));
try {
const result = await conn.request("session/prompt", { prompt: "Hello" });
console.log("got:", result);
} finally {
off();
await conn.close();
}ACP message names are passed through from the mcode acp protocol —
the SDK does not re-shape them. See examples/05-acp.ts.
State queries
import { MavisClient } from "@fectivnfy/minimax-code-sdk";
const client = new MavisClient();
console.log(await client.getVersion());
console.log(await client.getProviders());
console.log(await client.getPlugins());
console.log(await client.getActiveSessions());
console.log(await client.getBackgroundTasks());
console.log(await client.getCurrentAccount());
console.log(await client.getConfig());Session management (v0.2.0)
MavisClient.sessionStore exposes a typed, programmatic surface over the
runtime's session data. The store reads the runtime's SQLite index
(~/.minimax/v2/sqlite/runtime-state.sqlite) directly — no mcode shell-out
needed.
import { MavisClient, SessionNotFoundError } from "@fectivnfy/minimax-code-sdk";
const client = new MavisClient();
const store = client.sessionStore(); // read-only by default
// List recent sessions, with filters
const recent = await store.list({ limit: 10, agentName: "mavis" });
// Drill into a single session
const detail = await store.get(recent[0]!.id);
console.log(detail.title, detail.status, detail.model);
// Walk the persisted message transcript (with tool calls)
const messages = await store.getMessages(detail.id, {
limit: 5,
includeThinking: false, // opt in to surface thinking_content
});
// Token usage, rolled up by model and turn
const usage = await store.getUsage(detail.id);
console.log(`${usage.totalTokens} tokens across ${usage.callCount} calls`);
// Free-text search across titles and message bodies
const hits = await store.search("OAuth device flow", { scope: "all" });
// Aggregate stats across the runtime
const stats = await store.getStats();
console.log(`${stats.total} sessions, ${stats.byStatus["idle"] ?? 0} idle`);
// Write operations — opt in explicitly.
const rws = client.sessionStore({ writable: true });
await rws.archive(detail.id);
await rws.rename(detail.id, "renamed-by-script");
await rws.unarchive(detail.id);
await rws.delete(detail.id); // DESTRUCTIVE — be sure.See examples/07-session-store.ts for a runnable walk-through, and
docs/spec.md §2.1 for the full type surface.
Error handling:
import { SessionNotFoundError, SessionStoreError } from "@fectivnfy/minimax-code-sdk";
try {
await store.get("mvs_does_not_exist");
} catch (e) {
if (e instanceof SessionNotFoundError) {
console.error(`no such session: ${e.sessionId}`);
} else if (e instanceof SessionStoreError) {
console.error("store failure:", e.message, e.cause);
}
}Drive an existing session (v0.3.0)
client.sessionStore().send(id, prompt, options?) resumes a session by
spawning mcode exec --session <id> and streaming events back. It
returns the same QueryHandle as query():
const handle = client.sessionStore().send(detail.id, "and add 7");
for await (const ev of handle) {
if (ev.type === "result") console.log("done:", ev.result.answer);
}client.sessionStore().tailMessages(id, options?) is a polling
async-generator for "anything new since cursor" — useful for monitoring
sessions you don't own:
const ac = new AbortController();
setTimeout(() => ac.abort(), 60_000);
for await (const msg of client.sessionStore().tailMessages(detail.id, {
intervalMs: 1000,
includeThinking: false,
signal: ac.signal,
})) {
console.log(`[${msg.role}] ${msg.content.slice(0, 80)}`);
}Runtime managers (v0.3.0)
Four lazy properties on MavisClient for managing the runtime's
plugins, agents, cron tasks, and MCP servers:
// Plugins — wraps `mcode plugin …`
const plugins = await client.plugins.list();
await client.plugins.add("my-plugin@official");
await client.plugins.enable("my-plugin@official");
await client.plugins.remove("my-plugin@official");
// Agents — read-only (use the runtime UI to create)
const agents = await client.agents.list();
const def = await client.agents.getDefault();
const mavis = await client.agents.get("mavis");
// Cron — read-only (use the mavis native tool to create)
const crons = await client.cron.list();
const runs = await client.cron.listRuns(crons[0]!.cronId, { limit: 10 });
// MCP servers — read-only (edit `~/.minimax/mcp/mcp.json` to add)
const servers = await client.mcp.list();
const tv = await client.mcp.get("textvision");The agents / cron / mcp surfaces are read-only because the runtime
exposes no public CLI for create / update. See examples/08-runtime-managers.ts
for a runnable walk-through.
Providers (v0.3.1) — the only manager with a full CRUD surface
(decision Q59). The mcode provider CLI is a complete one, so the
SDK wraps it 1:1.
// List — richer than `getProviders()` (also returns `minimaxModelSource` + per-provider `kind`/`models`)
const result = await client.providers.list();
console.log(result.minimaxModelSource, result.active?.providerId);
// Add a custom provider (api key is referenced by env-var, never stored)
await client.providers.add({
name: "OpenCodeGo",
baseUrl: "https://api.opencodego.example.com",
apiFormat: "openai-completions",
models: ["deepseek-v4-flash"],
apiKeyEnv: "OPENCODEGO_KEY",
use: true,
});
// Connectivity test
const t = await client.providers.test("custom_provider:opencodego", { model: "deepseek-v4-flash" });
if (!t.ok) console.error("provider test failed:", t.output);
// Switch minimax credential source
await client.providers.use("api-key");
await client.providers.setMinimaxKey({ apiKeyEnv: "MINIMAX_API_KEY" });
// Remove a custom provider (pass `{ yes: true }` to skip the confirm)
await client.providers.remove("custom_provider:opencodego", { yes: true });Configuration
All options are snake_case (matching mcode's CLI) and live on the
second argument to query() / MavisClient.query().
| Option | Type | CLI equivalent | Notes |
|---|---|---|---|
| input | string | --input | mcode currently only accepts "-" |
| input_format | "text" \| "json" | --input-format | |
| cwd | string | --cwd | defaults to process.cwd() |
| files | string[] | --file (repeatable) | attachments |
| model | "<provider>/<model>" | --model | |
| session_id | string | --session | |
| continue_conversation | boolean | --continue | |
| config | string | --config | runtime config path |
| permission | "ask" \| "smart" \| "full" \| "off" | --permission | mcode-native |
| max_steps | number | --max-steps | |
| output_format | "text" \| "json" \| "stream-json" | --output-format | |
| output_schema | string | --output-schema | JSON Schema |
| signal | AbortSignal | (SDK-only) | cooperative cancel |
| timeout_ms | number | (SDK-only) | numeric ms; throws TimeoutError |
API Reference
The full public surface is exported from @fectivnfy/minimax-code-sdk:
MavisClient— main class withquery(),getVersion(),getCurrentAccount(),getConfig(),getProviders(),getPlugins(),getActiveSessions(),getBackgroundTasks(),sessionStore(overrides?).SessionStore— programmatic session management (v0.2.0).list/get/getMessages/search/getUsage/getAssets/getBackgroundTasks/getStats/archive/unarchive/rename/delete. Also available as a subpath import:@fectivnfy/minimax-code-sdk/session-store.query(prompt, options?)— convenience one-shot.- All
StreamEventtypes andQueryOptions. - 8 error classes (
MavisErrorbase + 7 subclasses:CLINotFoundError,CLIConnectionError,ProcessError,StreamJsonError,AbortError,TimeoutError,SessionNotFoundError,SessionStoreError). acp.start(options?)— Agent Client Protocol connection.resolveMcode()/whichMcodeInPath()— binary discovery helpers.parseStreamLine(line)— stream-json line parser (useful for custom replayers).
For exhaustive type-level documentation, generate dist/*.d.ts and
read the source.
Error handling
import { MavisError, CLINotFoundError, ProcessError, TimeoutError, AbortError, StreamJsonError } from "@fectivnfy/minimax-code-sdk";
try {
for await (const ev of query("…")) { /* … */ }
} catch (e) {
if (e instanceof CLINotFoundError) { /* mcode is not on PATH or in known dirs */ }
if (e instanceof ProcessError) { /* mcode child exited non-zero; e.stderr */ }
if (e instanceof StreamJsonError) { /* bad line in mcode stdout */ }
if (e instanceof TimeoutError) { /* timeout_ms elapsed */ }
if (e instanceof AbortError) { /* AbortSignal fired */ }
if (e instanceof MavisError) { /* any other SDK error */ }
}Companion CLI
mavis-sdk is installed alongside the SDK. v0.1.0 shipped four
subcommands; v0.2.0 added four read-only sessions subcommands;
v0.3.0 added four manager groups (Decision Q25 / Q51 / Q57):
mavis-sdk doctor # verify mcode is installed and reachable
mavis-sdk info # print SDK + mcode state summary
mavis-sdk replay fixture.jsonl # replay a stream-json fixture
mavis-sdk login # proxy mcode login (status if no args)
mavis-sdk sessions list [--agent X] [--status idle] [--archived] [--limit N] [--json]
mavis-sdk sessions show <id> [--json]
mavis-sdk sessions messages <id> [--limit N] [--include-thinking] [--json]
mavis-sdk sessions stats
mavis-sdk plugins list [--available] [--marketplace official] [--json]
mavis-sdk plugins add/remove/enable/disable <plugin[@marketplace]>
mavis-sdk plugins marketplace
mavis-sdk agents list/get/default [--json]
mavis-sdk cron list/get/runs [--json]
mavis-sdk mcp list/get [--json]sessions subcommands are read-only. Archive / rename / delete are
exposed only via the SDK and require { writable: true }.
Examples
Eight runnable examples live in examples/:
01-quickstart.ts— one prompt, one event stream02-streaming.ts— react to each of the 7 event types03-session.ts— multi-turn withcontinue_conversation04-permission.ts—permission: "off"for read-only runs05-acp.ts— Agent Client Protocol connection06-state-queries.ts— all seven state getters07-session-store.ts—SessionStorewalk-through (v0.2.0)08-runtime-managers.ts— plugins / agents / cron / mcp /send(v0.3.0)
Run any with npx tsx examples/0X-name.ts.
License
MIT — see LICENSE.
