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
ExportSessionRequestandImportSessionRequeststructs 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/gooseto access generated ACP structs fromgoose-sdk/src/custom_requests.rs. - Start the server: Run
goose acpto 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
OnReceivePermissioncallback to manage permission requests from the Rust core. - Stream responses: Consume the
Notifications()channel to receiveAgentMessageandToolCallevents from the session managed ingoose/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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →