@mimik/agent-kit
v1.1.0
Published
A powerful wrapper library for building intelligent agents that interact with multiple Model Context Protocol (MCP) servers, providing streaming capabilities, tool orchestration, and comprehensive security controls.
Keywords
Readme
agent-kit: MCP Agent Library
A powerful wrapper library for building intelligent agents that interact with multiple Model Context Protocol (MCP) servers, providing streaming capabilities, tool orchestration, and comprehensive security controls.
Table of Contents
- Installation
- Quick Start
- Core Concepts
- Configuration
- API Reference
- Streaming Events
- Security Features
- Examples
- Best Practices
- Error Handling
Installation
npm install @mimik/agent-kitconst { Agent, AGENT_EVENT } = require('@mimik/agent-kit');Quick Start
const { Agent, AGENT_EVENT } = require('@mimik/agent-kit');
// Create an agent instance
const agent = new Agent({
name: 'My Assistant',
instructions: 'You are a helpful assistant with access to MCP tools.',
llm: {
model: 'lmstudio-community/Qwen3-4B-GGUF',
temperature: 0.1
},
mcpEndpoints: ['http://localhost:3000/mcp'],
httpClient: global.http
});
// Initialize the agent
await agent.initialize();
// Run a conversation with a simple message
const streamIterator = await agent.run('find devices around me');
// Or pass conversation history (OpenAI messages format)
// const streamIterator = await agent.run([
// { role: 'user', content: 'find devices around me' },
// { role: 'assistant', content: 'I found 2 devices...' },
// { role: 'user', content: 'tell me more about device 1' }
// ]);
for await (const event of streamIterator) {
if (event.type === AGENT_EVENT.CONTENT_DELTA) {
sendSSE(event.data.content);
} else if (event.type === AGENT_EVENT.CONVERSATION_COMPLETE) {
console.log('\nFinal result:', event.data.finalOutput);
break;
}
}Core Concepts
Agent
The main orchestrator that connects to multiple MCP servers, routes tool calls, and manages conversation flow with automatic streaming execution.
MCP Servers
Multiple Model Context Protocol servers that provide tools, resources, and capabilities. The agent automatically routes tool calls to the appropriate server.
Streaming Execution
All agent conversations use real-time streaming for immediate feedback and responsive user experiences.
Tool Security
Multi-layered security with server-level tool whitelisting and runtime tool approval callbacks.
Configuration
Basic Configuration
const agent = new Agent({
name: 'My Agent', // Agent name for logging
instructions: 'System prompt...', // System instructions
maxIterations: 5, // Max conversation loops
llm: {
model: 'lmstudio-community/Qwen3-4B-GGUF',
temperature: 0.1,
max_tokens: 2048,
endpoint: 'https://api.anthropic.com/v1/messages',
apiKey: 'your-api-key',
headers: { 'x-session-id': 'your-session-id' }
},
mcpEndpoints: ['http://localhost:3000/mcp'],
httpClient: global.http
});Advanced Configuration with Security
const agent = new Agent({
name: 'Secure Assistant',
instructions: 'Help users safely with controlled tool access.',
maxIterations: 5,
llm: {
model: 'lmstudio-community/Qwen3-4B-GGUF',
temperature: 0.1,
max_tokens: 4096,
no_think: false // Disable thinking mode
},
mcpEndpoints: [
// Simple endpoint - all tools allowed
'http://localhost:3000/mcp',
// Endpoint with tool whitelist
{
url: 'http://localhost:3001/mcp',
apiKey: 'server-api-key',
options: {
toolWhitelist: ['gitStatus', 'gitLog', 'gitDiff'], // Only these tools
whitelistMode: 'include' // Include mode
}
},
// Endpoint blocking dangerous tools
{
url: 'http://localhost:3002/mcp',
options: {
toolWhitelist: ['deleteFile', 'formatDisk'], // Block these tools
whitelistMode: 'exclude' // Exclude mode
}
}
],
httpClient: global.http
});Configuration Options
Top-Level Options
name(string): Agent name for logging (default: 'MCP Assistant')instructions(string): System prompt (default: 'You are a helpful assistant...')maxIterations(number): Maximum conversation loops (default: 5)mcpEndpoints(array): MCP server configurationshttpClient(object): HTTP client instance
LLM Configuration
model(string): LLM model nametemperature(number): Sampling temperature (0.0-1.0)max_tokens(number): Maximum tokens per responseendpoint(string): LLM API endpointapiKey(string): LLM API keyno_think(boolean): Append '/no_think' to prompts for direct responsesheaders(object): Custom HTTP headers to include in LLM requests (e.g.,{ 'x-session-id': 'abc123' })
MCP Endpoint Configuration
- Simple:
'http://localhost:3000/mcp' - Advanced: Object with
url,apiKey, andoptions
Tool Whitelist Options
toolWhitelist(array): Tool names to include/excludewhitelistMode(string):'include'(only listed tools) or'exclude'(all except listed)
API Reference
Agent Class
Constructor
new Agent(config)Methods
initialize()
Connects to all MCP servers and loads available tools.
await agent.initialize();run(input, options)
Executes a conversation with streaming output.
Parameters:
input(string | array): Either a single user message string, or an array of OpenAI-format messages for conversation historyoptions(object): Configuration optionstoolApproval(function): Tool approval callback
Returns: AsyncIterator - Streaming events
// Simple string message
const streamIterator = await agent.run('find devices');
// Or with conversation history (OpenAI messages format)
const streamIterator = await agent.run([
{ role: 'user', content: 'What devices are on my network?' },
{ role: 'assistant', content: 'I found 3 devices: ...' },
{ role: 'user', content: 'Tell me more about the first one' }
], {
toolApproval: async (toolCalls) => ({
stopAfterExecution: false,
approvals: [true, false, true] // Approve, deny, approve
})
});When passing a messages array, the agent's system instructions are automatically prepended, and any existing system messages in the array are filtered out.
Tool Approval System
Callback Function
toolApproval: async (toolCalls) => {
// toolCalls = [{ id, type, function: { name, arguments } }, ...]
return {
stopAfterExecution: false, // Continue after execution
approvals: [
true, // Simple approval
false, // Simple denial
{ approve: true }, // Explicit approval
{ approve: false, reason: 'Not safe' } // Denial with reason
]
};
}Response Format
stopAfterExecution(boolean): End conversation after tool executionapprovals(array): Approval decision for each tool call
Approval Options
true- Approve the toolfalse- Deny the tool (default reason){ approve: true }- Explicit approval{ approve: false, reason: 'Custom reason' }- Denial with custom reason
Streaming Events
All agent conversations emit real-time events for responsive user experiences. Event types are available as constants via the AGENT_EVENT export:
const { Agent, AGENT_EVENT } = require('@mimik/agent-kit');Event Constants
| Constant | Value | Description |
|----------|-------|-------------|
| AGENT_EVENT.ITERATION_START | 'iteration_start' | Start of each conversation loop |
| AGENT_EVENT.RAW_MODEL_STREAM_EVENT | 'raw_model_stream_event' | Raw chunk from the LLM stream |
| AGENT_EVENT.CONTENT_DELTA | 'content_delta' | Assistant text content update |
| AGENT_EVENT.TOOL_CALL_DELTA | 'tool_call_delta' | Streaming tool call fragment |
| AGENT_EVENT.TOOL_CALLS_DETECTED | 'tool_calls_detected' | Complete tool calls ready for execution |
| AGENT_EVENT.TOOL_APPROVAL_RESULT | 'tool_approval_result' | Results of tool approval decisions |
| AGENT_EVENT.TOOL_RESULTS | 'tool_results' | Results from tool execution |
| AGENT_EVENT.CONVERSATION_COMPLETE | 'conversation_complete' | Final conversation result |
| AGENT_EVENT.MAX_ITERATIONS_REACHED | 'max_iterations_reached' | Max iteration limit hit |
Event Payloads
ITERATION_START
{
type: AGENT_EVENT.ITERATION_START,
data: {
iteration: 1,
messages: [/* conversation history */]
}
}CONTENT_DELTA
{
type: AGENT_EVENT.CONTENT_DELTA,
data: {
content: "I'll help you find devices..."
}
}TOOL_CALLS_DETECTED
{
type: AGENT_EVENT.TOOL_CALLS_DETECTED,
data: {
toolCalls: [{
id: "call_123",
type: "function",
function: { name: "discoverLocal", arguments: "{}" }
}]
}
}TOOL_APPROVAL_RESULT
{
type: AGENT_EVENT.TOOL_APPROVAL_RESULT,
data: {
approvalResult: {
stopAfterExecution: false,
approvals: [true, { approve: false, reason: 'Not safe' }]
}
}
}TOOL_RESULTS
{
type: AGENT_EVENT.TOOL_RESULTS,
data: {
results: [{
role: "tool",
tool_call_id: "call_123",
name: "discoverLocal",
content: JSON.stringify([/* results */])
}]
}
}CONVERSATION_COMPLETE
{
type: AGENT_EVENT.CONVERSATION_COMPLETE,
data: {
finalOutput: "I found 2 devices around you...",
messages: [/* complete history */],
iterations: 2,
stoppedAfterToolExecution: false
}
}Security Features
Tool Whitelisting
Control which tools are available from each MCP server.
Include Mode (Default)
Only allow specific tools:
{
url: 'http://localhost:3001/mcp',
options: {
toolWhitelist: ['readFile', 'listDirectory'],
whitelistMode: 'include'
}
}Exclude Mode
Block specific dangerous tools:
{
url: 'http://localhost:3002/mcp',
options: {
toolWhitelist: ['deleteFile', 'formatDisk'],
whitelistMode: 'exclude'
}
}Runtime Tool Approval
Additional security layer with approval callbacks:
toolApproval: async (toolCalls) => ({
stopAfterExecution: false,
approvals: toolCalls.map(tool => {
if (tool.function.name.includes('delete')) {
return { approve: false, reason: 'Destructive operations not allowed' };
}
return true;
})
})Security Benefits
- Principle of Least Privilege: Only expose necessary tools
- Defense in Depth: Multiple security layers
- Server Isolation: Different policies per MCP server
- Runtime Protection: Dynamic approval decisions
Examples
Basic Usage
const { Agent, AGENT_EVENT } = require('@mimik/agent-kit');
const agent = new Agent({
name: 'Basic Assistant',
llm: { model: 'lmstudio-community/Qwen3-4B-GGUF', temperature: 0.1 },
mcpEndpoints: ['http://localhost:3000/mcp'],
httpClient: global.http
});
await agent.initialize();
const streamIterator = await agent.run('help me find local devices');
for await (const event of streamIterator) {
switch (event.type) {
case AGENT_EVENT.CONTENT_DELTA:
sendSSE(event.data.content);
break;
case AGENT_EVENT.CONVERSATION_COMPLETE:
console.log('\nDone:', event.data.finalOutput);
return;
}
}Multi-Agent with Security
const agent = new Agent({
name: 'Secure Multi-Tool Agent',
instructions: 'Help users with controlled access to various tools.',
llm: {
model: 'lmstudio-community/Qwen3-4B-GGUF',
temperature: 0.2,
max_tokens: 4096
},
mcpEndpoints: [
{
url: 'http://localhost:3000/mcp', // Network tools
options: {
toolWhitelist: ['discoverLocal', 'pingDevice'],
whitelistMode: 'include'
}
},
{
url: 'http://localhost:3001/mcp', // Git tools
options: {
toolWhitelist: ['gitStatus', 'gitLog', 'gitDiff'],
whitelistMode: 'include'
}
},
{
url: 'http://localhost:3002/mcp', // File system (safe)
options: {
toolWhitelist: ['deleteFile', 'formatDisk'],
whitelistMode: 'exclude'
}
}
],
httpClient: global.http
});
await agent.initialize();
const streamIterator = await agent.run('scan network and check git status', {
toolApproval: async (toolCalls) => ({
stopAfterExecution: false,
approvals: toolCalls.map(tool => {
// Extra safety check
if (tool.function.name.toLowerCase().includes('format')) {
return { approve: false, reason: 'Disk formatting not allowed' };
}
return true;
})
})
});
for await (const event of streamIterator) {
switch (event.type) {
case AGENT_EVENT.CONTENT_DELTA:
sendSSE(event.data.content);
break;
case AGENT_EVENT.TOOL_CALLS_DETECTED:
console.log('\nExecuting:',
event.data.toolCalls.map(tc => tc.function.name).join(', '));
break;
case AGENT_EVENT.CONVERSATION_COMPLETE:
console.log('\nFinal:', event.data.finalOutput);
break;
}
}Conversation History
Maintain context across multiple turns by passing the full message history:
const { Agent, AGENT_EVENT } = require('@mimik/agent-kit');
const agent = new Agent({
name: 'Conversational Assistant',
llm: { model: 'lmstudio-community/Qwen3-4B-GGUF', temperature: 0.1 },
mcpEndpoints: ['http://localhost:3000/mcp'],
httpClient: global.http
});
await agent.initialize();
// Store conversation history
const conversationHistory = [];
async function chat(userMessage) {
// Add user message to history
conversationHistory.push({ role: 'user', content: userMessage });
// Pass full history to agent
const streamIterator = await agent.run(conversationHistory);
let assistantResponse = '';
for await (const event of streamIterator) {
if (event.type === AGENT_EVENT.CONTENT_DELTA) {
assistantResponse += event.data.content;
} else if (event.type === AGENT_EVENT.CONVERSATION_COMPLETE) {
break;
}
}
// Add assistant response to history
conversationHistory.push({ role: 'assistant', content: assistantResponse });
return assistantResponse;
}
// Multi-turn conversation
await chat('What devices are on my network?');
await chat('Tell me more about the first one'); // Agent has context from previous turn
await chat('Can you save that info as a note?'); // Agent still has full contextNo-Think Mode
For direct responses without reasoning display:
const agent = new Agent({
name: 'Direct Assistant',
llm: {
model: 'lmstudio-community/Qwen3-4B-GGUF',
no_think: true // Enables direct responses
},
mcpEndpoints: ['http://localhost:3000/mcp'],
httpClient: global.http
});
// Prompts sent to LLM will include '/no_think' suffix
const streamIterator = await agent.run('What time is it?');Web API Integration
const { Agent, AGENT_EVENT } = require('@mimik/agent-kit');
app.post('/chat', async (req, res) => {
const agent = new Agent({
name: 'API Assistant',
llm: {
model: 'lmstudio-community/Qwen3-4B-GGUF',
temperature: 0.1,
no_think: true
},
mcpEndpoints: [{
url: 'http://localhost:3000/mcp',
options: {
toolWhitelist: ['readFile', 'listDirectory'], // Read-only for web
whitelistMode: 'include'
}
}],
httpClient: global.http
});
await agent.initialize();
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
try {
const streamIterator = await agent.run(req.body.message, {
toolApproval: async (toolCalls) => ({
stopAfterExecution: false,
approvals: toolCalls.map(tool => {
// Block write operations via web
if (['writeFile', 'deleteFile'].includes(tool.function.name)) {
return { approve: false, reason: 'Write operations not allowed via web' };
}
return true;
})
})
});
for await (const event of streamIterator) {
res.write(`data: ${JSON.stringify(event)}\n\n`);
if (event.type === AGENT_EVENT.CONVERSATION_COMPLETE) {
break;
}
}
res.end();
} catch (error) {
res.write(`data: ${JSON.stringify({ error: error.message })}\n\n`);
res.end();
}
});Collecting Final Results
If you need just the final result without processing events:
const streamIterator = await agent.run('find devices around me');
let finalResult = null;
for await (const event of streamIterator) {
if (event.type === AGENT_EVENT.CONVERSATION_COMPLETE) {
finalResult = event.data;
break;
}
}
console.log('Final output:', finalResult.finalOutput);
console.log('Total iterations:', finalResult.iterations);Best Practices
1. Always Handle Streaming
Since run() always returns a streaming iterator, process events appropriately:
// Good - handle all relevant events
for await (const event of streamIterator) {
switch (event.type) {
case AGENT_EVENT.CONTENT_DELTA:
displayContent(event.data.content);
break;
case AGENT_EVENT.TOOL_CALLS_DETECTED:
showToolExecution(event.data.toolCalls);
break;
case AGENT_EVENT.CONVERSATION_COMPLETE:
return event.data.finalOutput;
}
}
// Avoid - only handling one event type2. Layer Security Controls
Combine whitelisting with runtime approval:
// Server-level whitelist
{
url: 'http://localhost:3000/mcp',
options: {
toolWhitelist: ['readFile', 'writeFile', 'deleteFile'],
whitelistMode: 'include'
}
}
// Runtime approval for additional safety
toolApproval: async (toolCalls) => ({
approvals: toolCalls.map(tool => {
if (tool.function.name === 'deleteFile') {
return { approve: false, reason: 'Delete operations require manual approval' };
}
return true;
})
})3. Organize Tools by Domain
Use multiple MCP servers for different tool categories:
mcpEndpoints: [
'http://localhost:3000/mcp', // Network tools
'http://localhost:3001/mcp', // File system tools
'http://localhost:3002/mcp', // Git tools
'http://localhost:3003/mcp' // System tools
]4. Set Appropriate Limits
Prevent infinite loops while allowing complex tasks:
{
maxIterations: 10, // Allow complex multi-step tasks
llm: {
max_tokens: 4096, // Sufficient for detailed responses
temperature: 0.1 // Consistent behavior
}
}5. Use No-Think Mode Appropriately
Enable for more direct responses when reasoning isn't needed:
// For APIs or when you want concise responses
llm: { no_think: true }
// For interactive assistants where reasoning helps users
llm: { no_think: false }6. Handle Errors Gracefully
Always wrap agent operations in try-catch:
try {
const streamIterator = await agent.run(userMessage);
// Process stream...
} catch (error) {
console.error('Agent error:', error.message);
// Handle error appropriately
}Error Handling
Common Error Types
Connection Errors
// Server unavailable
try {
await agent.initialize();
} catch (error) {
console.error('Failed to connect to MCP servers:', error.message);
}Tool Execution Errors
Tool errors are returned in tool results, conversation continues:
{
type: 'tool_results',
data: {
results: [{
role: "tool",
tool_call_id: "call_123",
name: "failingTool",
content: "Error: Tool execution failed"
}]
}
}Validation Errors
Invalid configurations throw errors during initialization:
// Invalid endpoint format
mcpEndpoints: ['invalid-url'] // Throws error
// Missing required HTTP client
httpClient: undefined // Throws errorLLM Errors
LLM failures are propagated to the caller:
try {
const streamIterator = await agent.run('test message');
} catch (error) {
if (error.message.includes('API key')) {
console.error('LLM authentication failed');
}
}Error Recovery Strategies
- Server Failures: Agent continues with available servers
- Tool Failures: Returned as error messages, conversation continues
- Approval Callback Errors: Defaults to denying all tools for safety
- Parsing Errors: Logged and skipped, processing continues
Performance Considerations
- Parallel Initialization: All MCP servers connect concurrently
- Efficient Tool Routing: O(1) lookup for tool-to-server mapping
- Streaming: Real-time events with minimal buffering
- Memory Management: Conversation history accumulates; consider limits for long conversations
- No-Think Mode: Reduces response time and token usage when appropriate
Need help? Check the examples above or refer to the MCP Kit documentation for server-side tool development.
