Integration Guide
Advanced integration patterns and best practices for connecting your applications with the Fenceline MCP Server.
Integration Patterns
1. Direct API Integration
Simple HTTP requests to the JSON-RPC endpoint for server-to-server communication.
Best For:
- Backend services
- Scheduled jobs
- Simple tool calls
Example:
const mcpClient = {
async callTool(name, args) {
const response = await fetch('https://mcp.fenceline.ai/mcp/rpc', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': this.apiKey
},
body: JSON.stringify({
jsonrpc: '2.0',
id: Date.now(),
method: 'tools/call',
params: { name, arguments: args }
})
});
return response.json();
}
};
2. Server-Sent Events (SSE)
Real-time streaming communication for interactive applications.
Best For:
- Web applications
- Real-time updates
- Interactive dashboards
Example:
const eventSource = new EventSource('https://mcp.fenceline.ai/mcp/sse', {
headers: { 'X-API-Key': 'YOUR_API_KEY' }
});
eventSource.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log('Received:', data);
};
Authentication Strategies
API Key Management
- Environment Variables: Store API keys in environment variables
- Secure Storage: Use secure key management systems
- Key Rotation: Implement regular key rotation policies
- Scope Limitation: Use keys with minimal required permissions
Example Configuration
// config.js
export const mcpConfig = {
apiKey: process.env.MCP_API_KEY,
baseUrl: 'https://mcp.fenceline.ai',
timeout: 30000,
retries: 3
};
Error Handling Patterns
Comprehensive Error Handling
class McpError extends Error {
constructor(message, code, data) {
super(message);
this.name = 'McpError';
this.code = code;
this.data = data;
}
}
async function callMcpTool(name, args) {
try {
const response = await fetch('https://mcp.fenceline.ai/mcp/rpc', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': mcpConfig.apiKey
},
body: JSON.stringify({
jsonrpc: '2.0',
id: Date.now(),
method: 'tools/call',
params: { name, arguments: args }
})
});
if (!response.ok) {
throw new McpError(
`HTTP ${response.status}: ${response.statusText}`,
response.status
);
}
const data = await response.json();
if (data.error) {
throw new McpError(
data.error.message,
data.error.code,
data.error.data
);
}
return data.result;
} catch (error) {
if (error instanceof McpError) {
throw error;
}
throw new McpError('Network or parsing error', -1, error.message);
}
}
Rate Limiting and Retry Logic
Exponential Backoff
async function callWithRetry(toolName, args, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await callMcpTool(toolName, args);
} catch (error) {
if (attempt === maxRetries) {
throw error;
}
// Handle rate limiting specifically
if (error.code === 429) {
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
// Don't retry on authentication or validation errors
if (error.code === 401 || error.code === 400) {
throw error;
}
// Exponential backoff for other errors
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
Caching Strategies
Tool Results Caching
class McpCache {
constructor(ttl = 300000) { // 5 minutes default
this.cache = new Map();
this.ttl = ttl;
}
generateKey(toolName, args) {
return `${toolName}:${JSON.stringify(args)}`;
}
get(toolName, args) {
const key = this.generateKey(toolName, args);
const cached = this.cache.get(key);
if (cached && Date.now() - cached.timestamp < this.ttl) {
return cached.data;
}
this.cache.delete(key);
return null;
}
set(toolName, args, data) {
const key = this.generateKey(toolName, args);
this.cache.set(key, {
data,
timestamp: Date.now()
});
}
}
const cache = new McpCache();
async function cachedToolCall(toolName, args) {
const cached = cache.get(toolName, args);
if (cached) {
return cached;
}
const result = await callMcpTool(toolName, args);
cache.set(toolName, args, result);
return result;
}
Batch Processing
Parallel Tool Execution
async function batchToolCalls(calls, concurrency = 5) {
const results = [];
for (let i = 0; i < calls.length; i += concurrency) {
const batch = calls.slice(i, i + concurrency);
const batchPromises = batch.map(({ name, args }) =>
callWithRetry(name, args).catch(error => ({ error: error.message }))
);
const batchResults = await Promise.all(batchPromises);
results.push(...batchResults);
}
return results;
}
Testing Integration
Unit Testing
// mcp.test.js
import { jest } from '@jest/globals';
import { callMcpTool } from './mcp-client.js';
// Mock the fetch function
global.fetch = jest.fn();
describe('MCP Integration', () => {
beforeEach(() => {
fetch.mockClear();
});
test('should handle successful tool call', async () => {
fetch.mockResolvedValueOnce({
ok: true,
json: async () => ({
jsonrpc: '2.0',
id: 1,
result: { content: [{ type: 'text', text: 'Success' }] }
})
});
const result = await callMcpTool('rag.search', { query: 'test' });
expect(result.content[0].text).toBe('Success');
});
test('should handle API errors', async () => {
fetch.mockResolvedValueOnce({
ok: false,
status: 401,
statusText: 'Unauthorized'
});
await expect(callMcpTool('rag.search', { query: 'test' }))
.rejects.toThrow('HTTP 401: Unauthorized');
});
});
Production Deployment
Environment Configuration
# .env.production
MCP_API_KEY=your_production_api_key
MCP_BASE_URL=https://mcp.fenceline.ai
MCP_TIMEOUT=30000
MCP_MAX_RETRIES=3
MCP_CACHE_TTL=300000
Monitoring and Logging
class McpLogger {
static logToolCall(toolName, args, duration, success) {
console.log(JSON.stringify({
timestamp: new Date().toISOString(),
tool: toolName,
args: JSON.stringify(args),
duration,
success,
level: success ? 'info' : 'error'
}));
}
}
async function monitoredToolCall(toolName, args) {
const start = Date.now();
try {
const result = await callMcpTool(toolName, args);
McpLogger.logToolCall(toolName, args, Date.now() - start, true);
return result;
} catch (error) {
McpLogger.logToolCall(toolName, args, Date.now() - start, false);
throw error;
}
}
Next Steps
- Review the Code Examples for specific implementations
- Test your integration using the Interactive Console
- Check the OpenAPI specification for complete API details
- Monitor your integration with proper logging and error handling