Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
4 changes: 1 addition & 3 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: publish-to-github-pages
on:
push:
branches:
- main
- course

permissions:
contents: read
Expand All @@ -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
Expand Down
114 changes: 114 additions & 0 deletions app/course/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -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<string, unknown>
}

async function getUnitBySlug(slug: string): Promise<UnitData | null> {
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 (
<div className="min-h-screen bg-gray-50">
<div className="max-w-4xl mx-auto px-4 py-8">
<div className="mb-8">
<nav className="mb-6">
<Link
href="/course/units"
className="text-blue-600 hover:text-blue-800 font-medium"
>
← Back to Course
</Link>
</nav>

<h1 className="text-4xl font-bold text-gray-900 mb-4">
{unit.title}
</h1>
</div>

<article className="bg-white rounded-lg shadow-md overflow-hidden">
<div className="p-8">
<div className="prose prose-lg max-w-none">
<div className="whitespace-pre-wrap text-gray-700 leading-relaxed">
{unit.content}
</div>
</div>
</div>
</article>
</div>
</div>
)
}

// 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
}
155 changes: 155 additions & 0 deletions app/course/page.tsx
Original file line number Diff line number Diff line change
@@ -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<Unit[]> {
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 (
<div className="min-h-screen bg-gray-50">
<div className="max-w-6xl mx-auto px-4 py-8">
{/* Hero Section */}
<div className="text-center mb-12">
<h1 className="text-5xl font-bold text-gray-900 mb-6">
Mastering CAMEL AI with Model Context Protocol (MCP)
</h1>
<p className="text-xl text-gray-600 max-w-3xl mx-auto leading-relaxed">
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.
</p>
<div className="mt-8">
<Link
href="/course/units"
className="inline-flex items-center px-6 py-3 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700 transition-colors"
>
Start Learning
</Link>
</div>
</div>

{/* Course Overview */}
<div className="bg-white rounded-lg shadow-md p-8 mb-12">
<h2 className="text-3xl font-bold text-gray-900 mb-6">Course Overview</h2>
<div className="grid md:grid-cols-2 gap-8">
<div>
<h3 className="text-xl font-semibold text-gray-900 mb-4">What You&apos;ll Learn</h3>
<ul className="space-y-2 text-gray-600">
<li>• Understanding tooling in CAMEL AI</li>
<li>• Model Context Protocol (MCP) fundamentals</li>
<li>• Building custom MCP servers</li>
<li>• Integrating MCP with CAMEL AI agents</li>
<li>• Creating versatile, tool-augmented AI systems</li>
</ul>
</div>
<div>
<h3 className="text-xl font-semibold text-gray-900 mb-4">Prerequisites</h3>
<ul className="space-y-2 text-gray-600">
<li>• Basic Python programming skills</li>
<li>• Understanding of functions and imports</li>
<li>• Familiarity with AI concepts (optional)</li>
<li>• Eagerness to learn advanced tooling</li>
</ul>
</div>
</div>
</div>

{/* Course Units */}
<div className="mb-12">
<h2 className="text-3xl font-bold text-gray-900 mb-8">Course Units</h2>
<div className="grid gap-6">
{units.map((unit, index) => (
<div key={unit.slug} className="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow">
<div className="p-6">
<div className="flex items-start">
<div className="flex-shrink-0">
<span className="inline-flex items-center justify-center w-12 h-12 bg-blue-100 text-blue-800 text-lg font-bold rounded-full">
{index + 1}
</span>
</div>
<div className="ml-4 flex-1">
<h3 className="text-xl font-semibold text-gray-900 mb-2">
{unit.title}
</h3>
<p className="text-gray-600 mb-4">
{unit.excerpt}
</p>
<Link
href={`/course/${unit.slug}`}
className="inline-flex items-center text-blue-600 hover:text-blue-800 font-medium"
>
Read Unit →
</Link>
</div>
</div>
</div>
</div>
))}
</div>
</div>

{/* Call to Action */}
<div className="text-center bg-blue-600 rounded-lg p-8 text-white">
<h2 className="text-3xl font-bold mb-4">Ready to Master MCP?</h2>
<p className="text-xl mb-6 opacity-90">
Start your journey into advanced AI tooling and interoperability
</p>
<Link
href="/course/units"
className="inline-flex items-center px-8 py-4 bg-white text-blue-600 font-semibold rounded-lg hover:bg-gray-100 transition-colors"
>
Begin Course
</Link>
</div>
</div>
</div>
)
}
90 changes: 90 additions & 0 deletions app/course/units/page.tsx
Original file line number Diff line number Diff line change
@@ -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<string, unknown>
}

async function getUnits(): Promise<Unit[]> {
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 (
<div className="min-h-screen bg-gray-50">
<div className="max-w-4xl mx-auto px-4 py-8">
<div className="mb-8">
<h1 className="text-4xl font-bold text-gray-900 mb-4">
CAMEL AI with Model Context Protocol (MCP) Course
</h1>
<p className="text-lg text-gray-600">
Master the art of building AI agents with advanced tooling capabilities
</p>
</div>

<div className="space-y-8">
{units.map((unit, index) => (
<article key={unit.slug} className="bg-white rounded-lg shadow-md overflow-hidden">
<div className="p-6">
<div className="flex items-center mb-4">
<span className="inline-flex items-center justify-center w-8 h-8 bg-blue-100 text-blue-800 text-sm font-medium rounded-full mr-3">
{index + 1}
</span>
<h2 className="text-2xl font-semibold text-gray-900">
{unit.title}
</h2>
</div>

<div className="prose prose-lg max-w-none">
<div className="whitespace-pre-wrap text-gray-700 leading-relaxed">
{unit.content}
</div>
</div>
</div>
</article>
))}
</div>
</div>
</div>
)
}
15 changes: 15 additions & 0 deletions app/course/units/unit0/introduction.mdx
Original file line number Diff line number Diff line change
@@ -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.
Loading
Loading