# Basic FastAPI Server (/cookbooks/fast-api)

This cookbook will guide you through building agents equipped with tools using `Devcaster`, `OpenAI`, and `FastAPI`.

Prerequisites [#prerequisites]

* Python 3.10+
* [UV](https://docs.astral.sh/uv/getting-started/installation/)
* Devcaster API key
* OpenAI API key

Building an AI agent that can interact with gmail service [#building-an-ai-agent-that-can-interact-with-gmail-service]

First, let's start with building a simple AI agent with Devcaster tools that lets the agent interact with `gmail`.

```python
from openai import OpenAI
from devcaster import Devcaster
from devcaster_openai import OpenAIProvider
import os

def run_gmail_agent(
    devcaster_client: Devcaster[OpenAIProvider],
    openai_client: OpenAI,
    user_id: str,  # Devcaster uses the User ID to store and access user-level authentication tokens.
    prompt: str,
):
    """
    Run the Gmail agent using devcaster and openai clients.
    """
    # Step 1: Fetch the necessary Gmail tools list with Devcaster
    tools = devcaster_client.tools.get(
        user_id=user_id,
        tools=[
            "GMAIL_FETCH_EMAILS",
            "GMAIL_SEND_EMAIL",
            "GMAIL_CREATE_EMAIL_DRAFT"
        ]
    )

    # Step 2: Use OpenAI to generate a response based on the prompt and available tools
    response = openai_client.chat.completions.create(
        model="gpt-4.1",
        tools=tools,
        messages=[{"role": "user", "content": prompt}],
    )

    # Step 3: Handle tool calls with Devcaster and return the result
    result = devcaster_client.provider.handle_tool_calls(response=response, user_id=user_id)
    return result
```

> This example demonstrates a basic agent without state management or agentic loops,
suitable for simple one-step tasks. For complex multi-step workflows requiring
memory and iterative reasoning, see our cookbooks with frameworks like LangChain,
CrewAI, or AutoGen.

To invoke this agent, authenticate your users with Devcaster's managed authentication service.

Authenticating users [#authenticating-users]

To authenticate your users with Devcaster you need an authentication config for the given app. In this case you need one for gmail.

To create an authentication config for `gmail` you need `client_id` and `client_secret` from your [Google OAuth Console](https://developers.google.com/identity/protocols/oauth2). Once you have the credentials, use the following piece of code to set up authentication for `gmail`.

```python
from devcaster import Devcaster
from devcaster_openai import OpenAIProvider

def create_auth_config(devcaster_client: Devcaster[OpenAIProvider]):
    """
    Create a auth config for the gmail toolkit.
    """
    client_id = os.getenv("GMAIL_CLIENT_ID")
    client_secret = os.getenv("GMAIL_CLIENT_SECRET")
    if not client_id or not client_secret:
        raise ValueError("GMAIL_CLIENT_ID and GMAIL_CLIENT_SECRET must be set")

    return devcaster_client.auth_configs.create(
        toolkit="GMAIL",
        options={
            "name": "default_gmail_auth_config",
            "type": "use_custom_auth",
            "auth_scheme": "OAUTH2",
            "credentials": {
                "client_id": client_id,
                "client_secret": client_secret,
            },
        },
    )
```

This will create a Gmail authentication config to authenticate your app’s users. Ideally, create one authentication object per project, so check for an existing auth config before creating a new one.

```python
def fetch_auth_config(devcaster_client: Devcaster[OpenAIProvider]):
    """
    Fetch the auth config for a given user id.
    """
    auth_configs = devcaster_client.auth_configs.list()
    for auth_config in auth_configs.items:
        if auth_config.toolkit == "GMAIL":
            return auth_config

    return None
```

> Devcaster platform provides devcaster managed authentication for some apps to
fast-track your development, `gmail` being one of them. You can use these
default auth configs for development, but for production, always use your
own oauth app configuration.

Once you have authentication management in place, we can start with connecting your users to your `gmail` app. Let's implement a function to connect users to your `gmail` app via devcaster.

```python
from fastapi import FastAPI

# Function to initiate a connected account
def create_connection(devcaster_client: Devcaster[OpenAIProvider], user_id: str):
    """
    Create a connection for a given user id and auth config id.
    """
    # Fetch or create the auth config for the gmail toolkit
    auth_config = fetch_auth_config(devcaster_client=devcaster_client)
    if not auth_config:
        auth_config = create_auth_config(devcaster_client=devcaster_client)

    # Create a connection for the user
    return devcaster_client.connected_accounts.initiate(
        user_id=user_id,
        auth_config_id=auth_config.id,
    )

# Setup FastAPI
app = FastAPI()

# Connection initiation endpoint
@app.post("/connection/create")
def _create_connection(
    request: CreateConnectionRequest,
    devcaster_client: DevcasterClient,
) -> dict:
    """
    Create a connection for a given user id.
    """
    # For demonstration, using a default user_id. Replace with real user logic in production.
    user_id = "default"

    # Create a new connection for the user
    connection_request = create_connection(devcaster_client=devcaster_client, user_id=user_id)
    return {
        "connection_id": connection_request.id,
        "redirect_url": connection_request.redirect_url,

```

Now, you can make a request to this endpoint on your client app, and your user will get a URL which they can use to authenticate.

Set Up FastAPI service [#set-up-fastapi-service]

We will use [`FastApi`](https://fastapi.tiangolo.com/) to build an HTTP service that authenticates your users and lets them interact with your agent. This guide will provide best practices for using devcaster client in production environments.

Setup dependencies [#setup-dependencies]

FastAPI allows [dependency injection](https://fastapi.tiangolo.com/reference/dependencies/) to simplify the usage of SDK clients that must be singletons. We recommend using devcaster SDK client as singleton.

```python
import os
import typing_extensions as te

from devcaster import Devcaster
from devcaster_openai import OpenAIProvider

from fastapi import Depends

_devcaster_client: Devcaster[OpenAIProvider] | None = None

def provide_devcaster_client():
    """
    Provide a Devcaster client.
    """
    global _devcaster_client
    if _devcaster_client is None:
        _devcaster_client = Devcaster(provider=OpenAIProvider())
    return _devcaster_client

DevcasterClient = te.Annotated[Devcaster, Depends(provide_devcaster_client)]
"""
A Devcaster client dependency.
"""
```

Invoke agent via FastAPI [#invoke-agent-via-fastapi]

When invoking an agent, make sure you validate the `user_id`.

```python
def check_connected_account_exists(
    devcaster_client: Devcaster[OpenAIProvider],
    user_id: str,
):
    """
    Check if a connected account exists for a given user id.
    """
    # Fetch all connected accounts for the user
    connected_accounts = devcaster_client.connected_accounts.list(
        user_ids=[user_id],
        toolkit_slugs=["GMAIL"],
    )

    # Check if there's an active connected account
    for account in connected_accounts.items:
        if account.status == "ACTIVE":
            return True

        # Ideally you should not have inactive accounts, but if you do, delete them.
        print(f"[warning] inactive account {account.id} found for user id: {user_id}")
    return False

def validate_user_id(user_id: str, devcaster_client: DevcasterClient):
    """
    Validate the user id, if no connected account is found, create a new connection.
    """
    if check_connected_account_exists(devcaster_client=devcaster_client, user_id=user_id):
        return user_id

    raise HTTPException(
        status_code=404, detail={"error": "No connected account found for the user id"}
    )

# Endpoint: Run the Gmail agent for a given user id and prompt
@app.post("/agent")
def _run_gmail_agent(
    request: RunGmailAgentRequest,
    devcaster_client: DevcasterClient,
    openai_client: OpenAIClient,  # OpenAI client will be injected as dependency
) -> List[ToolExecutionResponse]:
    """
    Run the Gmail agent for a given user id and prompt.
    """
    # For demonstration, using a default user_id. Replace with real user logic in production.
    user_id = "default"

    # Validate the user id before proceeding
    user_id = validate_user_id(user_id=user_id, devcaster_client=devcaster_client)

    # Run the Gmail agent using Devcaster and OpenAI
    result = run_gmail_agent(
        devcaster_client=devcaster_client,
        openai_client=openai_client,
        user_id=user_id,
        prompt=request.prompt,
    )
    return result
```

Putting everything together [#putting-everything-together]

So far, we have created an agent with ability to interact with `gmail` using the `devcaster` SDK, functions to manage connected accounts for users and a FastAPI service. Now let's run the service.

1. Clone the repository
```bash
git clone git@github.com:devcasterhq/devcaster-fastapi
cd devcaster-fastapi/
```
2. Setup environment

```bash
cp .env.example .env
```

Fill the api keys

```dotenv
DEVCASTER_API_KEY=
OPENAI_API_KEY=
```

Create the virtual env

```bash
make env
source .venv/bin/activate
```
3. Run the HTTP server
```bash
uvicorn simple_gmail_agent.server.api:create_app --factory
```

Testing the API with curl [#testing-the-api-with-curl]

Assuming the server is running locally on `http://localhost:8000`.

Check if a connection exists [#check-if-a-connection-exists]

```bash
curl -X POST http://localhost:8000/connection/exists
```

Create a connection [#create-a-connection]

Note: The body fields are required by the API schema, but are ignored internally in this example service.

```bash
curl -X POST http://localhost:8000/connection/create \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "default",
    "auth_config_id": "AUTH_CONFIG_ID_FOR_GMAIL_FROM_THE_DEVCASTER_DASHBOARD"
  }'
```

Response includes `connection_id` and `redirect_url`. Complete the OAuth flow at the `redirect_url`.

Check connection status [#check-connection-status]

Use the `connection_id` returned from the create step.

```bash
curl -X POST http://localhost:8000/connection/status \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "default",
    "connection_id": "CONNECTION_ID_FROM_CREATE_RESPONSE"
  }'
```

Run the Gmail agent [#run-the-gmail-agent]

Requires an active connected account for the `default` user.

```bash
curl -X POST http://localhost:8000/agent \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "default",
    "prompt": "Summarize my latest unread emails from the last 24 hours."
  }'
```

Fetch emails (direct action) [#fetch-emails-direct-action]

```bash
curl -X POST http://localhost:8000/actions/fetch_emails \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "default",
    "limit": 5
  }'
```

These examples are intended solely for testing purposes.

Using Devcaster for managed auth and tools [#using-devcaster-for-managed-auth-and-tools]

Devcaster reduces boilerplate for building AI agents that access and use various apps. In this cookbook, to build Gmail integration without Devcaster, you would have to write code to

* manage Gmail OAuth app
* manage user connections
* tools for your agents to interact with Gmail

Using Devcaster simplifies all of the above to a few lines of code as shown in the cookbook.

Best practices [#best-practices]

**🎯 Effective Prompts**:

* Be specific: "Send email to [john@company.com](mailto:john@company.com) about tomorrow's 2pm meeting" works better than "send email"
* Include context: "Reply to Sarah's email about the budget with our approval"
* Use natural language: The agent understands conversational requests

**🔒 User Management**:

* Use unique, consistent `user_id` values for each person
* Each user maintains their own Gmail connection
* User IDs can be email addresses, usernames, or any unique identifier

Troubleshooting [#troubleshooting]

**Connection Issues**:

* Ensure your `.env` file has valid `DEVCASTER_API_KEY` and `OPENAI_API_KEY`
* Check if the user has completed Gmail authorization.
* Verify the user\_id matches exactly between requests

**API Errors**:

* Check the server logs for detailed error messages
* Ensure request payloads match the expected format
* Visit `/docs` endpoint for API schema validation

**Gmail API Limits**:

* Gmail has rate limits; the agent will handle these gracefully
* For high-volume usage, consider implementing request queuing

---

📚 **More documentation:** [View all docs](https://docs.devcaster.dev/llms.txt) | [Cookbooks](https://docs.devcaster.dev/llms.mdx/cookbooks) | [API Reference](https://docs.devcaster.dev/llms.mdx/reference)

---

# Devcaster SDK — Instructions for AI Code Generators

**Purpose:** Enforce only the **current** and **correct** patterns for integrating [Devcaster](https://devcaster.dev/) tools into AI agents.
**Scope:** All AI-generated code using Devcaster must follow these guardrails.

---

## 1. Correct Devcaster Integration

Devcaster supports two integration modes: **Native Tools** (with a provider package) and **MCP** (no provider package needed).

### Native Tools

```python
# ✅ CORRECT — Python (defaults to OpenAI)
from devcaster import Devcaster

devcaster = Devcaster()
session = devcaster.create(user_id="user_123")
tools = session.tools()
# Pass tools to your agent/LLM framework
```

```typescript
// ✅ CORRECT — TypeScript (defaults to OpenAI)
import { Devcaster } from "@devcaster/core";

const devcaster = new Devcaster();
const session = await devcaster.create("user_123");
const tools = await session.tools();
// Pass tools to your agent/LLM framework
```

For other providers, pass the provider explicitly. Provider packages follow the naming convention: `devcaster_<provider>` for Python, `@devcaster/<provider>` for TypeScript.

### MCP

Use `session.mcp.url` and `session.mcp.headers` with any MCP-compatible client (Claude Desktop, Cursor, OpenAI Agents, etc.). No provider package needed.

---

## 2. CRITICAL INSTRUCTIONS FOR AI MODELS

### 2.1 — ALWAYS DO THE FOLLOWING

1. **Use `devcaster.create(user_id)`** to create a session — this is the entry point for all Devcaster integrations.
2. **Use `session.tools()`** for native tool integration or **`session.mcp.url`** for MCP integration.
3. **Import** from the correct provider package (`devcaster_<provider>` for Python, `@devcaster/<provider>` for TypeScript).
4. **Pass `user_id` to `devcaster.create()`**, not to individual tool calls.

### 2.2 — NEVER DO THE FOLLOWING

1. **Do not** instruct users to manually create auth configs or connected accounts before running.
2. **Do not** import from `devcaster_openai` / `@devcaster/openai` when using OpenAI Agents SDK — use `devcaster_openai_agents` / `@devcaster/openai-agents` instead.

### 2.3 — DISCOURAGED (do not use unless user explicitly asks)

Do **not** generate `devcaster.tools.get()`, `devcaster.tools.execute()`, or `devcaster.provider.handle_tool_calls()` unless the user explicitly requests direct tool execution. These are a supported but **not recommended** low-level interface.

