> ## Documentation Index
> Fetch the complete documentation index at: https://docs.barndoor.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# CrewAI

# Running a CrewAI Agent with Barndoor

This guide walks you through how to run a CrewAI task using the Barndoor SDK to connect to an MCP server (like Notion), fetch connection parameters, and use the MCP tools within a CrewAI agent.

Feel free to checkout [the repo](https://github.com/barndoor-ai/crew-ai-python-example) here and also run the app with Streamlit for a browser UI demo

## 📹 Demo: Running CrewAI with Barndoor MCP

<iframe
  src="https://player.vimeo.com/video/1108252984?h=694c676d9f&badge=0&autopause=0&player_id=0&app_id=58479&autoplay=1&loop=1"
  title="Embedded content"
  style={{
width: "100%",
height: "500px",
borderRadius: "12px"
}}
/>

## Prerequisites

* Python 3.10+
* A Barndoor account
* An MCP server registered with Barndoor (connected/authenticated)
* AI agent registered with Barndoor

## Source

<Expandable title="Code Example">
  ```python theme={null}
  import streamlit as st
  from datetime import datetime
  from pathlib import Path
  from dotenv import load_dotenv
  import barndoor.sdk as bd
  import asyncio
  from crewai import Agent, Task, Crew
  from crewai_tools import MCPServerAdapter



  # ─────────────────────────────────────────────
  # Page Config
  # ─────────────────────────────────────────────
  st.set_page_config(page_title="Barndoor + CrewAI Assistant", layout="centered", page_icon="Robot")
  st.title("Barndoor MCP + CrewAI Universal Assistant")
  st.markdown("Connect any app — Notion, Salesforce, Gmail, Slack, GitHub, Box — and get real results.")


  # ─────────────────────────────────────────────
  # Core async function (DO NOT use asyncio.run!)
  # ─────────────────────────────────────────────
  async def run_crewai_task(server_slug: str, user_query: str, log):
      load_dotenv(Path(__file__).parent / ".env")
      sdk = None

      try:
          log("Logging in to Barndoor...")
          sdk = await bd.login_interactive()
          log("Logged in!")

          servers = await sdk.list_servers()
          server = next((s for s in servers if s.slug == server_slug), None)
          if not server:
              raise ValueError(f"Server '{server_slug}' not found.")

          display_name = (
              server.name
              or getattr(server.mcp_server_directory, "name", None)
              or server_slug.replace("-", " ").title()
          )
          log(f"Using: **{display_name}**")

          await bd.ensure_server_connected(sdk, server_slug)
          params, public_url = await bd.make_mcp_connection_params(sdk, server_slug)

          # Fix token → authorization (handles both formats)
          if "token" in params:
              params["authorization"] = f"Bearer {params.pop('token')}"
          elif "authorization" in params and not params["authorization"].startswith("Bearer "):
              params["authorization"] = f"Bearer {params['authorization']}"

          log("Connected! Loading tools...")

          with MCPServerAdapter(params) as tools:
              log(f"Loaded **{len(tools)} tools**")

              agent = Agent(
                  role=f"{display_name} Expert",
                  goal=f"Complete any task in the user's {display_name} account using real data.",
                  backstory=f"You are a master of {display_name} with full access via Barndoor MCP.",
                  tools=tools,
                  verbose=True,
                  allow_delegation=False,
              )

              task = Task(
                  description=user_query,
                  expected_output="Clear, accurate, well-formatted answer. Use tables/lists when helpful.",
                  agent=agent,
              )

              crew = Crew(agents=[agent], tasks=[task], verbose=True)
              log("Running task...")
              result = await crew.kickoff_async()

          log("Done!")
          return str(result), display_name

      except Exception as e:
          error_msg = f"Error: {type(e).__name__}: {e}"
          log(error_msg)
          return error_msg, "Error"
      finally:
          if sdk:
              await sdk.aclose()


  # ─────────────────────────────────────────────
  # Load servers once at startup
  # ─────────────────────────────────────────────
  @st.cache_data(ttl=300)  # Refresh every 5 minutes
  def load_servers():
      import asyncio
      load_dotenv()
      loop = asyncio.new_event_loop()
      asyncio.set_event_loop(loop)
      try:
          sdk = loop.run_until_complete(bd.login_interactive())
          servers = loop.run_until_complete(sdk.list_servers())
          loop.run_until_complete(sdk.aclose())

          options = {}
          for s in servers:
              if s.connection_status in ("connected", "available"):
                  name = (
                      s.name
                      or getattr(s.mcp_server_directory, "name", None)
                      or s.slug.replace("-", " ").title()
                  )
                  options[f"{name} • {s.slug}"] = s.slug
          return options or {"No servers found": None}
      finally:
          loop.close()


  # ─────────────────────────────────────────────
  # Main UI
  # ─────────────────────────────────────────────
  servers = load_servers()

  if not servers or list(servers.values())[0] is None:
      st.error("No connected MCP servers found. Go to https://app.barndoor.ai and connect an app.")
      st.stop()

  server_choice = st.selectbox("Choose your app:", options=list(servers.keys()))
  selected_slug = servers[server_choice]

  query = st.text_area(
      "What do you want to do?",
      placeholder="Examples:\n• List my recent Salesforce opportunities\n• Summarize Notion pages tagged 'Q4'\n• Show unread Gmail from last 3 days",
      height=140,
  )

  if st.button("Run Agent", type="primary", use_container_width=True):
      if not query.strip():
          st.warning("Please enter a task.")
          st.stop()

      log_placeholder = st.empty()
      log_lines = []

      def log(msg):
          ts = datetime.now().strftime("%H:%M:%S")
          log_lines.append(f"<small>{ts}</small> {msg}")
          log_placeholder.markdown("\n".join(log_lines), unsafe_allow_html=True)

      with st.spinner("Working..."):
          # This is the CORRECT way in Streamlit
          result, app_name = asyncio.run(run_crewai_task(selected_slug, query, log))

      st.markdown("---")
      st.subheader(f"Result from {app_name}")

      if result.startswith("Error:"):
          st.error(result)
      else:
          st.markdown(result)

      st.caption(f"Completed • {datetime.now():%Y-%m-%d %I:%M %p}")


  ```
</Expandable>

###

### 1. Install Dependencies

```bash theme={null}
uv pip install -r requirements.txt 
```

### 2. Create a .env File

This script expects a .env file in the same directory as main.py. Add the following environment variables:

```bash theme={null}
# Replace with your agent's client ID and secret from Barndoor
# See: https://app.barndoor.ai/agents 
AGENT_CLIENT_ID={{barndoor-agent-client-id}}
AGENT_CLIENT_SECRET={{barndoor-agent-client-secret}}

# Replace with your tenant hostname
BARNDOOR_API=https://{{your-tenant}}.platform.barndoor.ai
BARNDOOR_URL=https://{{your-tenant}}.platform.barndoor.ai

# Valid OpenAI API Key
# see: https://platform.openai.com/api-keys
OPENAI_API_KEY={{your-openapi-key}}

# Default values, no changes required
AUTH_DOMAIN=auth.barndoor.ai
API_AUDIENCE=https://barndoor.ai/
MODE=production
```

Replace the placeholders with real values from your Barndoor project.

### 3. Run either Crew CLI or UI (browser) version:

```bash theme={null}
uv run crew-cli.py

or

uv run streamlit run crew-ui.py
```

🛠️ Troubleshooting

* Missing .env values: Make sure all required keys are present.
  -using a stale token.  try `rm ~/.barndoor/token.json` from root of project
* Invalid server slug: Double-check SERVER\_SLUG = "notion" matches your Barndoor dashboard.
* OAuth login doesn’t open: Manually open the URL shown in the terminal.
