Daydreams Logo
Concepts

Model Context Protocol (MCP)

Connect your agent to any MCP server for expanded capabilities and context.

What is MCP Integration?

Model Context Protocol (MCP) lets your agent connect to external services and data sources through a standardized interface. Think of it like adding superpowers from other applications to your agent.

Your agent becomes a headless MCP client that can:

  • Connect to any MCP server (local or remote)
  • Access their resources, tools, and prompts
  • Use these capabilities seamlessly alongside your other actions
  • Scale by connecting to multiple servers simultaneously

Real Examples

Here's what MCP servers can provide to your agent:

Database Explorer

sqlite-mcp-connection.ts
// MCP server provides database access
// → Agent can query any SQLite database
// → No need to write database connection code
 
const result = await ctx.callAction("mcp.callTool", {
  serverId: "sqlite-explorer",
  name: "query",
  arguments: {
    sql: "SELECT * FROM users WHERE active = 1",
  },
});
 
// Returns: User data from the database

Web Search Service

web-search-mcp.ts
// MCP server provides web search capabilities
// → Agent can search the internet
// → Get real-time information
 
const result = await ctx.callAction("mcp.callTool", {
  serverId: "web-search",
  name: "search",
  arguments: {
    query: "latest OpenAI announcements",
    maxResults: 5,
  },
});
 
// Returns: Current search results

File System Access

filesystem-mcp.ts
// MCP server provides file system access
// → Agent can read/write files
// → Access local documents and data
 
const content = await ctx.callAction("mcp.readResource", {
  serverId: "filesystem",
  uri: "file:///project/README.md",
});
 
// Returns: File contents

The Problem: Isolated Agent Capabilities

Without MCP, your agent is limited to what you explicitly code:

limited-agent-capabilities.ts
// ❌ Without MCP - every capability needs custom implementation
 
// Want database access? Write database code
const db = new Database(connectionString);
const users = await db.query("SELECT * FROM users");
 
// Want web search? Build web scraping
const response = await fetch(`https://api.search.com/q=${query}`);
const results = await response.json();
 
// Want file access? Handle file I/O
const content = await fs.readFile(filepath, "utf-8");
 
// Want weather data? Build weather client
const weather = await fetch(`https://api.weather.com/${city}`);
 
// Problems:
// 🔧 Manual integration for every data source
// 🐛 Custom error handling for each service
// 📚 Learning different APIs for each capability
// 🔄 Maintaining multiple integrations

The Solution: MCP Provides Universal Access

With MCP, your agent connects to any data source through one interface:

mcp-universal-access.ts
// ✅ With MCP - universal interface for all capabilities
 
// Database access through MCP
const users = await ctx.callAction("mcp.callTool", {
  serverId: "database",
  name: "query",
  arguments: { sql: "SELECT * FROM users" },
});
 
// Web search through MCP
const searchResults = await ctx.callAction("mcp.callTool", {
  serverId: "search",
  name: "web-search",
  arguments: { query: "OpenAI news" },
});
 
// File access through MCP
const fileContent = await ctx.callAction("mcp.readResource", {
  serverId: "filesystem",
  uri: "file:///project/data.json",
});
 
// Weather data through MCP
const weather = await ctx.callAction("mcp.callTool", {
  serverId: "weather",
  name: "current-conditions",
  arguments: { city: "San Francisco" },
});
 
// Benefits:
// 🎯 Same interface for all data sources
// 🛡️ Consistent error handling
// 📖 One API pattern to learn
// ⚡ Pre-built server integrations

How MCP Works with Daydreams

MCP integration happens through the extension system:

mcp-integration-flow.ts
// 1. Add MCP extension to your agent
const agent = createDreams({
  extensions: [
    createMcpExtension([
      {
        id: "my-database",
        name: "SQLite Explorer",
        transport: {
          type: "stdio",
          command: "npx",
          args: ["@modelcontextprotocol/server-sqlite", "path/to/database.db"],
        },
      },
      {
        id: "web-search",
        name: "Search Service",
        transport: {
          type: "sse",
          serverUrl: "http://localhost:3001",
        },
      },
    ]),
  ],
});
 
// 2. Agent automatically gets MCP actions:
// - mcp.listServers
// - mcp.listTools
// - mcp.callTool
// - mcp.listResources
// - mcp.readResource
// - mcp.listPrompts
// - mcp.getPrompt
 
