Working With MCP On Heroku
Last updated May 16, 2025
Table of Contents
What is MCP?
The Model Context Protocol (MCP) is an open standard that helps developers connect large language models to tools, services, and data sources in a standardized way. Developers can run MCP servers that expose tool capabilities, and MCP clients (AI applications) connect to these servers.
Deploying Custom MCP Servers To Heroku
Deploying an MCP server to Heroku is simple: create a Procfile
that defines your MCP process, and add the required files for your language of choice (e.g., requirements.txt
for Python, Gemfile
for Ruby). For examples, see our open source MCP templates.
When you deploy a standard MCP server to Heroku, you can use frameworks like LangChain with our v1/chat/completions
to make tool calls directly to your server. However, this approach comes with tradeoffs:
- You need to build and maintain your own control loop to handle tool calls from the model, execute tools, and return structured responses.
- Long-running SSE or streamable HTTP MCP servers are multi-tenant, which makes them less secure, and are expensive when sitting idle.
- Managing several different MCP servers and tool sets can become laborious.
Heroku addresses these challenges by offering native support for MCP servers through the Managed Inference and Agents add-on, which automatically handles tool registration, execution, and orchestration. You can combine multiple Heroku-hosted MCP servers into a unified toolkit, enabling seamless interaction with MCP clients and providing a centralized, secure way to manage and run your AI tools.
Registering Custom MCP Servers With Heroku
To use the /v1/mcp/servers
endpoint or enable automatic custom tool execution in a /v1/agents/heroku
inference request, you must first deploy and register an MCP server.
Heroku provides various open-source MCP example repos you can one-click deploy, or modify and deploy to Heroku:
Purpose | Repository |
---|---|
Ruby Code Execution | mcp-code-exec-ruby |
Python Code Execution | mcp-code-exec-python |
Go Code Execution | mcp-code-exec-go |
Node Code Execution | mcp-code-exec-node |
Document Parsing (HTML & PDF –> Markdown) | mcp-doc-reader |
These particular tools are also availible natively as heroku_tools
(require no MCP deployment). Deploying your own provides benefits like no upper limit on ttl_seconds
(dyno runtime), but furthermore we encourage you to fork these repos to develop and deploy your own custom tools.
Currently, MCP servers cannot run in Private Spaces.
These apps are standard MCP apps, with standard Heroku required files added (e.g. Procfile), plus a single special line added to the Procfile to declare each MCP server.
Procfile STDIO
server names must follow these requirements:
- Start with
"mcp"
- Be unique across all apps registered with your model resource
One-Click Deployment or Manual Setup
You can deploy any of these servers using the “Deploy to Heroku” button in one of the example READMEs above. We also encourage you to fork, customize, and deploy your own version - either with a one-click deploy button (recommended), or by following manual deployment steps.
Example one-click deploy button for mcp-code-exec-python:
Attach Your MCP Server to Your Add-on
After deploying, attach your MCP app to a Heroku Managed Inference and Agents chat model to grant access to your MCP server’s tools.
To attach a new model resource to an MCP app:
heroku ai:models:create claude-3-7-sonnet -a $APP_NAME --as INFERENCE
To attach an existing model resource to an MCP app:
heroku addons:attach inference-shaped-35880 -a $APP_NAME --as INFERENCE
If you already have an app you are running inference requests from, attaching the MCP server to your existing app’s inference add-on enables the app to access your new MCP tools.
Once attached, your MCP server is automatically registered, tools are synced, and requests made to /v1/agents/heroku
with your model will be able to execute your tools automatically in secure, short-lived, isolated one-off dynos.
This approach offers: - Cost efficiency: you’re only billed for the short duration the tool runs - Simplicity: Heroku handles registration, sync, and control flow for you - Security: each tool call runs in its own sandboxed dyno
Use your model resource’s INFERENCE_KEY
in the sections below:
export INFERENCE_KEY=$(heroku config:get INFERENCE_KEY -a $APP_NAME)
List Your Registered MCP Servers
Use the /v1/mcp/servers
endpoint to list your MCP servers registered to your model’s INFERENCE_KEY
:
curl "$INFERENCE_URL/v1/mcp/servers" \
-H "Authorization: Bearer $INFERENCE_KEY" \
-H "Content-Type: application/json" | jq .
This returns metadata about each server, including its process type, namespace, and all registered tools with their schemas and annotations. For a complete breakdown of the response format, see the /v1/mcp/servers
API reference.
Automatic Tool Execution In /v1/agents/heroku
Once registered, tools from your MCP server can be included in the tools
parameter of your /v1/agents/heroku
request. For each tool you wish your model to be able to access, include a Tool Object with "type": "mcp"
and the tool’s name. The tool will only be executed if the model chooses to call it during generation.
Example v1/agents/heroku
Request With mcp
Tool
curl "$INFERENCE_URL/v1/agents/heroku" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $INFERENCE_KEY" \
-H "X-Forwarded-Proto: https" \
-d @- <<EOF
{
"model": "claude-3-7-sonnet",
"messages": [
{
"role": "user",
"content": "Use the code exec tool to take the sha256 of the following string: 'Heroku Rocks'."
}
],
"tools": [
{
"type": "mcp",
"name": "code_exec_python"
}
]
}
EOF
Note how you don’t need to insert tool parameters schemas or descriptions here - because your MCP tool is registered, this metadata is injected into your request automatically.
See our v1/agents/heroku
docs to learn more about this endpoint.
Example v1/agents/heroku
mcp
Tool Call Response
event:message
data:{"id":"chatcmpl-183e353a1033220215165","object":"chat.completion","created":1746892233,"model":"claude-3-7-sonnet","system_fingerprint":"heroku-inf-n58omz","choices":[{"index":0,"message":{"role":"assistant","content":"I'll help you calculate the SHA-256 hash of the string 'Heroku Rocks' using the code execution tool.","refusal":null,"tool_calls":[{"id":"tooluse_G2XYvtSqSQqnEKawrixqRA","type":"function","function":{"name":"mcp/code_exec_python","arguments":"{\"code\":\"import hashlib\\n\\n# The string to hash\\ninput_string = 'Heroku Rocks'\\n\\n# Calculate the SHA-256 hash\\nhash_object = hashlib.sha256(input_string.encode())\\nhash_hex = hash_object.hexdigest()\\n\\nprint(f\\\"The SHA-256 hash of '{input_string}' is:\\\")\\nprint(hash_hex)\"}"}}]},"finish_reason":"tool_calls"}],"usage":{"prompt_tokens":492,"completion_tokens":176,"total_tokens":668}}
event:message
data:{"id":"chatcmpl-183e353a1033220215165","object":"tool.completion","created":1746892235,"system_fingerprint":"heroku-inf-n58omz","choices":[{"index":0,"message":{"role":"tool","content":"Tool 'mcp/code_exec_python' returned result: {\"content\":[{\"type\":\"text\",\"text\":\"{\\n \\\"returncode\\\": 0,\\n \\\"stdout\\\": \\\"The SHA-256 hash of 'Heroku Rocks' is:\\\\n7ebc92bcf8f51f0b623a18f0a357639410a409340043c7a670bb180acd138e6b\\\",\\n \\\"stderr\\\": \\\"\\\"\\n}\"}],\"isError\":false}","refusal":null,"tool_call_id":"tooluse_G2XYvtSqSQqnEKawrixqRA","name":"mcp/code_exec_python"},"finish_reason":""}],"usage":{}}
event:message
data:{"id":"chatcmpl-183e353a1033220215165","object":"chat.completion","created":1746892238,"model":"claude-3-7-sonnet","system_fingerprint":"heroku-inf-n58omz","choices":[{"index":0,"message":{"role":"assistant","content":"The SHA-256 hash of the string 'Heroku Rocks' is:\n\n```\n7ebc92bcf8f51f0b623a18f0a357639410a409340043c7a670bb180acd138e6b\n```\n\nThis hash was calculated by encoding the string 'Heroku Rocks' into bytes and then applying the SHA-256 hashing algorithm to it.","refusal":null},"finish_reason":"stop"}],"usage":{"prompt_tokens":824,"completion_tokens":98,"total_tokens":922}}
event:done
data:[DONE]
Dashboard MCP Server Management
You can also view and manage your registered MCP tools through the dashboard’s page for your add-on. Either open via heroku addons:open INFERENCE -a $APP_NAME
, or navigate to your app’s Resources page in the dashboard, and click on your add-on under ‘Add-on Services’.
Using Registered Tools With External Clients
Many external AI applications like cursor provide the ability to integrate with MCP servers via configuration files. To get this working with your deployed and registered Heroku mcp
tools, open up your add-on in the Heroku dashboard with:
heroku addons:open INFERENCE -a $APP_NAME
Next, plug your Toolkit URL ($INFERENCE_URL/mcp/sse
) and Toolkit Token (INFERENCE_KEY
) into your client’s configuration file.
For example, in Cursor, your .cursor/mcp.json
might look like this:
{
"mcpServers": {
"myCustomTools": {
"command": "npx",
"args": [
"mcp-remote",
"<YOUR-TOOLKIT-URL>",
"--header",
"Authorization:${AUTH_HEADER}"
],
"env": {
"AUTH_HEADER": "Bearer <YOUR-TOOLKIT-TOKEN>"
}
}
}
}
Using Registered Tools With MCP Inspector
To test your deployed MCP servers with MCP Inspector, run:
npx @modelcontextprotocol/inspector
After opening the locally hosted UI (typically http://127.0.0.1:6274
), select a transport type of SSE
in the left-hand pane. Specify a URL of $INFERENCE_KEY/mcp/sse
, e.g. https://us.inference.heroku.com/mcp/sse
.
Next, click open ‘Authentication’ and paste in your model add-on’s INFERENCE_KEY
into the Bearer Token
section. Click Connect
.
After connecting, you can list your tools, or make example requests.
Despite the interface using SSE, your underlying tool call executions will be running in secure, isolated one-off dynos (STDIO mode).