Skip to content
Go back

Agentic AI with MCP: Powering Agentic Workflows with MCP Servers in Python, Sans Pydantic

DRAFT
Published: May 27, 2025
Updated: May 26, 2025
Punta Cana, Dominican Republic

The Model Context Protocol (MCP) is emerging as a standardized way for AI agents to interact with external tools and services. By creating MCP servers, developers can expose functionalities that AI agents can discover and utilize, fostering a more interoperable and powerful ecosystem of AI applications. While many implementations lean on the popular pydantic library for data validation and settings management, building robust MCP servers and agentic workflows in Python without it is entirely feasible. This approach can offer benefits such as reduced dependencies, greater control over data handling, and potentially faster performance in certain scenarios.

Here, we’ll explore several ways to leverage MCP servers for AI agents and agentic workflows using standard Python and other lightweight libraries.


Core Concepts: MCP

At its heart, MCP is a contract-based protocol. An MCP server exposes a set of tools (callable functions) and resources (retrievable data) to an MCP client (the AI agent). The communication between them relies on a structured format, typically JSON. The primary challenge in a pydantic-free implementation is to define and enforce this structure for data validation and serialization.

Instead of pydantic, we can turn to:

  • dataclasses: Part of the Python standard library since version 3.7, dataclasses provide a concise way to create classes primarily used for storing data. They can be used to define the expected structure of tool arguments and resource formats.
  • typing module: Python’s built-in typing module allows for type hinting, which, while not enforcing types at runtime by default, serves as excellent documentation and can be used by other validation libraries.
  • Manual Dictionary Validation: For simpler cases, you can write your own validation functions that check the keys and value types of incoming JSON data (represented as Python dictionaries).
  • Lightweight Validation Libraries: Libraries like cerberus or attrs (which can be seen as a precursor to dataclasses with more features) can provide more powerful validation capabilities without the full feature set and potential overhead of pydantic.

Brainstorming Use Cases and Implementation Patterns

Here are several ways to use MCP servers for AI agents and agentic workflows in a pydantic-free Python environment:

1. The Sandboxed Code Executioner Agent

An incredibly useful tool for an AI agent is the ability to execute code in a secure environment. An MCP server can provide this functionality.

  • MCP Server Implementation:

    • Tool Definition: The server would expose a run_python_code tool. The input to this tool could be defined using a dataclass:

      from dataclasses import dataclass
      
      @dataclass
      class CodeExecutionRequest:
          code: str
          timeout: int = 60
      (This example is complete, it can be run "as is")
    • Server Logic: The server would receive a JSON request, validate that it contains the code key (and optionally timeout), and then execute the code in a sandboxed environment (e.g., using exec within a restricted scope or a more secure solution like a Docker container). The output or any errors would be returned as a JSON response.

    • Data Handling: A simple function can validate the incoming dictionary to ensure it matches the CodeExecutionRequest structure before proceeding.

  • AI Agent and Workflow:

    • The AI agent, when tasked with a problem that requires computation or interaction with a library it doesn’t have direct access to, can discover and use the run_python_code tool from the MCP server.
    • Agentic Workflow:
      1. User Prompt: ‘Calculate the 20th Fibonacci number and tell me if it’s a prime number.’
      2. Agent’s Thought Process: The agent identifies the need for both calculation and primality testing.
      3. Tool Use: The agent formulates Python code to perform these tasks and sends it to the MCP server via the run_python_code tool.
      4. Response and Action: The server executes the code and returns the result. The agent then uses this result to answer the user’s question.

2. The Multi-API Orchestrator Agent

