Skip to content

Commit a45facc

Browse files
authored
Merge pull request #22 from camel-ai/course
Course
2 parents 9c6a7eb + cbd474b commit a45facc

File tree

24 files changed

+1291
-15
lines changed

24 files changed

+1291
-15
lines changed
File renamed without changes.

.github/workflows/publish.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: publish-to-github-pages
33
on:
44
push:
55
branches:
6-
- main
6+
- course
77

88
permissions:
99
contents: read
@@ -27,8 +27,6 @@ jobs:
2727

2828
- name: Setup Pages ⚙️
2929
uses: actions/configure-pages@v4
30-
with:
31-
static_site_generator: next
3230

3331
- name: Build with Next.js 🏗️
3432
run: npx next build

app/course/[slug]/page.tsx

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import fs from 'fs'
2+
import path from 'path'
3+
import matter from 'gray-matter'
4+
import { notFound } from 'next/navigation'
5+
import Link from 'next/link'
6+
7+
interface PageProps {
8+
params: Promise<{
9+
slug: string
10+
}>
11+
}
12+
13+
interface UnitData {
14+
title: string
15+
content: string
16+
frontmatter: Record<string, unknown>
17+
}
18+
19+
async function getUnitBySlug(slug: string): Promise<UnitData | null> {
20+
try {
21+
// Parse the slug to get unit directory and file name
22+
const [unitDir, fileName] = slug.split('/')
23+
24+
if (!unitDir || !fileName) {
25+
return null
26+
}
27+
28+
const filePath = path.join(process.cwd(), 'app/course/units', unitDir, `${fileName}.mdx`)
29+
30+
if (!fs.existsSync(filePath)) {
31+
return null
32+
}
33+
34+
const source = fs.readFileSync(filePath, 'utf8')
35+
const { data: frontmatter, content } = matter(source)
36+
37+
// Extract title from the first heading or use filename
38+
const titleMatch = content.match(/^#\s+(.+)$/m)
39+
const title = titleMatch ? titleMatch[1] : fileName
40+
41+
return {
42+
title,
43+
content,
44+
frontmatter
45+
}
46+
} catch (error) {
47+
console.error('Error reading unit file:', error)
48+
return null
49+
}
50+
}
51+
52+
export default async function UnitPage({ params }: PageProps) {
53+
const { slug } = await params
54+
const unit = await getUnitBySlug(slug)
55+
56+
if (!unit) {
57+
notFound()
58+
}
59+
60+
return (
61+
<div className="min-h-screen bg-gray-50">
62+
<div className="max-w-4xl mx-auto px-4 py-8">
63+
<div className="mb-8">
64+
<nav className="mb-6">
65+
<Link
66+
href="/course/units"
67+
className="text-blue-600 hover:text-blue-800 font-medium"
68+
>
69+
← Back to Course
70+
</Link>
71+
</nav>
72+
73+
<h1 className="text-4xl font-bold text-gray-900 mb-4">
74+
{unit.title}
75+
</h1>
76+
</div>
77+
78+
<article className="bg-white rounded-lg shadow-md overflow-hidden">
79+
<div className="p-8">
80+
<div className="prose prose-lg max-w-none">
81+
<div className="whitespace-pre-wrap text-gray-700 leading-relaxed">
82+
{unit.content}
83+
</div>
84+
</div>
85+
</div>
86+
</article>
87+
</div>
88+
</div>
89+
)
90+
}
91+
92+
// Generate static params for all units
93+
export async function generateStaticParams() {
94+
const unitsDir = path.join(process.cwd(), 'app/course/units')
95+
const unitDirs = fs.readdirSync(unitsDir).filter(dir =>
96+
fs.statSync(path.join(unitsDir, dir)).isDirectory() && dir.startsWith('unit')
97+
)
98+
99+
const params: { slug: string }[] = []
100+
101+
for (const unitDir of unitDirs) {
102+
const unitPath = path.join(unitsDir, unitDir)
103+
const files = fs.readdirSync(unitPath).filter(file => file.endsWith('.mdx'))
104+
105+
for (const file of files) {
106+
const fileName = file.replace('.mdx', '')
107+
params.push({
108+
slug: `${unitDir}/${fileName}`
109+
})
110+
}
111+
}
112+
113+
return params
114+
}

app/course/page.tsx

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import Link from 'next/link'
2+
import fs from 'fs'
3+
import path from 'path'
4+
import matter from 'gray-matter'
5+
6+
interface Unit {
7+
slug: string
8+
title: string
9+
excerpt: string
10+
unitNumber: number
11+
}
12+
13+
async function getCourseUnits(): Promise<Unit[]> {
14+
const unitsDir = path.join(process.cwd(), 'app/course/units')
15+
const unitDirs = fs.readdirSync(unitsDir).filter(dir =>
16+
fs.statSync(path.join(unitsDir, dir)).isDirectory() && dir.startsWith('unit')
17+
)
18+
19+
const units: Unit[] = []
20+
21+
for (const unitDir of unitDirs) {
22+
const unitPath = path.join(unitsDir, unitDir)
23+
const files = fs.readdirSync(unitPath).filter(file => file.endsWith('.mdx'))
24+
25+
for (const file of files) {
26+
const filePath = path.join(unitPath, file)
27+
const source = fs.readFileSync(filePath, 'utf8')
28+
const { content } = matter(source)
29+
30+
// Extract title from the first heading or use filename
31+
const titleMatch = content.match(/^#\s+(.+)$/m)
32+
const title = titleMatch ? titleMatch[1] : file.replace('.mdx', '')
33+
34+
// Extract excerpt from the first paragraph
35+
const excerptMatch = content.match(/^#\s+.+?\n\n([\s\S]+?)(?=\n\n|$)/)
36+
const excerpt = excerptMatch ? excerptMatch[1].substring(0, 150) + '...' : 'Course content...'
37+
38+
const unitNumber = parseInt(unitDir.match(/unit(\d+)/)?.[1] || '0')
39+
40+
units.push({
41+
slug: `${unitDir}/${file.replace('.mdx', '')}`,
42+
title,
43+
excerpt,
44+
unitNumber
45+
})
46+
}
47+
}
48+
49+
// Sort units by their directory number
50+
return units.sort((a, b) => a.unitNumber - b.unitNumber)
51+
}
52+
53+
export default async function CoursePage() {
54+
const units = await getCourseUnits()
55+
56+
return (
57+
<div className="min-h-screen bg-gray-50">
58+
<div className="max-w-6xl mx-auto px-4 py-8">
59+
{/* Hero Section */}
60+
<div className="text-center mb-12">
61+
<h1 className="text-5xl font-bold text-gray-900 mb-6">
62+
Mastering CAMEL AI with Model Context Protocol (MCP)
63+
</h1>
64+
<p className="text-xl text-gray-600 max-w-3xl mx-auto leading-relaxed">
65+
Learn how to build powerful AI agents with advanced tooling capabilities.
66+
From basic tooling in CAMEL AI to mastering the Model Context Protocol (MCP),
67+
this comprehensive course will transform your AI development skills.
68+
</p>
69+
<div className="mt-8">
70+
<Link
71+
href="/course/units"
72+
className="inline-flex items-center px-6 py-3 bg-blue-600 text-white font-semibold rounded-lg hover:bg-blue-700 transition-colors"
73+
>
74+
Start Learning
75+
</Link>
76+
</div>
77+
</div>
78+
79+
{/* Course Overview */}
80+
<div className="bg-white rounded-lg shadow-md p-8 mb-12">
81+
<h2 className="text-3xl font-bold text-gray-900 mb-6">Course Overview</h2>
82+
<div className="grid md:grid-cols-2 gap-8">
83+
<div>
84+
<h3 className="text-xl font-semibold text-gray-900 mb-4">What You&apos;ll Learn</h3>
85+
<ul className="space-y-2 text-gray-600">
86+
<li>• Understanding tooling in CAMEL AI</li>
87+
<li>• Model Context Protocol (MCP) fundamentals</li>
88+
<li>• Building custom MCP servers</li>
89+
<li>• Integrating MCP with CAMEL AI agents</li>
90+
<li>• Creating versatile, tool-augmented AI systems</li>
91+
</ul>
92+
</div>
93+
<div>
94+
<h3 className="text-xl font-semibold text-gray-900 mb-4">Prerequisites</h3>
95+
<ul className="space-y-2 text-gray-600">
96+
<li>• Basic Python programming skills</li>
97+
<li>• Understanding of functions and imports</li>
98+
<li>• Familiarity with AI concepts (optional)</li>
99+
<li>• Eagerness to learn advanced tooling</li>
100+
</ul>
101+
</div>
102+
</div>
103+
</div>
104+
105+
{/* Course Units */}
106+
<div className="mb-12">
107+
<h2 className="text-3xl font-bold text-gray-900 mb-8">Course Units</h2>
108+
<div className="grid gap-6">
109+
{units.map((unit, index) => (
110+
<div key={unit.slug} className="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow">
111+
<div className="p-6">
112+
<div className="flex items-start">
113+
<div className="flex-shrink-0">
114+
<span className="inline-flex items-center justify-center w-12 h-12 bg-blue-100 text-blue-800 text-lg font-bold rounded-full">
115+
{index + 1}
116+
</span>
117+
</div>
118+
<div className="ml-4 flex-1">
119+
<h3 className="text-xl font-semibold text-gray-900 mb-2">
120+
{unit.title}
121+
</h3>
122+
<p className="text-gray-600 mb-4">
123+
{unit.excerpt}
124+
</p>
125+
<Link
126+
href={`/course/${unit.slug}`}
127+
className="inline-flex items-center text-blue-600 hover:text-blue-800 font-medium"
128+
>
129+
Read Unit →
130+
</Link>
131+
</div>
132+
</div>
133+
</div>
134+
</div>
135+
))}
136+
</div>
137+
</div>
138+
139+
{/* Call to Action */}
140+
<div className="text-center bg-blue-600 rounded-lg p-8 text-white">
141+
<h2 className="text-3xl font-bold mb-4">Ready to Master MCP?</h2>
142+
<p className="text-xl mb-6 opacity-90">
143+
Start your journey into advanced AI tooling and interoperability
144+
</p>
145+
<Link
146+
href="/course/units"
147+
className="inline-flex items-center px-8 py-4 bg-white text-blue-600 font-semibold rounded-lg hover:bg-gray-100 transition-colors"
148+
>
149+
Begin Course
150+
</Link>
151+
</div>
152+
</div>
153+
</div>
154+
)
155+
}

app/course/units/page.tsx

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import fs from 'fs'
2+
import path from 'path'
3+
import matter from 'gray-matter'
4+
5+
interface Unit {
6+
slug: string
7+
title: string
8+
content: string
9+
frontmatter: Record<string, unknown>
10+
}
11+
12+
async function getUnits(): Promise<Unit[]> {
13+
const unitsDir = path.join(process.cwd(), 'app/course/units')
14+
const unitDirs = fs.readdirSync(unitsDir).filter(dir =>
15+
fs.statSync(path.join(unitsDir, dir)).isDirectory() && dir.startsWith('unit')
16+
)
17+
18+
const units: Unit[] = []
19+
20+
for (const unitDir of unitDirs) {
21+
const unitPath = path.join(unitsDir, unitDir)
22+
const files = fs.readdirSync(unitPath).filter(file => file.endsWith('.mdx'))
23+
24+
for (const file of files) {
25+
const filePath = path.join(unitPath, file)
26+
const source = fs.readFileSync(filePath, 'utf8')
27+
const { data: frontmatter, content } = matter(source)
28+
29+
// Extract title from the first heading or use filename
30+
const titleMatch = content.match(/^#\s+(.+)$/m)
31+
const title = titleMatch ? titleMatch[1] : file.replace('.mdx', '')
32+
33+
units.push({
34+
slug: `${unitDir}/${file.replace('.mdx', '')}`,
35+
title,
36+
content,
37+
frontmatter
38+
})
39+
}
40+
}
41+
42+
// Sort units by their directory number
43+
return units.sort((a, b) => {
44+
const aNum = parseInt(a.slug.match(/unit(\d+)/)?.[1] || '0')
45+
const bNum = parseInt(b.slug.match(/unit(\d+)/)?.[1] || '0')
46+
return aNum - bNum
47+
})
48+
}
49+
50+
export default async function CourseUnitsPage() {
51+
const units = await getUnits()
52+
53+
return (
54+
<div className="min-h-screen bg-gray-50">
55+
<div className="max-w-4xl mx-auto px-4 py-8">
56+
<div className="mb-8">
57+
<h1 className="text-4xl font-bold text-gray-900 mb-4">
58+
CAMEL AI with Model Context Protocol (MCP) Course
59+
</h1>
60+
<p className="text-lg text-gray-600">
61+
Master the art of building AI agents with advanced tooling capabilities
62+
</p>
63+
</div>
64+
65+
<div className="space-y-8">
66+
{units.map((unit, index) => (
67+
<article key={unit.slug} className="bg-white rounded-lg shadow-md overflow-hidden">
68+
<div className="p-6">
69+
<div className="flex items-center mb-4">
70+
<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">
71+
{index + 1}
72+
</span>
73+
<h2 className="text-2xl font-semibold text-gray-900">
74+
{unit.title}
75+
</h2>
76+
</div>
77+
78+
<div className="prose prose-lg max-w-none">
79+
<div className="whitespace-pre-wrap text-gray-700 leading-relaxed">
80+
{unit.content}
81+
</div>
82+
</div>
83+
</div>
84+
</article>
85+
))}
86+
</div>
87+
</div>
88+
</div>
89+
)
90+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Mastering CAMEL AI with Model Context Protocol (MCP)
2+
3+
## Course Overview
4+
5+
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.
6+
7+
### Target Audience
8+
9+
- Developers with basic Python knowledge.
10+
- AI enthusiasts eager to explore advanced tooling and interoperability.
11+
12+
### Prerequisites
13+
14+
- Basic Python skills (functions, imports).
15+
- Optional: Familiarity with AI concepts.

0 commit comments

Comments
 (0)