How to Integrate Goose with Other Go Projects: Complete SDK Guide

To integrate Goose with other Go projects, import the goose-sdk Go module, spawn a Goose ACP server process, and use the SDK's generated structs to send prompts and handle streaming responses over stdio or HTTP.

The aaif-goose/goose repository provides a lightweight Go SDK that acts as a thin transport client for the Rust-based AI agent framework. By implementing the Agent Client Protocol (ACP), Go applications can leverage Goose's core capabilities—session management, tool execution, and permission handling—without reimplementing the agent logic.

Add the Goose SDK to Your go.mod

Begin integration by declaring the SDK as a module dependency. The goose-sdk is a minimal Go wrapper that compiles against the JSON schema defined in the Rust crate.

module myapp

go 1.22

require (
    github.com/aaif-goose/goose v0.0.0-<commit>
)

Replace <commit> with the latest SHA from the repository. At compile time, the SDK pulls generated ACP request and response structs from goose-sdk/src/custom_requests.rs, ensuring type-safe serialization that matches the Rust server implementation.

Start the Goose ACP Server

Before your Go program can send prompts, you must run a Goose ACP server instance. The simplest approach uses the built-in CLI command:

goose acp

By default, this listens on stdio, which is ideal for local development and child-process spawning. For production deployments, you can configure the server to use Streamable HTTP via the transport adapters defined in goose-acp/src/transport.rs.

The server reads configuration from ~/.config/goose/config.yaml, initializes the extension registry, and exposes the session API. All core logic—prompt handling, provider routing, and scheduling—lives in the Rust side according to goose/src/lib.rs, while your Go code manages only the transport layer.

Create an ACP Client in Go

The Go client implementation follows four distinct phases: spawning the server, establishing a byte-stream transport, configuring the client, and managing the session lifecycle.

Spawn the Server Process

Use os/exec to start the Goose binary as a child process. This gives your Go program direct access to the server's stdin and stdout for ACP communication.

gooseBin := "goose" // or path to binary
cmd := exec.Command(gooseBin, "acp")
stdin, _ := cmd.StdinPipe()
stdout, _ := cmd.StdoutPipe()
cmd.Stderr = os.Stderr

if err := cmd.Start(); err != nil {
    log.Fatalf("spawn: %v", err)
}

Initialize the Byte-Stream Transport

Wrap the process pipes into an ACP transport using the SDK's byte-stream implementation from goose-acp/src/transport.rs.

transport := sacp.NewByteStreams(stdout, stdin)

Build and Configure the Client

Construct the client with a name and permission handler. The OnReceivePermission callback processes permission requests from the Rust core—defined in goose-acp/src/lib.rs—allowing you to auto-approve or present interactive prompts.

client := sacp.NewClient().
    Name("my-go-client").
    OnReceivePermission(func(req sacp.RequestPermission) sacp.Response {
        // Auto-approve the first option
        if len(req.Options) == 0 {
            return sacp.PermissionResponseCancel()
        }
        return sacp.PermissionResponse(req.Options[0].ID)
    })

Connect to the transport and initialize the session protocol:

if err := client.Connect(transport); err != nil {
    log.Fatalf("connect: %v", err)
}
if _, err := client.Initialize(); err != nil {
    log.Fatalf("init: %v", err)
}

Manage Sessions and Send Prompts

Create a session and transmit prompts using strongly-typed structs generated from goose-sdk/src/custom_requests.rs. The SDK handles serialization while the Rust server manages the conversation state.

session := client.NewSession()
if err := session.SendPrompt("Explain the Go memory model"); err != nil {
    log.Fatalf("prompt: %v", err)
}

// Consume streamed notifications
for notif := range session.Notifications() {
    switch n := notif.(type) {
    case sacp.AgentMessage:
        fmt.Print(n.Text)
    case sacp.ToolCall:
        fmt.Printf("\n🔧 Tool: %s\n", n.Title)
    }
}

Work with Extensions and Tools

Goose supports MCP extensions such as file-system and computer-controller tools. The Go client can query available tools through the ACP protocol using request structs defined in goose-sdk/src/custom_requests.rs.

var resp goose_sdk.GetExtensionsResponse
if err := client.Call(&goose_sdk.GetExtensionsRequest{}, &resp); err != nil {
    log.Fatalf("list extensions: %v", err)
}
fmt.Printf("Available: %v\n", resp.Extensions)

Tool execution results stream back as ToolCall notifications. To return results to the agent, send a ToolResult request using the corresponding struct from the SDK.

Session Management Operations

