PromptisePromptise
Docs
GitHub
Promptise - AI Framework LogoPromptise

The foundation layer for agentic intelligence. Build, secure and operate autonomous AI systems with Promptise Foundry.

pip install promptise

[01] Foundry

  • MCPcast
  • The Promptise Agent
  • Reasoning Engine
  • MCP
  • Agent Runtime
  • Prompt Engineering
  • Execution Engine
  • Agent Identity

[02] Resources

  • Documentation
  • GitHub
  • Guides
  • Learning Paths
  • Questions

[03] Company

  • About
  • Terms of Service
  • Privacy Policy
  • Cookie Policy
  • Subprocessors

© 2026 Promptise by Manser Ventures. All rights reserved.

Open source · Python

← Guides> AI Engineering

How to Connect MCP Servers to Your AI Agent in Python

Give your AI agent real tools with the Model Context Protocol. Connect local and remote MCP servers to a Python agent, step by step, with code you can run.

Level
Beginner
Reading time
12 min
Published
Oct 10, 2026
By
Promptise Team
  • MCP
  • AI Agents
  • Python
  • Tool Calling
  • Promptise Foundry

An AI agent is only as useful as the tools it can reach. The Model Context Protocol (MCP) is the open standard for giving it those tools: a small server offers a set of functions, and your agent connects to it as a client. In this guide you'll build a tiny MCP server, connect it to a Python agent, then add a remote server and an open-source one next to it. Every snippet was run against Promptise Foundry 1.2.1, and the output you see is what it printed.

[01]

How do you connect an MCP server to an AI agent?

You tell the agent where each server lives, and it does the rest. With Promptise Foundry, that's the servers argument of build_agent:

Python
1
2
3
4
5
6
7
8
9
10
from promptise import build_agent
from promptise.config import HTTPServerSpec, StdioServerSpec

agent = await build_agent(
    model="openai:gpt-5-mini",
    servers={
        "orders": StdioServerSpec(command="python", args=["orders_server.py"]),
        "crm": HTTPServerSpec(url="https://mcp.example.com/mcp"),
    },
)

When the agent starts, it connects to every server, asks each one which tools it offers, and hands that list to the model. When the model decides a tool will help, the agent calls it on the right server and passes the result back. You write no glue code per tool. The rest of this guide shows each piece working.


[02]

How MCP fits into your agent

Think of an MCP server as a counter between your agent and your systems. The agent asks; the server decides what's allowed and does the work. The agent never touches your database directly.

Rendering diagram…

That boundary is what makes MCP worth it. You can swap the model, change frameworks, or add a second agent, and the servers stay exactly as they are.

Servers talk to agents in one of two ways:

stdio

HTTP

How it runs

The agent starts the server as a child process

The server runs on its own; the agent connects by URL

Good for

Local tools, development, single-user apps

Shared services, production, anything behind auth

In Promptise

StdioServerSpec

HTTPServerSpec


[03]

What you need

  • Python 3.10 or newer.

  • An API key for a model provider. This guide uses OpenAI. Anthropic, Gemini, Azure, Bedrock, Ollama and others work by changing the model string, as listed in Models & Providers.

  • Promptise Foundry, which installs from PyPI:

>_Terminal
pip install promptise
export OPENAI_API_KEY="sk-..."

[04]

Build and connect your first MCP server

We'll give a support assistant one job: tell customers where their order is.

Step 01

Write a small MCP server

An MCP server is a normal Python file. Each function you decorate with @server.tool() becomes a tool the agent can call. Promptise builds the tool's input schema from your type hints.

Pythonorders_server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from promptise.mcp.server import MCPServer

server = MCPServer("orders")

# A stand-in for your real database or API.
ORDERS = {
    "A-1001": {"status": "shipped", "carrier": "DHL", "eta": "2026-10-14"},
    "A-1002": {"status": "processing", "carrier": None, "eta": None},
}


@server.tool()
async def get_order_status(order_id: str) -> dict:
    """Get an order's status, carrier and expected delivery date.

    Args:
        order_id: The order ID, for example "A-1001".
    """
    order = ORDERS.get(order_id.strip().upper())
    if order is None:
        return {"error": f"No order found with ID {order_id}."}
    return {"order_id": order_id.strip().upper(), **order}


if __name__ == "__main__":
    server.run()

Two details matter more than they look:

  • The docstring is written for the model. Its first paragraph becomes the tool's description, and each entry under Args: describes a parameter. That's what the model reads when it decides whether to call the tool and what to pass, so say plainly what the tool does and what each input looks like.

  • A missing order comes back as data, not an exception. The model can read "No order found" and tell the customer something useful.

