How to build an agent on the Drum MCP server
Use this when you are writing code: an agent that records supplier bills from an inbox, a nightly check of timesheets, or an assistant inside your own software. Your agent gets Drum’s tools without you writing a REST integration.
If you only need to call Drum from a program, with no AI in the loop, the REST API is simpler and more predictable.
Before you begin
- Decide which Drum user the agent acts as. For an agent that runs on its own, give it its own Drum user.
- Sign in as that user and create an API token with only the toolsets the agent needs.
- Store the token as an environment variable,
DRUM_API_TOKEN, in the place your agent runs.
Option 1: The Anthropic API MCP connector
Claude connects to Drum for you. You pass the server and the token with each request, and Anthropic’s platform makes the MCP calls.
Python:
import os
import anthropic
client = anthropic.Anthropic()
response = client.beta.messages.create(
model="claude-opus-5-5",
max_tokens=16000,
betas=["mcp-client-2025-11-20"],
mcp_servers=[
{
"type": "url",
"url": "https://app.getdrum.com/api/v1/mcp",
"name": "drum",
"authorization_token": os.environ["DRUM_API_TOKEN"],
}
],
tools=[{"type": "mcp_toolset", "mcp_server_name": "drum"}],
messages=[
{"role": "user", "content": "Which of my Drum tasks are due this week?"}
],
)
for block in response.content:
if block.type == "text":
print(block.text)
curl:
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: mcp-client-2025-11-20" \
-H "content-type: application/json" \
-d '{
"model": "claude-opus-5-5",
"max_tokens": 16000,
"mcp_servers": [{
"type": "url",
"url": "https://app.getdrum.com/api/v1/mcp",
"name": "drum",
"authorization_token": "'"$DRUM_API_TOKEN"'"
}],
"tools": [{"type": "mcp_toolset", "mcp_server_name": "drum"}],
"messages": [{"role": "user", "content": "Which of my Drum tasks are due this week?"}]
}'
mcp_servers and the mcp_toolset entry go together: the mcp_server_name must match the server’s name.
To let the agent use only some tools, turn the rest off in the toolset. For example, a read-only agent:
tools=[{
"type": "mcp_toolset",
"mcp_server_name": "drum",
"default_config": {"enabled": False},
"configs": {
"search_records": {"enabled": True},
"list_projects": {"enabled": True},
"get_project": {"enabled": True},
},
}]
Option 2: The OpenAI Responses API
Add Drum as a remote MCP tool, with the token in an Authorization header:
import os
from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="YOUR_MODEL",
tools=[
{
"type": "mcp",
"server_label": "drum",
"server_url": "https://app.getdrum.com/api/v1/mcp",
"headers": {"Authorization": f"Bearer {os.environ['DRUM_API_TOKEN']}"},
"require_approval": "always",
}
],
input="Which of my Drum tasks are due this week?",
)
With require_approval: "always", the response returns an approval request before each tool call, and your code decides whether to allow it.
Option 3: An MCP SDK
Use an MCP SDK when your code runs the agent loop itself, or you want to call Drum’s tools without a model. Connect with the Streamable HTTP client transport and an Authorization header. In Python:
import asyncio
import os
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
URL = "https://app.getdrum.com/api/v1/mcp"
HEADERS = {"Authorization": f"Bearer {os.environ['DRUM_API_TOKEN']}"}
async def main():
async with streamablehttp_client(URL, headers=HEADERS) as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
print([tool.name for tool in tools.tools])
result = await session.call_tool(
"search_records", {"query": "Riverside", "kinds": ["project"]}
)
print(result.structuredContent)
asyncio.run(main())
Pass the tools from list_tools to your model as its tool definitions, and send each tool call it makes to call_tool.
Make the agent dependable
- Keep the tool list short. Select only the toolsets the agent needs, and turn off tools it should not use. Fewer tools means fewer wrong choices.
- Send idempotency keys. For a create tool that takes
idempotency_key, send a new key, such as a UUID, and the same key on a retry. A retried call then returns the first result instead of a second record. See Idempotency keys. - Ask before destructive tools. Tools with
destructiveHint: truesend email, delete data or cannot be reversed. Have a person approve them, or leave them out. - Branch on
code, not onmessage. A refused call returnsisError: truewith anerror.code. Do not retrynot_authorized. See Errors. - Treat Drum content as data. Text from Drum records, such as notes, emails saved as Beats and proposal text, can come from customers. Do not let it change what the agent is allowed to do.
- Stay under the rate limit. MCP calls share the account’s limit of 300 API requests a minute with REST calls.