# How MCP Integration Enables Agent Actions in Macro: Protocol Architecture and Implementation

> Discover how MCP integration unlocks agent actions in Macro. Explore the protocol architecture and implementation enabling external HTTP clients to leverage Macro's AI toolset.

- Repository: [Macro/macro](https://github.com/macro-inc/macro)
- Tags: architecture
- Published: 2026-08-17

---

**The Macro platform exposes its internal AI toolset to external agents through the Model Context Protocol (MCP), enabling HTTP-connected clients to invoke the same Rust-based tools as internal micro-services via a shared ToolService and unified agent context.**

The `macro-inc/macro` repository implements MCP integration as a protocol-agnostic bridge that makes Macro's AI capabilities accessible to external consumers. By re-using the identical `ToolService` and `AgentLoop` components that power internal services, the MCP server ensures that external agents perform actions—such as creating documents or sending messages—with the same side-effects and security guarantees as native micro-services.

## MCP Server Architecture and Configuration

The entry point for all MCP traffic resides in [`services/mcp_service/src/main.rs`](https://github.com/macro-inc/macro/blob/main/services/mcp_service/src/main.rs). This binary initializes a streamable HTTP server that binds to a configurable address and loads environment-specific settings to prepare the service for external agent connections.

### Configuration Loading and Environment Setup

The server configuration, defined in [`services/mcp_service/src/config.rs`](https://github.com/macro-inc/macro/blob/main/services/mcp_service/src/config.rs), loads critical environment variables including `MCP_PUBLIC_URL`, Redis connection strings, and OAuth credentials. These values establish the public base URL for resource linking and configure the authentication broker that validates incoming MCP clients before they can invoke tools.

```rust
// Conceptual structure based on services/mcp_service/src/config.rs
pub struct McpConfig {
    pub public_url: String,
    pub redis_url: String,
    pub oauth_credentials: OAuthCreds,
}

```

## Bridging Protocol-Agnostic Tools to MCP

The [`services/mcp_service/src/context.rs`](https://github.com/macro-inc/macro/blob/main/services/mcp_service/src/context.rs) file establishes the runtime bridge between generic MCP protocol requests and Macro's concrete service implementations. This module validates the `MCP_PUBLIC_URL`, initializes the Redis-backed auth proxy, and constructs the MCP-specific `ToolService` instance that will handle all incoming tool invocations.

### The ToolService Dispatch Layer

Located in [`services/mcp_service/src/tool_service.rs`](https://github.com/macro-inc/macro/blob/main/services/mcp_service/src/tool_service.rs), the **ToolService** serves as the central dispatcher for all MCP-exposed tools. When an agent invokes a tool such as `create_document` or `send_user_message`, the `ToolService` forwards the request to the identical Rust functions used by internal services, ensuring zero behavioral divergence between external and internal agents.

```rust
// Conceptual implementation based on services/mcp_service/src/tool_service.rs
impl ToolService {
    pub async fn dispatch(
        &self,
        tool_name: &str,
        args: serde_json::Value,
    ) -> Result<ToolOutput> {
        // Routes to the same implementation used by internal micro-services
        match tool_name {
            "create_document" => create_document(&self.ctx, args).await,
            "send_user_message" => send_user_message(&self.ctx, args).await,
            _ => Err(Error::UnknownTool),
        }
    }
}

```

## Consistent Agent Context Across Services

Macro ensures behavioral parity between internal and external agents through shared context wiring. In [`services/document_storage_service/src/main.rs`](https://github.com/macro-inc/macro/blob/main/services/document_storage_service/src/main.rs), internal services construct an `AgentLoop` containing `ai_tools::all_tools()`—the same toolset exposed via MCP.

### AgentLoop Integration

The MCP server re-uses this pattern, injecting the shared `ToolService` into its request handlers. This design eliminates code duplication and guarantees that MCP-connected agents operate within the same permission boundaries and business logic as internal services.

```rust
// Pattern derived from services/mcp_service/src/main.rs 
// and services/document_storage_service/src/main.rs
let macro_agent_tools = ai_tools::all_tools();
let tool_context = ToolService::new(macro_agent_tools);
let agent = AgentLoop::new(tool_context.recorder.clone())
    .with_model("gpt-4");

```

## MCP-Specific Model Behavior

To accommodate external clients that cannot render in-app markup, the [`crates/prompt/src/mcp_item_links.rs`](https://github.com/macro-inc/macro/blob/main/crates/prompt/src/mcp_item_links.rs) crate provides protocol-specific instruction snippets. These directives force the model to use plain URLs instead of custom tags like `<m-document-mention>` when communicating with MCP clients, ensuring the output is renderable in external environments.

```rust
// From crates/prompt/src/mcp_item_links.rs
pub const MCP_ITEM_LINKS: &str = r#"
When replying to an MCP client, always link Macro items using plain URLs:
e.g. `https://app.macro.com/documents/12345`.
Do NOT use in-app `<m-document-mention>` tags—they are invisible to the MCP client.
"#;

```

## Execution Flow: From HTTP Request to Tool Invocation

The complete lifecycle of an MCP-enabled agent action follows these stages:

1. **Authentication**: The MCP client authenticates via the OAuth broker (`services/mcp_auth_proxy`) and receives a short-lived token.
2. **Request Reception**: The client sends a streamable MCP request (JSON-Lines) to the `/mcp` endpoint handled by the server in [`main.rs`](https://github.com/macro-inc/macro/blob/main/main.rs).
3. **Tool Dispatch**: The `ToolService` parses the request, validates permissions against the Redis-backed auth proxy, and invokes the corresponding Rust implementation.
4. **Side Effect Execution**: The tool performs operations—writing to PostgreSQL, pushing to S3, or emitting messages—using the same pipelines as internal services.
5. **Response Streaming**: Results encode back into the MCP wire format and stream to the client, influenced by the prompt instructions in [`mcp_item_links.rs`](https://github.com/macro-inc/macro/blob/main/mcp_item_links.rs) for proper link formatting.

## Summary

- The MCP server in [`services/mcp_service/src/main.rs`](https://github.com/macro-inc/macro/blob/main/services/mcp_service/src/main.rs) exposes Macro's AI tools via a streamable HTTP API, binding to configurable addresses and loading environment-specific configuration from [`services/mcp_service/src/config.rs`](https://github.com/macro-inc/macro/blob/main/services/mcp_service/src/config.rs).
- **ToolService** in [`services/mcp_service/src/tool_service.rs`](https://github.com/macro-inc/macro/blob/main/services/mcp_service/src/tool_service.rs) routes MCP tool calls to the identical Rust implementations used by internal micro-services, ensuring behavioral consistency.
- **Shared agent context** through `ai_tools::all_tools()` allows MCP-connected agents to perform actions—such as creating documents or sending messages—without duplicated code paths.
- **MCP-specific prompting** in [`crates/prompt/src/mcp_item_links.rs`](https://github.com/macro-inc/macro/blob/main/crates/prompt/src/mcp_item_links.rs) ensures model outputs use plain URLs compatible with external clients rather than internal markup tags.
- The OAuth broker and Redis-backed auth proxy provide security boundaries for external agent access while maintaining the same permission models as internal services.

## Frequently Asked Questions

### What is the Model Context Protocol (MCP) in Macro?

The Model Context Protocol (MCP) in Macro is a communication layer that exposes the platform's internal AI toolset over standard HTTP. It allows external AI agents to authenticate and invoke tools—such as document creation or messaging—using the same Rust-based implementations that internal micro-services use, as configured in [`services/mcp_service/src/context.rs`](https://github.com/macro-inc/macro/blob/main/services/mcp_service/src/context.rs).

### How does Macro ensure consistency between internal and MCP-connected agents?

Macro ensures consistency by re-using the **ToolService** and **AgentLoop** components across all services. Whether an agent connects via MCP or runs as an internal micro-service, both execute identical code paths defined in [`services/mcp_service/src/tool_service.rs`](https://github.com/macro-inc/macro/blob/main/services/mcp_service/src/tool_service.rs) and [`services/document_storage_service/src/main.rs`](https://github.com/macro-inc/macro/blob/main/services/document_storage_service/src/main.rs), ensuring identical side-effects and permission checks.

### How does Macro handle item linking differently for MCP clients versus internal clients?

For MCP clients, Macro injects specific instructions from [`crates/prompt/src/mcp_item_links.rs`](https://github.com/macro-inc/macro/blob/main/crates/prompt/src/mcp_item_links.rs) that force the model to use plain HTTP URLs (e.g., `https://app.macro.com/documents/12345`). Internal clients receive markup tags like `<m-document-mention>`, which the MCP-specific instructions explicitly prohibit to ensure compatibility with external rendering environments that cannot parse custom XML tags.

### What security mechanisms protect MCP endpoints in Macro?

The MCP server validates clients through an OAuth broker referenced in the configuration ([`services/mcp_service/src/config.rs`](https://github.com/macro-inc/macro/blob/main/services/mcp_service/src/config.rs)) and uses a Redis-backed auth proxy for session management. These mechanisms enforce the same permission boundaries applied to internal services, ensuring MCP-connected agents cannot access resources beyond their authorized scope.