Skip to content

Prelooping External MCP Tools

Editions: OSS, Cloud, Enterprise. Unless stated otherwise, everything on this page ships in OSS.

Add external MCP servers to Preloop and wrap their tools with approval workflows.


Overview

External MCP tools are tools provided by MCP servers you connect to Preloop. Unlike built-in tools, these come from:

  • Example MCP servers (like example-mcp.preloop.ai)
  • Third-party MCP servers (Anthropic, community, vendors)
  • Your own custom MCP servers

Key benefit: Preloop ANY tool from ANY MCP server - deployment, database, filesystem, payment, etc.

Tools Page Layout

External MCP servers are listed above built-in tools in the Tools page. The same access rules system (allow/deny/require_approval, CEL conditions, priority ordering) applies to all tool types: built-in, MCP-proxied, and HTTP.


Why External MCP Tools?

Unlimited Tool Integration

Built-in tools (create_issue, update_issue) are limited to what Preloop provides. External tools let you:

  • Deploy to production - Wrap deployment tools with approval
  • Modify databases - Require approval before DROP/ALTER
  • Process payments - Approve large transactions
  • Modify infrastructure - Approve firewall/scaling changes
  • ANY operation - If it's exposed via MCP, you can preloop it

Real-World Examples

Preloop uses access rules: ordered rules that evaluate to deny, allow, or require_approval. Rules use CEL expressions to match on tool arguments. The first matching rule wins (priority-based evaluation).

Deployment Tools:

tools:
  - name: deploy_to_production
    source: my-deploy-server
    approval_workflow: "deploy-approval"
    conditions:
      - expression: 'args.environment == "production"'
        action: require_approval
        description: "Production deploys need human approval"
      - expression: 'args.environment == "staging"'
        action: allow
        description: "Staging deploys are safe"

Database Tools:

tools:
  - name: drop_table
    source: db-mcp-server
    approval_workflow: "db-approval"
    conditions:
      - expression: 'args.database.startsWith("prod_")'
        action: require_approval
        description: "Production DB changes need approval"
      - expression: 'args.database.startsWith("test_")'
        action: allow
        description: "Test databases are safe"
      - expression: "true"
        action: deny
        description: "Block all other databases by default"

Payment Tools:

tools:
  - name: process_refund
    source: payment-mcp-server
    approval_workflow: "finance-approval"
    conditions:
      - expression: "args.amount > 1000"
        action: require_approval
        description: "High-value refunds need approval"
      - expression: "args.amount <= 1000"
        action: allow
        description: "Small refunds are auto-approved"

Adding External MCP Servers

Step 1: Open Tools

  1. Open Tools in the sidebar
  2. Click Add MCP server

Step 2: Configure Server

The console form has these fields:

  • Server Name: a descriptive name (e.g., "Deployment Server")
  • Server URL: the MCP server endpoint
  • Transport: HTTP Streaming (fixed in the console; the API field transport defaults to http-streaming)
  • Authentication Type: None, Bearer Token or OAuth 2.0

Click Add to save the server. The API also accepts auth_type: api_key and a status of active, error or disabled.

Step 3: Scan for Tools

After adding the server:

  1. Click Scan for tools on the server
  2. Preloop discovers available tools
  3. Tools appear in your tools list
  4. Each tool shows its source server

Example: Adding the Example MCP Server

Preloop hosts an example MCP server for testing:

Configuration

Server Name: Example MCP Server
Server URL: https://example-mcp.preloop.ai/mcp
Transport: HTTP Streaming
Authentication Type: None

Available Tools

After scanning, you'll see:

  • pay(recipient, amount) - Send payment (simulated)
  • rollback_deployment(environment) - Rollback deployment (simulated)
  • get_random_number(min, max) - Generate random number
  • calculate_fibonacci(n) - Calculate fibonacci number
  • reverse_text(text) - Reverse text
  • count_words(text) - Count words
  • slow_fibonacci(n) - Fibonacci with progress updates

Testing

Perfect for testing the approval flow:

# In Claude Code or other MCP client
result = pay(recipient="alice@example.com", amount=1000)
# Approval request sent!

Running Your Own MCP Server

Custom MCP Server

