Resolving the “Can’t Connect to the MCP Server” Error in callin.io on CapRover
Context
I ran into the “can’t connect to the MCP server” error while using callin.io on a CapRover deployment. Specifically, a workflow involving an AI agent was attempting to connect to an MCP server (configured via the MCP Server Trigger in another workflow) using the MCP Client Tool. This error occurred consistently, preventing proper communication between the workflows.
After some investigation, I suspected that NGINX’s gzip compression was interfering with Server-Sent Events (SSE), which the MCP server relies on. CapRover, utilizing NGINX as its reverse proxy, seemed to be the source of the problem, but pinpointing the exact location for the fix was challenging.
Problem
The error message displayed was:
McpClientTool: Failed to connect to MCP Server
The AI agent would repeatedly attempt to connect to the MCP server, entering a loop due to the connection failure. The issue appeared to stem from CapRover’s default NGINX configuration, which likely applied gzip compression or lacked adequate SSE support for the MCP routes. This resulted in the SSE connection failing, leading to the observed error.
Environment
- callin.io Version: Latest (as of June 2025)
- Deployment: CapRover on a cloud server
- Operating System: Ubuntu (via CapRover’s Docker setup)
- MCP Setup: MCP Server Trigger in one workflow, MCP Client Tool in another
Solution
The resolution involved customizing the NGINX configuration for the callin.io app within CapRover. The goal was to disable gzip compression and ensure proper SSE support for the MCP routes. Here’s the process I followed:
Steps
- Access the CapRover Dashboard: Log in to your CapRover dashboard (e.g.,
https://captain.yourdomain.com
). -
Edit callin.io App NGINX Config:
- Navigate to the “Apps” section and select your callin.io app.
- Locate and select the “Edit Default Nginx config” option within the app’s settings.
- Add MCP-Specific Configuration:
- CapRover provides a default NGINX template that includes placeholders like
<%-s.publicDomain%>
. You need to add alocation /mcp/
block to manage the MCP routes. - Insert this new block after the existing
location /
block and before the locations for Let’s Encrypt and CapRover health checks (/.well-known/acme-challenge/
and/.well-known/captain-identifier/
). - The following snippet shows the relevant part of the modified
server
block:
server {
<%
if (!s.forceSsl) {
%>
listen 80;
<%
}
if (s.hasSsl) {
%>
listen 443 ssl http2;
ssl_certificate <%-s.crtPath%>;
ssl_certificate_key <%-s.keyPath%>;
<%
}
%>
client_max_body_size 500m;
server_name <%-s.publicDomain%>;
resolver 127.0.0.11 valid=10s;
set $upstream http://<%-s.localDomain%>:<%-s.containerHttpPort%>;
location / {
<% if (s.redirectToPath) { %>
return 302 <%-s.redirectToPath%>$request_uri;
<% } else { %>
<% if (s.httpBasicAuthPath) { %>
auth_basic "Restricted Access";
auth_basic_user_file <%-s.httpBasicAuthPath%>;
<% } %>
proxy_pass $upstream;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
<% if (s.websocketSupport) { %>
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_http_version 1.1;
<% } %>
<% } %>
}
# Custom configuration for MCP routes
location /mcp/ {
gzip off;
proxy_pass http://<%-s.localDomain%>:<%-s.containerHttpPort%>;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
# Used by Lets Encrypt
location /.well-known/acme-challenge/ {
root <%-s.staticWebRoot%>;
}
# Used by CapRover for health check
location /.well-known/captain-identifier {
root <%-s.staticWebRoot%>;
}
error_page 502 /captain_502_custom_error_page.html;
location = /captain_502_custom_error_page.html {
root <%-s.customErrorPagesDirectory%>;
internal;
}
}
- Save and Update:
- Paste the adjusted configuration into the “Edit Default Nginx config” field.
- Click “Save Configuration & Update” to apply the changes and restart the callin.io app.
- Test the Workflow:
- Rerun the workflow that uses the MCP Client Tool and MCP Server Trigger. The error should now be resolved, and the connection should function correctly.
Why This Works
- Disabling Gzip: The
gzip off;
directive instructs NGINX not to compress responses for MCP routes. This is crucial as gzip compression can interfere with SSE connections. - SSE Support: Directives such as
proxy_http_version 1.1;
,proxy_set_header Connection "";
,proxy_buffering off;
, andproxy_cache off;
ensure that NGINX handles SSE connections appropriately, maintaining a stable, persistent connection. - Timeout Settings:
proxy_read_timeout 3600s;
andproxy_send_timeout 3600s;
are configured to allow long-lived SSE connections to remain open without timing out.
Additional Notes
- Verify MCP Routes: If your MCP server utilizes different URL paths (e.g.,
/sse
or/webhook/*
), ensure you modify thelocation /mcp/
path in the NGINX configuration to match. - EXECUTIONS_MODE: Confirm that callin.io’s
EXECUTIONS_MODE
is set toown
(notqueue
) to facilitate direct SSE management. This setting can be verified in your callin.io configuration, typically within the.env
file or the callin.io settings interface. - Troubleshooting: If the problem persists, examine the callin.io logs for more detailed error messages. You can also verify the compiled NGINX configuration within CapRover’s Docker environment by checking the path
/captain/generated/nginx
. - Resources: This solution was inspired by discussions in the callin.io community (this thread) and information from CapRover’s NGINX customization documentation (here).
Conclusion
This approach successfully resolved the “can’t connect to the MCP server” error for my callin.io setup on CapRover. I hope this guide proves helpful to others encountering similar issues. Please feel free to share any questions or problems you encounter, and I’ll do my best to provide assistance!
Happy automating!