Step 02

Connect it to your agent

Create a second file next to the first one:

Pythonagent.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import asyncio
import sys

from promptise import build_agent
from promptise.config import StdioServerSpec


async def main():
    agent = await build_agent(
        model="openai:gpt-5-mini",
        servers={
            "orders": StdioServerSpec(command=sys.executable, args=["orders_server.py"]),
        },
        instructions=(
            "You are a friendly support assistant. Use your tools to answer "
            "questions about orders. Only offer help your tools can provide."
        ),
        trace_tools=True,
    )
    try:
        print("Tools:", agent.tool_names)
        result = await agent.ainvoke(
            {"messages": [{"role": "user", "content": "Hi! Where is my order A-1001?"}]}
        )
        print(result["messages"][-1].content)
    finally:
        await agent.shutdown()


asyncio.run(main())

A few things are happening here:

  • StdioServerSpec tells the agent to start orders_server.py itself. Using sys.executable runs the server with the same Python, and the same installed packages, as your agent.

  • trace_tools=True prints every tool call, so you can watch the agent work.

  • agent.shutdown() closes the connection and stops the server process. The try/finally makes sure that happens even if something fails.

Step 03

Run it

>_Terminal
python agent.py
Output
Tools: ['get_order_status']
→ Invoking tool: get_order_status with {'order_id': 'A-1001'}
✔ Tool result from get_order_status: {"order_id": "A-1001", "status": "shipped", "carrier": "DHL", "eta": "2026-10-14"}
Your order A-1001 is shipped via DHL and is expected to be delivered on 2026-10-14. …

Follow the arrows. The agent found one tool on the server. The model chose it, pulled the order ID out of the question, and wrote its answer from the result. Your agent code doesn't mention orders anywhere: it learned about the tool from the server. The wording of the final reply will be a little different each time you run it, because the model writes it fresh.


[05]

Connect a remote MCP server over HTTP

stdio is perfect while you build. Once several agents need the same server, or it lives on another machine, run it over HTTP instead. On the server, that's a one-line change at the bottom of orders_server.py:

Pythonorders_server.py
if __name__ == "__main__":
    server.run(transport="http", host="127.0.0.1", port=8080)

Start it in its own terminal with python orders_server.py. It now listens at http://127.0.0.1:8080/mcp. In your agent, swap the stdio spec for an HTTP one that points at that address:

Pythonagent.py
from promptise.config import HTTPServerSpec

servers = {
    "orders": HTTPServerSpec(url="http://127.0.0.1:8080/mcp"),
}

Nothing else changes. The agent finds the same tool and answers the same way.


[06]

Use several MCP servers in one agent

Real agents rarely live on one server. Add as many as you need to servers and Promptise merges their tools into one list for the model.

Here the agent uses your orders server over HTTP, plus mcp-server-time, an open-source server from the MCP project. uvx, which comes with uv, downloads and starts it for you.

Pythonagent.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import asyncio

from promptise import build_agent
from promptise.config import HTTPServerSpec, StdioServerSpec


async def main():
    agent = await build_agent(
        model="openai:gpt-5-mini",
        servers={
            # Your own server, already running over HTTP.
            "orders": HTTPServerSpec(url="http://127.0.0.1:8080/mcp"),
            # An open-source server from the MCP project, started on demand.
            "time": StdioServerSpec(command="uvx", args=["mcp-server-time"]),
        },
        instructions=(
            "You are a friendly support assistant. Use your tools to answer "
            "questions about orders. Only offer help your tools can provide."
        ),
        trace_tools=True,
    )
    try:
        print("Tools:", agent.tool_names)
        result = await agent.ainvoke(
            {"messages": [{"role": "user", "content": "How many days until order A-1001 arrives?"}]}
        )
        print(result["messages"][-1].content)
    finally:
        await agent.shutdown()


asyncio.run(main())
Output
Tools: ['get_order_status', 'get_current_time', 'convert_time']
→ Invoking tool: get_order_status with {'order_id': 'A-1001'}
→ Invoking tool: get_current_time with {'timezone': 'Europe/Zurich'}
✔ Tool result from get_current_time: {
  "timezone": "Europe/Zurich",
  "datetime": "2026-10-10T17:20:24+02:00",
  "day_of_week": "Saturday",
  "is_dst": true
}
✔ Tool result from get_order_status: {"order_id": "A-1001", "status": "shipped", "carrier": "DHL", "eta": "2026-10-14"}
Order A-1001 is marked "shipped" with DHL and expected on 2026-10-14.

That’s 4 days from today (today is 2026-10-10 in Europe/Zurich). …

