All docs

Pydantic AI Setup

Connect Pydantic AI agents to graph8 via MCP with typed agent outputs

Wire Pydantic AI into graph8’s MCP server. Pydantic AI’s typed agent model makes it a clean fit for graph8 - your structured prospect data flows through Pydantic models end-to-end.

Pydantic AI

Prerequisites

  • Python 3.10+
  • pydantic-ai with the MCP extra
pip install 'pydantic-ai[mcp]'

Hosted MCP (remote OAuth)

import asyncio
import os

from pydantic import BaseModel
from pydantic_ai import Agent
from pydantic_ai.mcp import MCPServerHTTP


class Prospect(BaseModel):
    name: str
    company: str
    title: str
    linkedin_url: str | None = None


class ProspectList(BaseModel):
    prospects: list[Prospect]


server = MCPServerHTTP(url="https://be.graph8.com/mcp/")
agent = Agent(
    "anthropic:claude-sonnet-4",
    mcp_servers=[server],
    output_type=ProspectList,
    system_prompt=(
        "You are a SaaS prospector. Use g8_find_contacts to preview prospects. "
        "Never call credit-charging tools without explicit user confirmation."
    ),
)


async def main() -> None:
    async with agent.run_mcp_servers():
        result = await agent.run(
            "Find 10 VP Engineering at Series B SaaS startups in the US."
        )
        for p in result.output.prospects:
            print(f"{p.name} - {p.title} @ {p.company}")


if __name__ == "__main__":
    asyncio.run(main())

The output_type=ProspectList constraint forces the agent to return a validated, typed list - no parsing or guessing.

Self-hosted MCP (stdio)

import os

from pydantic_ai.mcp import MCPServerStdio


server = MCPServerStdio(
    command="uvx",
    args=["g8-mcp-server"],
    env={
        "G8_API_KEY": os.environ["G8_API_KEY"],
        "G8_MCP_MODE": "gtm",
    },
)
# Same `agent = Agent(...)` as the hosted example.

Worked example: typed prospect-to-sequence flow

class SequenceEnrollment(BaseModel):
    sequence_name: str
    enrolled_count: int
    confirmation_required: bool


agent = Agent(
    "anthropic:claude-sonnet-4",
    mcp_servers=[server],
    output_type=SequenceEnrollment,
    system_prompt=(
        "Find prospects, save them to a list, and enroll in a sequence. "
        "Always set confirmation_required=true before calling "
        "g8_build_contact_list or g8_add_to_sequence."
    ),
)


async def main() -> None:
    async with agent.run_mcp_servers():
        result = await agent.run(
            "Enroll 25 VP Eng at Series B SaaS in the 'New SaaS Outreach' sequence."
        )
        if result.output.confirmation_required:
            input(f"Enroll {result.output.enrolled_count} contacts? [y/N] ")

Typed outputs make it trivial to gate credit-charging steps behind explicit confirmation logic in your code rather than relying on the model to surface a prompt.

Build with graph8