langchain-mcp-adapters library. Your agent gets all 36 HoopAI tools automatically — no manual tool wrappers needed.
Installation
pip install langchain-mcp-adapters langgraph langchain-anthropic
Quickstart
import asyncio
import os
from langchain_mcp_adapters.client import MultiServerMCPClient
from langgraph.prebuilt import create_react_agent
from langchain_anthropic import ChatAnthropic
async def main():
async with MultiServerMCPClient(
{
"hoopai": {
"transport": "streamable_http",
"url": "https://services.leadconnectorhq.com/mcp/",
"headers": {
"Authorization": f"Bearer {os.environ['HOOPAI_API_KEY']}",
"locationId": os.environ["HOOPAI_LOCATION_ID"],
},
}
}
) as client:
tools = client.get_tools()
model = ChatAnthropic(model="claude-sonnet-4-6")
agent = create_react_agent(model, tools)
result = await agent.ainvoke(
{"messages": [{"role": "user", "content": "Show me my last 5 contacts"}]}
)
print(result["messages"][-1].content)
asyncio.run(main())
export HOOPAI_API_KEY=your_private_integration_token
export HOOPAI_LOCATION_ID=your_location_id
export ANTHROPIC_API_KEY=your_anthropic_api_key
Multi-agent architecture
For production use, split tools across specialist agents controlled by a router. This keeps each agent focused and prevents accidental cross-domain actions.Specialist subagents
Give each subagent an allowlist of tool names so it can only call the tools relevant to its domain:def make_specialist(client, allowed_tools: list[str], system_prompt: str):
"""Create a specialist agent with a restricted tool set."""
all_tools = client.get_tools()
tools = [t for t in all_tools if t.name in allowed_tools]
model = ChatAnthropic(model="claude-sonnet-4-6")
return create_react_agent(model, tools, state_modifier=system_prompt)
# Contacts specialist — read/write contacts and tags only
contacts_agent = make_specialist(
client,
allowed_tools=[
"contacts_get-contacts",
"contacts_get-contact",
"contacts_create-contact",
"contacts_update-contact",
"contacts_upsert-contact",
"contacts_add-tags",
"contacts_remove-tags",
"contacts_get-all-tasks",
],
system_prompt=(
"You manage contacts. Always confirm before creating or updating records. "
"Never delete contacts. Return structured summaries."
),
)
# Conversations specialist — search and send messages
conversations_agent = make_specialist(
client,
allowed_tools=[
"conversations_search-conversation",
"conversations_get-messages",
"conversations_send-a-new-message",
],
system_prompt=(
"You handle conversations and messaging. "
"Always confirm the recipient and message content before sending. "
"Never send bulk messages without explicit user approval."
),
)
Recommended subagents
| Subagent | Tools to include | Guardrails |
|---|---|---|
| Contacts | get, search, create, update, upsert, add/remove tags | Confirm before write; never bulk-delete |
| Conversations | search, get messages, send message | Validate recipient; confirm before send |
| Calendar | get events, get appointment notes | Require timezone; confirm before booking |
| Opportunities | search, get pipelines, get, update | Require reason text for stage/value changes |
| Payments | get order, list transactions | Read-only by default; gate writes behind approval |
Router pattern
The router classifies intent and delegates to one specialist:ROUTER_PROMPT = """
You are a router. Delegate to exactly one specialist based on the user's intent.
Never call MCP tools directly — only route.
Routing rules:
- Contact lookup, tagging, segmentation → contacts
- Messages, inbox, follow-ups → conversations
- Appointments, scheduling → calendar
- Deals, pipelines, stages → opportunities
- Invoices, transactions, payments → payments
If locationId is missing, ask for it before routing.
If the task is destructive (delete, bulk-update), ask for explicit confirmation first.
"""
Full multi-agent example
import asyncio
import os
from langchain_mcp_adapters.client import MultiServerMCPClient
from langgraph.prebuilt import create_react_agent
from langchain_anthropic import ChatAnthropic
from langchain_core.messages import HumanMessage
SPECIALISTS = {
"contacts": [
"contacts_get-contacts", "contacts_get-contact",
"contacts_create-contact", "contacts_update-contact",
"contacts_add-tags", "contacts_remove-tags",
],
"conversations": [
"conversations_search-conversation",
"conversations_get-messages",
"conversations_send-a-new-message",
],
"opportunities": [
"opportunities_search-opportunity",
"opportunities_get-pipelines",
"opportunities_get-opportunity",
"opportunities_update-opportunity",
],
}
async def run_multi_agent(user_message: str):
async with MultiServerMCPClient(
{
"hoopai": {
"transport": "streamable_http",
"url": "https://services.leadconnectorhq.com/mcp/",
"headers": {
"Authorization": f"Bearer {os.environ['HOOPAI_API_KEY']}",
"locationId": os.environ["HOOPAI_LOCATION_ID"],
},
}
}
) as client:
all_tools = client.get_tools()
model = ChatAnthropic(model="claude-sonnet-4-6")
# Build specialist agents
agents = {}
for name, allowed in SPECIALISTS.items():
tools = [t for t in all_tools if t.name in allowed]
agents[name] = create_react_agent(model, tools)
# Router decides which specialist to use
router_tools = [t for t in all_tools if t.name == "locations_get-location"]
router = create_react_agent(model, router_tools, state_modifier=(
"You are a router. Respond with only the specialist name: "
"contacts, conversations, or opportunities."
))
route_result = await router.ainvoke(
{"messages": [HumanMessage(content=f"Route this: {user_message}")]}
)
specialist_name = route_result["messages"][-1].content.strip().lower()
agent = agents.get(specialist_name, agents["contacts"])
result = await agent.ainvoke(
{"messages": [HumanMessage(content=user_message)]}
)
return result["messages"][-1].content
result = asyncio.run(run_multi_agent("Find all contacts tagged as 'New Lead' added this week"))
print(result)
Implementation checklist
- One shared MCP client, reused across all subagents
- Per-subagent tool allowlists
- Approval middleware for write operations
- Log all MCP calls with
locationId, tool name, timestamp, and status - Add replay tests for common workflows (contact search, send message, move opportunity)