Skip to main content

Haystack

Connect a Haystack pipeline to Civic using MCPTool from the Haystack MCP integration. Because MCPTool wraps one named tool at a time, you first discover available tool names via the raw MCP client, then create a tool object for each.

Prerequisites

  • Python 3.11+
  • A Civic account at app.civic.com with a configured toolkit
  • A Civic token and an Anthropic API key

Installation

pip install haystack-ai haystack-integrations anthropic-haystack mcp python-dotenv

Environment Variables

CIVIC_URL=https://app.civic.com/hub/mcp?profile=your-toolkit
CIVIC_TOKEN=your-civic-token
ANTHROPIC_API_KEY=your-anthropic-key
Get Your Credentials

How to generate a Civic token and configure toolkit URL parameters

Connecting to Civic

Haystack's MCPTool wraps a single named tool. First discover all tool names using the raw mcp client, then create MCPTool instances and pass them to AnthropicChatGenerator:

import os
import asyncio
from dotenv import load_dotenv
from mcp.client.streamable_http import streamablehttp_client
from mcp import ClientSession
from haystack_integrations.tools.mcp import MCPTool, StreamableHttpServerInfo
from haystack.utils import Secret
from haystack_integrations.components.generators.anthropic import AnthropicChatGenerator
from haystack.dataclasses import ChatMessage

load_dotenv()

CIVIC_URL = os.environ["CIVIC_URL"]
CIVIC_TOKEN = os.environ["CIVIC_TOKEN"]

async def get_tool_names():
"""Discover available tool names from the Civic MCP Hub."""
headers = {"Authorization": f"Bearer {CIVIC_TOKEN}"}
async with streamablehttp_client(CIVIC_URL, headers=headers) as (r, w, _):
async with ClientSession(r, w) as session:
await session.initialize()
result = await session.list_tools()
return [t.name for t in result.tools]

def build_pipeline(tool_names):
server_info = StreamableHttpServerInfo(
url=CIVIC_URL,
token=Secret.from_env_var("CIVIC_TOKEN"),
)
civic_tools = [MCPTool(name=name, server_info=server_info) for name in tool_names]

generator = AnthropicChatGenerator(
model="claude-sonnet-4-6",
tools=civic_tools,
)
return generator, civic_tools

def main():
tool_names = asyncio.run(get_tool_names())
print(f"{len(tool_names)} tools discovered")

generator, _ = build_pipeline(tool_names)

messages = [
ChatMessage.from_system("You are a helpful assistant with access to Civic tools."),
ChatMessage.from_user("What events do I have today?"),
]
response = generator.run(messages=messages)
print(response["replies"][0].text)

main()
note

MCPTool wraps a single named tool — not a server. You must discover tool names first using the raw mcp package, then create one MCPTool per name. Import AnthropicChatGenerator from haystack_integrations.components.generators.anthropic (requires the anthropic-haystack package).

Production Configuration

For production agents, lock to a specific toolkit using the profile URL parameter:

CIVIC_URL=https://app.civic.com/hub/mcp?profile=your-production-toolkit

Reference Implementation

haystack-reference-implementation-civic

Complete implementation with FastAPI chat UI and deployment guide

Next Steps