> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/block/goose/llms.txt
> Use this file to discover all available pages before exploring further.

# Extensions

> Understanding the MCP extension system and how to add tools to Goose

Extensions bring external capabilities to Goose through the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/). They provide tools, resources, and prompts that the AI agent can use to interact with files, APIs, databases, and more.

## What are Extensions?

Extensions in Goose are MCP servers that provide:

* **Tools**: Functions the agent can call (read files, run commands, query APIs)
* **Resources**: Context the agent can access (file contents, documentation)
* **Prompts**: Reusable prompt templates

Each extension runs as an independent process, providing isolation and security.

```mermaid theme={null}
graph LR
    Agent[Goose Agent] --> ExtMgr[Extension Manager]
    ExtMgr --> DevExt[developer<br/>File & Shell Tools]
    ExtMgr --> MemExt[memory<br/>Knowledge Storage]
    ExtMgr --> WebExt[brave-search<br/>Web Search]
    ExtMgr --> CustomExt[Custom Extension<br/>Your Tools]
    
    DevExt -.->|stdio| DevProcess[Python/Node Process]
    MemExt -.->|in-process| MemServer[Built-in Server]
    WebExt -.->|stdio| WebProcess[NPX Process]
    CustomExt -.->|SSE/stdio| CustomProcess[Your Server]
```

## Extension Types

Goose supports three types of extensions:

### 1. Built-in Extensions

Bundled with Goose, run in-process for performance:

```yaml theme={null}
extensions:
  - type: builtin
    name: developer
    description: File operations and shell commands
```

Available built-in extensions:

<CardGroup cols={2}>
  <Card title="developer" icon="code">
    File operations, shell commands, and development tools

    * Read/write files
    * Execute shell commands
    * Search codebases
  </Card>

  <Card title="memory" icon="brain">
    Persistent knowledge storage across sessions

    * Store facts and learnings
    * Retrieve relevant context
    * Build knowledge graphs
  </Card>

  <Card title="computercontroller" icon="desktop">
    Desktop automation (macOS)

    * Control mouse and keyboard
    * Take screenshots
    * UI automation
  </Card>

  <Card title="autovisualiser" icon="chart-line">
    Chart and visualization generation

    * Create charts from data
    * Generate plots
    * Data visualization
  </Card>
</CardGroup>

### 2. Stdio Extensions

External processes communicating via stdin/stdout:

```yaml theme={null}
extensions:
  - type: stdio
    name: brave-search
    cmd: npx
    args: ["-y", "@modelcontextprotocol/server-brave-search"]
    env:
      BRAVE_API_KEY: "${BRAVE_API_KEY}"  # Environment variable expansion
```

Most third-party MCP servers use stdio transport.

### 3. SSE Extensions

HTTP-based extensions using Server-Sent Events:

```yaml theme={null}
extensions:
  - type: sse
    name: remote-tools
    url: https://mcp.company.internal/tools
    headers:
      Authorization: "Bearer ${API_TOKEN}"
```

Useful for:

* Remote MCP servers
* Cloud-hosted tools
* Internal enterprise services

## Extension Configuration

### Basic Configuration

```yaml theme={null}
extensions:
  - type: stdio
    name: github
    cmd: uvx
    args: ["mcp-server-github"]
    env:
      GITHUB_TOKEN: "${GITHUB_TOKEN}"
    description: "GitHub API access"
```

### Advanced Options

```yaml theme={null}
extensions:
  - type: stdio
    name: database-tools
    cmd: python
    args: ["/path/to/db_server.py"]
    
    # Environment variables
    env:
      DB_HOST: "localhost"
      DB_PORT: "5432"
      DB_PASSWORD: "${DB_PASSWORD}"  # From secrets
    
    # Working directory
    cwd: /opt/database-tools
    
    # Timeout (milliseconds)
    timeout: 30000
    
    # OAuth configuration
    oauth:
      client_id: "${OAUTH_CLIENT_ID}"
      client_secret: "${OAUTH_CLIENT_SECRET}"
      scopes: ["read:data", "write:data"]
```

## Extension Manager

The Extension Manager handles the lifecycle of all extensions:

```rust theme={null}
// From crates/goose/src/agents/extension_manager.rs
pub struct ExtensionManager {
    extensions: Mutex<HashMap<String, Extension>>,
    tools_cache: Mutex<Option<Arc<Vec<Tool>>>>,
    capabilities: ExtensionManagerCapabilities,
}

impl ExtensionManager {
    /// Load and initialize an extension
    pub async fn load_extension(
        &self,
        config: ExtensionConfig,
    ) -> Result<ExtensionInfo> {
        // 1. Validate configuration
        config.validate()?;
        
        // 2. Start MCP server process
        let client = self.create_mcp_client(&config).await?;
        
        // 3. Initialize MCP protocol
        let server_info = client.initialize().await?;
        
        // 4. Discover tools and resources
        let tools = client.list_tools().await?;
        
        // 5. Cache extension
        self.extensions.lock().await
            .insert(config.name.clone(), Extension {
                config,
                client,
                server_info,
            });
        
        Ok(ExtensionInfo { /* ... */ })
    }
    
    /// Call a tool from an extension
    pub async fn call_tool(
        &self,
        tool_name: &str,
        arguments: Value,
    ) -> Result<ToolResult> {
        // Find extension that provides this tool
        let extension = self.find_extension_for_tool(tool_name)?;
        
        // Call via MCP protocol
        let result = extension.client
            .call_tool(CallToolRequestParams {
                name: tool_name.to_string(),
                arguments: Some(arguments),
            })
            .await?;
        
        Ok(result)
    }
}
```

## Creating Custom Extensions

### MCP Server Basics

An MCP server exposes tools, resources, and prompts:

```python theme={null}
# example_server.py
from mcp.server import Server
from mcp.types import Tool, TextContent
import asyncio

# Create server
server = Server("example-tools")

# Define a tool
@server.tool()
async def search_database(query: str, limit: int = 10) -> list[TextContent]:
    """Search the company database.
    
    Args:
        query: Search query string
        limit: Maximum results to return
    """
    # Your implementation
    results = await db.search(query, limit=limit)
    
    return [TextContent(
        type="text",
        text=f"Found {len(results)} results: {results}"
    )]

# Define a resource
@server.resource("database://schema")
async def get_schema() -> str:
    """Return the database schema."""
    return await db.get_schema()

# Run server
if __name__ == "__main__":
    asyncio.run(server.run())
```

### Tool Definition

Tools are defined with JSON Schema parameters:

```python theme={null}
@server.tool()
async def process_payment(
    amount: float,
    currency: str,
    customer_id: str,
) -> list[TextContent]:
    """Process a payment transaction.
    
    Args:
        amount: Payment amount
        currency: Three-letter currency code (USD, EUR, etc.)
        customer_id: Unique customer identifier
    """
    # Validation
    if amount <= 0:
        raise ValueError("Amount must be positive")
    
    # Process payment
    result = payment_processor.charge(
        amount=amount,
        currency=currency,
        customer=customer_id,
    )
    
    return [TextContent(
        type="text",
        text=f"Payment processed: {result.transaction_id}"
    )]
```

The MCP server automatically generates JSON Schema from Python type hints:

```json theme={null}
{
  "name": "process_payment",
  "description": "Process a payment transaction.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "amount": {"type": "number"},
      "currency": {"type": "string"},
      "customer_id": {"type": "string"}
    },
    "required": ["amount", "currency", "customer_id"]
  }
}
```

### Using the Extension

```yaml theme={null}
# payment-recipe.yaml
title: Payment Processor
extensions:
  - type: stdio
    name: payment-tools
    cmd: python
    args: ["example_server.py"]
    env:
      PAYMENT_API_KEY: "${PAYMENT_API_KEY}"

instructions: |
  You can process payments using the payment-tools extension.
  Always confirm amounts and customer IDs before processing.
```

## Built-in Extension: Developer

The most commonly used extension provides file and shell tools:

```rust theme={null}
// From crates/goose-mcp/src/computercontroller/developer.rs

// Available tools:
// - read_file: Read file contents
// - write_file: Write/create files  
// - edit_file: Apply diffs to files
// - list_directory: List directory contents
// - search_files: Search file contents
// - execute_command: Run shell commands
// - get_file_info: Get file metadata
```

Example usage by the agent:

```json theme={null}
// Agent calls read_file
{
  "name": "read_file",
  "arguments": {
    "path": "src/main.rs"
  }
}

// Returns file contents
{
  "content": [
    {
      "type": "text",
      "text": "fn main() {\n    println!(\"Hello, world!\");\n}"
    }
  ]
}
```