Build your own MCP server to expose custom tools. You'll need to deploy it to a publicly accessible URL (e.g., on AWS, GCP, Heroku, or your own infrastructure) so Preloop can connect to it.

Example server.py:

from mcp.server import MCPServer
from mcp.types import Tool

server = MCPServer()

@server.tool()
def deploy_to_staging(version: str, dry_run: bool = False):
    """Deploy application to staging environment"""
    if dry_run:
        return {"status": "dry_run", "version": version}

    # Your deployment logic here
    result = run_deployment(version, "staging")

    return {
        "status": "success",
        "version": version,
        "environment": "staging",
        "url": f"https://staging.acme.com"
    }

@server.tool()
def rollback_deployment(environment: str):
    """Rollback to previous deployment"""
    # Your rollback logic
    return {"status": "rolled_back", "environment": environment}

if __name__ == "__main__":
    server.run(host="0.0.0.0", port=8001)

Deploy your server to a public URL:

  • AWS Lambda + API Gateway
  • Google Cloud Run
  • Heroku
  • Your own infrastructure with HTTPS

Then add to Preloop:

Name: Custom Deployment Server
URL: https://your-mcp-server.example.com
Transport: streamable-http
Auth Type: bearer
Token: your_secret_token_here
Status: active

See MCP Server Development for full guide.


Authentication

No Authentication

Public servers or local development:

Auth Type: none

Bearer Token

Most common for production:

Auth Type: bearer
Token: your_secret_token_here

Server receives:

Authorization: Bearer your_secret_token_here

API Key

Key sent as a custom header (defaults to X-API-Key):

Auth Type: api_key
API Key: your_api_key
Key Name: X-API-Key  # optional, header name override

Server receives:

X-API-Key: your_api_key

OAuth

OAuth access token, sent as a bearer token:

Auth Type: oauth
Access Token: your_oauth_access_token

Server receives:

Authorization: Bearer your_oauth_access_token

Preloop identity headers

Every request Preloop sends to an upstream MCP server (discovery, initialize, tools/list, tools/call, and the connection test) identifies Preloop, so a server owner can tell that a request arrived through a Preloop gate and join their own access record with the Preloop audit row:

Header Sent Value
User-Agent Always Preloop/<version>, the same value Preloop sends to model providers
X-Preloop-Correlation-Id On the session that serves a proxied tool call The correlation_id written to the policy_allow, policy_deny and tool_call audit rows for that call. A call replayed after approval carries the id of the call that paused (from its approval_created row), or none if that row is missing. Scans and connection tests carry none.
X-Preloop-Instance Only when the server sets send_instance_id: true A stable, non-secret id for this installation: a one-way hash of the installation id created at first start, or the value of PRELOOP_INSTANCE_ID when set. Never the account id.

Preloop never forwards headers from the calling agent to the upstream; only the headers above and the configured authentication are sent. Authentication headers are unchanged and win if a name collides.

Per-server settings in auth_config (booleans). They are header preferences, not credentials: changing them keeps an OAuth connection.

send_identity_headers: false  # remove all three headers for this server
send_instance_id: true        # add X-Preloop-Instance (off by default)

The headers are self-asserted. They are not an attestation: any client can send them, so use them to correlate records, not to authenticate the caller.


Prelooping External Tools

Once tools are discovered, you control access using access rules. Each rule has:

  • Action: deny, allow, or require_approval
  • Condition: A CEL expression that matches on the tool's arguments (e.g., args.amount > 1000)
  • Priority: Rules with a lower priority value are evaluated first
  • Approval workflow: For require_approval rules, which workflow to use (human or AI-driven)
  • Description: Human-readable explanation of the rule

Rule Evaluation

Rules are evaluated in priority order (lowest priority value first). The first matching rule determines the action. If no rules match, the tool's default behavior applies.

Option 1: Via Web UI

  1. Find the tool on the Tools page
  2. Click the tool to expand it
  3. Click Add rule to create an access rule
  4. Configure the rule:
  5. Action: Deny, Require approval, or Allow
  6. Condition: build it with the condition builder, or write a CEL Expression (e.g., args.amount > 500)
  7. Approval workflow (for Require approval): select an existing workflow or create a new one
    • Human approval: designated approvers review and decide
    • AI-driven approval: an AI model evaluates the request against guidelines (offered in the Cloud and Enterprise console)
  8. Description: explain what this rule does
  9. Drag rules to reorder priority
  10. Click Save