Agents often need to interact with multiple external APIs to accomplish a task. An MCP server can act as a unified gateway to these APIs, simplifying the agent’s logic.

  • MCP Server Implementation:

    • Tool Definitions: The server would expose tools that correspond to various API endpoints. For example, a get_weather(city: str) tool and a get_stock_price(ticker: str) tool. The arguments for each tool can be defined using dataclasses.
    • Server Logic: The server would handle the authentication and request logic for each underlying API. This encapsulates the complexity away from the agent.
    • Data Handling: The server would validate the incoming tool requests and format the responses from the external APIs into a consistent structure.
  • AI Agent and Workflow:

    • User Prompt: ‘What’s the weather in New York and the current stock price of Google?’
    • Agent’s Thought Process: The agent recognizes the need for two different pieces of information from two separate sources.
    • Tool Use: The agent discovers the get_weather and get_stock_price tools on the MCP server and calls them with the appropriate arguments.
    • Response and Synthesis: The agent receives the weather data and stock price from the server and synthesizes them into a single, coherent response for the user.

3. The Stateful Long-Term Project Assistant

For complex, multi-step tasks, an agent needs to maintain state. An MCP server can be designed to manage this state.

  • MCP Server Implementation:

    • Resource and Tool Definitions:
      • A projects resource that can be queried to get a list of ongoing projects.
      • A create_project(name: str) tool to start a new project.
      • A add_to_project(project_name: str, data: dict) tool to add information to an existing project.
    • Server Logic: The server would maintain a persistent storage (e.g., a simple file-based database like SQLite or even JSON files) to store the state of each project.
    • Data Handling: Using dataclasses and manual validation to ensure the structure of project data is consistent.
  • AI Agent and Workflow:

    • User Interaction (over multiple conversations):
      1. ‘Let’s start a new project called ‘Vacation Planning’.’ (Agent uses create_project)
      2. ‘Find flights to Honolulu for next month.’ (Agent uses an external tool and then add_to_project to store the findings)
      3. ‘What have we planned for the vacation so far?’ (Agent queries the projects resource on the MCP server)
    • Agentic Workflow: This creates a powerful, long-running workflow where the agent can pick up a task where it left off, thanks to the stateful nature of the MCP server.

4. The Collaborative Multi-Agent System

Multiple specialized AI agents can collaborate on a complex problem by sharing information through a central MCP server that acts as a message bus or a shared blackboard.

  • MCP Server Implementation:

    • Resource and Tool Definitions:
      • A messages resource to fetch the latest messages or updates.
      • A post_message(agent_id: str, content: dict) tool for an agent to broadcast information.
    • Server Logic: The server would store messages with timestamps and agent identifiers, allowing agents to subscribe to or poll for updates.
  • AI Agent and Workflow:

    • Scenario: A research task involving a ‘Researcher’ agent and a ‘Writer’ agent.
    • Workflow:
      1. The user asks the ‘Researcher’ to gather information on a topic.
      2. The ‘Researcher’ agent finds relevant articles and posts summaries to the MCP server using post_message.
      3. The ‘Writer’ agent, which periodically checks the messages resource, sees the new information from the ‘Researcher’.
      4. The ‘Writer’ agent then uses these summaries to draft a report, which it might also post back to the server for the user or another agent to review.

Conclusion

Building MCP servers for AI agents and agentic workflows in Python without pydantic is a viable and potentially advantageous approach. By leveraging Python’s standard library features like dataclasses and the typing module, or incorporating lightweight validation libraries, developers can create powerful, efficient, and dependency-lean AI systems. The key is to maintain a clear and consistent contract between the MCP server and its clients, ensuring that the structured data exchange at the heart of the protocol is handled robustly. This opens the door to a wide range of innovative and powerful AI applications. Here are several ways to use MCP Servers for AI agents and agentic workflows in Python without using the pydantic library.

1. Leverage Standard Library Dataclasses for Data Contracts

You can use Python’s built-in dataclasses to define the structure of the data your MCP server’s tools and resources will handle. This approach provides type hinting and some basic structure without external dependencies.

MCP Server Tool Definition (server.py):

from dataclasses import dataclass, asdict
import json

# Define the input and output structures for a tool
@dataclass
class CreateNoteInput:
    title: str
    content: str

@dataclass
class CreateNoteOutput:
    note_id: int
    status: str