## Extension Security

### Environment Variable Protection

Goose blocks dangerous environment variables:

```rust theme={null}
// From crates/goose/src/agents/extension.rs
impl Envs {
    const DISALLOWED_KEYS: [&'static str; 31] = [
        "PATH",              // Prevent binary hijacking
        "LD_LIBRARY_PATH",   // Prevent library injection
        "PYTHONPATH",        // Prevent module hijacking
        "NODE_OPTIONS",      // Prevent Node.js injection
        // ... 27 more dangerous variables
    ];
}
```

Attempting to set these variables will fail with an error.

### Permission System

Tools can require user confirmation:

```yaml theme={null}
# ~/.config/goose/config.yaml
permissions:
  execute_command: confirm    # Always ask before running commands
  read_file: allow            # Auto-allow file reads
  delete_file: deny           # Never allow deletions
```

### Malware Scanning

Goose scans downloaded extensions:

```rust theme={null}
// From crates/goose/src/agents/extension_malware_check.rs

pub async fn check_extension_safety(
    extension_path: &Path,
) -> Result<SafetyCheckResult> {
    // Check for:
    // - Known malware signatures
    // - Suspicious patterns
    // - Dangerous system calls
    // - Network activity to unknown hosts
}
```

### Process Isolation

Each extension runs in a separate process:

```rust theme={null}
// stdio extensions run as child processes
let mut child = Command::new(&config.cmd)
    .args(&config.args)
    .env_clear()  // Start with clean environment
    .envs(&safe_env_vars)  // Only add safe variables
    .stdin(Stdio::piped())
    .stdout(Stdio::piped())
    .stderr(Stdio::piped())
    .spawn()?;
```

## Resources (MCP)

Extensions can provide resources that the agent reads:

```python theme={null}
# Resource example
@server.resource("file:///{path}")
async def read_file_resource(path: str) -> str:
    """Read a file as a resource."""
    with open(path, 'r') as f:
        return f.read()

@server.resource("docs://api-reference")
async def get_api_docs() -> str:
    """Return API documentation."""
    return load_api_documentation()
```

The agent can access resources:

```rust theme={null}
// Agent requests resource
let resource = extension_manager
    .read_resource("docs://api-reference")
    .await?;

// Include in context
system_prompt += format!("\n\nAPI Documentation:\n{}", resource.contents);
```

## Prompts (MCP)

Extensions can provide reusable prompts:

````python theme={null}
@server.prompt("code-review")
async def code_review_prompt(file_path: str) -> str:
    """Generate a code review prompt."""
    code = await read_file(file_path)
    return f"""
Review this code for:
1. Security issues
2. Performance problems  
3. Code style

```{file_path}
{code}
````

"""

````

The agent or user can invoke prompts:

```rust
let prompt = extension_manager
    .get_prompt("code-review", args! {"file_path": "src/auth.rs"})
    .await?;
````

## Extension Discovery

Goose discovers extensions from:

1. **Built-in**: Bundled extensions
2. **Config file**: `~/.config/goose/config.yaml`
3. **Recipe**: Defined in recipe YAML
4. **Runtime**: Dynamically added via API

```rust theme={null}
// Get all available tools
let tools = extension_manager.get_tools().await?;

// Tools are cached and versioned for performance
// Cache invalidated when extensions change
```

## Dynamic Extension Management

Extensions can be loaded/unloaded at runtime:

```rust theme={null}
// Load extension
let info = extension_manager.load_extension(ExtensionConfig {
    extension_type: ExtensionType::Stdio,
    name: "new-tool".to_string(),
    cmd: "python".to_string(),
    args: vec!["tool.py".to_string()],
    env: HashMap::new(),
    // ...
}).await?;

// Unload extension  
extension_manager.unload_extension("new-tool").await?;

// List loaded extensions
let extensions = extension_manager.list_extensions().await?;
```

## OAuth Extensions

Extensions can use OAuth for authentication:

```yaml theme={null}
extensions:
  - type: stdio
    name: google-drive
    cmd: npx
    args: ["-y", "@modelcontextprotocol/server-gdrive"]
    oauth:
      client_id: "${GOOGLE_CLIENT_ID}"
      client_secret: "${GOOGLE_CLIENT_SECRET}"
      scopes:
        - "https://www.googleapis.com/auth/drive.readonly"
      auth_url: "https://accounts.google.com/o/oauth2/v2/auth"
      token_url: "https://oauth2.googleapis.com/token"