Option 2: Via Policy-as-Code (YAML)

Define rules declaratively in a YAML policy file:

version: "1.0"
metadata:
  name: "Payment Protection"
  description: "Access rules for payment tools"

approval_workflows:
  - name: "finance-human"
    timeout_seconds: 600
    approvals_required: 1

  - name: "finance-ai"
    approval_type: ai_driven
    ai_model: gpt-5.4
    ai_guidelines: "Approve refunds under $5000 with valid reason"
    ai_confidence_threshold: 0.85
    ai_fallback_behavior: escalate

tools:
  - name: "pay"
    source: example-mcp.preloop.ai/mcp
    approval_workflow: "finance-human"
    conditions:
      # Rule 1 (highest priority): Block payments to internal accounts
      - expression: 'args.recipient.endsWith("@internal.acme.com")'
        action: deny
        description: "Block payments to internal accounts"

      # Rule 2: Large payments need human approval
      - expression: "args.amount > 5000"
        action: require_approval
        description: "High-value payments need human review"

      # Rule 3: Medium payments can use AI approval
      - expression: "args.amount > 500"
        action: require_approval
        description: "Medium payments reviewed by AI"

      # Rule 4: Small payments auto-approved
      - expression: "args.amount <= 500"
        action: allow
        description: "Small payments are safe"

A tool the policy does not list is allowed. In defaults, apply (also with --dry-run) accepts only unknown_tools: allow; it rejects unknown_tools: deny or require_approval, require_approval_for_new_tools: true and default_approval_workflow. To put a tool behind approval, list it under tools: as pay is above.

Apply with the CLI:

preloop policy apply payment-protection.yaml

Option 3: Via API

# Create an access rule for a tool
curl -X POST https://preloop.ai/api/v1/tool-configurations/{config_id}/access-rules \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "require_approval",
    "condition_type": "cel",
    "condition_expression": "args.amount > 1000",
    "approval_workflow_id": "your_workflow_id",
    "priority": 100,
    "description": "Large payments need approval"
  }'

Tool Discovery

Automatic Discovery

When you click Scan for tools, Preloop:

  1. Connects to MCP server
  2. Calls tools/list method
  3. Receives tool schemas
  4. Parses tool names, descriptions, arguments
  5. Lists them on the Tools page

Manual Refresh

Tools don't update automatically. To refresh:

  1. Go to Tools
  2. Find your MCP server
  3. Click Scan for tools again
  4. New tools appear, removed tools disappear

Recommended: Scan after updating your MCP server.

Tool Filtering

Preloop shows different tools to different users based on:

  • RBAC - User role and permissions
  • MCP Server Status - Active vs inactive
  • Tool Configuration - Enabled vs disabled

Same Tool Name on Two Servers

Two MCP servers in one account can expose a tool with the same name, for example search or read_scope. Preloop never renames tools on its own: agents are prompted with tool names, and policies and tool configuration are keyed by them.

  • The oldest server owns the name. Among the account's active servers, the one created first (then the lower id) owns a name. Agents see the name once, calls go to that server, and that server's tool configuration applies.
  • The newer server's tool is shadowed. It is marked shadowed, hidden from agents and not callable. Preloop recomputes this when a server is added, updated, scanned or deleted. When the owner is deleted or disabled, the next oldest server takes the name over.
  • Where you see it. A warning badge on the server and a Shadowed badge on the tool in Tools, the warning list in the server's edit dialog, warnings in the API responses of create, update, scan, get and list (and per tool in GET /api/v1/mcp-servers/{id}/tools), warning: lines from preloop mcp-servers list|tools|scan|add|update, and one configuration_change audit event (config_type: mcp_tool_collision, action shadowed or unshadowed) per collision set, with the server ids and tool names.

The warning reads:

Tool 'read_scope' on MCP server 'newer' is shadowed by MCP server 'older', which was added earlier and exposes the same name. Agents see and call only the tool from 'older'. Set a tool prefix on this server to expose both.

Exposing both: an explicit tool prefix

Set Tool prefix on a server (console form, tool_prefix in the API, or --tool-prefix in the CLI) to expose its tools as <prefix>_<tool>:

preloop mcp-servers update newer --tool-prefix crm   # read_scope becomes crm_read_scope
preloop mcp-servers update newer --tool-prefix ""    # clear it

The prefixed name is the name everywhere: listing, routing, access rules, tool configuration and policy YAML (tool: crm_read_scope). The upstream server still receives read_scope. A prefix is lowercase [a-z0-9_], at most 32 characters, and the resulting name must be a valid MCP tool name (at most 128 characters from A-Z a-z 0-9 _ - .); a tool whose resulting name is invalid is not exposed and gets a warning. Changing a prefix renames the tools agents see, so update prompts and rules that use the old names. Preloop never sets a prefix for you.

Servers shared into the account by another account rank after the account's own servers.

Tool Output Filters

Some MCP tools return verbose JSON results full of fields your agent never uses: raw HTML, embedded metadata, duplicate URLs. Every one of those fields is paid for again on each subsequent model call as session context. Tool output filters strip named top-level fields from a tool's JSON results on the proxy hot path, before the result reaches the calling agent, without modifying the upstream tool.

How a filter works:

  • Each filter names a tool and a list of dropped fields (top-level keys to remove from each result object).
  • Filters are account-scoped. Optionally narrow one to a specific MCP server and/or a specific managed agent; left unset, it matches any server and applies account-wide.
  • When several filters match the same call, the union of their dropped fields is stripped.
  • Filtering only applies to results that parse as a JSON object (or a list of objects). Non-JSON output passes through unchanged, and any filtering error leaves the original result intact: filters can trim a result, never break it.

Cloud and Enterprise

Filter enforcement runs in OSS. Creating and managing filters is part of the cost-optimization tooling in Preloop Cloud and Preloop Enterprise: the Console's session-optimization suggestions open a filter dialog prefilled with a tool's droppable fields, and the billing API exposes CRUD at /api/v1/billing/cost/output-filters (GET list, POST create, DELETE /{filter_id}).


Common External Tool Types

1. Deployment Tools

Examples: deploy_to_production, rollback_deployment, scale_service

Access rules strategy:

tools:
  - name: deploy_to_production
    source: deploy-server
    approval_workflow: "deploy-approval"
    conditions:
      - expression: 'args.environment == "production"'
        action: require_approval
        description: "Production deploys always need approval"
      - expression: 'args.environment == "staging"'
        action: allow
        description: "Staging deploys are safe"
      - expression: "true"
        action: deny
        description: "Block unknown environments"

2. Database Tools

Examples: drop_table, alter_schema, backup_database

Access rules strategy:

tools:
  - name: drop_table
    source: db-server
    approval_workflow: "db-critical"
    conditions:
      - expression: 'args.database.startsWith("prod_")'
        action: require_approval
        description: "Production tables need approval"
      - expression: 'args.database.startsWith("test_")'
        action: allow
        description: "Test tables are safe"
      - expression: "true"
        action: deny
        description: "Block everything else"

  - name: backup_database
    source: db-server
    conditions:
      - expression: "true"
        action: allow
        description: "Backups are always safe"

3. Infrastructure Tools

Examples: modify_firewall, scale_cluster, rotate_credentials

Access rules strategy:

tools:
  - name: modify_firewall
    source: infra-server
    approval_workflow: "security-review"
    conditions:
      - expression: "true"
        action: require_approval
        description: "All firewall changes need review"

4. Payment Tools

Examples: process_payment, issue_refund, void_transaction

Access rules strategy, tiered by amount with AI + human approval:

approval_workflows:
  - name: "finance-ai"
    approval_type: ai_driven
    ai_model: gpt-5.4
    ai_guidelines: "Approve refunds with valid justification"
    ai_confidence_threshold: 0.9
    ai_fallback_behavior: escalate

  - name: "finance-human"
    timeout_seconds: 600
    approvals_required: 2

tools:
  - name: process_payment
    source: payment-server
    approval_workflow: "finance-human"
    conditions:
      # High-value: 2 human approvers
      - expression: "args.amount > 10000"
        action: require_approval
        description: "High-value payments: human review"
      # Medium-value: AI review
      - expression: "args.amount > 500"
        action: require_approval
        description: "Medium payments: AI review"
      # Small: auto-approve
      - expression: "args.amount <= 500"
        action: allow
        description: "Small payments are safe"

