Real HTTP server. Real SSE streams. Fixture-driven responses. Multi-provider mock — OpenAI, Claude, Gemini — any process on the machine can reach it.
npm install @copilotkit/llmock
{
"fixtures": [
{
"match": {
"userMessage": "capital of France"
},
"response": {
"content": "The capital of France is Paris."
}
}
]
}
Built for E2E test suites where multiple processes — your app, agent workers, framework runtimes — all need to hit the same mock endpoint.
Runs on an actual port. Any process on the machine can reach it — Next.js, Mastra, LangGraph, Agno, anything that speaks HTTP.
OpenAI, Claude, and Gemini APIs — authentic SSE format for each provider. Streaming and non-streaming modes.
Define responses as JSON — one file per feature. Load a directory, load a file, or register fixtures programmatically.
Return tool calls with structured arguments. Match on tool names, tool result IDs, or write custom predicates.
Queue one-shot errors — 429 rate limits, 503 outages, whatever. Fires once, then auto-removes itself.
Every request recorded. Inspect messages, verify tool calls, assert on conversation history. HTTP and programmatic access.
Match on the last user message — substring or regex. The fixture fires when it matches, streaming SSE chunks just like the real API.
{
"fixtures": [
{
"match": { "userMessage": "stock price of AAPL" },
"response": {
"content": "The current stock price of Apple Inc. (AAPL) is $150.25."
}
},
{
"match": { "userMessage": "capital of France" },
"response": {
"content": "The capital of France is Paris."
}
}
]
}
{
"fixtures": [
{
"match": { "userMessage": "one step with eggs" },
"response": {
"toolCalls": [{
"name": "generate_task_steps",
"arguments": "{\"steps\":[{\"description\":\"Crack eggs\"},{\"description\":\"Preheat oven\"}]}"
}]
}
},
{
"match": { "userMessage": "background color to blue" },
"response": {
"toolCalls": [{
"name": "change_background",
"arguments": "{\"background\":\"blue\"}"
}]
}
}
]
}
Return structured tool calls that agent frameworks execute directly. Used in production E2E tests for CopilotKit, Mastra, and LangGraph integrations.
When substring matching isn't enough, use predicates. Inspect the full request — system prompt flags, message history, model name, anything.
// Supervisor sees the same user message every time,
// but system prompt contains state flags
mock.addFixture({
match: {
predicate: (req) => {
const sys = req.messages
.find(m => m.role === "system");
return sys?.content
?.includes("Flights found: false");
}
},
response: {
toolCalls: [{
name: "supervisor_response",
arguments: '{"next_agent":"flights_agent"}'
}]
}
});
import { LLMock } from "@copilotkit/llmock";
const mock = new LLMock({ port: 5555 });
// Load JSON fixture files
mock.loadFixtureDir("./fixtures/openai");
// Catch-all for tool results
mock.addFixture({
match: {
predicate: (req) =>
req.messages.at(-1)?.role === "tool"
},
response: { content: "Done!" }
});
const url = await mock.start();
// Every process on the machine can reach this
process.env.OPENAI_BASE_URL = `${url}/v1`;
process.env.OPENAI_API_KEY = "mock-key";
Start the mock server once in Playwright's global setup. All child processes —
Next.js, agent workers, CopilotKit runtime — inherit OPENAI_BASE_URL and
hit the same server.
MSW is great for in-process API mocking. llmock is for when multiple processes need to hit the same LLM endpoint.
| Capability | llmock | MSW |
|---|---|---|
| Cross-process interception | Real server ✓ | In-process only |
| Chat Completions SSE | Built-in ✓ | Manual — build data/[DONE] yourself |
| Responses API SSE | Built-in ✓ | Manual — MSW sse() uses wrong format |
| Claude Messages API SSE | Built-in ✓ | Manual — build event/data SSE yourself |
| Gemini streaming | Built-in ✓ | Manual — build data SSE yourself |
| Multi-provider support | OpenAI + Claude + Gemini ✓ | Provider-agnostic (manual) |
| Fixture files (JSON) | Yes ✓ | No — handlers are code-only |
| Request journal | Yes ✓ | No — track manually |
| Non-streaming responses | Yes ✓ | Yes ✓ |
| Error injection (one-shot) | Yes ✓ | Yes (server.use) |
| CLI server | Yes ✓ | No |
| Dependencies | Zero | ~300KB |