Tip: This is sometimes referred to as "white labelling" — creating a branded or tailored version of an open source project for your organization.
This guide explains how to create custom distributions of goose tailored to your organization's needs—whether that's preconfigured models, custom tools, branded interfaces, or entirely new user experiences.
goose's architecture is designed for extensibility. Organizations can create "remixed" versions that:
- Preconfigure AI providers: Ship with a specific model (local or cloud) and API credentials
- Bundle custom tools: Include proprietary extensions for internal data sources
- Customize the experience: Modify branding, UI, and default behaviors
- Target specific audiences: Create specialized versions for developers, legal teams, designers, etc.
┌─────────────────────────────────────────────────────────────────┐
│ User Interfaces │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ CLI │ │ Desktop │ │ Your Custom UI │ │
│ │ (goose-cli)│ │ (Electron) │ │ (web, mobile, etc.) │ │
│ └──────┬──────┘ └──────┬──────┘ └────────────┬────────────┘ │
└─────────┼────────────────┼──────────────────────┼───────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ goose-server (goosed) │
│ REST API for all goose functionality │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Core (goose crate) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Providers │ │ Extensions │ │ Config & Recipes │ │
│ │ (AI models)│ │ (MCP tools)│ │ (behavior & defaults) │ │
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
| What You Want | Where to Look | Complexity |
|---|---|---|
| Preconfigure a model/provider | config.yaml, init-config.yaml, environment variables |
Low |
| Add custom AI providers | crates/goose/src/providers/declarative/ |
Low |
| Bundle custom MCP extensions | config.yaml extensions section, ui/desktop/src/built-in-extensions.json, ui/desktop/src/components/settings/extensions/bundled-extensions.json |
Medium |
| Modify system prompts | crates/goose/src/prompts/ |
Low |
| Customize desktop branding | ui/desktop/ (icons, names, colors) |
Medium |
| Build a new UI (web, mobile) | Integrate with goose-server REST API |
High |
| Create guided workflows | Recipes (YAML-based task definitions) | Low |
| Build complex multi-step workflows | Recipes with sub-recipes and subagents | Medium |
git clone https://github.com/YOUR_ORG/goose.git
cd goose- Configuration-only: Modify config files and environment variables (no code changes)
- Extension-based: Add custom MCP servers for your tools (minimal core changes)
- Deep customization: Modify core behavior, UI, or add new providers
See BUILDING_LINUX.md and ui/desktop/README.md for platform-specific build instructions.
goose is licensed under Apache License 2.0 (ASL v2). Custom distributions must:
- Include the original license and copyright notices
- Clearly indicate any modifications made
- Not use "Goose" trademarks in ways that imply official endorsement
For detailed guidance on ASL v2 compliance, see the Apache License FAQ.
While you're free to maintain private forks, contributing improvements upstream benefits everyone—including your distribution. Private forks that diverge significantly become expensive to maintain and miss out on security updates and new features. Consider upstreaming generic improvements while keeping only organization-specific customizations private.
goose includes optional telemetry (via PostHog) to help improve the project. For custom distributions, you can:
- Disable telemetry: Set
GOOSE_DISABLE_TELEMETRY=1 - Use your own instance: Modify
crates/goose/src/posthog.rsto point to your PostHog instance
To benefit from upstream improvements:
- Regularly sync your fork with the main repository
- Keep customizations isolated (config files, separate extension repos) when possible
- Use recipes for workflow customization rather than code changes
- Subscribe to release announcements for breaking changes
Goal: Ship goose preconfigured to use a local Ollama model, requiring no API keys.
- Create an init-config.yaml in your distribution root:
# init-config.yaml - Applied on first run if no config exists
GOOSE_PROVIDER: ollama
GOOSE_MODEL: qwen3-coder:latest- Set environment defaults in your launcher script or packaging:
export GOOSE_PROVIDER=ollama
export GOOSE_MODEL=qwen3-coder:latest
export OLLAMA_HOST=http://localhost:11434 # Or your hosted instance- Optionally hide provider selection in the UI by modifying
ui/desktop/src/components.
- Provider configuration:
crates/goose/src/config/base.rs - Ollama provider implementation:
crates/goose/src/providers/ollama.rs - Config precedence: Environment variables → config.yaml → defaults
Goal: Distribute goose internally with pre-provisioned API keys for a frontier model.
- Store API keys securely using goose's secret management:
# config.yaml (distributed with your package)
GOOSE_PROVIDER: anthropic
GOOSE_MODEL: claude-sonnet-4-20250514- Inject secrets at install time or via your MDM/configuration management:
# Secrets are stored in system keyring or ~/.config/goose/secrets.yaml
# if GOOSE_DISABLE_KEYRING=1
goose configure set-secret ANTHROPIC_API_KEY "your-corporate-key"- Lock down provider changes (optional) by modifying the settings UI or using a recipe that enforces the provider.
- Secret storage:
crates/goose/src/config/base.rs(SecretStorage enum) - Keyring integration: Uses system keyring by default, file-based fallback available
- Config file location:
~/.config/goose/config.yaml
Goal: Add MCP extensions that connect to your data lake, internal APIs, or proprietary systems.
- Create your MCP server following the MCP specification:
# Example: internal_data_mcp.py
from mcp.server import Server
from mcp.types import Tool
server = Server("internal-data")
@server.tool()
async def query_data_lake(query: str) -> str:
"""Query the corporate data lake."""
# Your implementation here
return results- Bundle as a built-in extension by adding to either:
ui/desktop/src/built-in-extensions.json(core built-ins surfaced in extension UI)ui/desktop/src/components/settings/extensions/bundled-extensions.json(bundled extension catalog in Settings)
Example:
{
"id": "internal-data",
"name": "Internal Data Lake",
"description": "Query corporate data sources",
"enabled": true,
"type": "stdio",
"cmd": "python",
"args": ["/path/to/internal_data_mcp.py"],
"env_keys": ["INTERNAL_DATA_API_KEY"],
"timeout": 300
}- Or distribute as a recipe that enables the extension:
# data-analyst.yaml
title: Data Analyst Assistant
description: goose configured for data analysis
instructions: |
You have access to the corporate data lake. Help users query and analyze data.
extensions:
- type: stdio
name: internal-data
cmd: python
args: ["/opt/corp-goose/internal_data_mcp.py"]
description: Corporate data lake access- Extension types:
crates/goose/src/agents/extension.rs(ExtensionConfig enum) - Built-in MCP servers:
crates/goose-mcp/ - Extension loading:
crates/goose/src/agents/extension_manager.rs
Goal: Rebrand the desktop application with your organization's identity.
-
Replace visual assets in
ui/desktop/src/images/:icon.png,icon.ico,icon.icns- Application icons- Update splash screens and logos as needed
-
Modify application metadata in
ui/desktop/forge.config.ts:
// forge.config.ts
module.exports = {
packagerConfig: {
name: 'YourCompany AI Assistant',
executableName: 'yourcompany-ai',
icon: 'src/images/your-icon',
// ...
},
// ...
};- Update the system prompt to reflect your branding in
crates/goose/src/prompts/system.md:
You are an AI assistant called [YourName], created by [YourCompany].
...-
Customize UI components in
ui/desktop/src/(React/TypeScript):- Color schemes in CSS/Tailwind config
- Component text and labels
- Feature visibility
-
Align packaging and updater names when rebranding:
-
Update static branding metadata in
ui/desktop/package.json(productName, description) and Linux desktop templates (ui/desktop/forge.deb.desktop,ui/desktop/forge.rpm.desktop) -
Set build/release environment variables consistently:
GITHUB_OWNERandGITHUB_REPOfor publisher + updater repository lookupGOOSE_BUNDLE_NAMEfor bundle/debug scripts and updater asset naming (defaults toGoose)
-
Example:
export GITHUB_OWNER="your-org"
export GITHUB_REPO="your-goose-fork"
export GOOSE_BUNDLE_NAME="InsightStream-goose"- Use this branding consistency checklist before release:
- Application metadata (
forge.config.ts,package.json,index.html) uses your distro name - Release artifact names and updater lookup names are consistent
- Desktop launchers (Linux
.desktoptemplates) point to the same executable name produced by packaging
- Application metadata (
- Electron config:
ui/desktop/forge.config.ts - UI entry point:
ui/desktop/src/renderer.tsx - System prompts:
crates/goose/src/prompts/
Goal: Create an entirely new frontend while leveraging goose's backend.
goose provides two integration options for building custom UIs:
Use goose-server for HTTP-based integrations (web apps, simple clients):
# Start the server
./target/release/goosed
# API available at http://localhost:3000Reference the OpenAPI spec at ui/desktop/openapi.json for available endpoints:
- Session management
- Message streaming
- Extension control
- Configuration
Key endpoints for a minimal integration:
POST /sessions # Create a new session
POST /sessions/{id}/messages # Send a message (streaming response)
GET /sessions/{id} # Get session state
GET /extensions # List available extensions
POST /extensions/{name}/enable # Enable an extension
Handle streaming responses - goose uses Server-Sent Events (SSE) for real-time responses.
For richer integrations (IDEs, desktop apps, embedded agents), use the Agent Client Protocol (ACP)—a standardized JSON-RPC protocol for AI agent communication over stdio or other transports.
ACP provides:
- Bidirectional communication: Agents can request permissions, stream updates, and receive cancellations
- Rich tool call handling: Detailed status updates, locations, and content for each tool invocation
- Session management: Create, load, and resume sessions with full conversation history
- MCP server integration: Dynamically add MCP servers to sessions
Start goose as an ACP agent:
# Run goose as an ACP server on stdio
goose acp --with-builtin developer,memory
# Or programmatically
cargo run -p goose-cli -- acp --with-builtin developerKey ACP methods:
| Method | Description |
|---|---|
initialize |
Establish connection and exchange capabilities |
session/new |
Create a new session with optional MCP servers |
session/load |
Resume an existing session by ID |
session/prompt |
Send a prompt and receive streaming responses |
session/cancel |
Cancel an in-progress prompt |
Example: Python ACP client (see test_acp_client.py for a complete example):
import subprocess
import json
class AcpClient:
def __init__(self):
self.process = subprocess.Popen(
['goose', 'acp', '--with-builtin', 'developer'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
text=True
)
def send_request(self, method, params=None):
request = {"jsonrpc": "2.0", "method": method, "id": 1}
if params:
request["params"] = params
self.process.stdin.write(json.dumps(request) + "\n")
self.process.stdin.flush()
return json.loads(self.process.stdout.readline())
# Initialize and create session
client = AcpClient()
client.send_request("initialize", {"protocolVersion": "2025-01-01"})
session = client.send_request("session/new", {"cwd": "/path/to/project"})
# Send a prompt (responses stream as notifications)
client.send_request("session/prompt", {
"sessionId": session["result"]["sessionId"],
"prompt": [{"type": "text", "text": "List files in this directory"}]
})ACP notifications (sent from agent to client):
session/notificationwithagentMessageChunk- Streaming text responsessession/notificationwithtoolCall- Tool invocation startedsession/notificationwithtoolCallUpdate- Tool status/result updatesrequestPermission- Agent requests user confirmation for sensitive operations
For the full ACP specification, see the Agent Client Protocol documentation.
REST API (goose-server):
- Server implementation:
crates/goose-server/src/routes/ - OpenAPI generation:
just generate-openapi - API client example:
ui/desktop/src/api/(generated TypeScript client)
ACP:
- ACP server implementation:
crates/goose-acp/src/server.rs - CLI integration:
crates/goose-cli/src/cli.rs(Command::Acp) - Protocol library:
sacpcrate (Rust implementation of ACP) - Test client example:
test_acp_client.py
Goal: Create a version of goose tailored for a specific professional audience.
- Create a specialized recipe that defines the experience:
# legal-assistant.yaml
title: Legal Research Assistant
description: AI assistant for legal professionals
instructions: |
You are a legal research assistant. You help lawyers and paralegals with:
- Case law research
- Document review and summarization
- Contract analysis
- Legal writing assistance
Always cite sources. Flag when you're uncertain. Never provide actual legal advice.
extensions:
- type: builtin
name: developer
description: File and document tools
- type: stdio
name: legal-database
cmd: python
args: ["/opt/legal-goose/legal_db_mcp.py"]
description: Legal database search
activities:
- "Research case law on..."
- "Summarize this contract..."
- "Find precedents for..."
settings:
goose_provider: anthropic
goose_model: claude-sonnet-4-20250514-
Customize the UI to show only relevant features and use domain-appropriate language.
-
Bundle domain-specific extensions for specialized data sources (legal databases, design tools, etc.).
- Recipe format:
crates/goose/src/recipe/mod.rs - Recipe loading:
crates/goose/src/recipe/local_recipes.rs - Activity suggestions: Shown in UI as quick-start prompts
Goal: Add support for a new AI provider or your self-hosted model endpoint.
Create a JSON file in ~/.config/goose/custom_providers/ or bundle in your distribution:
{
"name": "my_provider",
"engine": "openai",
"display_name": "My Custom Provider",
"description": "Our internal LLM endpoint",
"api_key_env": "MY_PROVIDER_API_KEY",
"base_url": "https://llm.internal.company.com/v1/chat/completions",
"models": [
{
"name": "company-llm-v1",
"context_limit": 32768
}
],
"supports_streaming": true,
"requires_auth": true
}Supported engines: openai, anthropic, ollama
For providers with unique APIs, implement the Provider trait:
- Create a new file in
crates/goose/src/providers/ - Implement the
Providertrait frombase.rs - Register in
crates/goose/src/providers/factory.rs
- Declarative providers:
crates/goose/src/config/declarative_providers.rs - Provider trait:
crates/goose/src/providers/base.rs - Provider registration:
crates/goose/src/providers/factory.rs - Example providers:
crates/goose/src/providers/declarative/*.json
Goal: Create standardized, repeatable workflows that users can run with minimal setup.
Recipes are YAML files that define complete goose experiences—instructions, extensions, parameters, and prompts bundled together. They're ideal for custom distributions because they require no code changes and can be distributed as simple files.
version: 1.0.0
title: Daily Standup Report Generator
description: Generates standup reports from GitHub activity
# Parameters users can customize at runtime
parameters:
- key: github_repo
input_type: string
requirement: required
description: "GitHub repository (e.g., 'owner/repo')"
- key: time_period
input_type: select
requirement: optional
default: "24h"
options: ["24h", "48h", "week"]
description: "Time period to analyze"
# System instructions for the AI
instructions: |
You are a standup report generator. Fetch PR and issue data from GitHub,
analyze activity, and generate a formatted report.
Always save reports to ./standup/standup-{date}.md
# Extensions this recipe needs
extensions:
- type: builtin
name: developer
description: File operations
- type: stdio
name: github
cmd: uvx
args: ["github-mcp-server"]
description: GitHub API access
# Quick-start suggestions shown in UI
activities:
- "Generate today's standup report"
- "Summarize this week's PRs"
# Initial prompt with parameter substitution
prompt: |
Generate a standup report for {{ github_repo }} covering the last {{ time_period }}.| Type | Description | Use Case |
|---|---|---|
string |
Free-form text input | Names, paths, queries |
number |
Numeric input | Counts, limits |
boolean |
True/false toggle | Feature flags |
date |
Date picker | Time-based filters |
file |
File path (content imported) | Document processing |
select |
Dropdown from options | Predefined choices |
- Bundle with your distribution in a known location
- Share via URL - users can import recipes from URLs
- Create a recipe library - a directory of recipes for different use cases
- Recipe schema:
crates/goose/src/recipe/mod.rs - Parameter handling:
crates/goose/src/recipe/template_recipe.rs - Recipe validation:
crates/goose/src/recipe/validate_recipe.rs
Goal: Build sophisticated multi-step workflows that orchestrate multiple specialized tasks.
For complex workflows, goose supports two powerful composition mechanisms:
- Sub-recipes: Predefined recipe templates that can be invoked by name
- Subagents: Independent AI agents spawned to handle specific tasks
Sub-recipes let you define reusable workflow components that the main recipe can invoke:
version: 1.0.0
title: Implementation Planner
description: Creates detailed implementation plans with research
instructions: |
Create implementation plans through research and iteration.
Use sub-recipes to delegate specialized research tasks.
# Define available sub-recipes
sub_recipes:
- name: "find_files"
path: "./subrecipes/codebase-locator.yaml"
description: "Locate relevant files in the codebase"
- name: "analyze_code"
path: "./subrecipes/code-analyzer.yaml"
description: "Analyze code structure and patterns"
- name: "find_patterns"
path: "./subrecipes/pattern-finder.yaml"
# Pre-fill some parameters
values:
search_depth: "3"
include_tests: "true"
extensions:
- type: builtin
name: developer
prompt: |
Create an implementation plan for the requested feature.
Use the available sub-recipes to research the codebase:
- find_files: Locate relevant source files
- analyze_code: Understand current implementation
- find_patterns: Find similar features to model afterThe AI can then invoke these sub-recipes using the subagent tool:
subagent(subrecipe: "find_files", parameters: {"search_term": "authentication"})
Subagents are independent AI instances that run with their own context. They're useful for:
- Parallel execution: Multiple tasks running simultaneously
- Context isolation: Preventing context window overflow
- Specialized tasks: Different model/settings per task
Create subagents on-the-fly with custom instructions:
prompt: |
To complete this task:
1. Spawn a subagent to analyze the frontend code:
subagent(instructions: "Analyze all React components in src/components/
and list their props and state management patterns")
2. Spawn another subagent for the backend:
subagent(instructions: "Document all API endpoints in src/api/
including their request/response schemas")
3. Synthesize findings from both subagents into a unified report.Multiple subagent calls in the same message execute in parallel:
prompt: |
Run these analyses in parallel by making all subagent calls at once:
subagent(instructions: "Count lines of code by language")
subagent(instructions: "Find all TODO comments")
subagent(instructions: "List external dependencies")
Then combine the results into a codebase health report.Customize model, provider, or behavior per subagent:
prompt: |
Use a faster model for simple tasks:
subagent(
instructions: "List all files modified in the last week",
settings: {
model: "gpt-4o-mini",
max_turns: 3
}
)
Use the full model for complex analysis:
subagent(
instructions: "Review this code for security vulnerabilities",
settings: {
model: "claude-sonnet-4-20250514",
temperature: 0.1
}
)Limit which extensions a subagent can access:
prompt: |
Create a sandboxed subagent with only file reading capabilities:
subagent(
instructions: "Analyze the README files in this project",
extensions: ["developer"] # Only developer extension, no network access
)version: 1.0.0
title: Comprehensive Code Review
description: Multi-stage code review with parallel analysis
sub_recipes:
- name: "security_scan"
path: "./subrecipes/security-scanner.yaml"
sequential_when_repeated: true # Don't run multiple security scans in parallel
- name: "style_check"
path: "./subrecipes/style-checker.yaml"
- name: "test_coverage"
path: "./subrecipes/coverage-analyzer.yaml"
parameters:
- key: pr_number
input_type: number
requirement: required
description: "Pull request number to review"
- key: review_depth
input_type: select
requirement: optional
default: "standard"
options: ["quick", "standard", "thorough"]
instructions: |
Perform a comprehensive code review using specialized sub-recipes.
## Review Process
### Phase 1: Parallel Analysis
Run these checks simultaneously:
- style_check: Code style and formatting
- test_coverage: Test coverage analysis
### Phase 2: Security Review
After initial checks pass, run security_scan (sequential to avoid conflicts).
### Phase 3: Synthesis
Combine all findings into a unified review report with:
- Critical issues (must fix)
- Suggestions (should consider)
- Positive observations (good practices found)
extensions:
- type: builtin
name: developer
- type: stdio
name: github
cmd: uvx
args: ["github-mcp-server"]
prompt: |
Review PR #{{ pr_number }} with {{ review_depth }} depth.
{% if review_depth == "quick" %}
Focus only on critical issues and security concerns.
{% elif review_depth == "thorough" %}
Perform exhaustive analysis including performance review.
{% endif %}
Start by fetching the PR details, then orchestrate the review phases.- Use sub-recipes for reusable components - Define once, use across multiple recipes
- Parallelize independent tasks - Multiple subagent calls in one message run concurrently
- Use
sequential_when_repeated: true- For tasks that shouldn't run in parallel (e.g., database migrations) - Scope extensions appropriately - Give subagents only the tools they need
- Use summary mode (default) - Subagents return concise summaries; use
summary: falseonly when you need full conversation history - Handle failures gracefully - Design workflows to continue even if one subagent fails
- Subagent tool:
crates/goose/src/agents/subagent_tool.rs - Subagent execution:
crates/goose/src/agents/subagent_handler.rs - Recipe sub_recipes field:
crates/goose/src/recipe/mod.rs(SubRecipe struct) - Template rendering:
crates/goose/src/recipe/template_recipe.rs