```

Goose handles the OAuth flow automatically:

1. Opens browser for user authentication
2. Exchanges code for tokens
3. Stores tokens securely
4. Refreshes tokens when expired

## Best Practices

<AccordionGroup>
  <Accordion title="Keep tools focused and composable">
    Each tool should do one thing well:

    ```python theme={null}
    # Good: Focused tools
    @server.tool()
    async def read_file(path: str): ...

    @server.tool()
    async def write_file(path: str, content: str): ...

    # Bad: Too many responsibilities
    @server.tool()
    async def file_operations(operation: str, path: str, content: str): ...
    ```
  </Accordion>

  <Accordion title="Provide clear descriptions">
    The AI uses descriptions to choose tools:

    ```python theme={null}
    @server.tool()
    async def search_code(query: str, language: str = "all") -> list[TextContent]:
        """Search code files for a pattern.
        
        Use this tool to find specific code patterns, function definitions,
        or variable usages across the codebase. Supports regex patterns.
        
        Args:
            query: Regex pattern to search for
            language: Filter by language (python, rust, javascript, or 'all')
        
        Returns:
            List of file paths and matching lines
        """
    ```
  </Accordion>

  <Accordion title="Handle errors gracefully">
    Return useful error messages:

    ```python theme={null}
    @server.tool()
    async def deploy_service(service_name: str) -> list[TextContent]:
        try:
            result = await deployer.deploy(service_name)
            return [TextContent(type="text", text=f"Deployed {service_name}: {result}")]
        except ServiceNotFound:
            return [TextContent(
                type="text",
                text=f"Error: Service '{service_name}' not found. Available services: {list_services()}"
            )]
        except DeploymentError as e:
            return [TextContent(
                type="text",
                text=f"Deployment failed: {e}. Check logs at /var/log/deploy/{service_name}.log"
            )]
    ```
  </Accordion>

  <Accordion title="Use resources for static context">
    Resources are more efficient than tools for static data:

    ```python theme={null}
    # Good: Use resource for API docs
    @server.resource("docs://api")
    async def api_docs() -> str:
        return STATIC_API_DOCUMENTATION

    # Bad: Tool for static data
    @server.tool()
    async def get_api_docs() -> list[TextContent]:
        return [TextContent(type="text", text=STATIC_API_DOCUMENTATION)]
    ```
  </Accordion>

  <Accordion title="Validate inputs thoroughly">
    Don't trust AI-generated arguments:

    ```python theme={null}
    @server.tool()
    async def delete_files(pattern: str) -> list[TextContent]:
        # Validate dangerous operations
        if pattern in ["/", "/*", "*"]:
            raise ValueError("Refusing to delete all files")
        
        # Validate paths are within allowed directories
        if not is_safe_path(pattern):
            raise ValueError(f"Path {pattern} is outside allowed directories")
        
        # Proceed with operation
        deleted = delete_matching_files(pattern)
        return [TextContent(type="text", text=f"Deleted {len(deleted)} files")]
    ```
  </Accordion>
</AccordionGroup>

## Troubleshooting

### Common Issues

**"Extension failed to initialize"**

```bash theme={null}
# Check extension logs
goose logs --extension developer

# Test extension directly
python your_server.py  # Should not crash
```

**"Tool not found"**

```bash theme={null}
# List available tools
goose extensions list-tools

# Reload extension
goose extensions reload developer
```

**"Permission denied"**

```yaml theme={null}
# Check permission settings
permissions:
  tool_name: allow  # or confirm/deny
```

**"Extension timeout"**

```yaml theme={null}
# Increase timeout
extensions:
  - type: stdio
    name: slow-tool
    timeout: 60000  # 60 seconds
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Recipes" icon="book" href="/concepts/recipes">
    Configure extensions in recipes
  </Card>

  <Card title="MCP Documentation" icon="book-open" href="https://modelcontextprotocol.io/">
    Learn the MCP specification
  </Card>

  <Card title="Example Extensions" icon="code" href="/guides/creating-extensions">
    Build your own MCP servers
  </Card>

  <Card title="Built-in Extensions" icon="puzzle-piece" href="/api/extensions/builtin">
    Reference for bundled extensions
  </Card>
</CardGroup>
