Skip to main content

MCP Integration

Integrate TealTiger with Model Context Protocol (MCP) to add policy enforcement to MCP tools and servers.

Why integrate TealTiger with MCP?

MCP enables AI models to access external tools and data. TealTiger adds:
  • Tool governance - Control which MCP tools can be used
  • Security policies - Block dangerous MCP operations
  • Cost tracking - Monitor MCP tool usage costs
  • Audit logging - Track all MCP tool executions

Quick start

Install both packages:
npm install tealtiger @modelcontextprotocol/sdk
# or
pip install tealtiger mcp
Wrap your MCP server with TealTiger:
import { TealTiger } from 'tealtiger';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';

// Initialize TealTiger
const teal = new TealTiger({
  policies: {
    tools: {
      'filesystem/read': { allowed: true },
      'filesystem/write': { allowed: false },
      'filesystem/delete': { allowed: false }
    }
  }
});

// Create MCP server
const server = new Server({
  name: 'governed-filesystem',
  version: '1.0.0'
});

// Register tools with governance
server.setRequestHandler('tools/call', async (request) => {
  // Evaluate policy before executing tool
  const decision = await teal.evaluate({
    action: 'tool.execute',
    tool: request.params.name,
    arguments: request.params.arguments
  });
  
  if (decision.action === 'DENY') {
    return {
      error: {
        code: -32000,
        message: `Tool blocked: ${decision.reason_codes.join(', ')}`
      }
    };
  }
  
  // Execute the tool
  return await executeMCPTool(request.params.name, request.params.arguments);
});

// Start server
const transport = new StdioServerTransport();
await server.connect(transport);

Integration patterns

Add governance to the MCP server:
// MCP server with governance
server.setRequestHandler('tools/call', async (request) => {
  const decision = await teal.evaluate({
    action: 'tool.execute',
    tool: request.params.name
  });
  
  if (decision.action === 'DENY') {
    throw new Error('Tool blocked by policy');
  }
  
  return await executeTool(request.params.name);
});
Pros:
  • Centralized governance
  • All clients protected
  • Easier to maintain
Cons:
  • Server-side only

Pattern 2: Client-side governance

Add governance to the MCP client:
// MCP client with governance
const client = new Client({
  name: 'governed-client',
  version: '1.0.0'
});

// Wrap tool calls
const originalCallTool = client.callTool.bind(client);
client.callTool = async (name, args) => {
  const decision = await teal.evaluate({
    action: 'tool.execute',
    tool: name
  });
  
  if (decision.action === 'DENY') {
    throw new Error('Tool blocked by policy');
  }
  
  return await originalCallTool(name, args);
};
Pros:
  • Client-side control
  • Works with any MCP server
Cons:
  • Need to wrap each client

Complete example: Governed filesystem MCP server

Here’s a complete MCP server with TealTiger governance:
import { TealTiger, PolicyMode } from 'tealtiger';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { promises as fs } from 'fs';

// Initialize TealTiger with filesystem policies
const teal = new TealTiger({
  policies: {
    tools: {
      'filesystem/read': {
        allowed: true,
        conditions: {
          // Only allow reading from safe directories
          allowedPaths: ['/data', '/public'],
          denyPaths: ['/etc', '/root', '/.ssh']
        }
      },
      'filesystem/write': {
        allowed: true,
        conditions: {
          // Require approval for writes
          requireApproval: true,
          allowedPaths: ['/data/uploads']
        }
      },
      'filesystem/delete': {
        allowed: false  // Never allow deletes
      }
    }
  },
  audit: {
    enabled: true,
    outputs: ['console', 'file']
  },
  mode: {
    defaultMode: PolicyMode.ENFORCE
  }
});

// Create MCP server
const server = new Server({
  name: 'governed-filesystem',
  version: '1.0.0'
}, {
  capabilities: {
    tools: {}
  }
});

// Register read tool
server.setRequestHandler('tools/list', async () => {
  return {
    tools: [
      {
        name: 'filesystem/read',
        description: 'Read a file',
        inputSchema: {
          type: 'object',
          properties: {
            path: { type: 'string' }
          }
        }
      },
      {
        name: 'filesystem/write',
        description: 'Write a file',
        inputSchema: {
          type: 'object',
          properties: {
            path: { type: 'string' },
            content: { type: 'string' }
          }
        }
      }
    ]
  };
});

// Handle tool calls with governance
server.setRequestHandler('tools/call', async (request) => {
  const { name, arguments: args } = request.params;
  
  // Evaluate policy
  const decision = await teal.evaluate({
    action: 'tool.execute',
    tool: name,
    arguments: args,
    context: {
      environment: 'production'
    }
  });
  
  // Handle decision
  if (decision.action === 'DENY') {
    return {
      content: [{
        type: 'text',
        text: `Tool blocked: ${decision.reason_codes.join(', ')}`
      }],
      isError: true
    };
  }
  
  if (decision.action === 'REQUIRE_APPROVAL') {
    return {
      content: [{
        type: 'text',
        text: `Tool requires approval. Reference: ${decision.correlation_id}`
      }],
      isError: true
    };
  }
  
  // Execute tool
  try {
    let result;
    
    if (name === 'filesystem/read') {
      const content = await fs.readFile(args.path, 'utf-8');
      result = { content };
    } else if (name === 'filesystem/write') {
      await fs.writeFile(args.path, args.content);
      result = { success: true };
    }
    
    // Log execution
    await teal.logToolExecution({
      tool: name,
      arguments: args,
      result,
      correlationId: decision.correlation_id
    });
    
    return {
      content: [{
        type: 'text',
        text: JSON.stringify(result)
      }]
    };
    
  } catch (error) {
    return {
      content: [{
        type: 'text',
        text: `Error: ${error.message}`
      }],
      isError: true
    };
  }
});

// Start server
const transport = new StdioServerTransport();
await server.connect(transport);

Policy examples for MCP

Example 1: Tool allowlist

Only allow specific MCP tools:
policies: {
  tools: {
    'filesystem/read': { allowed: true },
    'web/fetch': { allowed: true },
    'database/query': { allowed: false },
    'shell/execute': { allowed: false }
  }
}

Example 2: Path restrictions

Restrict filesystem access:
policies: {
  tools: {
    'filesystem/read': {
      allowed: true,
      conditions: {
        allowedPaths: ['/data', '/public'],
        denyPaths: ['/etc', '/root', '/.ssh', '/home']
      }
    }
  }
}

Example 3: Approval workflows

Require approval for sensitive operations:
policies: {
  tools: {
    'database/write': {
      allowed: true,
      conditions: {
        requireApproval: true,
        approvers: ['admin', 'dba']
      }
    }
  }
}

Best practices

  1. Govern at the server - Add governance to MCP servers, not clients
  2. Use path restrictions - Limit filesystem access
  3. Require approval - Add human review for sensitive operations
  4. Enable audit logging - Track all MCP tool executions
  5. Test in MONITOR mode - Validate policies before enforcing

Common issues

Issue 1: All tools blocked

Problem: MCP tools aren’t working Solution: Check that tools are in the allowlist:
policies: {
  tools: {
    'filesystem/read': { allowed: true }  // Explicitly allow
  }
}

Issue 2: Path restrictions too strict

Problem: Legitimate file access is blocked Solution: Adjust allowed paths:
conditions: {
  allowedPaths: ['/data', '/public', '/tmp']  // Add more paths
}

Next steps