21 KiB
name, overview, todos, isProject
| name | overview | todos | isProject | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| MCP Course Authoring from Structured Payloads | Build an MCP-compatible authoring surface that lets external AI clients such as Claude send normalized course structures into ClassroomIO, save them as reviewable drafts, revise them, and publish them into real ClassroomIO courses. |
|
false |
MCP Course Authoring from Structured Payloads
Status
- Draft
Date
- March 12, 2026
Purpose
Enable external AI clients such as Claude to create and edit ClassroomIO courses through MCP by sending normalized course structures into ClassroomIO.
The key product outcome is:
- Claude or another agent reads source material externally
- Claude turns that material into structured course JSON
- MCP sends that JSON to ClassroomIO
- ClassroomIO stores it as a draft
- the user reviews and explicitly publishes it
This supports prompts like:
- "Create a complete course on exponential functions in Classroom."
- "Read this PDF, extract the course structure, and publish it into Classroom."
with one important boundary:
In v1, ClassroomIO does not parse or extract PDFs itself.
Problem Statement
ClassroomIO already has the core CRUD primitives needed to create courses and content, but it does not yet expose a clean automation layer for external AI agents.
Current limitations:
- external AI clients have no MCP-compatible tool surface
- there is no normalized course payload contract for external agents
- there is no first-class draft persistence model for AI-generated course structures
- there is no explicit review/publish workflow for machine-generated content
- there is no stable API contract for "here is a complete course structure, store it safely"
This means agents can only create ClassroomIO content through brittle, record-by-record automation instead of a deliberate authoring pipeline.
Core Product Decision
Claude or the external agent is responsible for extraction. ClassroomIO is responsible for validation, persistence, review, authorization, and publish.
That means:
- if the source is a PDF, Claude reads it
- if the source is a website, Claude reads it
- if the source is a prompt, Claude expands it
- ClassroomIO only receives structured payloads
Primary Use Cases
Use Case 1: Prompt to Structured Draft
A teacher asks Claude to create a course on a topic. Claude reasons about the content and sends a normalized course payload to ClassroomIO through MCP.
Use Case 2: PDF to Structured Draft
A teacher provides one or more PDFs to Claude. Claude extracts the material and converts it into a normalized course payload. MCP sends that payload to ClassroomIO.
Use Case 3: Draft Revision
After initial creation, Claude updates the draft by:
- renaming sections
- rewriting lesson content
- reordering lessons
- adding or removing lessons
- publishing when the teacher confirms
Goals
- Provide an MCP-compatible authoring surface for external AI clients.
- Keep ClassroomIO as the source of truth for auth, permissions, validation, persistence, and publishing.
- Standardize a normalized course payload that any external agent can submit.
- Make draft review mandatory before publish in v1.
- Ensure org-scoped, permission-safe authoring with no direct DB access from the MCP server.
- Support both prompt-derived and PDF-derived content through one shared payload contract.
- Preserve human control by returning warnings and requiring explicit publish.
Non-Goals (v1)
- Student-facing AI workflows
- Direct DB writes from the MCP server
- Full autonomous publishing without a review step
- ClassroomIO-side PDF extraction, OCR, or parsing
- Async PDF extraction workers
- Cross-organization or cross-course content access
- Full LMS migration parity with Teachable/Thinkific/SCORM in this PRD
- Perfect fidelity for tables, diagrams, formulas, or source formatting
Confirmed Product Decisions
- MCP is an orchestration layer, not the persistence layer.
- ClassroomIO API owns validation, authorization, draft storage, and publish.
- Draft-first is required; AI-generated content is not published immediately in v1.
- External agents submit structured payloads; ClassroomIO does not ingest raw PDFs in v1.
- The MCP server should start as a thin tool adapter and not require its own model provider.
- Lesson long-form content should be stored in lesson language content, not only
lesson.note. - Publish remains an explicit second step.
Current-State Audit
| Capability | Current State | Notes |
|---|---|---|
| Course creation | Exists via POST /course |
Creates course shell only |
| Section creation | Exists via POST /course/:courseId/section |
One section at a time |
| Lesson creation | Exists via POST /course/:courseId/lesson |
One lesson at a time |
| Lesson language content | Exists via lesson-language routes | Good fit for generated lesson body |
| Course content reorder | Exists via content/section/lesson reorder routes | Useful after generation |
| Document upload | Exists for lesson documents | Not needed for v1 MCP ingestion |
| AI course assistant | PRD exists | Focused on in-app chat, not MCP |
| Public API keys | PRD exists | Likely dependency for auth model |
| Draft persistence for AI-generated course structures | Does not exist yet | Must be added |
Data Sources Checked
- apps/api/src/routes/course/course.ts
- apps/api/src/routes/course/section.ts
- apps/api/src/routes/course/lesson.ts
- apps/api/src/routes/course/lesson-language.ts
- apps/api/src/services/course/course.ts
- apps/api/src/services/course/section.ts
- apps/api/src/services/lesson/lesson.ts
- apps/api/src/services/lesson-language.ts
- packages/utils/src/validation/course/course.ts
- packages/utils/src/validation/lesson/lesson.ts
- packages/utils/src/validation/lesson/language.ts
- prd/ai-course-assistant/README.md
- prd/public-api/README.md
User Personas
Persona A: Solo Teacher
Wants to turn source material into a structured first course quickly, then lightly edit before publish.
Persona B: Training Operator
Uses Claude to convert internal manuals or SOPs into structured staff training without rebuilding content manually.
Persona C: Content Team
Uses Claude as an operator to draft or refactor course structures programmatically across multiple organizations and topics.
Jobs To Be Done
- Convert source intent or material into a usable draft course structure.
- Persist AI-generated course structures safely before publish.
- Review machine-generated content before it affects learner-facing content.
- Keep content generation inside org permissions and audit boundaries.
- Avoid brittle record-by-record automation from external agents.
Product Principles
- Draft before publish.
- API-first, not DB-first.
- One normalized course payload regardless of how the agent derived it.
- External agents think; ClassroomIO validates and persists.
- Warnings are first-class output, not hidden logs.
Proposed Architecture
flowchart TB
subgraph client [AI Client]
Claude["Claude or other MCP client"]
Source["Prompt / PDF / Website / Notes"]
end
subgraph mcp [MCP Server]
Tools["MCP tools"]
Adapter["ClassroomIO API adapter"]
end
subgraph api [ClassroomIO API]
Auth["Org-scoped automation key auth"]
DraftRoutes["Draft routes"]
DraftService["Draft orchestration service"]
PublishService["Draft publish service"]
end
subgraph db [Data Layer]
DraftTable["course_import_draft"]
CourseTables["course / section / lesson / lesson_language"]
end
Source --> Claude
Claude --> Tools
Tools --> Adapter
Adapter --> Auth
Auth --> DraftRoutes
DraftRoutes --> DraftService
DraftService --> DraftTable
DraftService --> PublishService
PublishService --> CourseTables
Scope Overview
In Scope
- MCP tool surface for structured course authoring workflows
- organization-scoped authentication for MCP clients
- draft persistence and preview APIs
- publish flow from draft to real course content
- warnings, audit logging, and idempotency
Out of Scope
- raw PDF ingestion by ClassroomIO
- OCR, parsing, or extraction jobs
- rich in-dashboard chat UX
- student preview or learner-side AI guidance
Functional Requirements
FR-1 MCP Tool Surface
The MCP server must expose a minimal, stable set of tools for AI clients:
create_course_draftget_course_draftupdate_course_draftpublish_course_draftlist_org_coursesget_course_structure
The MCP server must not write directly to the database.
FR-2 Authentication and Scoping
All MCP actions must be authenticated and scoped to an organization.
Requirements:
- use organization-scoped automation keys
- reuse the same underlying key system as the Public API
- allow MCP to use a narrower scope preset than generic API access
- every request resolves a single org context
- mutations require org admin or authorized course team membership
- cross-org access must be rejected
FR-3 Normalized Course Payload
All external agents must submit the same normalized course payload.
Minimum payload shape:
- course metadata
- sections
- lessons
- lesson content by locale
- optional exercises
- source references
- warnings
FR-4 Draft Review
Generated content must remain in draft state until explicitly published.
The draft API must allow:
- fetching current draft
- patching draft metadata
- adding/removing/reordering sections and lessons
- updating lesson titles and language content
- inspecting warnings before publish
FR-5 Structured Payload Ingestion
Users must be able to create a draft course by submitting a normalized course payload.
The API must not care whether the payload came from:
- a direct prompt
- a PDF processed by Claude
- a website processed by Claude
- manually authored JSON
FR-6 Publish Flow
Publishing a draft must create or update real ClassroomIO content through the existing service layer.
Publish orchestration should:
- create course shell
- create sections
- create lessons
- create lesson-language content
- optionally create exercises in later scope
- return created IDs and publish summary
FR-7 Idempotency and Retry Safety
MCP-triggered operations must be safe to retry.
Requirements:
- accept idempotency key on draft creation and publish
- prevent duplicate course creation on publish retries
FR-8 Observability
The system must expose enough detail for debugging and support.
Requirements:
- structured logs per draft create/update/publish action
- audit trail for actor, org, tool, and created course IDs
- warning list available to the AI client and internal admins
- rate limits per org and per key
Data Model
New Table: course_import_draft
Fields:
idorganizationIdcreatedByProfileIdsourceTypestatustitlelocaleidempotencyKeysummarydraftwarningssourceArtifactspublishedCourseId- timestamps
This table stores the normalized draft payload and the publish result.
Normalized Payload Contract
type CourseDraft = {
course: {
title: string;
description: string;
type: 'LIVE_CLASS' | 'SELF_PACED';
locale: string;
metadata?: Record<string, unknown>;
};
sections: Array<{
externalId: string;
title: string;
order: number;
}>;
lessons: Array<{
externalId: string;
sectionExternalId: string;
title: string;
order: number;
isUnlocked?: boolean;
public?: boolean;
}>;
lessonLanguages: Array<{
lessonExternalId: string;
locale: string;
content: string;
}>;
exercises?: Array<Record<string, unknown>>;
sourceReferences?: Array<{
type: 'prompt' | 'pdf';
label: string;
pageStart?: number;
pageEnd?: number;
}>;
warnings: Array<{
code: string;
message: string;
severity: 'info' | 'warning' | 'error';
}>;
};
Proposed API Design
Validation
Add:
packages/utils/src/validation/course-import/course-import.ts
Schemas:
ZCourseImportDraftCreateZCourseImportDraftGetParamZCourseImportDraftUpdateZCourseImportDraftPublish
Routes
Add organization-scoped route group:
apps/api/src/routes/organization/course-import.ts
Suggested endpoints:
| Method | Path | Purpose |
|---|---|---|
POST |
/organization/course-import/drafts |
Create draft from normalized payload |
GET |
/organization/course-import/drafts/:draftId |
Get draft |
PUT |
/organization/course-import/drafts/:draftId |
Update draft |
POST |
/organization/course-import/drafts/:draftId/publish |
Publish draft into course |
All routes must return a single response shape and follow existing route conventions.
Services
Add:
apps/api/src/services/course-import/course-import.ts
Responsibilities:
- draft persistence
- validation of structured payloads
- publish orchestration using current course/section/lesson/language services
MCP Server Design
Placement
Recommended:
apps/mcporpackages/mcp
Start with stdio transport for Claude Desktop and local agent usage.
Responsibilities
- expose MCP tools
- validate tool input
- forward requests to ClassroomIO API
- format structured responses for the AI client
Non-Responsibilities
- direct DB access
- authorization decisions independent of ClassroomIO
- content extraction from source materials
- PDF parsing or OCR
Tool Contracts
create_course_draft
Input:
organizationId- normalized
draft sourceTypeidempotencyKey?summary?sourceArtifacts?
Output:
draftIdsummarywarnings
get_course_draft
Input:
draftId
Output:
- stored draft payload
- summary
- warnings
- publish status
update_course_draft
Input:
draftId- patch payload
Output:
- updated summary
- warnings
publish_course_draft
Input:
draftId- optional overrides for title/description/type/metadata
Output:
courseIdcreatedSectionscreatedLessonslocaleCount
Shared API Key Design
MCP and Public API should reuse the same underlying key infrastructure.
They should not reuse the same default secret for every use case.
Recommended model:
- one shared
organization_api_keytable - one shared bearer-auth middleware
- different key presets by integration type
Recommended key fields:
idorganizationIdtype:mcp | api | zapierlabelsecretPrefixsecretHashscopescreatedByProfileIdlastUsedAtexpiresAtrevokedAtcreatedAtupdatedAt
Recommended key handling:
- show raw secret once on creation
- store only the hash
- allow multiple active keys per organization
- encourage one key per integration or agent
Recommended preset policy:
mcppreset:- draft create/read/update
- draft publish
apipreset:- broader route-specific scopes selected by admin
Route Design
Shared management routes should live under organization automation, not a separate MCP-only surface.
Recommended management routes:
GET /organization/automation/keysPOST /organization/automation/keysDELETE /organization/automation/keys/:keyIdPOST /organization/automation/keys/:keyId/rotate
Recommended request fields for create:
typelabelpresetscopesoptional for custom API keysexpiresAtoptional
Recommended auth flow for machine requests:
- client sends
Authorization: Bearer <key> - middleware hashes key
- API resolves organization from stored key row
- API attaches scopes and key metadata to context
- route checks scope and org permissions
Automation UI Design
The management UI should live in the org sidebar as Automation.
Tabs:
MCPZapierdisabled, coming soonAPIdisabled, coming soon until public API ships
MCP tab responsibilities:
- explain what MCP does
- generate org-scoped MCP keys
- list active MCP keys
- revoke or rotate keys
- show setup snippets for:
- Claude Code
- Codex
- Cursor
Permissions:
- org admins can create, rotate, revoke
- teachers can open the page but see the controls disabled
- all server mutations remain admin-only
UX and Review Model
The review loop is:
- external agent creates draft
- user inspects summary and warnings
- external agent revises draft if needed
- user explicitly publishes
The AI client should never assume silent publish in v1.
Failure Modes and Warnings
The system must capture and expose at least these issues:
- duplicate external IDs
- lessons referencing missing sections
- lesson-language entries referencing missing lessons
- empty or invalid titles/content
- invalid metadata shape
- duplicate publish attempts
Publishing should be blocked on validation errors. Informational warnings may still allow publish.
Security Considerations
- Reject cross-org access for all draft and publish routes.
- Enforce org admin or equivalent authoring permissions.
- Log key ID, key type, organization, and output course IDs for all MCP-triggered publishes.
- Apply rate limits per org and per key.
- Keep the MCP server outside the database trust boundary.
Success Metrics
Product Metrics
- Time from agent output to first draft available
- Draft-to-publish conversion rate
- Average manual edits before publish
- Percentage of generated drafts published within 24 hours
Quality Metrics
- Percentage of drafts requiring no structural edits
- Publish failure rate
- Duplicate publish rate
- Support tickets related to malformed structure payloads
Rollout Plan
Phase 1: Draft and Publish API
Ship:
- normalized payload schema
- draft create endpoint
- draft update endpoint
- publish endpoint
Phase 2: MCP Server
Ship:
- stdio MCP server
- auth wiring
- tool contracts for create/get/update/publish
Phase 3: Hardening
Ship:
- idempotency keys
- richer warnings
- observability
- docs and examples
Dependencies
- Existing course, section, lesson, and lesson-language services remain the publish target.
- Shared organization automation key infrastructure must be implemented.
- External agent must generate normalized course payloads that match the schema.
Open Questions
- Should
publish_course_draftremain a separate tool only, or support an explicitpublish: trueflag during draft creation? - Should exercise payloads be part of v1 or deferred until lesson drafting stabilizes?
- What locale set should be supported by default?
- Should cloud-only restrictions apply, similar to the AI assistant PRD?
- Should the shared key-management routes ship before public
/v1/*API routes, so MCP can adopt them first?
Recommendation Summary
The correct v1 shape is:
- thin MCP server
- ClassroomIO-owned draft/publish API
- external agent owns source extraction
- explicit draft review before publish
- publish via existing content services, not custom DB writes
That keeps the system small, matches your clarified boundary, and gives Claude a clean automation surface without forcing ClassroomIO to solve PDF extraction in v1.