// 3. Use MCP capabilities in any action
const weatherAction = action({
  name: "get-weather",
  handler: async ({ city }, ctx) => {
    // Call MCP weather server
    const weather = await ctx.callAction("mcp.callTool", {
      serverId: "weather-service",
      name: "current",
      arguments: { location: city },
    });
 
    return `Weather in ${city}: ${weather.result.description}`;
  },
});

MCP Transport Types

MCP supports two connection methods:

Local Servers (stdio)

For servers running as separate processes:

stdio-transport.ts
// Local MCP server via command line
{
  id: "sqlite-db",
  name: "SQLite Database",
  transport: {
    type: "stdio",
    command: "npx",
    args: ["@modelcontextprotocol/server-sqlite", "./data.db"]
  }
}
 
// Local Python MCP server
{
  id: "python-analysis",
  name: "Data Analysis Server",
  transport: {
    type: "stdio",
    command: "python",
    args: ["analysis_server.py"]
  }
}
 
// Local Node.js MCP server
{
  id: "file-system",
  name: "File System Access",
  transport: {
    type: "stdio",
    command: "node",
    args: ["filesystem-server.js"]
  }
}

Remote Servers (SSE)

For servers running as web services:

sse-transport.ts
// Remote MCP server via HTTP
{
  id: "cloud-search",
  name: "Cloud Search Service",
  transport: {
    type: "sse",
    serverUrl: "https://search-api.example.com"
  }
}
 
// Local development server
{
  id: "dev-database",
  name: "Development Database",
  transport: {
    type: "sse",
    serverUrl: "http://localhost:3001",
    sseEndpoint: "/events",      // Optional
    messageEndpoint: "/messages" // Optional
  }
}

Working with MCP Capabilities

Discover Available Tools

discover-mcp-tools.ts
// List all connected MCP servers
const servers = await ctx.callAction("mcp.listServers", {});
console.log("Connected servers:", servers.servers);
 
// Discover tools on a specific server
const tools = await ctx.callAction("mcp.listTools", {
  serverId: "database-server",
});
 
console.log("Available tools:", tools.tools);
// Output: [
//   { name: "query", description: "Execute SQL query" },
//   { name: "schema", description: "Get table schema" },
//   { name: "insert", description: "Insert new record" }
// ]

Use Server Tools

use-mcp-tools.ts
// Execute a database query
const queryResult = await ctx.callAction("mcp.callTool", {
  serverId: "database-server",
  name: "query",
  arguments: {
    sql: "SELECT name, email FROM users WHERE active = 1 LIMIT 10",
  },
});
 
if (queryResult.error) {
  console.error("Query failed:", queryResult.error);
} else {
  console.log("Query results:", queryResult.result);
}
 
// Call a web search tool
const searchResult = await ctx.callAction("mcp.callTool", {
  serverId: "search-server",
  name: "web-search",
  arguments: {
    query: "Daydreams AI framework tutorial",
    maxResults: 5,
    safeSearch: true,
  },
});

Access Server Resources

access-mcp-resources.ts
// List available resources
const resources = await ctx.callAction("mcp.listResources", {
  serverId: "filesystem-server",
});
 
console.log("Available resources:", resources.resources);
 
// Read a specific resource
const fileContent = await ctx.callAction("mcp.readResource", {
  serverId: "filesystem-server",
  uri: "file:///project/config.json",
});
 
console.log("File content:", fileContent.resource.contents[0].text);
 
// Read a database schema resource
const schema = await ctx.callAction("mcp.readResource", {
  serverId: "database-server",
  uri: "schema://users",
});

Use Server Prompts

use-mcp-prompts.ts
// List available prompts
const prompts = await ctx.callAction("mcp.listPrompts", {
  serverId: "analysis-server",
});
 
// Get a specific prompt with arguments
const analysisPrompt = await ctx.callAction("mcp.getPrompt", {
  serverId: "analysis-server",
  name: "analyze-data",
  arguments: {
    dataType: "sales",
    timeframe: "last-quarter",
  },
});
 
// Use the prompt content with your LLM
const llmResponse = await generateText({
  model: openai("gpt-4"),
  prompt: analysisPrompt.prompt.messages[0].content.text,
});

Real-World Examples

Customer Support Agent

