Hook up a custom MCP server

Aroovo bootstraps every Tenant with Zapier MCP for breadth, but the runtime is provider-agnostic — any URL implementing the Model Context Protocol can be registered alongside. This is the path for connecting Composio, Cloudflare Workers MCP, an internal Python MCP service, or your own stack.

SSRF safety. The platform validates the MCP URL against the SSRF block list before any outbound call: private IPv4 / IPv6 addresses, loopback names (localhost, 127.0.0.1), and non-HTTPS URLs in production all reject. If you need to point at a non-public endpoint, expose it via a public hostname (Cloudflare Tunnel, ngrok with a paid domain) before registering.

1. Confirm the MCP server is reachable

From any internet-facing host, hit the MCP server's SSE endpoint and confirm it responds to the standard handshake:

curl -N -H 'Accept: text/event-stream' \
  https://mcp.example.com/sse

2. Register with Aroovo

As Owner or Admin of the target Tenant:

POST /api/mcp-servers
{
  "name": "Composio HubSpot",
  "url": "https://mcp.composio.dev/v1/<account>/sse"
}

200 OK
{
  "id": "mcp_07",
  "name": "Composio HubSpot",
  "url": "https://mcp.composio.dev/v1/<account>/sse",
  "active": true,
  "toolCount": 24
}

On success, the platform:

  • Opened the MCP connection and introspected the tool list.
  • Compiled Ajv validators for every tool input schema (per the security hardening pass in ADR-001).
  • Wrote an McpServer row scoped to the Tenant.
  • Wrote a secret.created audit row if the server URL carried an API token in its path.

3. Grant agent access

Newly-registered MCP servers are not automatically allowlisted for any agent. Flip them on per agent:

PUT /api/agents/sales/mcp-servers/mcp_07
{ "allowed": true }

200 OK
{ "serverId": "mcp_07", "allowed": true }

4. Inspect the resolved tool catalogue

GET /api/agents/sales/mcp-tools

200 OK
{
  "items": [
    {
      "name": "composio:hubspot_create_contact",
      "description": "Create a HubSpot contact",
      "approvalPolicy": "single_approval"
    },
    ...
  ]
}

Tool names are namespaced by the server slug (the lowercased, slugified name from registration). The default approval policy for any new MCP server is single_approval for tools with side effects and no_gate for read-only lookups; the heuristic is driven by HTTP verb + tool name match. Override per tool:

PATCH /api/mcp-servers/mcp_07/tools/hubspot_create_contact
{ "approvalPolicy": "dual_approval" }

5. Test the call path end-to-end

From the agent's chat: “Create a HubSpot contact for Jane Doe at jane@example.com.” The agent loop will:

  1. Emit a tool_use for composio:hubspot_create_contact.
  2. Validate the args against the compiled Ajv schema. Invalid args become a synthetic is_error tool result and never hit the upstream server.
  3. Pause and write an Approval row because the policy is single_approval. Approve from /approvals.
  4. Dispatch the call. The MCP server's response is written into the conversation as a tool_result; the model continues with the new context.
  5. A row lands in the audit chain at each step (agent.tool_use, approval.opened, approval.resolved, agent.tool_result).

6. Disable or remove

PATCH /api/mcp-servers/mcp_07
{ "active": false }

DELETE /api/mcp-servers/mcp_07

Disabled servers stop accepting new calls but keep their audit history; deleted servers are removed but the audit rows referencing them remain (and are still HMAC-verifiable).

Full conceptual background: Concepts — MCP tools & approvals, ADR-001 — Self-hosted runtime + MCP.