Building a Proxy

This chapter explains how to build a proxy component using the sacp-proxy crate.

Overview

A proxy component intercepts messages between editors and agents, transforming them or adding side effects. Proxies are built using the sacp-proxy framework.

Basic Structure

#![allow(unused)]
fn main() {
use sacp_proxy::{AcpProxyExt, JsonRpcCxExt, ProxyHandler};
use sacp::{JsonRpcConnection, JsonRpcHandler};

// Your proxy's main handler
struct MyProxy {
    // State fields
}

impl JsonRpcHandler for MyProxy {
    async fn handle_message(&mut self, message: MessageAndCx) -> Result<Handled> {
        match message {
            // Handle messages from upstream (editor direction)
            MessageAndCx::Request(req, cx) => {
                match req {
                    // Transform and forward
                    AcpRequest::Prompt(mut prompt) => {
                        // Modify the prompt
                        prompt.messages.insert(0, my_context);
                        
                        // Forward to successor
                        cx.send_request_to_successor(prompt)
                          .await_when_result_received(|result| {
                              cx.respond_with_result(result)
                          })
                    }
                    // Other message types...
                }
            }
            MessageAndCx::Notification(notif, cx) => {
                // Handle notifications
            }
        }
    }
}
}

Key Traits

AcpProxyExt

Provides methods for handling messages from the successor:

#![allow(unused)]
fn main() {
use sacp_proxy::AcpProxyExt;

connection
    .on_receive_request_from_successor(|req, cx| async move {
        // Handle request from downstream component
    })
    .on_receive_notification_from_successor(|notif, cx| async move {
        // Handle notification from downstream
    })
    .proxy() // Enable automatic proxy capability handshake
}

JsonRpcCxExt

Provides methods for sending to successor:

#![allow(unused)]
fn main() {
use sacp_proxy::JsonRpcCxExt;

// Send request and handle response
cx.send_request_to_successor(request)
  .await_when_result_received(|result| {
      cx.respond_with_result(result)
  })

// Send notification (fire and forget)
cx.send_notification_to_successor(notification)
}

Proxy Patterns

Pass-through Proxy

The simplest proxy forwards everything unchanged:

#![allow(unused)]
fn main() {
impl JsonRpcHandler for PassThrough {
    async fn handle_message(&mut self, message: MessageAndCx) -> Result<Handled> {
        match message {
            MessageAndCx::Request(req, cx) => {
                cx.send_request_to_successor(req)
                  .await_when_result_received(|r| cx.respond_with_result(r))
            }
            MessageAndCx::Notification(notif, cx) => {
                cx.send_notification_to_successor(notif)
            }
        }
    }
}
}

Initialization Injection

Inject context or configuration during initialization:

#![allow(unused)]
fn main() {
MessageAndCx::Request(AcpRequest::Initialize(mut init), cx) => {
    // Add your capabilities
    init.capabilities.my_feature = true;
    
    cx.send_request_to_successor(init)
      .await_when_result_received(|result| {
          cx.respond_with_result(result)
      })
}
}

Prompt Transformation

Modify prompts before they reach the agent:

#![allow(unused)]
fn main() {
MessageAndCx::Request(AcpRequest::Prompt(mut prompt), cx) => {
    // Prepend system context
    let context_message = Message {
        role: Role::User,
        content: vec![Content::Text { text: context }],
    };
    prompt.messages.insert(0, context_message);
    
    cx.send_request_to_successor(prompt)
      .await_when_result_received(|result| {
          cx.respond_with_result(result)
      })
}
}

MCP Server Provider

Provide MCP servers to the agent:

#![allow(unused)]
fn main() {
use sacp_proxy::AcpProxyExt;

connection
    .provide_mcp(my_mcp_server_uuid, my_mcp_handler)
    .proxy()
}

See the Protocol Reference for details on the MCP-over-ACP protocol.

Complete Example

For a complete example of a production proxy, see the sparkle-acp-proxy implementation.

Next Steps

  • See Protocol Reference for message format details
  • Read the sacp-proxy crate documentation for API details
  • Study the sparkle-acp-proxy implementation for patterns