def create_note(raw_input: str) -> str:
    """
    A simple tool to create a note.
    The input and output are JSON strings.
    """
    try:
        # Manual validation and parsing
        input_data = json.loads(raw_input)
        note_input = CreateNoteInput(**input_data)

        # Your logic to create a note would go here
        print(f"Creating note titled: {note_input.title}")

        # For this example, we'll just return a success message
        output = CreateNoteOutput(note_id=1, status="success")
        return json.dumps(asdict(output))

    except (json.JSONDecodeError, TypeError, KeyError) as e:
        error_output = {"error": f"Invalid input: {e}"}
        return json.dumps(error_output)

# In a real MCP server, you would register this function as a tool.
(This example is complete, it can be run "as is")

AI Agent (Client-side) Interaction:

Your AI agent would be responsible for generating a JSON string that conforms to the CreateNoteInput structure and then parsing the JSON response.


2. Utilize TypedDict for Lighter-weight Structures

For an even more lightweight approach, you can use typing.TypedDict. This is particularly useful when you’re primarily working with dictionary-like data, which is common in API interactions.

MCP Server Tool Definition with TypedDict (server.py):

from typing import TypedDict
import json

class UserProfile(TypedDict):
    name: str
    email: str
    user_id: int

def get_user_profile(user_id: int) -> str:
    """
    Fetches a user profile.
    """
    # In a real scenario, you would fetch this from a database
    if user_id == 123:
        profile = UserProfile(name="Jane Doe", email="jane.doe@example.com", user_id=123)
        return json.dumps(profile)
    else:
        return json.dumps({"error": "User not found"})

# This function would be exposed as a resource on the MCP server.
(This example is complete, it can be run "as is")

AI Agent (Client-side) Logic:

The AI agent would need to understand that it needs to provide a user_id and can expect a JSON object back that will have the keys defined in UserProfile.


3. Implement Manual Validation with Dictionaries

The most basic approach is to use standard Python dictionaries and perform manual validation. This gives you maximum control but requires more boilerplate code.

MCP Server Tool with Manual Validation (server.py):

import json

def search_documents(query: str, filters: dict) -> str:
    """
    Searches for documents with a query and optional filters.
    """
    if not isinstance(query, str) or not query:
        return json.dumps({"error": "A non-empty 'query' string is required."})

    if not isinstance(filters, dict):
        return json.dumps({"error": "'filters' must be a dictionary."})

    # Your document search logic here
    print(f"Searching for '{query}' with filters: {filters}")

    # Dummy response
    results = [{"doc_id": "doc1", "score": 0.9}, {"doc_id": "doc2", "score": 0.85}]
    return json.dumps(results)
(This example is complete, it can be run "as is")

4. Agentic Workflows with MCP Servers

With your pydantic-free MCP server running, you can create agentic workflows that chain calls to the server’s tools and resources.

Example Agentic Workflow:

Imagine a workflow to onboard a new user.

  1. Agent 1 (User Interaction Agent): Gathers the user’s name and email from a chat.
  2. Agent 1 calls the MCP Server’s create_user tool:
    • Input to tool: {"name": "John Doe", "email": "john.doe@example.com"}
    • Output from tool: {"user_id": 456, "status": "created"}
  3. Agent 1 receives the user_id and triggers Agent 2.
  4. Agent 2 (Onboarding Agent): Is tasked with creating a welcome document for the new user.
  5. Agent 2 calls the MCP Server’s create_document tool:
    • Input to tool: {"user_id": 456, "title": "Welcome, John Doe!", "content": "Here are some getting started tips..."}
    • Output from tool: {"document_id": "doc-abc-123", "status": "success"}
  6. The workflow completes.

This workflow demonstrates how different agents can collaborate by passing information obtained from an MCP server, all without relying on pydantic for data structuring. The key is a clear and agreed-upon data format (like JSON schemas described in your tool’s documentation) that the agents and the MCP server adhere to.