TypeScript SDK
Type-safe access to your agents
Build web and Node.js applications that interact with MUXI formations. Full TypeScript support with type definitions for all API operations.
GitHub: muxi-ai/muxi-typescript
Installation
npm install @muxi-ai/muxi-typescript
# or
yarn add @muxi-ai/muxi-typescript
Platform Support
| Platform | Support |
|---|---|
| Node.js | Full support (v18+) |
| Browser | Full support (modern browsers) |
| Deno | Compatible |
| Bun | Compatible |
| Edge runtimes | Compatible (Vercel Edge, Cloudflare Workers) |
The SDK uses the standard fetch API internally, making it compatible with any JavaScript runtime that supports fetch.
Quick Start
import { FormationClient } from "@muxi-ai/muxi-typescript";
const formation = new FormationClient({
serverUrl: "http://localhost:7890",
formationId: "my-assistant",
clientKey: "your_client_key",
});
console.log(await formation.health());
Formation Client
Initialize
import { FormationClient } from "@muxi-ai/muxi-typescript";
// Production (via server proxy)
const formation = new FormationClient({
serverUrl: "http://localhost:7890",
formationId: "my-assistant",
clientKey: "your_client_key",
adminKey: "your_admin_key", // Optional, for admin operations
});
// Local development (with muxi up)
const formation = new FormationClient({
serverUrl: "http://localhost:7890",
formationId: "my-assistant",
mode: "draft", // Uses /draft/ prefix instead of /api/
clientKey: "your_client_key",
});
// Direct connection (bypasses server proxy)
const formation = new FormationClient({
baseUrl: "http://localhost:8001/v1", // Direct to formation port
clientKey: "your_client_key",
});
Local dev workflow: Use mode: "draft" during development with muxi up, then remove it when deploying to production.
Chat (Streaming)
// Streaming chat (recommended)
for await (const chunk of await formation.chatStream(
{ message: "Hello!" },
"user_123"
)) {
if (chunk.type === "text") {
process.stdout.write(chunk.text);
}
}
Chunk types: text, tool_call, tool_result, agent_handoff, thinking, error, done.
Agents
// List agents (requires admin key)
const agents = await formation.getAgents();
agents.forEach(agent => console.log(agent.id, agent.name));
// Get specific agent
const agent = await formation.getAgent("researcher");
Sessions
// List sessions
const sessions = await formation.getSessions("user_123");
sessions.forEach(s => console.log(s.session_id));
// Get session messages
const history = await formation.getSessionMessages("sess_abc123", "user_123");
for (const message of history.messages) {
console.log(${message.role}: ${message.content});
}
// Restore session (sessionId, userId, messages)
await formation.restoreSession("sess_abc123", "user_123", messages);
Memory
// Get memory config
const config = await formation.getMemoryConfig();
// Get memories for user
const memories = await formation.getMemories("user_123");
// Add a memory (userId, type, detail)
await formation.addMemory("user_123", "preference", "User prefers TypeScript");
// Delete a memory
await formation.deleteMemory("user_123", "mem_abc123");
// Clear user buffer
await formation.clearUserBuffer("user_123");
// Get buffer stats
const stats = await formation.getBufferStats();
Triggers
// Fire a trigger (name, data, asyncMode, userId)
const response = await formation.fireTrigger(
"github-issue",
{
repository: "muxi/runtime",
issue: { number: 123, title: "Bug report" }
},
false, // asyncMode
"user_123" // userId
);
// Fire async trigger
await formation.fireTrigger("daily-report", { date: "2024-01-15" }, true, "user_123");
Scheduler
// List scheduled jobs
const jobs = await formation.getSchedulerJobs("user_123");
// Create a job (jobType, schedule, message, userId)
const job = await formation.createSchedulerJob(
"prompt", // jobType
"0 9 * * *", // schedule (cron)
"Generate daily summary", // message
"user_123" // userId
);
// Delete a job
await formation.deleteSchedulerJob("job_abc123");
Event Streaming
// Stream events for a user
for await (const event of await formation.streamEvents("user_123")) {
console.log(event);
}
// Stream logs (admin)
for await (const log of await formation.streamLogs({ level: "info" })) {
console.log(log);
}
Server Client
For managing formations (deploy, start, stop):
import { ServerClient } from "@muxi-ai/muxi-typescript";
const server = new ServerClient({
url: "http://localhost:7890",
keyId: "muxi_pk_...",
secretKey: "muxi_sk_...",
});
// Check server status
console.log(await server.status());
// List formations
const formations = await server.listFormations();
// Deploy a formation (with streaming progress)
for await (const event of await server.streamDeployFormation({
bundlePath: "my-bot.tar.gz"
})) {
console.log(event);
}
// Stop/start/restart
await server.stopFormation("my-bot");
await server.startFormation("my-bot");
await server.restartFormation("my-bot");
Error Handling
import {
MuxiError,
AuthenticationError,
AuthorizationError,
NotFoundError,
ValidationError,
RateLimitError,
ServerError,
ConnectionError,
} from "@muxi-ai/muxi-typescript";
try {
const stream = await formation.chatStream({ message: "Hello!" }, "user_123");
for await (const chunk of stream) {
// ...
}
} catch (err) {
if (err instanceof AuthenticationError) {
console.log("Auth failed:", err.message);
} else if (err instanceof RateLimitError) {
console.log(Rate limited, retry after: ${err.retryAfter}s);
} else if (err instanceof MuxiError) {
console.log(Error: ${err.code} - ${err.message});
}
}
All errors include:
code- Error code stringmessage- Human-readable messagestatusCode- HTTP status coderetryAfter- Seconds to wait (for rate limits)
Webhook Handlers
Handle incoming async/trigger webhook callbacks with signature verification:
import { webhook } from "@muxi-ai/muxi-typescript";
app.post("/webhooks/muxi", (req, res) => {
const signature = req.headers["x-muxi-signature"] as string;
// Verify signature (prevents spoofing and replay attacks)
if (!webhook.verifySignature(req.rawBody, signature, WEBHOOK_SECRET)) {
return res.status(401).send("Invalid signature");
}
// Parse into typed WebhookEvent
const event = webhook.parse(req.rawBody);
if (event.status === "completed") {
for (const item of event.content) {
if (item.type === "text") console.log(item.text);
}
} else if (event.status === "failed") {
console.error(Error: ${event.error?.message});
} else if (event.status === "awaiting_clarification") {
console.log(Question: ${event.clarification?.question});
}
res.json({ received: true });
});
WebhookEvent fields:
requestId,status,timestampcontent- Array ofContentItem(type, text, file)error-ErrorDetails(code, message, trace)clarification-Clarification(question, clarificationRequestId)formationId,userId,processingTime,raw
Configuration
const formation = new FormationClient({
serverUrl: "http://localhost:7890",
formationId: "my-assistant",
clientKey: "your_client_key",
timeout: 30000, // Request timeout (ms)
maxRetries: 3, // Retry count for 429/5xx
debug: true, // Enable debug logging
});
Examples
Chat Bot (Node.js)
import { FormationClient } from "@muxi-ai/muxi-typescript";
import * as readline from "readline";
const formation = new FormationClient({
serverUrl: "http://localhost:7890",
formationId: "my-assistant",
clientKey: "your_client_key",
});
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
async function chat(message: string) {
process.stdout.write("Assistant: ");
for await (const chunk of await formation.chatStream(
{ message },
"user_123"
)) {
if (chunk.type === "text") {
process.stdout.write(chunk.text);
}
}
console.log();
}
function prompt() {
rl.question("You: ", async (input) => {
if (input.toLowerCase() === "quit") {
rl.close();
return;
}
await chat(input);
prompt();
});
}
prompt();
Browser Usage
The SDK works directly in browsers. Use the client key (not admin key) for browser applications:
// In your frontend code
import { FormationClient } from "@muxi-ai/muxi-typescript";
const formation = new FormationClient({
serverUrl: "https://api.yourapp.com", // Your MUXI server
formationId: "my-assistant",
clientKey: "your_client_key", // Safe to expose in browser
});
// Chat with streaming
async function chat(message: string, userId: string) {
const response = [];
for await (const chunk of await formation.chatStream({ message }, userId)) {
if (chunk.type === "text") {
response.push(chunk.text);
// Update your UI here
}
}
return response.join("");
}
Security: Only use the clientKey in browser code. Never expose your adminKey in client-side applications. The client key has limited permissions (chat, sessions, memory) while admin operations require server-side code.
React Hook
import { useState, useCallback } from "react";
import { FormationClient } from "@muxi-ai/muxi-typescript";
const formation = new FormationClient({
serverUrl: "http://localhost:7890",
formationId: "my-assistant",
clientKey: "your_client_key",
});
export function useChat(userId: string) {
const [messages, setMessages] = useState<Array<{ role: string; content: string }>>([]);
const [isLoading, setIsLoading] = useState(false);
const sendMessage = useCallback(async (content: string) => {
setMessages(prev => [...prev, { role: "user", content }]);
setIsLoading(true);
let response = "";
try {
for await (const chunk of await formation.chatStream(
{ message: content },
userId
)) {
if (chunk.type === "text") {
response += chunk.text;
setMessages(prev => [
...prev.slice(0, -1),
{ role: "assistant", content: response },
]);
}
}
} finally {
setIsLoading(false);
}
}, [userId]);
return { messages, sendMessage, isLoading };
}
Next.js API Route
// app/api/chat/route.ts
import { FormationClient } from "@muxi-ai/muxi-typescript";
const formation = new FormationClient({
serverUrl: process.env.MUXI_SERVER_URL!,
formationId: process.env.MUXI_FORMATION_ID!,
clientKey: process.env.MUXI_CLIENT_KEY!,
});
export async function POST(req: Request) {
const { message, userId } = await req.json();
const encoder = new TextEncoder();
const stream = new ReadableStream({
async start(controller) {
for await (const chunk of await formation.chatStream(
{ message },
userId
)) {
if (chunk.type === "text") {
controller.enqueue(encoder.encode(chunk.text));
}
}
controller.close();
},
});
return new Response(stream, {
headers: { "Content-Type": "text/plain; charset=utf-8" },
});
}