<!--
Copyright 2026 The Dapr Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

# MCP Agent with STDIO Transport

This quickstart demonstrates how to build a simple agent that uses tools exposed via the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) over STDIO transport. You'll learn how to create MCP tools in a standalone module and connect to them using STDIO communication.

## Prerequisites

- uv package manager
- OpenAI API key
- Dapr CLI and Docker (for the state stores and pub/sub sidecars)

## Environment Setup

```bash
uv venv
# Activate the virtual environment
# On Windows:
.venv\Scripts\activate
# On macOS/Linux:
source .venv/bin/activate
uv sync --active
```

## Configuration

The quickstart ships with Dapr component templates under `resources/`. You can inject your OpenAI key via environment variables or by editing the component directly.

### Option 1: Using Environment Variables (Recommended)

1. Create a `.env` file in the project root and add your OpenAI API key:

```env
OPENAI_API_KEY=your_api_key_here
```

2. Export the environment variable before running:

```bash
# Load env vars from the repo-level .env if present
export $(grep -v '^#' ../../.env | xargs)
```

### Option 2: Direct Component Configuration

Update `resources/openai.yaml` by replacing the `value` for `name: key` with your API key. This approach is convenient for demos but avoid committing secrets to source control.

### Additional Components

Make sure Dapr is initialized:

```bash
dapr init
```

The `resources/` folder already includes Redis-backed state stores (`agentstatestore`, `agentregistrystore`, `conversationstore`), pub/sub, and workflow state components required by the durable agent.

## MCP Tools

`tools.py` defines the MCP tools using `FastMCP`. The STDIO transport starts this module as a subprocess automatically—no extra server process is required:

```python
mcp = FastMCP("TestServer")

@mcp.tool()
async def get_weather(location: str) -> str:
    temperature = random.randint(60, 80)
    return f"{location}: {temperature}F."
```

## Running the Example

1. Render the components and start the agent (as shown above).
2. In another terminal, send a request to the hosted endpoint:

```bash
curl -X POST http://localhost:8001/agent/run \
  -H "Content-Type: application/json" \
  -d '{"task": "What is the weather in New York?"}'
```

The agent connects to the MCP tools over STDIO, reasons about the query, invokes `get_weather`, and returns the final response. Conversation history and tool traces are persisted in the configured Dapr state stores.

## Key Concepts

### MCP Tool Definition

- The `@mcp.tool()` decorator registers functions as MCP tools
- Each tool has a docstring that helps the LLM understand its purpose

### STDIO Transport

- STDIO transport uses standard input/output streams for communication
- No network ports or HTTP servers are required for this transport
- Ideal for local development and testing

### Agent Setup with MCP Client

- The `MCPClient` class manages connections to MCP tool servers
- `connect_stdio()` starts a subprocess and establishes communication
- The client translates MCP tools into agent tools automatically

### Execution Flow

1. The agent renders Dapr components and starts the Workflow runtime.
2. `MCPClient.connect_stdio()` launches `tools.py` as a subprocess and discovers available tools.
3. An HTTP request to `/agent/run` triggers the durable workflow (via `AgentRunner.serve`).
4. The LLM decides whether to call an MCP tool.
5. Tool calls are dispatched over STDIO; results flow back into the conversation loop.
6. Final responses are returned to the HTTP caller and saved in Dapr state.

## Alternative: Using SSE Transport

While this quickstart uses STDIO transport, MCP also supports Server-Sent Events (SSE) for network-based communication. This approach is useful when:

- Tools need to run as separate services
- Tools are distributed across different machines
- You need long-running services that multiple agents can connect to

To explore SSE transport, check out the related [MCP with SSE Transport quickstart](../07-agent-mcp-client-sse).

## Troubleshooting

1. **OpenAI API Key**: Ensure the key is present in `.env` or baked into `openai.yaml`.
2. **STDIO communication**: If the agent hangs while loading tools, run `python tools.py` manually to confirm it starts.
3. **Dapr Components**: Redis must be running (provided by `dapr init`). Check the sidecar logs if you see `state store ... is not found`.
4. **gRPC Deadline**: For long prompts/responses set `DAPR_API_TIMEOUT_SECONDS=300` so the Dapr client waits beyond the 60 s default.
5. **Module Imports**: Run `pip install -r requirements.txt` to install `dapr-agents`, `fastmcp`, and other dependencies.

## Next Steps

After completing this quickstart, you might want to explore:

- Checkout SSE transport example [MCP with SSE Transport quickstart](../07-agent-mcp-client-sse).
- Exploring the [MCP specification](https://modelcontextprotocol.io/) for advanced usage