customer-support-with-mcp.ts
const customerSupportAgent = createDreams({
  extensions: [
    createMcpExtension([
      {
        id: "crm-database",
        name: "Customer Database",
        transport: {
          type: "stdio",
          command: "npx",
          args: ["@company/crm-mcp-server"],
        },
      },
      {
        id: "knowledge-base",
        name: "Support Knowledge Base",
        transport: {
          type: "sse",
          serverUrl: "https://kb.company.com/mcp",
        },
      },
    ]),
  ],
 
  actions: [
    action({
      name: "handle-support-ticket",
      handler: async ({ ticketId, customerEmail }, ctx) => {
        // Get customer history from CRM
        const customerData = await ctx.callAction("mcp.callTool", {
          serverId: "crm-database",
          name: "get-customer",
          arguments: { email: customerEmail },
        });
 
        // Search knowledge base for solutions
        const solutions = await ctx.callAction("mcp.callTool", {
          serverId: "knowledge-base",
          name: "search-solutions",
          arguments: {
            query: customerData.result.lastIssueCategory,
            limit: 3,
          },
        });
 
        return {
          customer: customerData.result,
          suggestedSolutions: solutions.result,
          escalate:
            customerData.result.tier === "premium" &&
            solutions.result.length === 0,
        };
      },
    }),
  ],
});

Trading Analytics Agent

trading-agent-with-mcp.ts
const tradingAgent = createDreams({
  extensions: [
    createMcpExtension([
      {
        id: "market-data",
        name: "Market Data Provider",
        transport: {
          type: "sse",
          serverUrl: "wss://market-data.example.com/mcp",
        },
      },
      {
        id: "portfolio-db",
        name: "Portfolio Database",
        transport: {
          type: "stdio",
          command: "python",
          args: ["portfolio_server.py"],
        },
      },
    ]),
  ],
 
  actions: [
    action({
      name: "analyze-portfolio",
      handler: async ({ portfolioId }, ctx) => {
        // Get current portfolio positions
        const positions = await ctx.callAction("mcp.callTool", {
          serverId: "portfolio-db",
          name: "get-positions",
          arguments: { portfolioId },
        });
 
        // Get real-time market data for each position
        const marketData = await Promise.all(
          positions.result.map((position) =>
            ctx.callAction("mcp.callTool", {
              serverId: "market-data",
              name: "get-quote",
              arguments: { symbol: position.symbol },
            })
          )
        );
 
        // Calculate portfolio performance
        const analysis = {
          totalValue: 0,
          dayChange: 0,
          positions: positions.result.map((position, i) => ({
            ...position,
            currentPrice: marketData[i].result.price,
            dayChange: marketData[i].result.change,
          })),
        };
 
        return analysis;
      },
    }),
  ],
});

Error Handling Best Practices

Graceful Fallbacks

mcp-error-handling.ts
const robustAction = action({
  name: "get-data-with-fallback",
  handler: async ({ query }, ctx) => {
    // Try primary MCP server first
    let result = await ctx.callAction("mcp.callTool", {
      serverId: "primary-search",
      name: "search",
      arguments: { query },
    });
 
    if (result.error) {
      console.warn("Primary search failed, trying backup:", result.error);
 
      // Fallback to secondary server
      result = await ctx.callAction("mcp.callTool", {
        serverId: "backup-search",
        name: "search",
        arguments: { query },
      });
    }
 
    if (result.error) {
      // Final fallback to local search
      return {
        results: [],
        source: "local-cache",
        message: "External search unavailable",
      };
    }
 
    return {
      results: result.result,
      source: result.error ? "backup" : "primary",
    };
  },
});

Connection Monitoring

mcp-connection-monitoring.ts
const monitorConnections = action({
  name: "check-mcp-health",
  handler: async (args, ctx) => {
    const servers = await ctx.callAction("mcp.listServers", {});
 
    const healthChecks = await Promise.all(
      servers.servers.map(async (server) => {
        try {
          // Try to list tools as a health check
          const tools = await ctx.callAction("mcp.listTools", {
            serverId: server.id,
          });
 
          return {
            serverId: server.id,
            name: server.name,
            status: tools.error ? "error" : "healthy",
            error: tools.error,
          };
        } catch (error) {
          return {
            serverId: server.id,
            name: server.name,
            status: "disconnected",
            error: error.message,
          };
        }
      })
    );
 
    return {
      timestamp: new Date().toISOString(),
      servers: healthChecks,
    };
  },
});

Available MCP Servers

The MCP ecosystem includes servers for common use cases:

Official Servers

official-mcp-servers.ts
// SQLite database access
{
  id: "sqlite",
  name: "SQLite Database",
  transport: {
    type: "stdio",
    command: "npx",
    args: ["@modelcontextprotocol/server-sqlite", "./database.db"]
  }
}
 
// File system access
{
  id: "filesystem",
  name: "File System",
  transport: {
    type: "stdio",
    command: "npx",
    args: ["@modelcontextprotocol/server-filesystem", "./data"]
  }
}
 
