clawdbot-workspace/research-goose-architecture.md
2026-02-06 23:01:30 -05:00

578 lines
23 KiB
Markdown

# Goose (Block) AI Agent — Architecture & Customization Research Report
> **Last Updated:** 2026-02-06
> **Purpose:** Deep research for forking/cloning Goose into a custom agent
> **License:** Apache 2.0 (permissive, allows derivative works & white-labeling)
---
## Table of Contents
1. [Repository Structure](#1-repository-structure)
2. [Core Architecture Deep Dive](#2-core-architecture-deep-dive)
3. [Desktop UI Architecture](#3-desktop-ui-architecture)
4. [MCP Integration System](#4-mcp-integration-system)
5. [Extension/Plugin System](#5-extensionplugin-system)
6. [Permission & HITL System](#6-permission--hitl-system)
7. [Forking Strategy](#7-forking-strategy)
8. [Community Extensions & Patterns](#8-community-extensions--patterns)
9. [Key Findings for Custom Agent Development](#9-key-findings-for-custom-agent-development)
10. [Sources](#10-sources)
---
## 1. Repository Structure
**Repo:** https://github.com/block/goose
Goose is a Rust + TypeScript monorepo with the following key structure:
```
block/goose/
├── crates/
│ ├── goose/ # Core agent logic (the brain)
│ │ └── src/
│ │ ├── agents/
│ │ │ ├── agent.rs # Main agent implementation
│ │ │ └── extension.rs # ExtensionConfig enum (L87-200)
│ │ └── providers/
│ │ └── base.rs # Provider trait definition
│ ├── goose-bench/ # Benchmarking framework
│ ├── goose-cli/ # CLI entry point
│ │ └── src/
│ │ ├── main.rs # CLI entry point
│ │ └── commands/
│ │ ├── configure.rs # Provider/extension configuration
│ │ └── mcp.rs # MCP server command handling
│ ├── goose-server/ # Backend HTTP server (binary: goosed)
│ │ └── src/main.rs # Server entry point
│ ├── goose-mcp/ # Built-in MCP extensions (developer, memory, etc.)
│ │ └── src/
│ │ └── developer/
│ │ └── tools/
│ │ └── shell.rs # Shell tool example
│ ├── goose-test/ # Test utilities
│ ├── mcp-client/ # MCP client implementation
│ ├── mcp-core/ # MCP shared types/primitives
│ └── mcp-server/ # MCP server framework
├── temporal-service/ # Go-based scheduler service
├── ui/desktop/ # Electron + React desktop app
│ ├── package.json # Dependencies & build scripts
│ ├── src/
│ │ ├── main.ts # Electron main process
│ │ ├── App.tsx # React root component
│ │ ├── goosed.ts # Backend connection manager
│ │ ├── built-in-extensions.json # Built-in extension registry
│ │ ├── components/ # React UI components
│ │ │ ├── BaseChat.tsx # Base chat container
│ │ │ ├── ChatInput.tsx # User input component (59KB!)
│ │ │ ├── ChatSessionsContainer.tsx
│ │ │ ├── ElicitationRequest.tsx # MCP elicitation UI
│ │ │ ├── ConfigContext.tsx
│ │ │ ├── ExtensionInstallModal.test.tsx
│ │ │ └── ErrorBoundary.tsx
│ │ ├── api/ # Generated OpenAPI client
│ │ ├── assets/ # Static assets (branding)
│ │ ├── contexts/ # React contexts
│ │ ├── constants/
│ │ └── utils/
│ │ ├── logger.ts
│ │ ├── autoUpdater.ts
│ │ ├── settings.ts
│ │ └── urlSecurity.ts
│ └── openapi.json # Auto-generated, DO NOT EDIT
├── AGENTS.md # AI-agent dev instructions
├── HOWTOAI.md # AI-assisted dev guide
├── GOVERNANCE.md # Project governance
├── LICENSE # Apache 2.0
└── Justfile # Task runner recipes
```
### Entry Points
| Entry Point | File | Purpose |
|---|---|---|
| CLI | `crates/goose-cli/src/main.rs` | Terminal interface |
| Server | `crates/goose-server/src/main.rs` | Backend daemon (`goosed`) |
| Desktop UI | `ui/desktop/src/main.ts` | Electron main process |
| Agent Core | `crates/goose/src/agents/agent.rs` | Core agent logic |
---
## 2. Core Architecture Deep Dive
### Execution Flow
The core flow is: **Message → Agent → Provider (LLM) → Tool execution → Response**
The agent operates in an **interactive loop**:
1. **Human Request** → User provides input via CLI or Desktop
2. **Provider Chat** → Agent sends request + available tools list to LLM provider
3. **Model Extension Call** → LLM returns tool call request (JSON); Goose executes it
4. **Response to Model** → Tool results sent back to LLM; loop repeats if more tools needed
5. **Context Revision** → Old/irrelevant info pruned (token management)
6. **Model Response** → Final text response back to user
### Session Management
- Sessions are persisted as **JSONL files** with automatic backup and recovery
- Session history includes conversation, tool execution results, and user preferences
- Sessions survive restarts via the `session/load` mechanism
- Config stored at `~/.config/goose/config.yaml` (macOS/Linux) or `%APPDATA%\Block\goose\config\config.yaml` (Windows)
### Provider Abstraction
Goose supports multiple LLM providers through a unified `Provider` trait:
- Defined in `crates/goose/src/providers/base.rs`
- Implementations handle auth, request formatting, response streaming
- Multi-model config: supports lead/worker mode, planner model, and tool shim model
- Key config variables: `GOOSE_PROVIDER`, `GOOSE_MODEL`, `GOOSE_TEMPERATURE`
### Backend Server (goosed)
The backend `goosed` runs as a separate process, currently using a custom REST + SSE streaming API:
- **Current:** Custom SSE-based streaming consumed by the Electron desktop app
- **Upcoming (ACP migration — Issue #6642):** Replacing with Agent Communication Protocol (ACP) over HTTP
- JSON-RPC 2.0 transport
- `POST /acp/session` — Create session
- `POST /acp/session/{id}/message` — Send JSON-RPC request
- `GET /acp/session/{id}/stream` — SSE stream for all server→client messages
- Supports permission flow: server sends `request_permission`, client responds with `allow_once`/deny
### Error Handling
- Errors are captured and sent back to the model as tool responses
- Uses `anyhow::Result` throughout the Rust codebase
- Invalid JSON, missing tools, etc. → LLM gets error context to self-correct
---
## 3. Desktop UI Architecture
### Tech Stack
| Layer | Technology |
|---|---|
| Framework | **Electron** (v40.1.0) |
| Frontend | **React 19** + TypeScript |
| Build | **Vite** + **electron-forge** |
| Styling | **TailwindCSS 4** + Radix UI primitives |
| State | SWR for data fetching, React contexts |
| Markdown | react-markdown + remark-gfm + rehype-katex |
| Code Highlighting | react-syntax-highlighter |
| Icons | lucide-react + react-icons |
| API Client | Auto-generated from OpenAPI spec (`@hey-api/openapi-ts`) |
| Testing | Vitest + Playwright (E2E) + Testing Library |
| MCP UI | `@mcp-ui/client` (v5.17.3) |
### Key UI Components
| Component | File | Purpose |
|---|---|---|
| Base Chat | `BaseChat.tsx` (17.7KB) | Core chat container, message rendering |
| Chat Input | `ChatInput.tsx` (59KB) | User input with slash commands, file attachments |
| Chat Sessions | `ChatSessionsContainer.tsx` | Multi-session management |
| Elicitation | `ElicitationRequest.tsx` | MCP elicitation request UI |
| Extension Install | `ExtensionInstallModal.test.tsx` | Extension installation flow |
| Config | `ConfigContext.tsx` | App configuration context |
| Error Boundary | `ErrorBoundary.tsx` | Error handling wrapper |
| API Key Tester | `ApiKeyTester.tsx` | Provider key validation |
### Frontend-Backend Communication
1. Electron main process (`main.ts`) spawns `goosed` backend
2. React frontend communicates via generated OpenAPI client
3. Real-time streaming via SSE connections
4. IPC bridge for Electron-specific features (file dialogs, system tray, etc.)
### Deep Link Protocol
Goose supports `goose://` protocol URLs for:
- `goose://extension?cmd=...` — Install extensions
- `goose://bot` or `goose://recipe` — Launch recipes
- `goose://sessions` — Open shared sessions
### Branding Files
Key branding touchpoints for white-labeling:
- `ui/desktop/package.json``productName: "Goose"`, `name: "goose-app"`
- `ui/desktop/src/assets/` — Logo, icons, images
- `ui/desktop/src/main.ts` — Window title, tray icon, protocol handler (`goose://`)
- `ui/desktop/src/built-in-extensions.json` — Default extension list
---
## 4. MCP Integration System
### Architecture
Goose acts as an **MCP Host** managing multiple MCP clients, each connected to a server (extension):
```
┌─────────────────────────────────────┐
│ Goose Host Process │
│ ┌────────────────────────────────┐ │
│ │ Goose Application (Host) │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Client 1 │ │ Client 2 │ │ │
│ │ └────┬─────┘ └────┬─────┘ │ │
│ └──────┼──────────────┼──────────┘ │
└────────┼──────────────┼─────────────┘
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ Server 1 │ │ Server 2 │
│(Developer)│ │(Memory) │
└──────────┘ └──────────┘
```
### How Goose Discovers & Connects to MCP Servers
1. **Built-in extensions** are compiled into the `goosed` binary (defined in `crates/goose-mcp/`)
2. **Config-based extensions** are defined in `~/.config/goose/config.yaml`
3. **Deep links** install via `goose://extension?cmd=...`
4. **Extension Manager** (built-in platform extension) can dynamically discover, enable, and disable extensions during a session
5. **Smart Extension Recommendation** analyzes user tasks and suggests relevant extensions automatically
### Transport Mechanisms
| Type | Config Key | Use Case |
|---|---|---|
| **STDIO** | `type: stdio` | Local extensions (process-based, stdin/stdout) |
| **SSE** | `type: sse` | Remote HTTP-based extensions |
| **Streamable HTTP** | `type: streamablehttp` | Modern remote transport |
### Extension Config Format (config.yaml)
```yaml
extensions:
github:
name: GitHub
cmd: npx
args: [-y, @modelcontextprotocol/server-github]
enabled: true
envs: { "GITHUB_PERSONAL_ACCESS_TOKEN": "<YOUR_TOKEN>" }
type: stdio
timeout: 300
```
### MCP UI Rendering (Issue #3562 — Active Development)
Goose is building support for **rendering rich UI from MCP tool responses**:
- **Inline display**: Embedded in chat flow (data viz, previews)
- **Sidecar display**: Side panel for rich interaction (app-like experiences)
- Uses `@mcp-ui/client` library for deterministic rendering
- Tool responses include `_meta.goose.toolUI` metadata specifying display type and renderer
- Example MCP tool returning UI:
```typescript
server.tool('render_ui_inline', 'Display UI', {}, async () => {
return {
_meta: {
goose: {
toolUI: {
displayType: 'inline',
name: 'inline example',
renderer: 'mcp-ui',
},
},
},
content: [
createUIResource({
uri: 'ui://component-html',
content: { type: 'rawHtml', htmlString: '<p>Hello World</p>' },
encoding: 'text',
}),
],
};
});
```
---
## 5. Extension/Plugin System
### Extension Trait (Rust)
```rust
#[async_trait]
pub trait Extension: Send + Sync {
fn name(&self) -> &str;
fn description(&self) -> &str;
fn instructions(&self) -> &str;
fn tools(&self) -> &[Tool];
async fn status(&self) -> AnyhowResult<HashMap<String, Value>>;
async fn call_tool(&self, tool_name: &str, parameters: HashMap<String, Value>) -> ToolResult<Value>;
}
```
### Built-in Extensions
Defined in `ui/desktop/src/built-in-extensions.json`:
| ID | Name | Default Enabled | Description |
|---|---|---|---|
| `developer` | Developer | ✅ | Shell, file editing, project tools |
| `computercontroller` | Computer Controller | ❌ | Webscraping, file caching, automations |
| `memory` | Memory | ❌ | Learn and recall user preferences |
| `autovisualiser` | Auto Visualiser | ❌ | Auto-generate data visualizations |
| `tutorial` | Tutorial | ❌ | Interactive learning guides |
### Platform Extensions (Always Available)
| Extension | Purpose |
|---|---|
| Chat Recall | Search across all session history |
| Code Execution | Execute JavaScript for tool discovery |
| Extension Manager | Dynamic extension discovery/management (enabled by default) |
| Skills | Load agent skills from project/global directories (enabled by default) |
| Todo | Task tracking across sessions (enabled by default) |
### Building Custom Extensions
Extensions are MCP servers. The recommended pattern:
1. Create an MCP server (Python with `FastMCP`, TypeScript with `@modelcontextprotocol/sdk`, or Rust)
2. Define tools with `@mcp.tool()` decorator (Python) or equivalent
3. Test with MCP Inspector (`mcp dev server.py`)
4. Register in Goose via Desktop UI, CLI, config file, or deep link
### Extension Allowlist (Enterprise)
For corporate environments, administrators can restrict which extensions can be installed:
```yaml
# Deployed YAML file at GOOSE_ALLOWLIST URL
extensions:
- id: slack
command: uvx mcp_slack
- id: github
command: uvx mcp_github
```
Set via `export GOOSE_ALLOWLIST=https://example.com/goose-allowlist.yaml`
---
## 6. Permission & HITL System
### Permission Modes
| Mode | Config Value | Behavior |
|---|---|---|
| **Autonomous** | `auto` | Full autonomy, no approval needed (**default**) |
| **Manual Approval** | `approve` | Confirmation before any tool use |
| **Smart Approval** | `smart_approve` | Risk-based: auto-approve low-risk, flag high-risk |
| **Chat Only** | `chat` | No tool use, conversation only |
Configuration: `GOOSE_MODE: smart_approve` in `config.yaml`
### How Approval Works
In `approve` and `smart_approve` modes:
1. Goose classifies tools as read or write (best-effort, LLM-interpreted)
2. Write tools trigger "Allow" / "Deny" buttons in the UI
3. The server sends a `request_permission` JSON-RPC request
4. Client responds with `{ outcome: "allow_once" }` or deny
5. Only after approval does the tool execute
### Additional Safety
- `.gooseignore` file prevents access to sensitive files (`.env*`, `*.key`, `target/`, `.git/`)
- `.goosehints` provides project-specific guidance to the agent
- Prompt injection detection (`SECURITY_PROMPT_ENABLED: true`)
- ML-based classifier endpoint for advanced threat detection
- Malware scanning for external extensions before activation
- Maximum turns limit (`GOOSE_MAX_TURNS`) prevents runaway loops
---
## 7. Forking Strategy
### License: Apache 2.0
**Permissive license** — allows:
- Commercial use
- Modification and derivative works
- Distribution under different terms
- White-labeling and rebranding
- No copyleft/share-alike requirements
**Requirements:** Must include original copyright notice and license text.
**Note:** As of Dec 2025, Goose was contributed to the **Agentic AI Foundation (AAIF)** under the Linux Foundation. The Apache 2.0 license remains; governance is now community-driven.
### Key Files to Modify for White-Labeling
| What to Change | Files |
|---|---|
| **App Name & Branding** | `ui/desktop/package.json` (productName, name, description) |
| **Window Title** | `ui/desktop/src/main.ts` (BrowserWindow title) |
| **Protocol Handler** | `ui/desktop/src/main.ts` (`goose://``yourapp://`) |
| **Logo & Icons** | `ui/desktop/src/assets/` |
| **Built-in Extensions** | `ui/desktop/src/built-in-extensions.json` |
| **Default Config** | `crates/goose-server/` + `crates/goose-cli/` default values |
| **Config Directory** | Code references to `~/.config/goose/` |
| **System Prompts** | `crates/goose/src/agents/` — agent instructions |
| **Provider Defaults** | `GOOSE_PROVIDER`, `GOOSE_MODEL` defaults |
| **Update Server** | `ui/desktop/src/utils/autoUpdater.ts` |
| **Telemetry** | `GOOSE_TELEMETRY_ENABLED` and endpoints |
| **Extension Directory** | URL in extension browsing/discovery code |
### Adding Custom MCP Servers as Built-in Extensions
Two approaches:
1. **Rust built-in** (compiled into binary):
- Add to `crates/goose-mcp/src/`
- Register in the built-in server matching logic (`crates/goose-cli/src/commands/mcp.rs`)
- Update `ui/desktop/src/built-in-extensions.json`
2. **Bundled external** (shipped alongside but run as separate process):
- Include the MCP server binary/package in the distribution
- Pre-configure in default `config.yaml`
- Set `bundled: true` in extension config
### Build Process
```bash
# Setup
source bin/activate-hermit
cargo build
# Build release
cargo build --release
just release-binary
# Desktop app
just generate-openapi # After server changes
just run-ui # Development
cd ui/desktop && npm run bundle:default # Production build
```
---
## 8. Community Extensions & Patterns
### Extension Discovery
- **Official directory:** https://block.github.io/goose/extensions/ (web-based catalog)
- **Community lists:** `appcypher/awesome-mcp-servers`, `punkpeye/awesome-mcp-servers`
- **Registries:** PulseMCP, Playbooks, MCP.so
- **Dynamic discovery:** Extension Manager extension searches available servers at runtime
### Common Extension Patterns
1. **STDIO MCP servers** (most common): `uvx`, `npx`, `jbang`, `docker` launchers
2. **Python + FastMCP**: Simplest pattern for custom tools
3. **TypeScript + @modelcontextprotocol/sdk**: For Node.js ecosystem
4. **Rust**: For performance-critical or compiled extensions
### Notable Community Extensions
| Extension | Command | Purpose |
|---|---|---|
| GitHub | `npx -y @modelcontextprotocol/server-github` | GitHub API integration |
| Slack | `uvx mcp_slack` | Slack workspace access |
| Postgres | `uvx mcp-server-postgres` | Database querying |
| Fetch | `uvx mcp-server-fetch` | HTTP request making |
| Filesystem | `uv run mcp-filesystem` | File system access |
| Playwright | MCP server | Browser automation |
| Tavily Search | `npx -y mcp-tavily-search` | Web search |
| Perplexity | `npx -y mcp-perplexity-search` | AI search |
### Recipes System
Goose supports **recipes** — shareable YAML files that package entire workflows:
- Include goal, required extensions, setup steps, example activities
- Configurable via `GOOSE_RECIPE_GITHUB_REPO`
- Custom slash commands can trigger recipes
- Deep linkable: `goose://recipe?...`
### Subagents
Goose supports spawning **subagents** — isolated instances for parallel/long-running tasks:
- Keep main conversation clean
- Process isolation for experimental work
- Parallel execution of independent tasks
---
## 9. Key Findings for Custom Agent Development
### Why Fork Goose?
| Advantage | Detail |
|---|---|
| **Mature MCP integration** | Best-in-class MCP host with dynamic discovery |
| **Desktop + CLI** | Two interfaces already built |
| **Apache 2.0** | Full commercial freedom |
| **Active development** | Frequent releases, large community |
| **Multi-provider** | Works with any LLM (OpenAI, Anthropic, Google, local) |
| **Permission system** | Built-in HITL with smart approval |
| **Extension ecosystem** | Thousands of MCP servers available |
### Customization Effort Estimate
| Change | Effort | Complexity |
|---|---|---|
| Rebrand (name, logo, colors) | 1-2 days | Low |
| Custom default extensions | 1 day | Low |
| Custom system prompts | Hours | Low |
| New built-in MCP extension (Rust) | 3-5 days | Medium |
| Custom UI components in chat | 1-2 weeks | Medium-High |
| Custom approval flow | 1 week | Medium |
| Replace backend protocol (ACP) | Already in progress upstream | High |
### Desktop App is More Extensible
The Electron desktop app provides:
- Full React component library for custom UI
- Deep link protocol for integrations
- Extension install modal workflow
- MCP UI rendering (inline + sidecar) — under active development
- System tray, keyboard shortcuts, auto-updates
- Multi-window/multi-session support
The CLI is simpler but less extensible for custom UI experiences.
### Risks & Considerations
1. **Active ACP migration** (Issue #6642): The backend communication protocol is being replaced. Fork timing matters.
2. **Governance shift**: Now under AAIF/Linux Foundation — community governance may affect upstream direction
3. **Rapid iteration**: Multiple releases per week; staying in sync with upstream requires effort
4. **Electron size**: Desktop app ships Chromium (~200MB+)
5. **Rust build times**: Full build can be slow; incremental builds are faster
---
## 10. Sources
| Source | URL |
|---|---|
| GitHub Repository | https://github.com/block/goose |
| Official Documentation | https://block.github.io/goose/ |
| AGENTS.md (repo structure) | https://raw.githubusercontent.com/block/goose/main/AGENTS.md |
| HOWTOAI.md (architecture) | https://raw.githubusercontent.com/block/goose/main/HOWTOAI.md |
| GOVERNANCE.md | https://raw.githubusercontent.com/block/goose/main/GOVERNANCE.md |
| Architecture Overview | https://block.github.io/goose/docs/goose-architecture/ |
| Extensions Design | https://block.github.io/goose/docs/goose-architecture/extensions-design/ |
| Config Files Guide | https://block.github.io/goose/docs/guides/config-files/ |
| Permission Modes | https://block.github.io/goose/docs/guides/goose-permissions/ |
| Using Extensions | https://block.github.io/goose/docs/getting-started/using-extensions/ |
| Extension Allowlist | https://block.github.io/goose/docs/guides/allowlist/ |
| MCP UI Issue #3562 | https://github.com/block/goose/issues/3562 |
| ACP Migration Issue #6642 | https://github.com/block/goose/issues/6642 |
| Block Announcement | https://block.xyz/inside/block-open-source-introduces-codename-goose |
| AAIF Launch | https://www.linuxfoundation.org/press/linux-foundation-announces-the-formation-of-the-agentic-ai-foundation |
| Extension Deep Dive (dev.to) | https://dev.to/lymah/deep-dive-into-gooses-extension-system-and-model-context-protocol-mcp-3ehl |
| Dynamic MCP Discovery | https://dev.to/amandamartindev/dynamic-mcp-server-discovery-with-goose-3m41 |
| What is Block Goose | https://jeffbailey.us/blog/2025/07/29/what-is-block-goose/ |
| Docker + Goose | https://www.docker.com/blog/building-ai-agents-with-goose-and-docker/ |
| Extensions Directory | https://block.github.io/goose/extensions/ |
| Desktop package.json | https://raw.githubusercontent.com/block/goose/main/ui/desktop/package.json |
| Built-in Extensions JSON | https://raw.githubusercontent.com/block/goose/main/ui/desktop/src/built-in-extensions.json |
| DeepWiki Overview | https://deepwiki.com/block/goose |