The SDK exposes session lifecycle operations that map to HTTP endpoints in goose-server/src/routes/session.rs:

  • List sessions: client.Call(&goose_sdk.ListSessionsRequest{}, &resp)
  • Get session: client.Call(&goose_sdk.GetSessionRequest{SessionID: id}, &resp)
  • Export/Import: Use ExportSessionRequest and ImportSessionRequest structs for persistence

Complete Minimal Example

The following self-contained program demonstrates the full integration flow, adapted from goose-sdk/examples/acp_client.rs.

package main

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

	"github.com/aaif-goose/goose/goose_sdk"
	"github.com/aaif-goose/goose/sacp"
)

func main() {
	if len(os.Args) < 2 {
		log.Fatalf("usage: %s PROMPT", os.Args[0])
	}
	prompt := os.Args[1]

	// Spawn Goose ACP server
	cmd := exec.Command("goose", "acp")
	stdin, _ := cmd.StdinPipe()
	stdout, _ := cmd.StdoutPipe()
	cmd.Stderr = os.Stderr
	
	if err := cmd.Start(); err != nil {
		log.Fatalf("spawn: %v", err)
	}

	// Byte-stream transport
	transport := sacp.NewByteStreams(stdout, stdin)

	// Build client with auto-approve permissions
	client := sacp.NewClient().
		Name("go-example").
		OnReceivePermission(func(req sacp.RequestPermission) sacp.Response {
			if len(req.Options) == 0 {
				return sacp.PermissionResponseCancel()
			}
			return sacp.PermissionResponse(req.Options[0].ID)
		})

	// Connect and initialize
	if err := client.Connect(transport); err != nil {
		log.Fatalf("connect: %v", err)
	}
	if _, err := client.Initialize(); err != nil {
		log.Fatalf("init: %v", err)
	}

	// Create session and send prompt
	sess := client.NewSession()
	if err := sess.SendPrompt(prompt); err != nil {
		log.Fatalf("prompt: %v", err)
	}

	// Stream notifications
	for n := range sess.Notifications() {
		switch ev := n.(type) {
		case sacp.AgentMessage:
			fmt.Print(ev.Text)
		case sacp.ToolCall:
			fmt.Printf("\n🔧 %s\n", ev.Title)
		}
	}
	
	_ = cmd.Process.Kill()
}

Run the example after building Goose:

cargo build -p goose
go run main.go "Explain the Go memory model in one sentence"

The program streams the agent's response line-by-line and prints tool call notifications as they occur.

Summary

  • Add the SDK: Import github.com/aaif-goose/goose to access generated ACP structs from goose-sdk/src/custom_requests.rs.
  • Start the server: Run goose acp to launch the Rust-based ACP server on stdio or HTTP.
  • Establish transport: Use sacp.NewByteStreams() to wrap stdio pipes for JSON-RPC communication.
  • Handle permissions: Implement the OnReceivePermission callback to manage permission requests from the Rust core.
  • Stream responses: Consume the Notifications() channel to receive AgentMessage and ToolCall events from the session managed in goose/src/lib.rs.

Frequently Asked Questions

What is the Agent Client Protocol (ACP) in Goose?

The Agent Client Protocol (ACP) is the JSON-RPC interface defined in goose-acp/src/lib.rs that standardizes communication between client applications and the Goose agent. It handles message routing, permission requests, and tool execution notifications. The Go SDK implements the client side of this protocol using generated structs from goose-sdk/src/custom_requests.rs, while the Rust core implements the server side with session logic in goose/src/lib.rs.

Can I use Goose with Go without running a separate Rust binary?

No. Because Goose's core logic—including LLM providers, tool scheduling, and permission engines—is implemented in Rust, you must run a Goose ACP server process. The Go SDK acts only as a thin transport layer that serializes requests and forwards them over byte streams (stdio) or HTTP. The architecture intentionally keeps all AI logic in the Rust runtime while allowing Go programs to control the agent via the ACP protocol.

How do I handle permission requests when integrating Goose with Go?

Implement the OnReceivePermission callback when building the client with sacp.NewClient(). This function receives a RequestPermission struct containing options defined by the permission engine in goose-acp/src/lib.rs. You can either auto-approve by returning sacp.PermissionResponse(option.ID) for the first option, or implement interactive logic to prompt end users for approval before returning the response.

Which transport should I use for production Go integrations?

For production deployments, use the Streamable HTTP transport rather than stdio. While the examples in goose-sdk/examples/acp_client.rs demonstrate stdio transport suitable for local child-process spawning, the HTTP adapters defined in goose-acp/src/transport.rs provide better scalability and network isolation for server-side Go applications connecting to remote Goose instances.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →