← Back to Blog

MCP + Pilot: Give Your Agent Tools AND a Network

February 9, 2026 mcp integration anthropic

The Model Context Protocol (MCP) gives agents tools: database queries, API calls, file access, web search. Pilot Protocol gives agents a network: peer discovery, encrypted tunnels, trust management, task delegation. Neither replaces the other. An agent that can query a database but cannot share the results with a peer agent is half an agent. An agent that can reach peers but cannot access tools is the other half. This article shows you how to build the complete picture.

MCP has crossed 97 million monthly SDK downloads. Thousands of MCP servers exist for everything from GitHub to Postgres to Slack. What none of them provide is peer communication. An MCP-equipped agent can gather data from any source, but delivering that data to another agent still requires building your own transport, discovery, and encryption layer. That is exactly what Pilot provides.

The Two Halves of Agent Infrastructure

Think of agent infrastructure as two orthogonal capabilities:

Vertical: Tools (MCP). An agent reaches down into systems. It queries databases, calls APIs, reads files, executes code. MCP standardizes this with a client-server protocol: the agent runs an MCP client, connects to MCP servers (each wrapping a tool), and invokes tools through a structured JSON-RPC interface. The agent talks to systems.

Horizontal: Network (Pilot). An agent reaches across to peers. It discovers other agents, establishes trust, opens encrypted connections, sends messages, delegates tasks, subscribes to events. Pilot standardizes this with virtual addresses, UDP tunnels, and a port-based service model. The agent talks to agents.

Neither layer knows about the other. MCP does not care how your agent communicates with peers. Pilot does not care what tools your agent uses. This separation is a feature: you can upgrade your tools without changing your network layer, and vice versa. But at the application level, you need both.

The key insight: MCP gives an agent eyes and hands (it can see data, it can act on systems). Pilot gives an agent a voice and ears (it can tell peers what it found, it can hear what peers need). A fully capable agent needs all four.

Architecture: Same Process, Two Capabilities

The integration is architecturally simple. A single agent process runs two clients:

  1. MCP Client — connects to one or more MCP servers (database, API, file system, etc.) via stdio or HTTP transport.
  2. Pilot Driver — connects to the local Pilot daemon via IPC socket (/tmp/pilot.sock) for peer networking.

The agent's application logic coordinates between them. When the agent needs data, it calls MCP. When it needs to share data, delegate work, or respond to a peer, it calls Pilot. The two clients are independent: they run on different protocols, connect to different endpoints, and have different lifecycle management.

// Conceptual architecture (Go)
type Agent struct {
    mcp    *mcpclient.Client  // MCP: tool access
    pilot  *driver.Driver     // Pilot: peer networking
    llm    *llmclient.Client  // LLM: reasoning
}

// The agent loop: receive task, gather data, reason, deliver
func (a *Agent) Run() {
    for task := range a.pilot.AcceptTasks() {
        // 1. Use MCP to gather data
        data := a.mcp.CallTool("database_query", task.Params)

        // 2. Use LLM to reason about the data
        result := a.llm.Complete(task.Prompt + data)

        // 3. Use Pilot to deliver the result
        a.pilot.SendResults(task.ID, result)
    }
}

The Pilot daemon runs as a separate process (started with pilot-daemon). The MCP servers run as separate processes too (started by the MCP client). Your agent process is the coordinator that ties them together.

Example: Research Agent with MCP + Pilot

Let us build a concrete example. A research agent that:

  1. Receives a research request from a peer via Pilot's task system
  2. Uses MCP to query a database and fetch web content
  3. Produces a summary using an LLM
  4. Returns the summary to the requesting agent via Pilot
  5. Publishes a "research.completed" event so other interested agents are notified

Go Implementation

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "os/exec"

    "github.com/TeoSlayer/pilotprotocol/pkg/driver"
    "github.com/TeoSlayer/pilotprotocol/pkg/tasksubmit"
    "github.com/TeoSlayer/pilotprotocol/pkg/eventstream"
)

func main() {
    // Connect to the local Pilot daemon
    drv, err := driver.Connect("/tmp/pilot.sock")
    if err != nil {
        log.Fatal(err)
    }
    defer drv.Close()

    // Advertise task readiness
    drv.SetTaskReady(true)

    log.Println("Research agent online, waiting for tasks...")

    for {
        // Accept incoming research tasks via Pilot
        task, err := drv.AcceptTask()
        if err != nil {
            log.Printf("accept error: %v", err)
            continue
        }

        log.Printf("Accepted task %s: %s", task.ID, task.Description)

        // Use MCP to query the database
        queryResult := callMCPTool("database", "query", map[string]string{
            "sql": fmt.Sprintf(
                "SELECT title, abstract FROM papers WHERE topic LIKE '%%%s%%' LIMIT 10",
                task.Params["topic"],
            ),
        })

        // Use MCP to fetch web content for additional context
        webResult := callMCPTool("web-search", "search", map[string]string{
            "query": task.Params["topic"] + " recent developments",
        })

        // Combine sources and produce summary (LLM call omitted for clarity)
        summary := fmt.Sprintf(
            "Research summary for: %s\n\nDatabase: %d papers found\n%s\n\nWeb: %s",
            task.Params["topic"],
            len(queryResult),
            formatPapers(queryResult),
            webResult,
        )

        // Return results to the requester via Pilot
        err = drv.SendTaskResults(task.ID, []byte(summary))
        if err != nil {
            log.Printf("send results error: %v", err)
            continue
        }

        // Publish event so other agents know research is done
        event := eventstream.Event{
            Topic: "research.completed",
            Data:  map[string]string{
                "task_id": task.ID,
                "topic":   task.Params["topic"],
                "papers":  fmt.Sprintf("%d", len(queryResult)),
            },
        }
        eventData, _ := json.Marshal(event)
        drv.Publish("research.completed", eventData)

        log.Printf("Task %s completed, event published", task.ID)
    }
}

// callMCPTool invokes an MCP tool via the MCP client CLI.
// In production, use the MCP Go SDK for direct integration.
func callMCPTool(server, tool string, params map[string]string) string {
    args := []string{"call", "--server", server, "--tool", tool}
    for k, v := range params {
        args = append(args, "--param", k+"="+v)
    }
    out, err := exec.Command("mcp", args...).Output()
    if err != nil {
        log.Printf("MCP tool %s/%s error: %v", server, tool, err)
        return ""
    }
    return string(out)
}

The flow is straightforward: Pilot handles the peer communication (receiving the task, sending the results, publishing the event), while MCP handles the tool access (database query, web search). The agent's logic is the glue between them.

Python Implementation

For Python agents, the pattern uses subprocess calls to pilotctl for Pilot integration and the MCP Python SDK for tool access:

import subprocess
import json
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def research_agent():
    # Connect to MCP database server
    server_params = StdioServerParameters(
        command="mcp-server-postgres",
        args=["--connection-string", "postgresql://localhost/research"]
    )

    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as mcp:
            await mcp.initialize()

            while True:
                # Accept tasks via pilotctl
                result = subprocess.run(
                    ["pilotctl", "task", "accept", "--json"],
                    capture_output=True, text=True, timeout=60
                )
                if result.returncode != 0:
                    continue

                task = json.loads(result.stdout)
                topic = task["params"]["topic"]

                # Use MCP to query the database
                db_result = await mcp.call_tool("query", {
                    "sql": f"SELECT title, abstract FROM papers "
                           f"WHERE topic LIKE '%{topic}%' LIMIT 10"
                })

                # Produce summary
                summary = f"Found {len(db_result.content)} papers on {topic}\n"
                for row in db_result.content:
                    summary += f"- {row.text}\n"

                # Return results via pilotctl
                subprocess.run([
                    "pilotctl", "task", "send-results",
                    "--task-id", task["id"],
                    "--data", summary
                ])

                # Publish completion event via pilotctl
                event = json.dumps({
                    "task_id": task["id"],
                    "topic": topic,
                    "papers": str(len(db_result.content))
                })
                subprocess.run([
                    "pilotctl", "events", "publish",
                    "--topic", "research.completed",
                    "--data", event
                ])

The Python version shells out to pilotctl for Pilot operations, which is practical for prototyping and small-scale agents. For production workloads, a native Python binding to the Pilot IPC socket would eliminate the subprocess overhead.

What MCP + Pilot Enables

With both capabilities in a single agent, several patterns become natural:

Data Pipeline Across Agents

Agent A uses MCP to query a database. It publishes a "data.ready" event via Pilot's event stream. Agent B subscribes to "data.*" events. When it receives the notification, it connects to Agent A on port 1001 (data exchange) and downloads the dataset. Agent B uses MCP to write the processed results to a different database. Three agents, two databases, zero shared infrastructure beyond the Pilot registry.

Tool Delegation

Not every agent has access to every tool. Agent A has MCP access to a proprietary API. Agent B needs data from that API but does not have credentials. Agent B submits a task to Agent A via Pilot's task system: "query the API for X." Agent A executes the query via MCP, returns the results via Pilot. The API credentials never leave Agent A's machine. Trust between Agent A and Agent B is managed by Pilot's handshake system, not by sharing API keys.

Monitoring and Alerting

A monitoring agent uses MCP to connect to logging and metrics systems. When it detects an anomaly, it publishes an event via Pilot's event stream. Response agents subscribe to anomaly events and take action: one agent uses MCP to roll back a deployment, another uses MCP to page the on-call engineer via Slack, a third uses MCP to capture diagnostics. The monitoring agent does not need to know which response agents exist or what tools they have. It publishes the event; the network handles the rest.

Every MCP Agent Could Benefit from a Pilot Address

Today, MCP agents are islands. They connect to tools, they process data, they produce results. But they have no standardized way to find each other, establish trust, or exchange those results. Each MCP deployment reinvents peer communication with ad-hoc HTTP endpoints, message queues, or shared databases.

A Pilot address gives an MCP agent an identity on a peer network. It can be discovered by other agents (or stay invisible, if private). It can receive tasks, publish events, and exchange data. It can do all of this over encrypted tunnels that traverse NAT without any infrastructure changes.

The integration cost is minimal: start a Pilot daemon alongside your MCP agent, add a few pilotctl calls (or driver API calls in Go) to your agent loop, and your MCP agent becomes a networked MCP agent. The tools still work the same way. The LLM still works the same way. You just added the ability to collaborate with peers.

MCP handles the vertical. Pilot handles the horizontal. Together, they are the full stack for agent infrastructure. For more integration patterns, see Building A2A Agent Cards Over Pilot Tunnels and Build an Agent Swarm That Self-Organizes via Reputation. For the technical details of the Pilot driver API, check the documentation.

Add a Network to Your MCP Agent

Install Pilot, start the daemon, and give your agent a peer address in under 5 minutes.

View on GitHub