No single server could answer that question. The agent asked both at once, the orders server for the delivery date and the time server for today's date, then did the subtraction itself. Promptise runs independent tool calls in parallel, which is why both calls start before either result comes back. That's the real payoff of MCP: small, focused servers that combine into something more capable.

Rendering diagram…

Tip

Give every tool a distinct name across all your servers. If two servers both offer a tool with the same name, the agent keeps both and the model can't tell them apart.


[07]

Connect to an MCP server that needs an API key

A server on a network should know who's calling. Promptise servers can check an API key before they answer anything:

Pythonorders_server.py
1
2
3
4
5
6
7
8
import os

from promptise.mcp.server import APIKeyAuth, AuthMiddleware, MCPServer

server = MCPServer("orders", require_auth=True)
server.add_middleware(
    AuthMiddleware(APIKeyAuth(keys={os.environ["ORDERS_API_KEY"]: "support-agent"}))
)

require_auth=True puts every tool behind the key. A client without a valid key gets 401 Unauthorized and never sees the tool list.

On the agent side, pass the key to the spec. Promptise sends it in the x-api-key header:

Pythonagent.py
1
2
3
4
5
6
7
8
9
10
import os

from promptise.config import HTTPServerSpec

servers = {
    "orders": HTTPServerSpec(
        url="http://127.0.0.1:8080/mcp",
        api_key=os.environ["ORDERS_API_KEY"],
    ),
}

If your server uses JSON Web Tokens from an identity provider instead, pass bearer_token= and Promptise sends it as Authorization: Bearer …. Either way, keep keys in environment variables or a secret manager, never in your code. Authentication & Security covers JWTs, roles and per-tool guards.


[08]

When the agent doesn't use your tools

Most connection problems come down to one of these:

  • The tool is never called. The model doesn't understand when it's useful. Rewrite the docstring's first paragraph so it says plainly what the tool does and when to use it.

  • The stdio server isn't found. Relative paths in args are resolved from the folder you run the agent in. Use an absolute path, or set cwd= on StdioServerSpec.

  • The model offers things it can't do. Models like to be helpful. Say what the agent may and may not offer in instructions, and keep in mind that the model still words its own replies.

  • Nothing happens with a local model. The model has to support tool calling. With Ollama, pick a model that does.

  • The HTTP server refuses the connection. build_agent stops with an MCPConnectionRejectedError that names the server and the status, for example Server 'orders' rejected the connection: 401 Unauthorized. A 401 or 403 means the key or token on the spec doesn't match the server's. If the URL doesn't end in /mcp, the error reads Failed to connect to server 'orders': … (McpError: Session terminated) instead.


[09]

Frequently asked questions

What is an MCP server?

A program that offers tools, data or prompts to AI applications through the Model Context Protocol. Anything that speaks MCP can use it: your own agent, Claude Desktop, an IDE assistant. You write the integration once and every client can use it. What is MCP? explains the protocol in more depth.

Do I need Promptise to use MCP?

No. MCP is an open protocol, and any MCP client can connect to the servers in this guide. Promptise Foundry is a framework for the agent around those connections: reasoning, memory, guardrails, human approval and a runtime for production. Its MCP client is built in, so connecting servers takes one argument.

Which models can I use?

Any model with tool calling. Promptise ships with providers for OpenAI, Anthropic, Google Gemini and Vertex AI, Azure, Amazon Bedrock, Ollama, Mistral, Groq, OpenRouter and more. You switch by changing the model string, for example anthropic:claude-sonnet-4-5 or ollama:llama3.1.

Should I use stdio or HTTP?

Start with stdio: there's nothing to deploy and the agent manages the server for you. Move to HTTP when a server needs to be shared between agents, run on another machine, or sit behind authentication.

Can I turn my existing REST API into an MCP server?

Yes. If your API has an OpenAPI spec, MCPcast generates a complete MCP server from it, with read-only defaults and human approval on anything that writes.

Is it safe to let an agent call tools?

It's as safe as the server makes it. Keep each server's tools narrow, require authentication, and ask a human before anything that changes data. Promptise servers support all three, including approval gates that pause a tool call until someone says yes.


[10]

Where to go next

  • Quick Start: the shortest path from install to a working agent.

  • Server configuration: every option on StdioServerSpec and HTTPServerSpec.

  • MCP server fundamentals: resources, prompts, routers and middleware.

  • Deployment: running MCP servers in production.

  • Promptise Foundry on GitHub: the source, open under Apache 2.0.

Learning paths

Want more structure? Paths put guides in order, like a short course.

See the paths →

Keep going.

Browse every guide by topic and level, or follow a learning path that puts them in order.

All guidesLearning paths