diff --git a/.github/workflows/nextjs.yml b/.github/workflows/nextjs.yml.disabled similarity index 100% rename from .github/workflows/nextjs.yml rename to .github/workflows/nextjs.yml.disabled diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9ae4cba..4a4e298 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -3,7 +3,7 @@ name: publish-to-github-pages on: push: branches: - - main + - course permissions: contents: read @@ -27,8 +27,6 @@ jobs: - name: Setup Pages ⚙️ uses: actions/configure-pages@v4 - with: - static_site_generator: next - name: Build with Next.js 🏗️ run: npx next build diff --git a/app/course/[slug]/page.tsx b/app/course/[slug]/page.tsx new file mode 100644 index 0000000..2389389 --- /dev/null +++ b/app/course/[slug]/page.tsx @@ -0,0 +1,114 @@ +import fs from 'fs' +import path from 'path' +import matter from 'gray-matter' +import { notFound } from 'next/navigation' +import Link from 'next/link' + +interface PageProps { + params: Promise<{ + slug: string + }> +} + +interface UnitData { + title: string + content: string + frontmatter: Record +} + +async function getUnitBySlug(slug: string): Promise { + try { + // Parse the slug to get unit directory and file name + const [unitDir, fileName] = slug.split('/') + + if (!unitDir || !fileName) { + return null + } + + const filePath = path.join(process.cwd(), 'app/course/units', unitDir, `${fileName}.mdx`) + + if (!fs.existsSync(filePath)) { + return null + } + + const source = fs.readFileSync(filePath, 'utf8') + const { data: frontmatter, content } = matter(source) + + // Extract title from the first heading or use filename + const titleMatch = content.match(/^#\s+(.+)$/m) + const title = titleMatch ? titleMatch[1] : fileName + + return { + title, + content, + frontmatter + } + } catch (error) { + console.error('Error reading unit file:', error) + return null + } +} + +export default async function UnitPage({ params }: PageProps) { + const { slug } = await params + const unit = await getUnitBySlug(slug) + + if (!unit) { + notFound() + } + + return ( +
+
+
+ + +

+ {unit.title} +

+
+ +
+
+
+
+ {unit.content} +
+
+
+
+
+
+ ) +} + +// Generate static params for all units +export async function generateStaticParams() { + const unitsDir = path.join(process.cwd(), 'app/course/units') + const unitDirs = fs.readdirSync(unitsDir).filter(dir => + fs.statSync(path.join(unitsDir, dir)).isDirectory() && dir.startsWith('unit') + ) + + const params: { slug: string }[] = [] + + for (const unitDir of unitDirs) { + const unitPath = path.join(unitsDir, unitDir) + const files = fs.readdirSync(unitPath).filter(file => file.endsWith('.mdx')) + + for (const file of files) { + const fileName = file.replace('.mdx', '') + params.push({ + slug: `${unitDir}/${fileName}` + }) + } + } + + return params +} diff --git a/app/course/page.tsx b/app/course/page.tsx new file mode 100644 index 0000000..32599eb --- /dev/null +++ b/app/course/page.tsx @@ -0,0 +1,155 @@ +import Link from 'next/link' +import fs from 'fs' +import path from 'path' +import matter from 'gray-matter' + +interface Unit { + slug: string + title: string + excerpt: string + unitNumber: number +} + +async function getCourseUnits(): Promise { + const unitsDir = path.join(process.cwd(), 'app/course/units') + const unitDirs = fs.readdirSync(unitsDir).filter(dir => + fs.statSync(path.join(unitsDir, dir)).isDirectory() && dir.startsWith('unit') + ) + + const units: Unit[] = [] + + for (const unitDir of unitDirs) { + const unitPath = path.join(unitsDir, unitDir) + const files = fs.readdirSync(unitPath).filter(file => file.endsWith('.mdx')) + + for (const file of files) { + const filePath = path.join(unitPath, file) + const source = fs.readFileSync(filePath, 'utf8') + const { content } = matter(source) + + // Extract title from the first heading or use filename + const titleMatch = content.match(/^#\s+(.+)$/m) + const title = titleMatch ? titleMatch[1] : file.replace('.mdx', '') + + // Extract excerpt from the first paragraph + const excerptMatch = content.match(/^#\s+.+?\n\n([\s\S]+?)(?=\n\n|$)/) + const excerpt = excerptMatch ? excerptMatch[1].substring(0, 150) + '...' : 'Course content...' + + const unitNumber = parseInt(unitDir.match(/unit(\d+)/)?.[1] || '0') + + units.push({ + slug: `${unitDir}/${file.replace('.mdx', '')}`, + title, + excerpt, + unitNumber + }) + } + } + + // Sort units by their directory number + return units.sort((a, b) => a.unitNumber - b.unitNumber) +} + +export default async function CoursePage() { + const units = await getCourseUnits() + + return ( +
+
+ {/* Hero Section */} +
+

+ Mastering CAMEL AI with Model Context Protocol (MCP) +

+

+ Learn how to build powerful AI agents with advanced tooling capabilities. + From basic tooling in CAMEL AI to mastering the Model Context Protocol (MCP), + this comprehensive course will transform your AI development skills. +

+
+ + Start Learning + +
+
+ + {/* Course Overview */} +
+

Course Overview

+
+
+

What You'll Learn