5. Filesystem Tools

Examples: read_file, write_file, delete_file

Access rules strategy:

tools:
  - name: write_file
    source: fs-server
    approval_workflow: "file-approval"
    conditions:
      # Block credentials
      - expression: 'args.path.endsWith(".env") || args.path.endsWith(".pem")'
        action: deny
        description: "Never modify credential files"
      # System files need approval
      - expression: 'args.path.startsWith("/etc/") || args.path.startsWith("/var/")'
        action: require_approval
        description: "System files need human review"
      # Everything else is fine
      - expression: "true"
        action: allow
        description: "Other files are safe"

  - name: delete_file
    source: fs-server
    approval_workflow: "file-approval"
    conditions:
      - expression: "true"
        action: require_approval
        description: "All deletes need approval"

Security Considerations

1. Verify Server Trust

Only connect to trusted MCP servers:

  • Your own servers
  • Official vendor servers
  • Community servers with good reputation

Never connect to:

  • Unknown/suspicious servers
  • Servers over HTTP (not HTTPS)
  • Servers without authentication

2. Use Strong Authentication

# Bad - no auth for sensitive tools
Auth Type: none

# Good - require authentication
Auth Type: bearer
Token: <use_secure_secret_manager>

3. Principle of Least Privilege

Only expose tools that are needed:

# On MCP server side - don't expose dangerous tools
@server.tool()
def read_config():
    """Safe - read-only"""
    return load_config()

# Don't expose unless absolutely needed
# @server.tool()
# def delete_all_data():
#     """Dangerous - don't expose"""

4. Audit External Tool Usage

Monitor all external tool calls:

  • Who called what tool
  • With what arguments
  • Approved or declined
  • Result or error

Tool calls are recorded in each runtime session's timeline (Audit > Sessions). Audit > All events lists them account-wide in every edition.

5. Rotate Credentials

# Rotate MCP server tokens quarterly
1. Generate new token on MCP server
2. Update in Preloop: Tools > Edit server (pencil icon) > new token
3. Test connection
4. Revoke old token

Advanced Patterns

Pattern 1: Multi-Environment Setup

# Development: no rules, everything allowed
Name: Deploy (Dev)
URL: https://deploy-dev.acme.com
Status: active

# Staging: AI approval for low friction
Name: Deploy (Staging)
URL: https://deploy-staging.acme.com
Status: active
# Tools use AI-driven approval workflow

# Production: human approval, 2 reviewers
Name: Deploy (Prod)
URL: https://deploy-prod.acme.com
Status: active
# Tools use human approval workflow with approvals_required: 2

Pattern 2: Layered Rules

Combine deny, allow, and require_approval rules on a single tool. CEL string matching is case-sensitive, so match the forms your tools actually emit:

tools:
  - name: query_database
    source: db-server
    approval_workflow: "db-review"
    conditions:
      # Always block DROP/DELETE
      - expression: 'args.query.contains("DROP") || args.query.contains("DELETE")'
        action: deny
        description: "Block destructive queries"
      # Approve writes to production
      - expression: 'args.query.startsWith("INSERT") || args.query.startsWith("UPDATE")'
        action: require_approval
        description: "Write queries need approval"
      # Allow reads
      - expression: 'args.query.startsWith("SELECT")'
        action: allow
        description: "Read queries are safe"

OAuth connections

Choose OAuth 2.0 when adding a server. Preloop discovers the protected resource and authorization server, then opens provider consent. If public registration is unavailable, enter the provider's pre-registered client ID and optional client secret. Enter the exact issuer when several authorization servers are advertised, and configure any scopes your integration requires.

The server shows Disconnected, Connecting, Connected, or Reconnect required. Access tokens refresh automatically before expiry. Use Reconnect when a grant expires, is rejected, or a token exchange cannot be confirmed. Disconnect removes Preloop's local grant; it does not revoke provider-side consent. Cancel connection also invalidates pending browser authorization. Secret fields remain write-only when editing a connection.

If a dispatched tool times out, its outcome may be unknown. Check the provider for the result before invoking it again; Preloop does not replay tool calls. For self-hosted callback, network, and migration settings, see the upstream OAuth architecture.