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