+
    +
  • • Understanding tooling in CAMEL AI
  • +
  • • Model Context Protocol (MCP) fundamentals
  • +
  • • Building custom MCP servers
  • +
  • • Integrating MCP with CAMEL AI agents
  • +
  • • Creating versatile, tool-augmented AI systems
  • +
+
+
+

Prerequisites

+
    +
  • • Basic Python programming skills
  • +
  • • Understanding of functions and imports
  • +
  • • Familiarity with AI concepts (optional)
  • +
  • • Eagerness to learn advanced tooling
  • +
+
+
+
+ + {/* Course Units */} +
+

Course Units

+
+ {units.map((unit, index) => ( +
+
+
+
+ + {index + 1} + +
+
+

+ {unit.title} +

+

+ {unit.excerpt} +

+ + Read Unit → + +
+
+
+
+ ))} +
+
+ + {/* Call to Action */} +
+

Ready to Master MCP?

+

+ Start your journey into advanced AI tooling and interoperability +

+ + Begin Course + +
+
+
+ ) +} \ No newline at end of file diff --git a/app/course/units/page.tsx b/app/course/units/page.tsx new file mode 100644 index 0000000..7de4885 --- /dev/null +++ b/app/course/units/page.tsx @@ -0,0 +1,90 @@ +import fs from 'fs' +import path from 'path' +import matter from 'gray-matter' + +interface Unit { + slug: string + title: string + content: string + frontmatter: Record +} + +async function getUnits(): Promise { + const unitsDir = path.join(process.cwd(), 'app/course/units') + const unitDirs = fs.readdirSync(unitsDir).filter(dir => + fs.statSync(path.join(unitsDir, dir)).isDirectory() && dir.startsWith('unit') + ) + + const units: Unit[] = [] + + for (const unitDir of unitDirs) { + const unitPath = path.join(unitsDir, unitDir) + const files = fs.readdirSync(unitPath).filter(file => file.endsWith('.mdx')) + + for (const file of files) { + const filePath = path.join(unitPath, file) + const source = fs.readFileSync(filePath, 'utf8') + const { data: frontmatter, content } = matter(source) + + // Extract title from the first heading or use filename + const titleMatch = content.match(/^#\s+(.+)$/m) + const title = titleMatch ? titleMatch[1] : file.replace('.mdx', '') + + units.push({ + slug: `${unitDir}/${file.replace('.mdx', '')}`, + title, + content, + frontmatter + }) + } + } + + // Sort units by their directory number + return units.sort((a, b) => { + const aNum = parseInt(a.slug.match(/unit(\d+)/)?.[1] || '0') + const bNum = parseInt(b.slug.match(/unit(\d+)/)?.[1] || '0') + return aNum - bNum + }) +} + +export default async function CourseUnitsPage() { + const units = await getUnits() + + return ( +
+
+
+

+ CAMEL AI with Model Context Protocol (MCP) Course +

+

+ Master the art of building AI agents with advanced tooling capabilities +

+
+ +
+ {units.map((unit, index) => ( +
+
+
+ + {index + 1} + +

+ {unit.title} +

+
+ +
+
+ {unit.content} +
+
+
+
+ ))} +
+
+
+ ) +} diff --git a/app/course/units/unit0/introduction.mdx b/app/course/units/unit0/introduction.mdx new file mode 100644 index 0000000..ca73d12 --- /dev/null +++ b/app/course/units/unit0/introduction.mdx @@ -0,0 +1,15 @@ +# Mastering CAMEL AI with Model Context Protocol (MCP) + +## Course Overview + +This course takes you on a journey from understanding basic tooling in CAMEL AI to mastering the Model Context Protocol (MCP), a powerful standard for connecting AI agents with external tools. You’ll learn how to configure tools in CAMEL AI, explore MCP’s role as a universal bridge, build custom MCP servers, and integrate them with CAMEL AI agents. By the end, you’ll be able to create versatile, tool-augmented AI agents that work seamlessly across platforms. + +### Target Audience + +- Developers with basic Python knowledge. +- AI enthusiasts eager to explore advanced tooling and interoperability. + +### Prerequisites + +- Basic Python skills (functions, imports). +- Optional: Familiarity with AI concepts. \ No newline at end of file diff --git a/app/course/units/unit1/module1.mdx b/app/course/units/unit1/module1.mdx new file mode 100644 index 0000000..49d4178 --- /dev/null +++ b/app/course/units/unit1/module1.mdx @@ -0,0 +1,98 @@ +# Module 1: Understanding Tooling in CAMEL AI + +**Objective:** Learn what tools are in CAMEL AI, how to configure and use them, and why traditional tooling has limitations. + +## 1.1 Introduction to Tools in CAMEL AI + +Tools in CAMEL AI are like superpowers for AI agents, extending their abilities beyond simple text generation. Just as humans use hammers or calculators, AI agents use tools to interact with the world—fetching data, performing calculations, or executing tasks. + +In CAMEL AI, tools are typically Python functions that you define and wrap with `FunctionTool`. These tools are then handed to a `ChatAgent`, which decides when to use them based on user queries. + +**Example:** + +Imagine an AI agent that can add numbers. You’d give it an `add` tool to handle the math. + +```python +# Define a tool +def add(a: int, b: int) -> int: + """Add two integers.""" + return a + b + +# Wrap it +add_tool = FunctionTool(add) + +# Create a model +model = ModelFactory.create( + model_platform=ModelPlatformType.OPENAI, + model_type=ModelType.GPT_3_5_TURBO, + model_config_dict={"temperature": 0.0} +) + +# Initialize the agent +system_message = BaseMessage.make_system_message( + role_name="MathAssistant", + content="You help with math using tools." +) +agent = ChatAgent(model=model, tools=[add_tool]) + +# Use the agent +response = agent.step("What is 5 plus 3?") +print(response.msgs[0].content) # Output: "8" + +``` + +Tools are nothing but pre built python functions that add additional functionality to your LLMs to make them usable in real world ie agents. + +Internally they just trigger actions for us when we give a certain indicator to the agents + +## 1.2 Using Toolkits + +CAMEL AI provides pre-built toolkits, like ArxivToolkit, that simplify complex API calls and tool configurations. With just a few lines of code, you can plug these tool collections into your agent to perform tasks such as searching academic papers. + +**Code Example:** + +```python +from camel.toolkits import ArxivToolkit + +# Initialize the toolkit +toolkit = ArxivToolkit() + +# Create an agent with the toolkit +agent = ChatAgent(model=model, tools=toolkit.get_tools()) +response = agent.step("Find papers on AI ethics.") +print(response.msgs[0].content) # Outputs paper summaries + +``` + +## 1.3 Limitations of Traditional Tooling + +While powerful, traditional tooling in CAMEL AI has drawbacks: + +- **Incompatible Provider-Specific Schemas:** Each AI provider (e.g., OpenAI, Gemini) uses a unique format for defining tools. A `search_web` tool built for OpenAI won't work with Gemini without significant reformatting due to their differing schema requirements. +- **Extensive Custom Adapters and Rewriting:** Because of these disparate schemas, developers must write custom adapters or completely rewrite tools for each provider. A `send_email` tool, for instance, would need a separate version tailored for OpenAI, another for Gemini, and so on, leading to high maintenance overhead. +- **Limited Interoperability and Framework Silos:** Tools and workflows developed within one AI framework (e.g., LangChain) are often incompatible with others (e.g., CAMEL AI). This creates functional silos, preventing seamless integration and component reuse across different multi-agent systems or development platforms. + +**Conceptual Schema Mismatch:** + +```json +{ + "openai_tool": { + "function": { + "name": "get_weather", + "parameters": {"location": "string"} + } + }, + "gemini_tool": { + "function_declarations": [ + {"name": "get_weather", "args": {"location": "str"}} + ] + } +} + +``` + +These limitations call for a better solution—MCP. + +**Reflection Question:** + +What tool would you create for an AI agent, and how would you define it? \ No newline at end of file diff --git a/app/course/units/unit2/module2.mdx b/app/course/units/unit2/module2.mdx new file mode 100644 index 0000000..c59a7b0 --- /dev/null +++ b/app/course/units/unit2/module2.mdx @@ -0,0 +1,49 @@ +# Module 2: Introducing the Model Context Protocol (MCP) + +**Objective:** Understand MCP, its principles, and how it solves tooling limitations. + +## 2.1 What is MCP? + +MCP is an open standard that lets AI agents use tools universally, no matter the model or provider. It's like a "USB-C port for AI"—a common plug that connects any agent to any tool. + +## 2.2 Core Principles + +- **Standardization:** Uses JSON-RPC 2.0 for consistent communication. +- **Model-Agnosticism:** Works with any AI system (GPT, Claude, etc.). +- **Openness:** Free and collaborative, avoiding vendor lock-in. +- **Context Awareness:** Provides real-time data access. +- **Security:** Includes encryption and user consent. + +## 2.3 How MCP Solves Tooling Issues + +MCP offers: + +- **Common Interface:** One standard for all tools. +- **Plug-and-Play:** Tools work across providers without custom code. +- **No Schema Mismatch:** Unlike CAMEL's `agent.step` vs. LangChain's `chain.invoke`, MCP unifies tool calls. + +```mermaid +graph TD + subgraph Traditional ["Traditional Approach"] + A[CAMEL Agent] -->|custom code| C[Tool] + B[LangChain] -->|different code| C + end + + subgraph MCP ["MCP"] + D[Any Agent] -->|MCP| E[Tool] + end + + style Traditional fill:#ffebee + style MCP fill:#e8f5e8 + style A fill:#f3e5f5 + style B fill:#f3e5f5 + style C fill:#fff3e0 + style D fill:#e3f2fd + style E fill:#fff3e0 +``` + +![how-mcp-solves-tooling-issues.png](mcp.camel-ai.org/course/module2/how-mcp-solves-tooling-issues.png) + +**Reflection Question:** + +How might MCP's standardization change AI development? diff --git a/app/course/units/unit3/module3.mdx b/app/course/units/unit3/module3.mdx new file mode 100644 index 0000000..5a90566 --- /dev/null +++ b/app/course/units/unit3/module3.mdx @@ -0,0 +1,73 @@ +# Module 3: Understanding MCP’s Architecture + +**Objective:** Get familiar with how MCP’s client-server design works and follow the steps of its interaction flow. + +## 3.1 What is MCP’s Client-Server Architecture? + +MCP’s architecture is like a well-organized team where each member has a specific role, working together to get things done. It splits tasks between a few key players, making it easy for AI agents to use tools from different sources. Let’s meet the team: + +- **Host:** This is the main AI application, like your CAMEL AI setup, where all the action starts. Think of it as the brain that decides what needs to be done. +- **MCP Client:** The client is the go-between, connecting the host to external tools. It sends requests to servers and brings back the results, like a messenger.( eg Claude Desktop, Cursor, CAMEL-AI Agent ) +- **MCP Server:** These are the tool providers, offering specific functions, data, or prompts. For example, one server might handle Notion tasks, while another searches Arxiv papers.( eg ACI.dev, Composio etc ) + +This setup keeps things flexible, letting your AI app talk to multiple servers at once, each offering unique tools. + +**Diagram:** + +```mermaid +graph LR + subgraph Computer [" Your Computer "] + Host["Host with MCP Client
(CAMEL Agent, Claude , IDE's)"] + + ServerA["Notion
MCP Server"] + ServerB["File System
MCP Server"] + ServerC["ArXiv
MCP Server"] + + DataA[("Local
Notion Cache")] + DataB[("Local
Files")] + end + + subgraph Internet [" Internet "] + RemoteService[("ArXiv
Database")] + end + + %% MCP Protocol connections + Host -.->|MCP Protocol| ServerA + Host -.->|MCP Protocol| ServerB + Host -.->|MCP Protocol| ServerC + + %% Data connections + ServerA <--> DataA + ServerB <--> DataB + ServerC -.->|Web APIs| RemoteService + + %% Styling + style Host fill:#4a90e2,color:#fff,stroke:#2c5aa0,stroke-width:2px + style ServerA fill:#333,color:#fff,stroke:#666,stroke-width:2px + style ServerB fill:#333,color:#fff,stroke:#666,stroke-width:2px + style ServerC fill:#333,color:#fff,stroke:#666,stroke-width:2px + style DataA fill:#666,color:#fff,stroke:#999,stroke-width:2px + style DataB fill:#666,color:#fff,stroke:#999,stroke-width:2px + style RemoteService fill:#666,color:#fff,stroke:#999,stroke-width:2px + style Computer fill:#f0f0f0,stroke:#999,stroke-width:2px + style Internet fill:#e6f3ff,stroke:#4a90e2,stroke-width:2px + + ![what-is-mcp-client-server-architecture.png](mcp.camel-ai.org/course/module3/what-is-mcp-client-server-architecture.png) + +``` + +## 3.2 Interaction Flow + +1. **Connect:** Client links to a server. +2. **Discover:** Client lists available tools. +3. **Choose:** Agent picks a tool. +4. **Invoke:** Client requests tool execution. +5. **Return:** Server sends the result. + +**Example:** + +A CAMEL agent asks, “What time is it?” It connects to a Time MCP Server, discovers `get_current_time`, invokes it, and gets the time. + +**Reflection Question:** + +Why is dynamic tool discovery useful? \ No newline at end of file diff --git a/app/course/units/unit4/module4.mdx b/app/course/units/unit4/module4.mdx new file mode 100644 index 0000000..503a328 --- /dev/null +++ b/app/course/units/unit4/module4.mdx @@ -0,0 +1,57 @@ +# Module 4: Building Your Own MCP Server + +**Objective:** Learn how to create your own MCP server using FastMCP to share tools with AI agents. + +## 4.1 Introduction to FastMCP + +`FastMCP` is a Python framework that makes building MCP servers a breeze. Think of it as a helpful assistant that takes care of the tricky protocol details, so you can focus on creating cool tools for your AI agents. `FastMCP` sets up the server to share these with any MCP-compatible client, like a CAMEL AI agent. + +FastMCP is great because it’s lightweight and flexible, letting you define exactly what your server does without wrestling with complex networking code. It’s like setting up a lemonade stand—you decide what’s on the menu, and FastMCP handles the customers. + +## 4.2 Creating an MCP Server + +To build an MCP server, you use FastMCP’s decorators to turn regular Python functions into tools, resources, or prompts that AI agents can discover and use. It’s as simple as writing a function, adding a decorator, and letting FastMCP do the rest. + +Here’s a step-by-step look: + +1. **Set Up the Server:** Create a FastMCP server instance with a name. +2. **Define Functions:** Write Python functions for your tools or resources. +3. **Add Decorators:** Use FastMCP decorators to make them MCP-ready. + +**Code Example:** + +```python +from mcp.server.fastmcp import FastMCP + +# Create a server named "DemoServer" +mcp = FastMCP("DemoServer") + +# Define a tool for adding numbers +@mcp.tool() +def add(a: int, b: int) -> int: + """Add two numbers.""" + return a + b + +# Define a resource for personalized greetings +@mcp.resource("greeting://{name}") +def get_greeting(name: str) -> str: + """Get a personalized greeting.""" + return f"Hello, {name}!" + +``` + +## 4.3 How Decorators Work + +Decorators are like labels that tell FastMCP how to use your functions. Here’s what they do: + +- `@mcp.tool():` Makes a function a tool for actions like calculations, using docstrings and type hints to define it. +- `@mcp.resource():` Sets up a data source, like a message, accessible via a URI (e.g., greeting://{name}). +- `@mcp.prompt():` Creates a template to guide AI responses, such as for questions or summaries. + +**Why Different?** + +Unlike traditional toolkits, MCP servers decouple tool logic from the agent, enabling standardized, discoverable access. + +**Reflection Question:** + +What custom tool would you build with FastMCP? \ No newline at end of file diff --git a/app/course/units/unit5/module5.mdx b/app/course/units/unit5/module5.mdx new file mode 100644 index 0000000..e2d1820 --- /dev/null +++ b/app/course/units/unit5/module5.mdx @@ -0,0 +1,264 @@ +# Module 5: Integrating CAMEL AI with MCP + +**Objective:** Configure CAMEL AI agents as MCP clients to use tools from MCP servers. + +## 5.1 CAMEL AI as an MCP Client + +Turning your CAMEL AI agent into an MCP client is like giving it a magic key to unlock tools from infinite MCP servers. The `MCPToolkit` makes this super easy by connecting your agent to servers, fetching their tools, and letting your agent use them without fuss. + +You’ll set up a config file, connect to servers, and plug the tools into your agent. Plus, you can use tools like **ACI.dev**, **Composio**, or **npx** to download and configure MCP servers, making the process even smoother. Here’s how it all comes together. + +### Steps to Set Up + +1. **Define a Config File:** Create a JSON file to tell `MCPToolkit` which MCP servers to connect to and how to access them. +2. **Initialize and Connect `MCPToolkit`:** Load the config and establish connections to the servers. +3. **Pass Tools to `ChatAgent`:** Hand the server’s tools to your CAMEL AI agent for seamless use. + +```mermaid +graph TB + subgraph CAMEL [" CAMEL AI Application "] + Agent["ChatAgent
(CAMEL AI)"] + Toolkit["MCPToolkit
(CAMEL)"] + Config["Config File
(JSON)"] + end + + subgraph Local [" Local MCP Servers "] + TimeServer["Time Server
(Python Script)"] + FileServer["File System
Server"] + end + + subgraph Remote [" Remote MCP Servers "] + ACI["ACI.dev
(600+ Tools)"] + Composio["Composio
(250+ Apps)"] + NPX["NPX Servers
(Community)"] + end + + subgraph Services [" External Services "] + Notion[("Notion
Workspace")] + Gmail[("Gmail
API")] + GitHub[("GitHub
API")] + ArXiv[("ArXiv
Database")] + end + + %% Configuration and Connection Flow + Config -.->|Configuration| Toolkit + Toolkit -.->|Load Tools| Agent + + %% MCP Protocol Connections + Toolkit -.->|MCP Protocol stdio| TimeServer + Toolkit -.->|MCP Protocol stdio| FileServer + Toolkit -.->|MCP Protocol SSE| ACI + Toolkit -.->|MCP Protocol SSE| Composio + Toolkit -.->|MCP Protocol npx| NPX + + %% External Service Connections + ACI <--> Gmail + ACI <--> GitHub + Composio <--> Notion + NPX <--> ArXiv + + %% User Interaction + User["User Query:
What time is it?
Create GitHub issue"] + User --> Agent + Agent -.->|Uses Tools| Toolkit + + %% Styling + style Agent fill:#4a90e2,color:#fff,stroke:#2c5aa0,stroke-width:2px + style Toolkit fill:#ff6b35,color:#fff,stroke:#d4501f,stroke-width:2px + style Config fill:#f7dc6f,color:#333,stroke:#f4d03f,stroke-width:2px + + style TimeServer fill:#333,color:#fff,stroke:#666,stroke-width:2px + style FileServer fill:#333,color:#fff,stroke:#666,stroke-width:2px + + style ACI fill:#28a745,color:#fff,stroke:#1e7e34,stroke-width:2px + style Composio fill:#17a2b8,color:#fff,stroke:#117a8b,stroke-width:2px + style NPX fill:#6f42c1,color:#fff,stroke:#59359a,stroke-width:2px + + style Notion fill:#666,color:#fff,stroke:#999,stroke-width:2px + style Gmail fill:#666,color:#fff,stroke:#999,stroke-width:2px + style GitHub fill:#666,color:#fff,stroke:#999,stroke-width:2px + style ArXiv fill:#666,color:#fff,stroke:#999,stroke-width:2px + + style User fill:#e74c3c,color:#fff,stroke:#c0392b,stroke-width:2px + + style CAMEL fill:#e8f4fd,stroke:#4a90e2,stroke-width:2px + style Local fill:#f0f0f0,stroke:#666,stroke-width:2px + style Remote fill:#e8f5e8,stroke:#28a745,stroke-width:2px + style Services fill:#fff3cd,stroke:#ffc107,stroke-width:2px + + ![camel-ai-as-an-mcp-client.png](mcp.camel-ai.org/course/module5/camel-ai-as-an-mcp-client.png) + +``` + +### Why Config Files? + +Config files are like a treasure map for `MCPToolkit`. They list the MCP servers you want to use, including details like how to start a local server (e.g., running a Python script) or connect to a remote one (e.g., via a URL). This keeps your code tidy—you don’t need to hard-code server details, and you can easily add, remove, or update servers by editing the JSON. It’s a neat way to manage multiple servers without rewriting your program. + +### Understanding Transport Methods: stdio, sse, and streamable-http + +The transport method in your config file decides *how* your MCPToolkit talks to an MCP server. It’s like choosing whether to send a message by hand (local), over the internet (remote), or with a shiny new gadget (streamable-http). MCP supports three main transport methods: + +- **stdio (Standard Input/Output):** Used for local servers running on your computer. The client communicates with the server through standard input and output streams, like a direct phone line. It’s great for testing or custom servers (e.g., a Python script you wrote). For example, launching time_server.py locally uses stdio for fast, secure communication without network setup. +- **sse (Server-Sent Events):** Used for remote servers hosted on the web. The client connects over HTTP, receiving updates via a stream of events, like a live chat. It’s ideal for managed servers from Composio or ACI.dev, where the server runs on a cloud platform. SSE requires a URL and handles network security, like TLS encryption. +- **streamable-http:** The latest option for remote servers, offering a smoother and faster connection. Think of it as upgrading from a clunky old modem to a high-speed router. It uses HTTP with efficient event streaming, making it a step up from sse with better performance and easier setup. Perfect for cloud-hosted servers, it’s designed to be the go-to choice for a snappy, hassle-free experience. + +### Downloading and Configuring MCP Servers + +Before connecting, you need MCP servers to work with. You can build your own (as shown in Module 4) or download existing ones using tools like **ACI.dev**, **Composio**, or **npx**. Here’s how each helps: + +- **ACI.dev:** ACI.dev offers a unified MCP server approach that connects to over 600 tools, like Gmail or Brave Search, through a single interface. It’s like a Swiss Army knife for AI agents, letting you access multiple apps without juggling separate servers. To use it: + 1. Sign up at `platform.aci.dev` and get an API key and account ID. + 2. Install the `uv` package manager with `pip install uv`. + 3. Configure the server in your JSON file (see below). ACI.dev handles authentication (e.g., OAuth) automatically, so you can focus on tasks like searching or automation. +- **Composio:** Composio provides pre-built MCP servers for 250+ apps, such as Notion, Slack, or GitHub, with built-in authentication support (OAuth, API keys, etc.). It’s perfect for quickly adding tools to your workflow. To get started: + 1. Install the Composio CLI with `npm install -g composio-core@rc`. + 2. Sign in at `composio.dev` to get an API key. + 3. Browse `mcp.composio.dev` for servers (e.g., Notion) and copy their SSE URL, like `https://mcp.composio.dev/notion/your-server-id`. + 4. Add the server to your config file using `npx` to run it. Composio’s managed servers save you from building custom integrations. +- **npx:** This Node.js tool lets you run MCP servers without permanent installation, ideal for testing or community tools. For example, run a memory server for Claude with: + + ```bash + npx -y @modelcontextprotocol/server-memory + ``` + + Configure it in your JSON file (see below). Find servers at `github.com/modelcontextprotocol/servers` or `mcpserverfinder.com`, like GitHub or Google Calendar servers, and launch them via `npx`. + + +### Where to Find MCP Servers + +- **Build Your Own:** Use FastMCP to create custom servers (Module 4) with Python scripts like `time_server.py`. +- **Download from Hubs:** Check `mcp.camel-ai.org` or `mcp.composio.dev` for official and community servers. Composio offers Notion or Linear servers, while ACI.dev lists apps at `platform.aci.dev/apps`. +- **Package Managers:** Use `npm` (e.g., `npm install -g @modelcontextprotocol/server-filesystem`) or `pip` (e.g., `pip install mcp-server`) for Python-based servers. +- **Community Repos:** Explore `github.com/modelcontextprotocol/servers` or `mcpserverfinder.com` for servers like Semantic Scholar or Blender MCP. Use community servers cautiously, as they may be untested. + +### Example Config Files + +Here are config file examples for different servers, highlighting transport methods: + +**Local Time Server (Python Script, `stdio`):** + +```json +{ + "mcpServers": { + "time_server": { + "command": "python", + "args": ["time_server.py"], + "transport": "stdio" + } + } +} + +``` + +**Composio Notion Server (npx,** `streamable-http`**):** + +```json +{ + "mcpServers": { + "composio-notion": { + "command": "npx", + "args": ["composio-core@rc", "mcp", "", "--client", "camel"], + "env": { + "COMPOSIO_API_KEY": "your-api-key-here" + }, + "transport": "streamable-http" + } + } +} + +``` + +**ACI.dev Unified Server (`sse`):** + +```json +{ + "mcpServers": { + "aci_apps": { + "command": "uvx", + "args": [ + "aci-mcp", + "apps-server", + "--apps=BRAVE_SEARCH,GITHUB,ARXIV", + "--linked-account-owner-id", + "" + ], + "env": { + "ACI_API_KEY": "your_aci_api_key" + } + } + } +} + +``` + +Save these as `config/time.json` or similar, adjusting paths and IDs as needed. + +**Code Example (Using the Time Server):** + +```python +import asyncio +from camel.toolkits.mcp_toolkit import MCPToolkit +from camel.agents import ChatAgent + +async def main(): + # Load the config file + mcp_toolkit = MCPToolkit(config_path="config/time.json") + # Connect to the server (using stdio for local time server) + await mcp_toolkit.connect() + # Create an agent with server tools + agent = ChatAgent(model=model, tools=mcp_toolkit.get_tools()) + # Ask a question + response = await agent.astep("What time is it now?") + print(response.msgs[0].content) # Outputs: "The current time is 03:52 PM IST" + # Disconnect when done + await mcp_toolkit.disconnect() + +asyncio.run(main()) + +``` + +### Working with MCP Servers + +After connecting, `MCPToolkit` queries the server for available tools (e.g., `get_current_time`). These tools are passed to your `ChatAgent`, which uses them based on queries. For example, asking “What time is it now?” triggers the `get_current_time` tool via `stdio`, and the server returns “03:52 PM IST”. With ACI.dev or Composio, you can access tools like fetching Notion pages or creating GitHub issues using `sse`, all through the same process. The server handles the heavy lifting, and `MCPToolkit` makes it plug-and-play. + +**Tips for Success:** + +- **Choose the Right Transport:** Use `stdio` for local testing (faster, no network needed) and `sse` for remote servers (cloud-based, scalable). +- **Test Locally First:** Start with `stdio` servers to avoid network issues, then try `sse` for remote servers like Composio’s. +- **Check Logs:** If a server fails, check logs (e.g., `~/Library/Logs/Claude/mcp-server.log`) or use the MCP Inspector (`npx @modelcontextprotocol/inspector`). +- **Secure APIs:** Store API keys for ACI.dev or Composio in the `env` field, not hard-coded. + +**Try It Out:** + +Write a config file for a ACI app server using `npx` and `sse` transport. What tools might it offer? + +**Reflection Question:** + +How does using a config file with transport methods like `stdio` or `sse` make managing MCP servers easier than coding them directly? + +### 5.2 Toolkit as an MCP Server + +Turn CAMEL toolkits into MCP servers to share tools with other clients—like flipping from tool user to tool provider. + +**Code Example:** + +```python +from camel.toolkits import ArxivToolkit +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument("--mode", default="stdio") +args = parser.parse_args() + +toolkit = ArxivToolkit() +toolkit.mcp.run(args.mode) + +``` + +**How It Works:** + +The `mcp.run()` method (with an assumed `@MCPServer()` decorator in `ArxivToolkit`) exposes toolkit methods as MCP tools, listed via `tools/list`. Other MCP clients can then tap in. + +**Reflection Question:** + +Why bother with config files for MCP servers? \ No newline at end of file diff --git a/app/course/units/unit6/module6.mdx b/app/course/units/unit6/module6.mdx new file mode 100644 index 0000000..f63d657 --- /dev/null +++ b/app/course/units/unit6/module6.mdx @@ -0,0 +1,114 @@ +## Module 6: Advanced MCP Concepts + +**Objective:** Explore server discovery and future MCP trends. + +### 6.1 Discovering MCP Servers + +`PulseMCPSearchToolkit` lets you hunt down MCP servers dynamically—no more manual digging. + +**Code Example:** + +```python +from camel.toolkits.mcp import PulseMCPSearchToolkit + +search_toolkit = PulseMCPSearchToolkit() +results = search_toolkit.search_mcp_servers(query="Slack", top_k=1) +print(results) + +``` + +### 6.2 ChatAgent as MCP + +Export your CAMEL `ChatAgent` as an MCP server so other clients (e.g., Claude, Cursor) can call it. Two ways to do it: + +1. Via the scripts in `services/`, just provide the config file to your favorite MCP clients, e.g., Claude, Cursor, etc + + ```python + "camel-chat-agent": { + "command": "/path/to/python", + "args": [ + "/path/to/camel/services/agent_mcp_server.py" + ], + "env": { + "OPENAI_API_KEY": "...", + "OPENROUTER_API_KEY": "...", + "BRAVE_API_KEY": "..." + } + } + ``` + + Attached screenshots shows Claude calls the CAMEL MCP servers. + + ![chatagent-as-mcp.png](mcp.camel-ai.org/course/module6/chatagent-as-mcp.png) + + ![chatagent-as-mcp2.png](mcp.camel-ai.org/course/module6/chatagent-as-mcp2.png) + +2. Using the `to_mcp()` function, as simple as the following script, it turns your agent into a MCP server. + + ```python + from camel.agents import ChatAgent + + # Create a chat agent with a model + agent = ChatAgent(model="gpt-4o-mini") + + # Create an MCP server from the agent + mcp_server = agent.to_mcp( + name="demo", description="A demonstration of ChatAgent to MCP conversion" + ) + + # Run the server + if __name__ == "__main__": + print("Starting MCP server on http://localhost:8000") + mcp_server.run(transport="streamable-http") + + ``` + + +### 6.3 MCP agent! + +1. MCP agent using MCP registry + + https://github.com/camel-ai/camel/blob/master/examples/agents/mcp_agent/mcp_agent_using_registry.py + + Connect your MCP agent to popular MCP registries, such as ACI.dev, and + + ```python + aci_config = ACIRegistryConfig( + api_key=os.getenv("ACI_API_KEY"), + linked_account_owner_id=os.getenv("ACI_LINKED_ACCOUNT_OWNER_ID"), + ) + + model = ModelFactory.create( + model_platform=ModelPlatformType.OPENAI, + model_type=ModelType.GPT_4O, + ) + + # Create MCPAgent with registry configurations + agent = MCPAgent( + model=model, + registry_configs=[aci_config], + ) + + ``` + + Now your agent is connected to the [ACI.dev](http://ACI.dev) ecosystem, and be able to use all the servers from there. + +2. MCP agent without function calling ability + +https://github.com/camel-ai/camel/blob/master/examples/agents/mcp_agent/mcp_agent_without_function_calling.py + +### 6.4 CAMEL MCP hub + +check https://mcp.camel-ai.org/ + +### 6.5 Workforce as MCP (ongoing) + +Expose CAMEL AI multi-agent systems as MCP servers for complex tasks (in development). + +**Example:** + +A “FinancialAnalysisWorkforce” MCP server could analyze reports using multiple agents, returning results via MCP. + +**Reflection Question:** + +How could MCP enhance multi-agent systems? \ No newline at end of file diff --git a/app/globals.css b/app/globals.css index 602d7f6..1c164d1 100644 --- a/app/globals.css +++ b/app/globals.css @@ -122,3 +122,77 @@ @apply bg-background text-foreground; } } + +@layer components { + .prose { + @apply text-gray-900; + } + + .prose h1 { + @apply text-3xl font-bold mb-6 mt-8; + } + + .prose h2 { + @apply text-2xl font-semibold mb-4 mt-6; + } + + .prose h3 { + @apply text-xl font-semibold mb-3 mt-5; + } + + .prose h4 { + @apply text-lg font-semibold mb-2 mt-4; + } + + .prose p { + @apply mb-4 leading-relaxed; + } + + .prose ul { + @apply mb-4 list-disc list-inside; + } + + .prose ol { + @apply mb-4 list-decimal list-inside; + } + + .prose li { + @apply mb-1; + } + + .prose blockquote { + @apply border-l-4 border-blue-500 pl-4 italic text-gray-700 mb-4; + } + + .prose code { + @apply bg-gray-100 px-1 py-0.5 rounded text-sm font-mono; + } + + .prose pre { + @apply bg-gray-900 text-white p-4 rounded-lg overflow-x-auto mb-4; + } + + .prose pre code { + @apply bg-transparent p-0 text-white; + } + + .prose a { + @apply text-blue-600 hover:text-blue-800 underline; + } + + .prose img { + @apply rounded-lg shadow-md mb-4; + } + + .prose table { + @apply w-full border-collapse border border-gray-300 mb-4; + } + + .prose th { + @apply border border-gray-300 px-4 py-2 bg-gray-100 font-semibold; + } + + .prose td { + @apply border border-gray-300 px-4 py-2; + } +} diff --git a/app/page.tsx b/app/page.tsx index 89d27d4..ab51bf0 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -105,7 +105,7 @@ function Modal({ server, onClose }: ModalProps) { ) : server.source === 'camel' ? ( Camel ) : ( Anthropic handleFilterChange('camel')} className={`px-4 py-2 text-sm font-bold rounded-md transition-colors flex items-center gap-1.5 cursor-pointer ${filter === 'camel' ? 'bg-card shadow-sm' : 'hover:bg-background/50'}`} > - Camel + Camel CAMEL @@ -272,7 +272,7 @@ function HomeContent() { ) : server.source === 'camel' ? ( Camel ) : server.source === 'community' ? ( Community ) : ( Anthropic ( + {header} + )) + const rows = data.rows.map((row, index) => ( + + {row.map((cell, cellIndex) => ( + {cell} + ))} + + )) + + return ( + + + {headers} + + {rows} +
+ ) +} + +interface CustomLinkProps extends React.AnchorHTMLAttributes { + href: string + children: React.ReactNode +} + +function CustomLink(props: CustomLinkProps) { + const { href, ...rest } = props + + if (href.startsWith('/')) { + return ( + + {props.children} + + ) + } + + if (href.startsWith('#')) { + return + } + + return +} + +interface RoundedImageProps extends React.ComponentProps { + alt: string +} + +function RoundedImage(props: RoundedImageProps) { + const { alt, ...rest } = props + return {alt +} + +interface CodeProps extends React.HTMLAttributes { + children: string +} + +function Code({ children, ...props }: CodeProps) { + const codeHTML = highlight(children) + return +} + +function slugify(str: string): string { + return str + .toString() + .toLowerCase() + .trim() // Remove whitespace from both ends of a string + .replace(/\s+/g, '-') // Replace spaces with - + .replace(/&/g, '-and-') // Replace & with 'and' + .replace(/[^\w\-]+/g, '') // Remove all non-word characters except for - + .replace(/\-\-+/g, '-') // Replace multiple - with single - +} + +function createHeading(level: number) { + const Heading = ({ children }: { children: React.ReactNode }) => { + const slug = slugify(children as string) + return React.createElement( + `h${level}`, + { id: slug }, + [ + React.createElement('a', { + href: `#${slug}`, + key: `link-${slug}`, + className: 'anchor', + }), + ], + children + ) + } + + Heading.displayName = `Heading${level}` + + return Heading +} + +// Custom table components for markdown tables +function MarkdownTable(props: React.HTMLAttributes) { + return +} + +function MarkdownThead(props: React.HTMLAttributes) { + return +} + +function MarkdownTbody(props: React.HTMLAttributes) { + return +} + +function MarkdownTr(props: React.HTMLAttributes) { + return +} + +function MarkdownTh(props: React.HTMLAttributes) { + return
+} + +function MarkdownTd(props: React.HTMLAttributes) { + return +} + +const components = { + h1: createHeading(1), + h2: createHeading(2), + h3: createHeading(3), + h4: createHeading(4), + h5: createHeading(5), + h6: createHeading(6), + Image: RoundedImage, + a: CustomLink, + code: Code, + Table, + // Markdown table components + table: MarkdownTable, + thead: MarkdownThead, + tbody: MarkdownTbody, + tr: MarkdownTr, + th: MarkdownTh, + td: MarkdownTd, +} + +interface CustomMDXProps { + source: string + components?: Record> +} + +export async function CustomMDX(props: CustomMDXProps) { + return await MDXRemote({ + ...props, + components: { ...components, ...(props.components || {}) }, + }) +} diff --git a/components/nav.tsx b/components/nav.tsx index d35901d..b285973 100644 --- a/components/nav.tsx +++ b/components/nav.tsx @@ -38,6 +38,12 @@ export function Nav() { > CAMEL GitHub 🌟: {stars.toLocaleString()} +