How MCP Integration Enables Agent Actions in Macro: Protocol Architecture and Implementation
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. 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, 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.
// 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 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, 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.
// 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, 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.
// 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 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.
// 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:
- Authentication: The MCP client authenticates via the OAuth broker (
services/mcp_auth_proxy) and receives a short-lived token. - Request Reception: The client sends a streamable MCP request (JSON-Lines) to the
/mcpendpoint handled by the server inmain.rs. - Tool Dispatch: The
ToolServiceparses the request, validates permissions against the Redis-backed auth proxy, and invokes the corresponding Rust implementation. - Side Effect Execution: The tool performs operations—writing to PostgreSQL, pushing to S3, or emitting messages—using the same pipelines as internal services.
- Response Streaming: Results encode back into the MCP wire format and stream to the client, influenced by the prompt instructions in
mcp_item_links.rsfor proper link formatting.
Summary
- The MCP server in
services/mcp_service/src/main.rsexposes Macro's AI tools via a streamable HTTP API, binding to configurable addresses and loading environment-specific configuration fromservices/mcp_service/src/config.rs. - ToolService in
services/mcp_service/src/tool_service.rsroutes 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.rsensures 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.
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 and 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 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) 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.
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 →