// Git repository access
{
  id: "git",
  name: "Git Repository",
  transport: {
    type: "stdio",
    command: "npx",
    args: ["@modelcontextprotocol/server-git", "./repo"]
  }
}

Community Servers

community-mcp-servers.ts
// Web search capabilities
{
  id: "web-search",
  name: "Web Search",
  transport: {
    type: "stdio",
    command: "python",
    args: ["-m", "mcp_server_web_search"]
  }
}
 
// AWS services access
{
  id: "aws",
  name: "AWS Services",
  transport: {
    type: "stdio",
    command: "node",
    args: ["aws-mcp-server.js"]
  }
}

Building Custom MCP Servers

You can create custom MCP servers for your specific needs:

custom-mcp-server.ts
// Simple MCP server example
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
 
const server = new McpServer({
  name: "Custom Business Server",
  version: "1.0.0",
});
 
// Add a tool for business logic
server.tool(
  "calculate-roi",
  {
    investment: z.number(),
    returns: z.number(),
    timeframe: z.number(),
  },
  async ({ investment, returns, timeframe }) => {
    const roi = ((returns - investment) / investment) * 100;
    const annualizedRoi = roi / timeframe;
 
    return {
      content: [
        {
          type: "text",
          text: JSON.stringify({
            roi: roi.toFixed(2),
            annualizedRoi: annualizedRoi.toFixed(2),
            profitable: roi > 0,
          }),
        },
      ],
    };
  }
);
 
// Add a resource for business data
server.resource("business-metrics", async () => ({
  contents: [
    {
      uri: "metrics://quarterly",
      text: JSON.stringify({
        revenue: 1000000,
        expenses: 750000,
        profit: 250000,
        customers: 5000,
      }),
    },
  ],
}));
 
// Start the server
const transport = new StdioServerTransport();
await server.connect(transport);

Best Practices

1. Server Organization

organize-mcp-servers.ts
// ✅ Good - group related servers by purpose
const databaseServers = [
  {
    id: "user-db",
    name: "User Database",
    transport: {
      /* ... */
    },
  },
  {
    id: "analytics-db",
    name: "Analytics Database",
    transport: {
      /* ... */
    },
  },
];
 
const externalServers = [
  {
    id: "web-search",
    name: "Web Search",
    transport: {
      /* ... */
    },
  },
  {
    id: "weather",
    name: "Weather API",
    transport: {
      /* ... */
    },
  },
];
 
createMcpExtension([...databaseServers, ...externalServers]);
 
// ❌ Bad - unclear server purposes
createMcpExtension([
  {
    id: "server1",
    name: "Server",
    transport: {
      /* ... */
    },
  },
  {
    id: "server2",
    name: "Other Server",
    transport: {
      /* ... */
    },
  },
]);

2. Error Boundaries

mcp-error-boundaries.ts
// ✅ Good - wrap MCP calls in try-catch
const safeAction = action({
  name: "safe-mcp-call",
  handler: async ({ query }, ctx) => {
    try {
      const result = await ctx.callAction("mcp.callTool", {
        serverId: "search-server",
        name: "search",
        arguments: { query },
      });
 
      if (result.error) {
        return { error: `Search failed: ${result.error}` };
      }
 
      return { data: result.result };
    } catch (error) {
      return { error: `Connection failed: ${error.message}` };
    }
  },
});

3. Resource Validation

mcp-resource-validation.ts
// ✅ Good - validate MCP responses
const validateResponse = (mcpResult: any) => {
  if (mcpResult.error) {
    throw new Error(`MCP Error: ${mcpResult.error}`);
  }
 
  if (!mcpResult.result) {
    throw new Error("MCP returned no result");
  }
 
  return mcpResult.result;
};
 
const validatedAction = action({
  name: "validated-mcp-call",
  handler: async ({ id }, ctx) => {
    const result = await ctx.callAction("mcp.callTool", {
      serverId: "database",
      name: "get-user",
      arguments: { id },
    });
 
    const userData = validateResponse(result);
 
    // Now safe to use userData
    return { user: userData };
  },
});

Next Steps

Key Takeaways

  • Universal interface - One API pattern for all external capabilities
  • Headless client - Your agent connects to any MCP server seamlessly
  • Seamless integration - MCP actions work like any other agent action
  • Multiple transports - Support both local (stdio) and remote (SSE) servers
  • Ecosystem ready - Connect to existing MCP servers or build custom ones

MCP transforms your agent from isolated code into a connected system that can access any data source or service through a standardized protocol.