Skip to main content
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 nexus.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://nexus.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()
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://nexus.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

Agent Deployment

Production deployment guide: profile locking, URL params, authentication

Guardrails

Constrain what tools your agent can call

Audit Trail

Query what your agent did via Civic Chat

Get Credentials

Token generation and URL parameter reference