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

# Model Context Protocol (MCP)

> Use MCP to extend your agents with custom capabilities

[Model Context Protocol (MCP)](https://modelcontextprotocol.io/) is an open standard that
allows your agents to connect securely to external tools and data sources.
Think of MCP as a "universal connector" for AI - it standardizes how language models interact
with various systems like databases, APIs, file systems, and custom tools.

MCP transforms your agents from isolated models into powerful assistants that can access real-time data,
perform actions, and interact with your entire digital ecosystem through a single, standardized protocol.

## Example: Using filesystem MCP

[Filesystem](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem)
is a simple MCP that allows the LLM to execute disk based tools on your server. For example,
list files, read file, write file etc. In this guide, we will integrate it with `<C1Chat />` to create a
conversational agent that can answer questions about your filesystem.

### Setting up the MCP Client

First, let's install the necessary dependencies to work with MCP in your C1 application.

<Note>
  You'll need to install the MCP client library and any specific MCP servers you want to use. For this example, we'll use a filesystem MCP server.
</Note>

<CodeGroup dropdown>
  ```ts cli theme={null}
  > npm install @modelcontextprotocol/sdk
  ```

  ```python cli theme={null}
  > pip install mcp
  ```
</CodeGroup>

### Create an MCP client integration

Now let's create the MCP client using the `@modelcontextprotocol/sdk` package.
This implementation connects to a filesystem MCP server and handles tool execution.

<CodeGroup dropdown>
  ```ts app/api/chat/mcp.ts expandable theme={null}
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
  import OpenAI from "openai";

  export class MCPClient {
    private mcp: Client;
    private transport: StdioClientTransport | null = null;
    public tools: OpenAI.ChatCompletionTool[] = [];

    constructor() {
      this.mcp = new Client({
        name: "c1-chat-mcp-client",
        version: "1.0.0"
      });
    }

    async connect() {
      // Connect to filesystem MCP server (no authentication required)
      const command = "npx";
      const args = [
        "-y",
        "@modelcontextprotocol/server-filesystem@latest",
        process.cwd(),
      ];

      this.transport = new StdioClientTransport({
        command,
        args,
      });

      await this.mcp.connect(this.transport);

      // List available tools from the MCP server
      const toolsResult = await this.mcp.listTools();
      this.tools = toolsResult.tools.map((tool) => ({
        type: "function" as const,
        function: {
            name: tool.name,
            description: tool.description,
            parameters: tool.inputSchema,
        },
      }));
    }

    async runTool({
      tool_call_id,
      name,
      args,
    }: {
      tool_call_id: string;
      name: string;
      args: Record<string, unknown>;
    }) {
      try {
        const result = await this.mcp.callTool({
          name,
          arguments: args,
        });

        return {
          tool_call_id,
          role: "tool" as const,
          content: JSON.stringify(result.content),
        };
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : "Unknown error";
        return {
          tool_call_id,
          role: "tool" as const,
          content: JSON.stringify({
            error: `Tool call failed: ${errorMessage}`,
          }),
        };
      }
    }

    async disconnect() {
      if (this.transport) {
        await this.transport.close();
      }
    }
  }
  ```

  ```python mcp.py expandable theme={null}
  import os
  from typing import Any, Dict, List

  from mcp.client import Client
  from mcp.client.stdio import StdioClientTransport


  class MCPClient:
      def __init__(self) -> None:
          self._client = Client(name="c1-chat-mcp-client", version="1.0.0")
          self._transport: StdioClientTransport | None = None
          self.tools: List[Dict[str, Any]] = []

      async def connect(self) -> None:
          # Connect to filesystem MCP server (no authentication required)
          command = "npx"
          args = [
              "-y",
              "@modelcontextprotocol/server-filesystem@latest",
              os.getcwd(),
          ]

          self._transport = StdioClientTransport(command=command, args=args)
          await self._client.connect(self._transport)

          # List available tools from the MCP server and map to OpenAI tool schema
          tools_result = await self._client.list_tools()
          self.tools = [
              {
                  "type": "function",
                  "function": {
                      "name": tool.name,
                      "description": tool.description or "",
                      "parameters": tool.inputSchema,
                      "strict": True,
                  },
              }
              for tool in tools_result.tools
          ]

      async def run_tool(self, *, tool_call_id: str, name: str, args: Dict[str, Any]) -> Dict[str, Any]:
          try:
              result = await self._client.call_tool(name=name, arguments=args)
              return {
                  "tool_call_id": tool_call_id,
                  "role": "tool",
                  "content": result.content,
              }
          except Exception as e:  # noqa: BLE001
              return {
                  "tool_call_id": tool_call_id,
                  "role": "tool",
                  "content": {"error": f"Tool call failed: {e}"},
              }

      async def disconnect(self) -> None:
          if self._transport is not None:
              await self._transport.close()
  ```
</CodeGroup>

### Integrate MCP with your C1 agent

Now let's update your chat route to use the streamlined MCP integration from the thesysdev examples. This approach uses OpenAI's `runTools` method for automatic tool execution.

Install the dependencies for streaming

<CodeGroup dropdown>
  ```ts cli theme={null}
  > npm install @crayonai/stream
  ```

  ```python cli theme={null}
  > # no package required
  ```
</CodeGroup>

<CodeGroup dropdown>
  ```ts app/api/chat/route.ts {5, 9-10, 17-22, 40-54} expandable theme={null}
  import { NextRequest, NextResponse } from "next/server";
  import OpenAI from "openai";
  import { transformStream } from "@crayonai/stream";
  import { DBMessage, getMessageStore } from "./messageStore";
  import { MCPClient } from "./mcp";
  import { JSONSchema } from "openai/lib/jsonschema.mjs";

  // Initialize MCP client
  const mcpClient = new MCPClient();

  interface RequestBody {
    prompt: DBMessage;
    threadId: string;
    responseId: string;
  }

  async function ensureMCPConnection(): Promise<void> {
    if (mcpClient.tools.length === 0) {
      await mcpClient.connect();
    }
  }

  export async function POST(req: NextRequest): Promise<NextResponse> {
    const { prompt, threadId, responseId } = (await req.json()) as RequestBody;

    const client = new OpenAI({
      baseURL: "https://api.thesys.dev/v1/embed/",
      apiKey: process.env.THESYS_API_KEY,
    });


    // Ensure MCP connection is established
    await ensureMCPConnection();

    const llmStream = await client.beta.chat.completions.runTools({
      model: "c1/anthropic/claude-sonnet-4/v-20251230",
      messages: [
        ...messages,
        { role: "user", content: prompt }
      ],
      tools: mcpClient.tools.map((tool) => ({
        type: "function",
        function: {
          name: tool.function.name,
          description: tool.function.description || "",
          parameters: tool.function.parameters as unknown as JSONSchema,
          parse: JSON.parse,
          function: async (args: unknown) => {
            const results = await mcpClient.runTool({
              tool_call_id: tool.function.name + Date.now().toString(),
              name: tool.function.name,
              args: args as Record<string, unknown>,
            });
            return results.content;
          },
        },
      })),
      stream: true,
    });

    const responseStream = transformStream(
      llmStream,
      (chunk) => {
        return chunk.choices[0].delta.content;
      },
    ) as ReadableStream<string>;

    return new NextResponse(responseStream, {
      headers: {
        "Content-Type": "text/event-stream",
        "Cache-Control": "no-cache, no-transform",
        Connection: "keep-alive",
      },
    });
  }
  ```

  ```python main.py expandable theme={null}
  import os
  import json
  from typing import Any, Dict, List

  from fastapi import FastAPI
  from pydantic import BaseModel
  from openai import OpenAI

  from mcp import MCPClient


  app = FastAPI()

  client = OpenAI(
      api_key=os.environ.get("THESYS_API_KEY"),
      base_url="https://api.thesys.dev/v1/embed",
  )

  mcp_client = MCPClient()


  class ChatRequest(BaseModel):
      prompt: str
      history: List[Dict[str, Any]] = []


  async def ensure_mcp_connection() -> None:
      if not mcp_client.tools:
          await mcp_client.connect()


  @app.post("/chat")
  async def chat(req: ChatRequest) -> Dict[str, Any]:
      await ensure_mcp_connection()

      messages: List[Dict[str, Any]] = [
          *req.history,
          {"role": "user", "content": req.prompt},
      ]

      # First request with available tools from MCP
      completion = client.chat.completions.create(
          model="c1/anthropic/claude-sonnet-4/v-20251230",
          messages=messages,
          tools=mcp_client.tools,
      )

      # Handle tool calls loop until the model returns a final answer
      while True:
          choice = completion.choices[0]
          message = choice.message
          tool_calls = message.tool_calls or []

          if not tool_calls:
              return message.content

          # Append assistant message that requested tools
          messages.append(
              {
                  "role": "assistant",
                  "content": message.content or "",
                  "tool_calls": [
                      {
                          "id": tc.id,
                          "type": "function",
                          "function": {
                              "name": tc.function.name,
                              "arguments": tc.function.arguments,
                          },
                      }
                      for tc in tool_calls
                  ],
              }
          )

          # Execute tools (via MCP) and append results
          for tc in tool_calls:
              args = json.loads(tc.function.arguments or "{}")
              tool_result = await mcp_client.run_tool(
                  tool_call_id=tc.id,
                  name=tc.function.name,
                  args=args,
              )
              messages.append(
                  {
                      "role": "tool",
                      "tool_call_id": tool_result["tool_call_id"],
                      "content": json.dumps(tool_result["content"]),
                  }
              )

          # Ask the model again with tool results
          completion = client.chat.completions.create(
              model="c1/anthropic/claude-sonnet-4/v-20251230",
              messages=messages,
              tools=mcp_client.tools,
          )
  ```
</CodeGroup>

### Test your MCP-enabled agent

Your agent now has access to powerful filesystem operations through MCP! You can test it with prompts like:

* **File operations**: "Create a new file called 'notes.txt' with today's meeting summary"
* **Directory browsing**: "List all the files in the current directory"
* **File reading**: "Read the contents of package.json and summarize the project dependencies"
* **File searching**: "Find all TypeScript files in the src directory"

<Card title="View Source Code" icon="github" img="/images/mcp-integration.png" href="https://github.com/thesysdev/examples/tree/main/mcp-with-c1-chat">
  See the full code with integrations for thinking states and error handling.
</Card>
