tufin-mcp

tufin-mcp

3.4

The Tufin MCP Server is an open-source project designed to integrate Tufin's APIs with modern AI workflows through a secure REST API.

The Tufin MCP Server acts as a secure proxy and abstraction layer for Tufin APIs, specifically targeting version 25.1. It provides a standardized REST/JSON API interface documented via OpenAPI, centralizes Tufin authentication using Basic Auth, and offers API Key Authentication for MCP clients with keys hashed using bcrypt. The server supports Role-Based Access Control with roles such as Admin, Ticket Manager, and User. It includes endpoints for key SecureChange and SecureTrack operations, structured JSON logging with request IDs, IP-based rate limiting, and Docker support. The server is designed to be extensible, allowing for the addition of more Tufin APIs and potentially other security platforms. It aims to support newer Tufin APIs like GraphQL and features like bulk operations, which can be complex via direct API calls.

Features

  • Standard REST/JSON API interface documented via OpenAPI
  • Centralized Tufin Authentication using Basic Auth
  • API Key Authentication with bcrypt hashing
  • Configurable Role-Based Access Control
  • Endpoints for key SecureChange and SecureTrack operations

Usage with Different Platforms

local_development

bash
# From the project root directory
uvicorn src.app.main:app --reload --port ${MCP_PORT:-8000}

docker_container

bash
# From the project root directory
docker build -t tufin-mcp-server:latest .

docker run --rm -d \
  --name tufin-mcp \
  --env-file .env \
  -p ${MCP_PORT:-8000}:${MCP_PORT:-8000} \
  tufin-mcp-server:latest

python_client_library

python
from tufin_mcp_client import TufinMCPClient, TufinMCPClientError

SERVER_URL = "http://localhost:8000" 
API_KEY = "your_api_key_here"

with TufinMCPClient(base_url=SERVER_URL, api_key=API_KEY) as client:
    try:
        health = client.get_health()
        print(f"Health: {health}")
        
        devices = client.list_devices()
        print(f"Devices Found: {devices.total}")
        
        if devices.devices:
            first_device_id = devices.devices[0].id
            device_details = client.get_device(first_device_id)
            print(f"Device {first_device_id}: {device_details.name} ({device_details.vendor})")
        
        ticket_data = {
            "workflow_name": "Example Firewall Workflow",
            "subject": "Client Lib Test", 
            "details": {
                "description": "Testing ticket creation via client",
                "priority": "Medium"
            }
        }
        created_ticket = client.create_ticket(ticket_data)
        print(f"Created Ticket ID: {created_ticket.id}, Status: {created_ticket.status}")
        ticket_id = created_ticket.id
        
        if ticket_id:
            retrieved_ticket = client.get_ticket(ticket_id)
            print(f"Retrieved Ticket {ticket_id}: Subject: {retrieved_ticket.subject}")
            
            updated_data = {"status": "In Progress"}
            updated_ticket = client.update_ticket(ticket_id, updated_data)
            print(f"Updated Ticket {ticket_id}: Status: {updated_ticket.status}")
            
            asa_device = {
              "display_name": "MCP-ASA-Test-Client",
              "ip_address": "10.1.2.4",
              "vendor": "Cisco",
              "model": "ASA",
              "securetrack_domain": "Default",
              "enable_topology": True,
              "device_data": {
                 "user_name": "tufin-api-user", 
                 "password": "tufin-api-password",
                 "enable_password": "tufin-enable-password"
              }
            }
            client.add_devices([asa_device])
            print("Device add request accepted.")
            
        import_details = {
            "devices": [
                {
                    "device_id": "1",
                    "device_data": {
                        "import_all": False,
                        "import_devices": [
                            {"name": "DG_CLIENT", "import_all": True}
                        ]
                    }
                }
            ]
        }
        client.import_managed_devices(import_details)
        print("Managed device import request accepted.")

        rule_filter = "action accept and source.ip 192.168.1.0/24"
        rules_response = client.query_rules_graphql(tql_filter=rule_filter)
        print(f"\nFound {rules_response.rules.count} rules matching filter:")
        for rule in rules_response.rules.values:
            print(f"  - ID: {rule.id}, Name: {rule.name}, Action: {rule.action}")
            
    except TufinMCPClientError as e:
        print(f"MCP Client Error: {e}")
        if e.status_code:
            print(f"  Status Code: {e.status_code}")
        if e.response_text:
            print(f"  Response: {e.response_text}")
    except Exception as e:
        print(f"An unexpected error occurred: {e}")

typescript_client_library

typescript
import { TufinMCPClient, TufinMCPClientError } from 'tufin-mcp-client-js';

const SERVER_URL = 'http://localhost:8000';
const API_KEY = 'your_api_key_here';

const client = new TufinMCPClient(SERVER_URL, API_KEY);

async function runClient() {
    try {
        const health = await client.getHealth();
        console.log('Health:', health);

        const devices = await client.listDevices({ limit: 5 });
        console.log(`Devices Found: ${devices.total}`);
        console.log('First device:', devices.devices[0]);
        
        const ticketData = {
            workflow_name: 'Example Firewall Workflow',
            subject: 'TS Client Test',
            details: {
                description: 'Testing ticket from TS client',
                priority: 'Low'
            }
        };
        const createdTicket = await client.createTicket(ticketData);
        console.log(`Created Ticket ID: ${createdTicket.id}, Status: ${createdTicket.status}`);

        const asaDevice = {
          display_name: "MCP-ASA-Test-JS",
          ip_address: "10.1.2.5",
          vendor: "Cisco",
          model: "ASA",
          securetrack_domain: "Default",
          enable_topology: true,
          device_data: {
             user_name: "tufin-api-user", 
             password: "tufin-api-password",
             enable_password: "tufin-enable-password"
          }
        };
        await client.addDevices([asaDevice]);
        console.log("Device add request accepted.");

        const importDetails = {
          devices: [
            {
              device_id: "1",
              device_data: {
                import_all: false,
                import_devices: [
                  { name: "DG_JS_CLIENT", import_all: true }
                ]
              }
            }
          ]
        };
        await client.importManagedDevices(importDetails);
        console.log("Managed device import request accepted.");

        const ruleFilter = "disabled true";
        const rulesResponse = await client.queryRulesGraphQL({tql_filter: ruleFilter});
        console.log(`\nFound ${rulesResponse.rules.count} disabled rules:`);
        rulesResponse.rules.values.forEach(rule => {
            console.log(`  - ID: ${rule.id}, Name: ${rule.name}`);
        });

    } catch (error) {
        if (error instanceof TufinMCPClientError) {
            console.error('MCP Client Error:', error.message);
            if (error.status) {
                console.error('  Status Code:', error.status);
            }
            if (error.data) {
                console.error('  Response Data:', error.data);
            }
        } else {
            console.error('An unexpected error occurred:', error);
        }
    }
}

runClient();