@classroomio/mcp 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,7 +10,7 @@ The package does not parse PDFs or generate course structures. The agent does th
10
10
  Teacher / Admin
11
11
  |
12
12
  v
13
- Claude Code / Codex / Cursor / other MCP client
13
+ Claude Code / Codex / Cursor / OpenCode / other MCP client
14
14
  |
15
15
  | 1. Reads prompt, PDF, or existing course goal
16
16
  | 2. Produces or edits normalized course JSON
@@ -43,6 +43,7 @@ Current tools:
43
43
 
44
44
  - `list_org_courses`
45
45
  - `get_course_structure`
46
+ - `reorder_course_content`
46
47
  - `update_course_landing_page`
47
48
  - `create_course_draft`
48
49
  - `create_course_draft_from_course`
@@ -122,6 +123,25 @@ codex mcp add classroomio \
122
123
  -- npx -y @classroomio/mcp
123
124
  ```
124
125
 
126
+ ### OpenCode
127
+
128
+ ```json
129
+ {
130
+ "$schema": "https://opencode.ai/config.json",
131
+ "mcp": {
132
+ "classroomio": {
133
+ "type": "local",
134
+ "command": ["npx", "-y", "@classroomio/mcp"],
135
+ "enabled": true,
136
+ "environment": {
137
+ "CLASSROOMIO_API_URL": "https://api.classroomio.com",
138
+ "CLASSROOMIO_API_KEY": "<cio_mcp_key>"
139
+ }
140
+ }
141
+ }
142
+ }
143
+ ```
144
+
125
145
  ### Cursor
126
146
 
127
147
  ```json
@@ -167,7 +187,9 @@ Drafts support:
167
187
  Lesson authoring rule:
168
188
 
169
189
  - `lessonLanguages[].content` should contain only the lesson body HTML
170
- - do not include the lesson title or a top-level heading that repeats `lessons[].title`
190
+ - do not include the lesson title
191
+ - do not use `h1` or `h2` in lesson content
192
+ - start headings at `h3`
171
193
  - ClassroomIO already renders the lesson title in the course UI
172
194
 
173
195
  ## After Publish
@@ -323,7 +345,22 @@ What this publish does today:
323
345
  - upserts lesson language content
324
346
  - merges draft tags into the existing course
325
347
 
326
- ### Flow 6: Add exercises to a live course
348
+ ### Flow 6: Reorder live course content
349
+
350
+ User says:
351
+
352
+ ```text
353
+ Move the quiz before lesson 2 and put the recap lesson into the last section.
354
+ ```
355
+
356
+ Expected tool sequence:
357
+
358
+ 1. Agent calls `reorder_course_content`.
359
+
360
+ Use this direct path when the live course only needs section ordering or lesson or exercise moves between sections.
361
+ It avoids the full draft round-trip for simple structural changes.
362
+
363
+ ### Flow 7: Add exercises to a live course
327
364
 
328
365
  User says:
329
366
 
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{McpServer as ht}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as St}from"@modelcontextprotocol/sdk/server/stdio.js";var b=class extends Error{constructor(n,i,u,l){super(n);this.status=i;this.code=u;this.field=l;this.name="ClassroomIoApiError"}},S=class{constructor(t){this.config=t}async createCourseDraft(t){return this.request("/organization/course-import/drafts",{method:"POST",body:t})}async createCourseDraftFromCourse(t){return this.request("/organization/course-import/drafts/from-course",{method:"POST",body:t})}async getCourseStructure(t){return this.request(`/organization/course-import/courses/${t}/structure`,{method:"GET"})}async updateCourseLandingPage(t,n){return this.request(`/course/${t}/landing-page`,{method:"PUT",body:n})}async getCourseDraft(t){return this.request(`/organization/course-import/drafts/${t}`,{method:"GET"})}async listOrganizationCourses(t={}){let n=new URLSearchParams;t.tags&&n.set("tags",t.tags);let i=n.toString()?`?${n.toString()}`:"";return this.request(`/organization/courses${i}`,{method:"GET"})}async listCourseExercises(t,n={}){let i=new URLSearchParams;n.lessonId&&i.set("lessonId",n.lessonId),n.sectionId&&i.set("sectionId",n.sectionId);let u=i.toString()?`?${i.toString()}`:"";return this.request(`/course/${t}/exercise${u}`,{method:"GET"})}async getCourseExercise(t,n){return this.request(`/course/${t}/exercise/${n}`,{method:"GET"})}async createCourseExercise(t,n){return this.request(`/course/${t}/exercise`,{method:"POST",body:{...n,courseId:t}})}async createCourseExerciseFromTemplate(t,n){return this.request(`/course/${t}/exercise/from-template`,{method:"POST",body:n})}async updateCourseExercise(t,n,i){return this.request(`/course/${t}/exercise/${n}`,{method:"PUT",body:i})}async updateCourseDraft(t,n){return this.request(`/organization/course-import/drafts/${t}`,{method:"PUT",body:n})}async tagCourseDraft(t,n){return this.request(`/organization/course-import/drafts/${t}/tags`,{method:"PUT",body:n})}async publishCourseDraft(t,n){return this.request(`/organization/course-import/drafts/${t}/publish`,{method:"POST",body:n})}async publishCourseDraftToExistingCourse(t,n){return this.request(`/organization/course-import/drafts/${t}/publish-existing-course`,{method:"POST",body:n})}async tagCourses(t){return this.request("/organization/tags/courses/assign",{method:"PUT",body:t})}async request(t,n){let i=await fetch(new URL(t,this.config.CLASSROOMIO_API_URL),{method:n.method,headers:{Authorization:`Bearer ${this.config.CLASSROOMIO_API_KEY}`,"content-type":"application/json","user-agent":this.config.CLASSROOMIO_USER_AGENT},body:n.body?JSON.stringify(n.body):void 0}),u=await i.json().catch(()=>null);if(!i.ok){let l=u;throw new b(l?.error??l?.message??`ClassroomIO request failed with status ${i.status}`,i.status,l?.code,l?.field)}if(!u||typeof u!="object"||!("success"in u)||!u.success)throw new b("ClassroomIO returned an invalid response payload",i.status);return u.data}};import{z as O}from"zod";var be=O.object({CLASSROOMIO_API_URL:O.url().default("https://api.classroomio.com"),CLASSROOMIO_API_KEY:O.string().min(1),CLASSROOMIO_USER_AGENT:O.string().min(1).default("classroomio-mcp/0.0.1")});function F(o=process.env){return be.parse(o)}import*as p from"zod";var Te=["#EF4444","#F97316","#F59E0B","#84CC16","#22C55E","#14B8A6","#06B6D4","#3B82F6","#8B5CF6","#EC4899"],M=p.enum(Te),wt=p.object({groupId:p.uuid()}),he=p.object({name:p.string().min(1).max(80),description:p.string().max(240).optional(),order:p.number().int().min(0).optional()}),Lt=he.partial().refine(o=>Object.keys(o).length>0,{message:"At least one field is required"}),Rt=p.object({tagId:p.uuid()}),Dt=p.object({name:p.string().min(1).max(80),description:p.string().max(240).optional(),groupId:p.uuid(),color:M}),Nt=p.object({name:p.string().min(1).max(80).optional(),description:p.string().max(240).optional(),groupId:p.uuid().optional(),color:M.optional()}).refine(o=>Object.keys(o).length>0,{message:"At least one field is required"}),Zt=p.object({courseId:p.uuid()}),Ut=p.object({tagIds:p.array(p.uuid()).max(100).default([])}),Se=p.enum(["merge","replace"]).default("merge"),Oe=p.string().trim().min(1).max(80),K=p.object({draftId:p.string().uuid()}),L=p.object({tagNames:p.array(Oe).min(1).max(100),mode:Se,groupName:p.string().trim().min(1).max(80).optional()}),R=L.extend({courseIds:p.array(p.string().uuid()).min(1).max(100)});import*as a from"zod";var H=["academy","app","apps","blog","build","campaign","church","cloud","community","conference","course","courses","demo","dev","forum","group","groups","help","launch","launchweek","mobile","play","prod","production","schools","stage","staging","support","teacher","teachers","tech","training"];var q;(function(o){o.Section="SECTION",o.Lesson="LESSON",o.Exercise="EXERCISE"})(q||(q={}));var Yt=a.object({siteName:a.string().min(1).optional(),customDomain:a.string().min(1).optional(),isCustomDomainVerified:a.boolean().optional()}),Xt=a.object({siteName:a.string().min(1),tags:a.string().optional()}),D=a.object({tags:a.string().optional()}),$t=a.object({siteName:a.string().min(1)}),Vt=a.object({orgId:a.uuid(),planName:a.enum(["EARLY_ADOPTER","ENTERPRISE","BASIC"]),subscriptionId:a.string().min(1),triggeredBy:a.number().int().positive(),payload:a.record(a.string(),a.unknown())}),Jt=a.object({subscriptionId:a.string().min(1),payload:a.record(a.string(),a.unknown())}),Wt=a.object({subscriptionId:a.string().min(1),payload:a.record(a.string(),a.unknown())}),eo=a.object({name:a.string().min(5).refine(o=>!/^[-]|[-]$/.test(o),{message:"Organization name cannot start or end with a hyphen"}),siteName:a.string().min(5).refine(o=>!/^[-]|[-]$/.test(o),{message:"Site name cannot start or end with a hyphen"}).refine(o=>!H.includes(o),{message:"Sitename already exists."})}),to=a.object({name:a.string().min(5).refine(o=>!/^[-]|[-]$/.test(o),{message:"Organization name cannot start or end with a hyphen"}).optional(),avatarUrl:a.url().optional(),theme:a.string().optional(),landingpage:a.record(a.string(),a.unknown()).optional(),siteName:a.string().min(1).optional(),customDomain:a.string().nullable().optional(),isCustomDomainVerified:a.boolean().optional(),customization:a.record(a.string(),a.unknown()).optional(),disableSignup:a.boolean().optional(),disableSignupMessage:a.string().optional(),disableEmailPassword:a.boolean().optional(),disableGoogleAuth:a.boolean().optional(),settings:a.object({signup:a.object({inviteOnly:a.boolean().optional()}).optional()}).optional()}),oo=a.object({emails:a.array(a.string().email()).min(1).max(50),roleId:a.number().int().positive()}),ro=a.object({memberId:a.coerce.number().int().positive()}),no=a.object({userId:a.uuid()});import*as g from"zod";var ao=g.object({orgId:g.uuid(),quizId:g.uuid()}),io=g.object({orgId:g.uuid()}),uo=g.object({title:g.string().min(1),questions:g.array(g.any()).optional(),timelimit:g.string().optional(),theme:g.string().optional()}),po=g.object({title:g.string().min(1).optional(),questions:g.array(g.any()).optional(),timelimit:g.string().optional(),theme:g.string().optional()});import*as A from"zod";var lo=A.object({orgId:A.uuid()});import*as _ from"zod";var go=_.object({token:_.string().min(10).max(512)});import*as c from"zod";var Ae=["OKTA","GOOGLE_WORKSPACE","AUTH0"],fo=c.object({provider:c.enum(Ae),displayName:c.string().min(1,"Display name is required"),issuer:c.url("Must be a valid URL"),domain:c.string().min(1,"Domain is required").regex(/^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}$/,"Invalid domain format"),clientId:c.string().min(1,"Client ID is required"),clientSecret:c.string().min(1,"Client secret is required"),scopes:c.string().default("openid profile email")}),Io=c.object({displayName:c.string().min(1).optional(),issuer:c.url().optional(),domain:c.string().optional(),clientId:c.string().optional(),clientSecret:c.string().optional(),scopes:c.string().optional(),isActive:c.boolean().optional()}),Co=c.object({forceSso:c.boolean().optional(),autoJoinSsoDomains:c.boolean().optional(),breakGlassEnabled:c.boolean().optional(),defaultRoleId:c.number().int().positive().optional(),roleMapping:c.record(c.string(),c.number()).optional()}),xo=c.object({email:c.email("Valid email is required")}),yo=c.object({orgId:c.uuid()});import{z as E}from"zod";var To=E.object({token:E.string().min(1),redirect:E.string().optional()}),ho=E.object({sub:E.uuid(),email:E.email(),type:E.literal("login-link")});import{z as C}from"zod";var Ao=C.object({token:C.string().min(1),redirect:C.string().optional()}),_o=C.object({sub:C.string(),email:C.email(),name:C.string().optional(),avatar:C.url().optional(),iat:C.number(),exp:C.number()});import{z as x}from"zod";var N=x.enum(["mcp","api","zapier"]),_e=x.enum(["course_import:draft:create","course_import:draft:read","course_import:draft:update","course_import:draft:publish","course:read","course:write","course:tag:write","course:exercise:read","course:exercise:write","public_api:*"]),Lo=x.object({type:N.optional()}),Ro=x.object({type:N.default("mcp")}),Do=x.object({type:N,label:x.string().min(1).max(120).optional(),expiresAt:x.string().datetime().optional(),scopes:x.array(_e).min(1).optional()}),No=x.object({keyId:x.string().uuid()});import*as s from"zod";var m={RADIO:"RADIO",CHECKBOX:"CHECKBOX",TEXTAREA:"TEXTAREA",TRUE_FALSE:"TRUE_FALSE",SHORT_ANSWER:"SHORT_ANSWER",NUMERIC:"NUMERIC",FILL_BLANK:"FILL_BLANK",FILE_UPLOAD:"FILE_UPLOAD",MATCHING:"MATCHING",ORDERING:"ORDERING",HOTSPOT:"HOTSPOT",LINK:"LINK"},qo=[m.RADIO,m.CHECKBOX,m.TEXTAREA];var P=[{key:m.RADIO,typename:"RADIO",label:"Single answer",id:1,autoGradable:!0,supportsPartialCredit:!1,manualGradingRequired:!1},{key:m.CHECKBOX,typename:"CHECKBOX",label:"Multiple answers",id:2,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:m.TEXTAREA,typename:"TEXTAREA",label:"Paragraph",id:3,autoGradable:!1,supportsPartialCredit:!1,manualGradingRequired:!0},{key:m.TRUE_FALSE,typename:"TRUE_FALSE",label:"True/False",id:4,autoGradable:!0,supportsPartialCredit:!1,manualGradingRequired:!1},{key:m.SHORT_ANSWER,typename:"SHORT_ANSWER",label:"Short answer",id:5,autoGradable:!1,supportsPartialCredit:!1,manualGradingRequired:!0},{key:m.NUMERIC,typename:"NUMERIC",label:"Numeric answer",id:6,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:m.FILL_BLANK,typename:"FILL_BLANK",label:"Fill in the blank",id:7,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:m.FILE_UPLOAD,typename:"FILE_UPLOAD",label:"File upload",id:8,autoGradable:!1,supportsPartialCredit:!1,manualGradingRequired:!0},{key:m.MATCHING,typename:"MATCHING",label:"Matching",id:9,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:m.ORDERING,typename:"ORDERING",label:"Ordering",id:10,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:m.HOTSPOT,typename:"HOTSPOT",label:"Hotspot",id:11,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:m.LINK,typename:"LINK",label:"Links",id:12,autoGradable:!1,supportsPartialCredit:!1,manualGradingRequired:!0}],Pe=Object.freeze(P.reduce((o,t)=>(o[t.key]=t,o),{})),Z=Object.freeze(P.reduce((o,t)=>(o[t.key]=t.id,o),{})),we=Object.freeze(P.reduce((o,t)=>(o[t.typename]=t.key,o),{})),Le=Object.freeze(P.reduce((o,t)=>(o[t.id]=t.key,o),{}));var Re=new Set(["true","1","yes"]),De=new Set(["false","0","no"]);function Ne(o){let t=o.trim().toLowerCase();if(Re.has(t))return!0;if(De.has(t))return!1}var Ze={type:"RADIO",toApiPayload(o,t){return{questionId:t,optionId:o.optionId}},fromApiPayload(o){let t=o.optionId;return t===void 0||Number.isNaN(t)?null:{type:"RADIO",optionId:t}}},Ue={type:"CHECKBOX",toApiPayload(o,t){return{questionId:t,answer:JSON.stringify({type:"CHECKBOX",optionIds:o.optionIds})}},fromApiPayload(o){if(!o.answer)return null;let{optionIds:t}=JSON.parse(o.answer);return Array.isArray(t)?{type:"CHECKBOX",optionIds:t}:null}},je={type:"TRUE_FALSE",toApiPayload(o,t){return{questionId:t,answer:String(o.value)}},fromApiPayload(o){if(o.answer===void 0)return null;let t=Ne(o.answer);return t===void 0?null:{type:"TRUE_FALSE",value:t}}},ve={type:"TEXTAREA",toApiPayload(o,t){return{questionId:t,answer:o.text}},fromApiPayload(o){return o.answer===void 0?null:{type:"TEXTAREA",text:o.answer}}},Ge={type:"SHORT_ANSWER",toApiPayload(o,t){return{questionId:t,answer:o.text}},fromApiPayload(o){return o.answer===void 0?null:{type:"SHORT_ANSWER",text:o.answer}}},ke={type:"NUMERIC",toApiPayload(o,t){return{questionId:t,answer:JSON.stringify({type:"NUMERIC",value:o.value})}},fromApiPayload(o){if(!o.answer)return null;let{value:t}=JSON.parse(o.answer);return{type:"NUMERIC",value:t}}},Fe={type:"FILL_BLANK",toApiPayload(o,t){return{questionId:t,answer:JSON.stringify({type:"FILL_BLANK",answers:o.values})}},fromApiPayload(o){if(!o.answer)return null;let{answers:t}=JSON.parse(o.answer),n=t.map(i=>String(i).trim()).filter(Boolean);return n.length>0?{type:"FILL_BLANK",values:n}:null}},Me={type:"FILE_UPLOAD",toApiPayload(o,t){let n={fileKey:o.fileKey,fileName:o.fileName,mimeType:o.mimeType,size:o.size};return{questionId:t,answer:JSON.stringify(n)}},fromApiPayload(o){if(!o.answer)return null;let t=JSON.parse(o.answer);return{type:"FILE_UPLOAD",fileKey:t.fileKey,fileName:t.fileName??"",mimeType:t.mimeType,size:t.size}}},Ke={type:"MATCHING",toApiPayload(o,t){return{questionId:t,answer:JSON.stringify({type:"MATCHING",value:o.pairs})}},fromApiPayload(o){if(!o.answer)return null;let{value:t}=JSON.parse(o.answer);return t.length>0?{type:"MATCHING",pairs:t}:null}},He={type:"ORDERING",toApiPayload(o,t){return{questionId:t,answer:JSON.stringify({type:"ORDERING",value:o.orderedValues})}},fromApiPayload(o){if(!o.answer)return null;let{value:t}=JSON.parse(o.answer);return t.length>0?{type:"ORDERING",orderedValues:t}:null}},qe={type:"LINK",toApiPayload(o,t){return{questionId:t,answer:JSON.stringify({type:"LINK",links:o.urls})}},fromApiPayload(o){if(!o.answer)return null;let{links:t}=JSON.parse(o.answer),n=t.map(i=>String(i).trim()).filter(Boolean);return n.length>0?{type:"LINK",urls:n}:null}},Be={type:"HOTSPOT",toApiPayload(o,t){return{questionId:t,answer:JSON.stringify({type:"HOTSPOT",value:o.coordinates})}},fromApiPayload(o){if(!o.answer)return null;let{value:t}=JSON.parse(o.answer);return t.length>0?{type:"HOTSPOT",coordinates:t}:null}},nr={[m.RADIO]:Ze,[m.CHECKBOX]:Ue,[m.TRUE_FALSE]:je,[m.TEXTAREA]:ve,[m.SHORT_ANSWER]:Ge,[m.NUMERIC]:ke,[m.FILL_BLANK]:Fe,[m.FILE_UPLOAD]:Me,[m.MATCHING]:Ke,[m.ORDERING]:He,[m.LINK]:qe,[m.HOTSPOT]:Be};var B=["video/mp4","video/quicktime","video/x-msvideo","video/x-matroska"],Q=["application/pdf","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/msword"];var h=Z;var Qe={[h.RADIO]:[o=>{let t=o.options||[];return t.length>0&&t.length<2?"Options must have at least 2 items for RADIO/CHECKBOX questions":null},o=>{let t=o.options||[];return t.length>0&&!t.some(n=>n.isCorrect===!0)?"Please mark an option as the correct answer":null}],[h.CHECKBOX]:[o=>{let t=o.options||[];return t.length>0&&t.length<2?"Options must have at least 2 items for RADIO/CHECKBOX questions":null},o=>{let t=o.options||[];return t.length>0&&!t.some(n=>n.isCorrect===!0)?"Please mark an option as the correct answer":null}],[h.TRUE_FALSE]:[o=>{let t=o.options||[];return t.length>0&&t.length<2?"True/False questions need both True and False options":null},o=>{let t=o.options||[];return t.length>0&&!t.some(n=>n.isCorrect===!0)?"Please mark an option as the correct answer":null}],[h.TEXTAREA]:[]},Ye=s.object({id:s.number().int().optional(),question:s.string().min(1),questionTypeId:s.number().int().min(1).optional(),points:s.number().int().min(0).optional(),order:s.number().int().min(0).optional(),settings:s.record(s.string(),s.unknown()).optional(),deletedAt:s.string().optional(),options:s.array(s.object({id:s.number().int().optional(),label:s.string().min(1),isCorrect:s.boolean(),settings:s.record(s.string(),s.unknown()).optional(),deletedAt:s.string().optional()})).optional()});function Xe(o,t){if(o.deletedAt)return;let n=Qe[o.questionTypeId??-1]??[];for(let i of n){let u=i(o);u&&t.addIssue({code:"custom",message:u,path:["options"]})}}var $e=Ye.superRefine(Xe),Y=s.object({title:s.string().min(1),description:s.string().optional(),lessonId:s.string().optional(),sectionId:s.string().optional(),order:s.number().int().optional(),courseId:s.string().min(1),dueBy:s.string().optional(),questions:s.array(s.object({question:s.string().min(1),questionTypeId:s.number().int().min(1).optional(),points:s.number().int().min(0).optional(),settings:s.record(s.string(),s.unknown()).optional(),options:s.array(s.object({label:s.string().min(1),isCorrect:s.boolean(),settings:s.record(s.string(),s.unknown()).optional()})).min(2)})).optional()}),X=s.object({title:s.string().min(1).optional(),description:s.string().optional(),lessonId:s.string().optional(),sectionId:s.string().optional(),order:s.number().int().optional(),isUnlocked:s.boolean().optional(),dueBy:s.string().optional(),questions:s.array($e).optional()}),U=s.object({exerciseId:s.string().min(1)}),$=s.object({lessonId:s.string().optional(),sectionId:s.string().optional()}),hr=s.object({exerciseId:s.string().min(1),answers:s.array(s.object({questionId:s.number().int(),optionId:s.number().int().optional(),answer:s.string().optional()})).min(1)}),V=s.object({lessonId:s.string().optional(),sectionId:s.string().optional(),order:s.number().int().optional(),templateId:s.number().int().min(1)});import*as r from"zod";import*as e from"zod";var Pr=e.object({title:e.string().min(1),description:e.string().optional(),slug:e.string().min(1),organizationId:e.string().min(1)}),wr=e.object({courseId:e.string().min(1)}),Ve=e.object({courseId:e.string().min(1)}),Lr=e.object({slug:e.string().optional()}),Rr=e.object({slug:e.string().min(1)}),Dr=e.object({courseId:e.string().min(1)}),Nr=e.object({inviteToken:e.string().min(1).optional()}),Zr=e.object({courseId:e.string().min(1)}),Ur=e.object({theme:e.string().min(1),studentName:e.string().min(1),courseName:e.string().min(1),courseDescription:e.string().min(1),orgName:e.string().min(1),orgLogoUrl:e.string().url().optional(),facilitator:e.string().optional()}),jr=e.object({courseTitle:e.string().min(1),orgName:e.string().min(1),orgTheme:e.string().min(1),lessons:e.array(e.object({lessonTitle:e.string().min(1),lessonNumber:e.string().min(1),lessonNote:e.string(),slideUrl:e.string().url().optional(),video:e.array(e.string().url()).optional()}))}),vr=e.object({title:e.string().min(1),number:e.string().min(1),orgName:e.string().min(1),note:e.string(),slideUrl:e.string().url().optional(),video:e.array(e.string().url()).optional(),courseTitle:e.string().min(1)}),Gr=e.object({fileName:e.string().min(1),fileType:e.enum(B)}),kr=e.object({fileName:e.string().min(1),fileType:e.enum(Q)}),Fr=e.object({keys:e.array(e.string().min(1)).min(1)}),Je=e.object({id:e.string().min(1),type:e.enum(["LESSON","EXERCISE"]),isUnlocked:e.boolean().optional(),order:e.number().int().min(0).optional(),sectionId:e.string().nullable().optional()}),Mr=e.object({items:e.array(Je).min(1)}),We=e.object({id:e.string().min(1),type:e.enum(["LESSON","EXERCISE"])}),Kr=e.object({sectionId:e.string().min(1).optional(),items:e.array(We).min(1).optional()}).refine(o=>!!o.sectionId!=!!o.items,{message:"Provide either sectionId or items",path:["sectionId"]}),Hr=e.object({title:e.string().min(1),description:e.string().min(1),type:e.enum(["LIVE_CLASS","SELF_PACED"]),organizationId:e.string().min(1)}),J=e.object({show:e.boolean(),description:e.string()}),W=e.object({name:e.string(),role:e.string(),coursesNo:e.number(),description:e.string(),imgUrl:e.string()}),ee=e.object({templateUrl:e.string()}),te=e.object({id:e.number(),hide:e.boolean(),name:e.string(),avatar_url:e.string(),rating:e.number(),created_at:e.number(),description:e.string()}),oe=e.array(e.object({id:e.union([e.literal(1),e.literal(2),e.literal(3),e.literal(4)]),name:e.string()})),w=e.object({requirements:e.string().optional(),description:e.string().optional(),goals:e.string().optional(),videoUrl:e.string().optional(),showDiscount:e.boolean().optional(),discount:e.number().optional(),paymentLink:e.string().optional(),reward:J.optional(),instructor:W.optional(),certificate:ee.optional(),reviews:e.array(te).optional(),lessonTabsOrder:oe.optional(),grading:e.boolean().optional(),lessonDownload:e.boolean().optional(),allowNewStudent:e.boolean(),sectionDisplay:e.record(e.string(),e.boolean()).optional(),isContentGroupingEnabled:e.boolean().optional()}),et=e.object({requirements:e.string().optional(),description:e.string().optional(),goals:e.string().optional(),videoUrl:e.string().optional(),showDiscount:e.boolean().optional(),discount:e.number().optional(),paymentLink:e.string().optional(),reward:J.partial().optional(),instructor:W.partial().optional(),certificate:ee.partial().optional(),reviews:e.array(te).optional(),lessonTabsOrder:oe.optional(),grading:e.boolean().optional(),lessonDownload:e.boolean().optional(),allowNewStudent:e.boolean().optional(),sectionDisplay:e.record(e.string(),e.boolean()).optional(),isContentGroupingEnabled:e.boolean().optional()}),qr=e.object({title:e.string().min(1).optional(),description:e.string().min(1).optional(),type:e.enum(["LIVE_CLASS","SELF_PACED"]).optional(),logo:e.string().optional(),slug:e.string().optional(),isPublished:e.boolean().optional(),overview:e.string().optional(),metadata:w.optional(),cost:e.number().int().min(0).optional(),currency:e.enum(["NGN","USD"]).optional(),isCertificateDownloadable:e.boolean().optional(),certificateTheme:e.string().optional(),tagIds:e.array(e.uuid()).max(100).optional()}),Br=e.object({courseId:e.string().min(1)}),re=e.object({title:e.string().min(1).optional(),description:e.string().min(1).optional(),overview:e.string().optional(),cost:e.number().int().min(0).optional(),currency:e.enum(["NGN","USD"]).optional(),imageUrl:e.url().optional(),generateImage:e.boolean().optional(),imageQuery:e.string().min(1).max(120).optional(),metadata:et.optional()}),Qr=e.object({courseId:e.string().min(1)}),Yr=e.object({courseId:e.string().min(1)}),Xr=e.object({profileId:e.string().uuid()}),$r=e.object({courseId:e.string().min(1),userId:e.string().uuid()});var ne=r.enum(["en","hi","fr","pt","de","vi","ru","es","pl","da"]),tt="Lesson body HTML only. Do not include the lesson title or a top-level heading that repeats lessons[].title because the ClassroomIO UI already renders the lesson title separately.",se=r.object({code:r.string().min(1),message:r.string().min(1),severity:r.enum(["info","warning","error"])}),ot=r.object({type:r.enum(["prompt","pdf","course"]),label:r.string().min(1),pageStart:r.number().int().min(1).optional(),pageEnd:r.number().int().min(1).optional()}),rt=r.object({title:r.string().min(1),description:r.string().min(1),type:r.enum(["LIVE_CLASS","SELF_PACED"]),locale:ne.default("en"),metadata:w.optional()}),nt=r.object({externalId:r.string().min(1),title:r.string().min(1),order:r.number().int().min(0)}),st=r.object({externalId:r.string().min(1),sectionExternalId:r.string().min(1),title:r.string().min(1),order:r.number().int().min(0),isUnlocked:r.boolean().optional(),public:r.boolean().optional()}),at=r.object({lessonExternalId:r.string().min(1),locale:ne,content:r.string().min(1).describe(tt)}),ae=r.object({course:rt,tags:r.array(r.string().trim().min(1).max(80)).max(100).default([]),sections:r.array(nt).min(1),lessons:r.array(st).min(1),lessonLanguages:r.array(at).min(1),exercises:r.array(r.record(r.string(),r.unknown())).optional(),sourceReferences:r.array(ot).optional(),warnings:r.array(se).default([])}).superRefine((o,t)=>{let n=new Set;o.tags.forEach((l,I)=>{let k=l.trim().toLowerCase();n.has(k)&&t.addIssue({code:r.ZodIssueCode.custom,path:["tags",I],message:"Draft tags must be unique"}),n.add(k)});let i=new Set;o.sections.forEach((l,I)=>{i.has(l.externalId)&&t.addIssue({code:r.ZodIssueCode.custom,path:["sections",I,"externalId"],message:"Section externalId must be unique"}),i.add(l.externalId)});let u=new Set;o.lessons.forEach((l,I)=>{u.has(l.externalId)&&t.addIssue({code:r.ZodIssueCode.custom,path:["lessons",I,"externalId"],message:"Lesson externalId must be unique"}),u.add(l.externalId),i.has(l.sectionExternalId)||t.addIssue({code:r.ZodIssueCode.custom,path:["lessons",I,"sectionExternalId"],message:"Lesson sectionExternalId must reference an existing section"})}),o.lessonLanguages.forEach((l,I)=>{u.has(l.lessonExternalId)||t.addIssue({code:r.ZodIssueCode.custom,path:["lessonLanguages",I,"lessonExternalId"],message:"Lesson language must reference an existing lesson"})})}),j=r.object({sourceType:r.enum(["prompt","pdf","course"]),idempotencyKey:r.string().min(1).optional(),summary:r.record(r.string(),r.unknown()).optional(),sourceArtifacts:r.array(r.record(r.string(),r.unknown())).optional(),draft:ae}),y=r.object({courseId:r.string().min(1)}),v=r.object({courseId:r.string().min(1),idempotencyKey:r.string().min(1).optional(),summary:r.record(r.string(),r.unknown()).optional(),sourceArtifacts:r.array(r.record(r.string(),r.unknown())).optional()}),T=r.object({draftId:r.string().uuid()}),ie=r.object({summary:r.record(r.string(),r.unknown()).optional(),sourceArtifacts:r.array(r.record(r.string(),r.unknown())).optional(),warnings:r.array(se).optional(),draft:ae.optional()}),G=r.object({title:r.string().min(1).optional(),description:r.string().min(1).optional(),type:r.enum(["LIVE_CLASS","SELF_PACED"]).optional(),metadata:w.optional(),bannerImageUrl:r.string().url().optional(),bannerImageQuery:r.string().min(1).max(120).optional(),generateBannerImage:r.boolean().optional()}),ue=G.extend({courseId:r.string().min(1)});import*as z from"zod";var on=z.object({title:z.string().min(1),courseId:z.string().min(1),order:z.number().int().min(0).optional()}),rn=z.object({title:z.string().min(1).optional(),order:z.number().int().min(0).optional()}),nn=z.object({title:z.string().min(1)}),sn=z.object({sectionId:z.string().min(1)}),an=z.object({sections:z.array(z.object({id:z.string().min(1),order:z.number().int().min(0)})).min(1)});import*as d from"zod";var pn=d.object({courseId:d.string().min(1)}),cn=d.object({courseId:d.string().min(1),inviteId:d.string().uuid()}),ln=d.object({courseId:d.string().min(1),inviteId:d.string().uuid()}),dn=d.object({token:d.string().min(1)}),mn=d.object({courseId:d.uuid()}),it=d.enum(["ONE_TIME_24H","MULTI_USE_7D","MULTI_USE_30D","CUSTOM"]),gn=d.object({preset:it.default("MULTI_USE_30D"),expiresAt:d.string().min(1).optional(),maxUses:d.number().int().min(1).max(1e3).optional(),allowedEmails:d.array(d.string().email()).max(100).optional(),allowedDomains:d.array(d.string().min(1)).max(100).optional(),recipientEmails:d.array(d.string().email()).max(500).optional(),recipientCsv:d.string().max(25e3).optional(),sendEmail:d.boolean().default(!1),metadata:d.record(d.string(),d.unknown()).optional()}).refine(o=>{let t=(o.recipientEmails?.length??0)>0,n=typeof o.recipientCsv=="string"&&o.recipientCsv.trim().length>0;return t||n},{message:"recipientEmails or recipientCsv is required",path:["recipientEmails"]});var le=ie.extend({draftId:T.shape.draftId}),de=L.extend({draftId:K.shape.draftId}),me=$.extend({courseId:y.shape.courseId}),ge=U.extend({courseId:y.shape.courseId}),ze=Y.omit({courseId:!0}).extend({courseId:y.shape.courseId}),fe=V.extend({courseId:y.shape.courseId}),Ie=X.extend({courseId:y.shape.courseId,exerciseId:U.shape.exerciseId}),Ce=re.extend({courseId:y.shape.courseId}),xe=G.extend({draftId:T.shape.draftId}),ye=ue.extend({draftId:T.shape.draftId}),ut=j.shape,pt=v.shape,ct=D.shape,lt=y.shape,dt=T.shape,mt=le.shape,gt=de.shape,zt=me.shape,ft=ge.shape,It=ze.shape,Ct=fe.shape,xt=Ie.shape,yt=Ce.shape,Et=xe.shape,bt=ye.shape,Tt=R.shape,pe="Supported questionTypeId values: RADIO=1 (Single answer), CHECKBOX=2 (Multiple answers), TEXTAREA=3 (Paragraph), TRUE_FALSE=4 (True/False), SHORT_ANSWER=5 (Short answer), NUMERIC=6 (Numeric answer), FILL_BLANK=7 (Fill in the blank), FILE_UPLOAD=8 (File upload), MATCHING=9 (Matching), ORDERING=10 (Ordering), HOTSPOT=11 (Hotspot), LINK=12 (Links). Use the numeric ids in exercise payloads.",ce="For draft lesson HTML, put only the lesson body in lessonLanguages[].content. Do not include the lesson title or a top-level heading that repeats lessons[].title because the ClassroomIO UI already renders the lesson title.";function Ee(o,t){o.tool("list_org_courses","List all courses in the authenticated organization. Use this when the user refers to a course by name and you need to discover the available course IDs first.",ct,async n=>{let i=D.parse(n),u=await t.listOrganizationCourses(i);return f(u)}),o.tool("get_course_structure","Read the current live course structure. Use this before creating a fresh draft for post-publish edits.",lt,async n=>{let{courseId:i}=y.parse(n),u=await t.getCourseStructure(i);return f(u)}),o.tool("update_course_landing_page","Update landing-page-facing course fields on a live course, including headline copy, overview, requirements, goals, pricing, reviews, instructor information, and the course image. Use imageUrl to set an explicit cover image, or set generateImage/imageQuery to fetch a random Unsplash-based image.",yt,async n=>{let{courseId:i,...u}=Ce.parse(n),l=await t.updateCourseLandingPage(i,u);return f(l)}),o.tool("create_course_draft",`Create a new unpublished course draft from structured course JSON. Use this before the first publish. ${ce}`,ut,async n=>{let i=j.parse(n),u=await t.createCourseDraft(i);return f(u)}),o.tool("create_course_draft_from_course","Create a fresh draft from an existing live course. Use this after a course has already been published and the user wants more changes.",pt,async n=>{let i=v.parse(n),u=await t.createCourseDraftFromCourse(i);return f(u)}),o.tool("get_course_draft","Fetch an existing draft. Draft responses include status and publishedCourseId so you can decide whether to keep editing the draft or seed a fresh one from the live course.",dt,async n=>{let{draftId:i}=T.parse(n),u=await t.getCourseDraft(i);return f(u)}),o.tool("list_course_exercises","List exercises for a live course, optionally filtered by lessonId or sectionId.",zt,async n=>{let{courseId:i,...u}=me.parse(n),l=await t.listCourseExercises(i,u);return f(l)}),o.tool("get_course_exercise","Fetch a single exercise from a live course, including its questions and options.",ft,async n=>{let{courseId:i,exerciseId:u}=ge.parse(n),l=await t.getCourseExercise(i,u);return f(l)}),o.tool("create_course_exercise",`Create a new exercise directly on a live course. Use this for adding exercises after a course has already been published. ${pe}`,It,async n=>{let{courseId:i,...u}=ze.parse(n),l=await t.createCourseExercise(i,u);return f(l)}),o.tool("create_course_exercise_from_template","Create a new exercise on a live course from an existing template.",Ct,async n=>{let{courseId:i,...u}=fe.parse(n),l=await t.createCourseExerciseFromTemplate(i,u);return f(l)}),o.tool("update_course_exercise",`Update an existing exercise on a live course, including questions and options. ${pe}`,xt,async n=>{let{courseId:i,exerciseId:u,...l}=Ie.parse(n),I=await t.updateCourseExercise(i,u,l);return f(I)}),o.tool("update_course_draft",`Update an unpublished draft. If the draft is already published, do not keep editing it blindly; create a fresh draft from the live course first. ${ce}`,mt,async n=>{let{draftId:i,...u}=le.parse(n),l=await t.updateCourseDraft(i,u);return f(l)}),o.tool("tag_course_draft","Attach human-readable tags to a draft before publish. The tags are stored on the draft and applied during publish.",gt,async n=>{let{draftId:i,...u}=de.parse(n),l=await t.tagCourseDraft(i,u);return f(l)}),o.tool("publish_course_draft","Publish an unpublished draft as a new live course. The result includes courseId and courseUrl. After publish, future major edits should usually start from a fresh draft created from the live course.",Et,async n=>{let{draftId:i,...u}=xe.parse(n),l=await t.publishCourseDraft(i,u);return f(l)}),o.tool("publish_course_draft_to_existing_course","Apply a fresh draft back onto an existing live course. Use this after reading the live course and creating a new draft from it.",bt,async n=>{let{draftId:i,...u}=ye.parse(n),l=await t.publishCourseDraftToExistingCourse(i,u);return f(l)}),o.tool("tag_courses","Assign tags directly to one or more live courses by name. Default mode is merge, so existing tags are preserved unless replace is requested.",Tt,async n=>{let i=R.parse(n),u=await t.tagCourses(i);return f(u)})}function f(o){return{content:[{type:"text",text:JSON.stringify(o)}]}}async function Ot(){let o=F(),t=new S(o),n=new ht({name:"classroomio-course-authoring",version:"0.0.1"});Ee(n,t);let i=new St;await n.connect(i),process.stdin.on("close",()=>{n.close()})}Ot().catch(o=>{o instanceof b&&(console.error(JSON.stringify({error:o.message,status:o.status,code:o.code,field:o.field})),process.exit(1)),console.error(o),process.exit(1)});
2
+ import{McpServer as Gt}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as kt}from"@modelcontextprotocol/sdk/server/stdio.js";var S=class extends Error{constructor(n,a,u,d){super(n);this.status=a;this.code=u;this.field=d;this.name="ClassroomIoApiError"}},A=class{constructor(t){this.config=t}async createCourseDraft(t){return this.request("/organization/course-import/drafts",{method:"POST",body:t})}async createCourseDraftFromCourse(t){return this.request("/organization/course-import/drafts/from-course",{method:"POST",body:t})}async getCourseStructure(t){return this.request(`/organization/course-import/courses/${t}/structure`,{method:"GET"})}async updateCourseLandingPage(t,n){return this.request(`/course/${t}/landing-page`,{method:"PUT",body:n})}async reorderCourseContent(t,n){return this.request(`/course/${t}/content/reorder`,{method:"PUT",body:n})}async getCourseDraft(t){return this.request(`/organization/course-import/drafts/${t}`,{method:"GET"})}async listOrganizationCourses(t={}){let n=new URLSearchParams;t.tags&&n.set("tags",t.tags);let a=n.toString()?`?${n.toString()}`:"";return this.request(`/organization/courses${a}`,{method:"GET"})}async listCourseExercises(t,n={}){let a=new URLSearchParams;n.lessonId&&a.set("lessonId",n.lessonId),n.sectionId&&a.set("sectionId",n.sectionId);let u=a.toString()?`?${a.toString()}`:"";return this.request(`/course/${t}/exercise${u}`,{method:"GET"})}async getCourseExercise(t,n){return this.request(`/course/${t}/exercise/${n}`,{method:"GET"})}async createCourseExercise(t,n){return this.request(`/course/${t}/exercise`,{method:"POST",body:{...n,courseId:t}})}async createCourseExerciseFromTemplate(t,n){return this.request(`/course/${t}/exercise/from-template`,{method:"POST",body:n})}async updateCourseExercise(t,n,a){return this.request(`/course/${t}/exercise/${n}`,{method:"PUT",body:a})}async updateCourseDraft(t,n){return this.request(`/organization/course-import/drafts/${t}`,{method:"PUT",body:n})}async tagCourseDraft(t,n){return this.request(`/organization/course-import/drafts/${t}/tags`,{method:"PUT",body:n})}async publishCourseDraft(t,n){return this.request(`/organization/course-import/drafts/${t}/publish`,{method:"POST",body:n})}async publishCourseDraftToExistingCourse(t,n){return this.request(`/organization/course-import/drafts/${t}/publish-existing-course`,{method:"POST",body:n})}async tagCourses(t){return this.request("/organization/tags/courses/assign",{method:"PUT",body:t})}async request(t,n){let a=await fetch(new URL(t,this.config.CLASSROOMIO_API_URL),{method:n.method,headers:{Authorization:`Bearer ${this.config.CLASSROOMIO_API_KEY}`,"content-type":"application/json","user-agent":this.config.CLASSROOMIO_USER_AGENT},body:n.body?JSON.stringify(n.body):void 0}),u=await a.json().catch(()=>null);if(!a.ok){let d=u;throw new S(d?.error??d?.message??`ClassroomIO request failed with status ${a.status}`,a.status,d?.code,d?.field)}if(!u||typeof u!="object"||!("success"in u)||!u.success)throw new S("ClassroomIO returned an invalid response payload",a.status);return u.data}};import{z as _}from"zod";var we=_.object({CLASSROOMIO_API_URL:_.url().default("https://api.classroomio.com"),CLASSROOMIO_API_KEY:_.string().min(1),CLASSROOMIO_USER_AGENT:_.string().min(1).default("classroomio-mcp/0.0.9")});function q(o=process.env){return we.parse(o)}import*as c from"zod";var Re=["#EF4444","#F97316","#F59E0B","#84CC16","#22C55E","#14B8A6","#06B6D4","#3B82F6","#8B5CF6","#EC4899"],B=c.enum(Re),qt=c.object({groupId:c.uuid()}),Le=c.object({name:c.string().min(1).max(80),description:c.string().max(240).optional(),order:c.number().int().min(0).optional()}),Bt=Le.partial().refine(o=>Object.keys(o).length>0,{message:"At least one field is required"}),Qt=c.object({tagId:c.uuid()}),Yt=c.object({name:c.string().min(1).max(80),description:c.string().max(240).optional(),groupId:c.uuid(),color:B}),Xt=c.object({name:c.string().min(1).max(80).optional(),description:c.string().max(240).optional(),groupId:c.uuid().optional(),color:B.optional()}).refine(o=>Object.keys(o).length>0,{message:"At least one field is required"}),$t=c.object({courseId:c.uuid()}),Vt=c.object({tagIds:c.array(c.uuid()).max(100).default([])}),Ze=c.enum(["merge","replace"]).default("merge"),De=c.string().trim().min(1).max(80),Q=c.object({draftId:c.string().uuid()}),D=c.object({tagNames:c.array(De).min(1).max(100),mode:Ze,groupName:c.string().trim().min(1).max(80).optional()}),N=D.extend({courseIds:c.array(c.string().uuid()).min(1).max(100)});import*as x from"zod";var to=x.object({recipientCsv:x.string().max(25e3),courseIds:x.array(x.string().uuid()).optional(),allCourses:x.boolean().optional().default(!1),sendEmail:x.boolean().default(!0)}),oo=x.object({profileIds:x.array(x.string().uuid()).min(1).max(500),courseIds:x.array(x.string().uuid()).min(1),sendEmail:x.boolean().default(!0)});import*as i from"zod";var Y=["academy","app","apps","blog","build","campaign","church","cloud","community","conference","course","courses","demo","dev","forum","group","groups","help","launch","launchweek","mobile","play","prod","production","schools","stage","staging","support","teacher","teachers","tech","training"];var X;(function(o){o.Section="SECTION",o.Lesson="LESSON",o.Exercise="EXERCISE"})(X||(X={}));var mo=i.object({siteName:i.string().min(1).optional(),customDomain:i.string().min(1).optional(),isCustomDomainVerified:i.boolean().optional()}),go=i.object({siteName:i.string().min(1),tags:i.string().optional()}),U=i.object({tags:i.string().optional()}),zo=i.object({siteName:i.string().min(1)}),fo=i.object({orgId:i.uuid(),planName:i.enum(["EARLY_ADOPTER","ENTERPRISE","BASIC"]),subscriptionId:i.string().min(1),triggeredBy:i.number().int().positive(),payload:i.record(i.string(),i.unknown())}),Io=i.object({subscriptionId:i.string().min(1),payload:i.record(i.string(),i.unknown())}),Co=i.object({subscriptionId:i.string().min(1),payload:i.record(i.string(),i.unknown())}),xo=i.object({name:i.string().min(5).refine(o=>!/^[-]|[-]$/.test(o),{message:"Organization name cannot start or end with a hyphen"}),siteName:i.string().min(5).refine(o=>!/^[-]|[-]$/.test(o),{message:"Site name cannot start or end with a hyphen"}).refine(o=>!Y.includes(o),{message:"Sitename already exists."})}),Eo=i.object({name:i.string().min(5).refine(o=>!/^[-]|[-]$/.test(o),{message:"Organization name cannot start or end with a hyphen"}).optional(),avatarUrl:i.url().optional(),theme:i.string().optional(),landingpage:i.record(i.string(),i.unknown()).optional(),siteName:i.string().min(1).optional(),customDomain:i.string().nullable().optional(),isCustomDomainVerified:i.boolean().optional(),customization:i.record(i.string(),i.unknown()).optional(),disableSignup:i.boolean().optional(),disableSignupMessage:i.string().optional(),disableEmailPassword:i.boolean().optional(),disableGoogleAuth:i.boolean().optional(),settings:i.object({signup:i.object({inviteOnly:i.boolean().optional()}).optional()}).optional()}),yo=i.object({emails:i.array(i.string().email()).min(1).max(50),roleId:i.number().int().positive()}),bo=i.object({memberId:i.coerce.number().int().positive()}),To=i.object({userId:i.uuid()});import*as z from"zod";var So=z.object({orgId:z.uuid(),quizId:z.uuid()}),Oo=z.object({orgId:z.uuid()}),Ao=z.object({title:z.string().min(1),questions:z.array(z.any()).optional(),timelimit:z.string().optional(),theme:z.string().optional()}),_o=z.object({title:z.string().min(1).optional(),questions:z.array(z.any()).optional(),timelimit:z.string().optional(),theme:z.string().optional()});import*as P from"zod";var wo=P.object({orgId:P.uuid()});import*as w from"zod";var Lo=w.object({token:w.string().min(10).max(512)});import*as p from"zod";var Ne=["OKTA","GOOGLE_WORKSPACE","AUTH0"],Do=p.object({provider:p.enum(Ne),displayName:p.string().min(1,"Display name is required"),issuer:p.url("Must be a valid URL"),domain:p.string().min(1,"Domain is required").regex(/^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}$/,"Invalid domain format"),clientId:p.string().min(1,"Client ID is required"),clientSecret:p.string().min(1,"Client secret is required"),scopes:p.string().default("openid profile email")}),No=p.object({displayName:p.string().min(1).optional(),issuer:p.url().optional(),domain:p.string().optional(),clientId:p.string().optional(),clientSecret:p.string().optional(),scopes:p.string().optional(),isActive:p.boolean().optional()}),Uo=p.object({forceSso:p.boolean().optional(),autoJoinSsoDomains:p.boolean().optional(),breakGlassEnabled:p.boolean().optional(),defaultRoleId:p.number().int().positive().optional(),roleMapping:p.record(p.string(),p.number()).optional()}),jo=p.object({email:p.email("Valid email is required")}),vo=p.object({orgId:p.uuid()});import{z as h}from"zod";var Fo=h.object({token:h.string().min(1),redirect:h.string().optional()}),Mo=h.object({sub:h.uuid(),email:h.email(),type:h.literal("login-link")});import{z as y}from"zod";var qo=y.object({token:y.string().min(1),redirect:y.string().optional()}),Bo=y.object({sub:y.string(),email:y.email(),name:y.string().optional(),avatar:y.url().optional(),iat:y.number(),exp:y.number()});import{z as b}from"zod";var j=b.enum(["mcp","api","zapier"]),Ue=b.enum(["course_import:draft:create","course_import:draft:read","course_import:draft:update","course_import:draft:publish","course:read","course:write","course:tag:write","course:exercise:read","course:exercise:write","public_api:*"]),Xo=b.object({type:j.optional()}),$o=b.object({type:j.default("mcp")}),Vo=b.object({type:j,label:b.string().min(1).max(120).optional(),expiresAt:b.string().datetime().optional(),scopes:b.array(Ue).min(1).optional()}),Jo=b.object({keyId:b.string().uuid()});import*as s from"zod";var m={RADIO:"RADIO",CHECKBOX:"CHECKBOX",TEXTAREA:"TEXTAREA",TRUE_FALSE:"TRUE_FALSE",SHORT_ANSWER:"SHORT_ANSWER",NUMERIC:"NUMERIC",FILL_BLANK:"FILL_BLANK",FILE_UPLOAD:"FILE_UPLOAD",MATCHING:"MATCHING",ORDERING:"ORDERING",HOTSPOT:"HOTSPOT",LINK:"LINK"},pr=[m.RADIO,m.CHECKBOX,m.TEXTAREA];var R=[{key:m.RADIO,typename:"RADIO",label:"Single answer",id:1,autoGradable:!0,supportsPartialCredit:!1,manualGradingRequired:!1},{key:m.CHECKBOX,typename:"CHECKBOX",label:"Multiple answers",id:2,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:m.TEXTAREA,typename:"TEXTAREA",label:"Paragraph",id:3,autoGradable:!1,supportsPartialCredit:!1,manualGradingRequired:!0},{key:m.TRUE_FALSE,typename:"TRUE_FALSE",label:"True/False",id:4,autoGradable:!0,supportsPartialCredit:!1,manualGradingRequired:!1},{key:m.SHORT_ANSWER,typename:"SHORT_ANSWER",label:"Short answer",id:5,autoGradable:!1,supportsPartialCredit:!1,manualGradingRequired:!0},{key:m.NUMERIC,typename:"NUMERIC",label:"Numeric answer",id:6,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:m.FILL_BLANK,typename:"FILL_BLANK",label:"Fill in the blank",id:7,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:m.FILE_UPLOAD,typename:"FILE_UPLOAD",label:"File upload",id:8,autoGradable:!1,supportsPartialCredit:!1,manualGradingRequired:!0},{key:m.MATCHING,typename:"MATCHING",label:"Matching",id:9,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:m.ORDERING,typename:"ORDERING",label:"Ordering",id:10,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:m.HOTSPOT,typename:"HOTSPOT",label:"Hotspot",id:11,autoGradable:!0,supportsPartialCredit:!0,manualGradingRequired:!1},{key:m.LINK,typename:"LINK",label:"Links",id:12,autoGradable:!1,supportsPartialCredit:!1,manualGradingRequired:!0}],je=Object.freeze(R.reduce((o,t)=>(o[t.key]=t,o),{})),v=Object.freeze(R.reduce((o,t)=>(o[t.key]=t.id,o),{})),ve=Object.freeze(R.reduce((o,t)=>(o[t.typename]=t.key,o),{})),Ge=Object.freeze(R.reduce((o,t)=>(o[t.id]=t.key,o),{}));var ke=new Set(["true","1","yes"]),Fe=new Set(["false","0","no"]);function Me(o){let t=o.trim().toLowerCase();if(ke.has(t))return!0;if(Fe.has(t))return!1}var Ke={type:"RADIO",toApiPayload(o,t){return{questionId:t,optionId:o.optionId}},fromApiPayload(o){let t=o.optionId;return t===void 0||Number.isNaN(t)?null:{type:"RADIO",optionId:t}}},He={type:"CHECKBOX",toApiPayload(o,t){return{questionId:t,answer:JSON.stringify({type:"CHECKBOX",optionIds:o.optionIds})}},fromApiPayload(o){if(!o.answer)return null;let{optionIds:t}=JSON.parse(o.answer);return Array.isArray(t)?{type:"CHECKBOX",optionIds:t}:null}},qe={type:"TRUE_FALSE",toApiPayload(o,t){return{questionId:t,answer:String(o.value)}},fromApiPayload(o){if(o.answer===void 0)return null;let t=Me(o.answer);return t===void 0?null:{type:"TRUE_FALSE",value:t}}},Be={type:"TEXTAREA",toApiPayload(o,t){return{questionId:t,answer:o.text}},fromApiPayload(o){return o.answer===void 0?null:{type:"TEXTAREA",text:o.answer}}},Qe={type:"SHORT_ANSWER",toApiPayload(o,t){return{questionId:t,answer:o.text}},fromApiPayload(o){return o.answer===void 0?null:{type:"SHORT_ANSWER",text:o.answer}}},Ye={type:"NUMERIC",toApiPayload(o,t){return{questionId:t,answer:JSON.stringify({type:"NUMERIC",value:o.value})}},fromApiPayload(o){if(!o.answer)return null;let{value:t}=JSON.parse(o.answer);return{type:"NUMERIC",value:t}}},Xe={type:"FILL_BLANK",toApiPayload(o,t){return{questionId:t,answer:JSON.stringify({type:"FILL_BLANK",answers:o.values})}},fromApiPayload(o){if(!o.answer)return null;let{answers:t}=JSON.parse(o.answer),n=t.map(a=>String(a).trim()).filter(Boolean);return n.length>0?{type:"FILL_BLANK",values:n}:null}},$e={type:"FILE_UPLOAD",toApiPayload(o,t){let n={fileKey:o.fileKey,fileName:o.fileName,mimeType:o.mimeType,size:o.size};return{questionId:t,answer:JSON.stringify(n)}},fromApiPayload(o){if(!o.answer)return null;let t=JSON.parse(o.answer);return{type:"FILE_UPLOAD",fileKey:t.fileKey,fileName:t.fileName??"",mimeType:t.mimeType,size:t.size}}},Ve={type:"MATCHING",toApiPayload(o,t){return{questionId:t,answer:JSON.stringify({type:"MATCHING",value:o.pairs})}},fromApiPayload(o){if(!o.answer)return null;let{value:t}=JSON.parse(o.answer);return t.length>0?{type:"MATCHING",pairs:t}:null}},Je={type:"ORDERING",toApiPayload(o,t){return{questionId:t,answer:JSON.stringify({type:"ORDERING",value:o.orderedValues})}},fromApiPayload(o){if(!o.answer)return null;let{value:t}=JSON.parse(o.answer);return t.length>0?{type:"ORDERING",orderedValues:t}:null}},We={type:"LINK",toApiPayload(o,t){return{questionId:t,answer:JSON.stringify({type:"LINK",links:o.urls})}},fromApiPayload(o){if(!o.answer)return null;let{links:t}=JSON.parse(o.answer),n=t.map(a=>String(a).trim()).filter(Boolean);return n.length>0?{type:"LINK",urls:n}:null}},et={type:"HOTSPOT",toApiPayload(o,t){return{questionId:t,answer:JSON.stringify({type:"HOTSPOT",value:o.coordinates})}},fromApiPayload(o){if(!o.answer)return null;let{value:t}=JSON.parse(o.answer);return t.length>0?{type:"HOTSPOT",coordinates:t}:null}},Tr={[m.RADIO]:Ke,[m.CHECKBOX]:He,[m.TRUE_FALSE]:qe,[m.TEXTAREA]:Be,[m.SHORT_ANSWER]:Qe,[m.NUMERIC]:Ye,[m.FILL_BLANK]:Xe,[m.FILE_UPLOAD]:$e,[m.MATCHING]:Ve,[m.ORDERING]:Je,[m.LINK]:We,[m.HOTSPOT]:et};var $=["video/mp4","video/quicktime","video/x-msvideo","video/x-matroska"],V=["application/pdf","application/vnd.openxmlformats-officedocument.wordprocessingml.document","application/msword"];var I=v;var tt=[s.literal(I.RADIO),s.literal(I.CHECKBOX),s.literal(I.TEXTAREA),s.literal(I.TRUE_FALSE),s.literal(I.SHORT_ANSWER),s.literal(I.NUMERIC),s.literal(I.FILL_BLANK),s.literal(I.FILE_UPLOAD),s.literal(I.MATCHING),s.literal(I.ORDERING),s.literal(I.HOTSPOT),s.literal(I.LINK)],L=s.union(tt),ot={[I.RADIO]:[o=>{let t=o.options||[];return t.length>0&&t.length<2?"Options must have at least 2 items for RADIO/CHECKBOX questions":null},o=>{let t=o.options||[];return t.length>0&&!t.some(n=>n.isCorrect===!0)?"Please mark an option as the correct answer":null}],[I.CHECKBOX]:[o=>{let t=o.options||[];return t.length>0&&t.length<2?"Options must have at least 2 items for RADIO/CHECKBOX questions":null},o=>{let t=o.options||[];return t.length>0&&!t.some(n=>n.isCorrect===!0)?"Please mark an option as the correct answer":null}],[I.TRUE_FALSE]:[o=>{let t=o.options||[];return t.length>0&&t.length<2?"True/False questions need both True and False options":null},o=>{let t=o.options||[];return t.length>0&&!t.some(n=>n.isCorrect===!0)?"Please mark an option as the correct answer":null}],[I.TEXTAREA]:[]},rt=s.object({id:s.number().int().optional(),question:s.string().min(1),questionTypeId:L.optional(),points:s.number().int().min(0).optional(),order:s.number().int().min(0).optional(),settings:s.record(s.string(),s.unknown()).optional(),deletedAt:s.string().optional(),options:s.array(s.object({id:s.number().int().optional(),label:s.string().min(1),isCorrect:s.boolean(),settings:s.record(s.string(),s.unknown()).optional(),deletedAt:s.string().optional()})).optional()});function J(o,t){if(o.deletedAt)return;let n=ot[o.questionTypeId??-1]??[];for(let a of n){let u=a(o);u&&t.addIssue({code:"custom",message:u,path:["options"]})}}var nt=rt.superRefine(J),st=s.object({question:s.string().min(1),questionTypeId:L.optional(),points:s.number().int().min(0).optional(),order:s.number().int().min(0).optional(),settings:s.record(s.string(),s.unknown()).optional(),options:s.array(s.object({label:s.string().min(1),isCorrect:s.boolean(),settings:s.record(s.string(),s.unknown()).optional()})).optional()}),it=st.superRefine(J),W=s.object({title:s.string().min(1),description:s.string().optional(),lessonId:s.string().optional(),sectionId:s.string().optional(),order:s.number().int().optional(),courseId:s.string().min(1),dueBy:s.string().optional(),questions:s.array(it).optional()}),ee=s.object({title:s.string().min(1).optional(),description:s.string().optional(),lessonId:s.string().optional(),sectionId:s.string().optional(),order:s.number().int().optional(),isUnlocked:s.boolean().optional(),dueBy:s.string().optional(),questions:s.array(nt).optional()}),G=s.object({exerciseId:s.string().min(1)}),te=s.object({lessonId:s.string().optional(),sectionId:s.string().optional()}),Kr=s.object({exerciseId:s.string().min(1),answers:s.array(s.object({questionId:s.number().int(),optionId:s.number().int().optional(),answer:s.string().optional()})).min(1)}),oe=s.object({lessonId:s.string().optional(),sectionId:s.string().optional(),order:s.number().int().optional(),templateId:s.number().int().min(1)});import*as r from"zod";import*as e from"zod";var Yr=e.object({title:e.string().min(1),description:e.string().optional(),slug:e.string().min(1),organizationId:e.string().min(1)}),Xr=e.object({courseId:e.string().min(1)}),at=e.object({courseId:e.string().min(1)}),$r=e.object({slug:e.string().optional()}),Vr=e.object({slug:e.string().min(1)}),Jr=e.object({courseId:e.string().min(1)}),Wr=e.object({inviteToken:e.string().min(1).optional()}),en=e.object({courseId:e.string().min(1)}),tn=e.object({theme:e.string().min(1),studentName:e.string().min(1),courseName:e.string().min(1),courseDescription:e.string().min(1),orgName:e.string().min(1),orgLogoUrl:e.string().url().optional(),facilitator:e.string().optional()}),on=e.object({courseTitle:e.string().min(1),orgName:e.string().min(1),orgTheme:e.string().min(1),lessons:e.array(e.object({lessonTitle:e.string().min(1),lessonNumber:e.string().min(1),lessonNote:e.string(),slideUrl:e.string().url().optional(),video:e.array(e.string().url()).optional()}))}),rn=e.object({title:e.string().min(1),number:e.string().min(1),orgName:e.string().min(1),note:e.string(),slideUrl:e.string().url().optional(),video:e.array(e.string().url()).optional(),courseTitle:e.string().min(1)}),nn=e.object({fileName:e.string().min(1),fileType:e.enum($)}),sn=e.object({fileName:e.string().min(1),fileType:e.enum(V)}),an=e.object({keys:e.array(e.string().min(1)).min(1)}),re=e.object({id:e.string().min(1),type:e.enum(["LESSON","EXERCISE"]),isUnlocked:e.boolean().optional(),order:e.number().int().min(0).optional(),sectionId:e.string().nullable().optional()}),un=e.object({items:e.array(re).min(1)}),ut=e.object({id:e.string().min(1),order:e.number().int().min(0)}),ct=re.omit({isUnlocked:!0}),k=e.object({sections:e.array(ut).min(1).optional(),items:e.array(ct).min(1).optional()}),ne=k.superRefine((o,t)=>{!o.sections?.length&&!o.items?.length&&t.addIssue({code:e.ZodIssueCode.custom,path:["sections"],message:"Provide sections or items to reorder"});let n=new Set;o.sections?.forEach((u,d)=>{n.has(u.id)&&t.addIssue({code:e.ZodIssueCode.custom,path:["sections",d,"id"],message:"Section IDs must be unique"}),n.add(u.id)});let a=new Set;o.items?.forEach((u,d)=>{u.order===void 0&&u.sectionId===void 0&&t.addIssue({code:e.ZodIssueCode.custom,path:["items",d],message:"Each item must provide order or sectionId"});let g=`${u.type}:${u.id}`;a.has(g)&&t.addIssue({code:e.ZodIssueCode.custom,path:["items",d,"id"],message:"Item IDs must be unique per content type"}),a.add(g)})}),pt=e.object({id:e.string().min(1),type:e.enum(["LESSON","EXERCISE"])}),cn=e.object({sectionId:e.string().min(1).optional(),items:e.array(pt).min(1).optional()}).refine(o=>!!o.sectionId!=!!o.items,{message:"Provide either sectionId or items",path:["sectionId"]}),pn=e.object({title:e.string().min(1),description:e.string().min(1),type:e.enum(["LIVE_CLASS","SELF_PACED"]),organizationId:e.string().min(1)}),se=e.object({show:e.boolean(),description:e.string()}),ie=e.object({name:e.string(),role:e.string(),coursesNo:e.number(),description:e.string(),imgUrl:e.string()}),ae=e.object({templateUrl:e.string()}),ue=e.object({id:e.number(),hide:e.boolean(),name:e.string(),avatar_url:e.string(),rating:e.number(),created_at:e.number(),description:e.string()}),ce=e.array(e.object({id:e.union([e.literal(1),e.literal(2),e.literal(3),e.literal(4)]),name:e.string()})),Z=e.object({requirements:e.string().optional(),description:e.string().optional(),goals:e.string().optional(),videoUrl:e.string().optional(),showDiscount:e.boolean().optional(),discount:e.number().optional(),paymentLink:e.string().optional(),reward:se.optional(),instructor:ie.optional(),certificate:ae.optional(),reviews:e.array(ue).optional(),lessonTabsOrder:ce.optional(),grading:e.boolean().optional(),lessonDownload:e.boolean().optional(),allowNewStudent:e.boolean(),sectionDisplay:e.record(e.string(),e.boolean()).optional(),isContentGroupingEnabled:e.boolean().optional()}),lt=e.object({requirements:e.string().optional(),description:e.string().optional(),goals:e.string().optional(),videoUrl:e.string().optional(),showDiscount:e.boolean().optional(),discount:e.number().optional(),paymentLink:e.string().optional(),reward:se.partial().optional(),instructor:ie.partial().optional(),certificate:ae.partial().optional(),reviews:e.array(ue).optional(),lessonTabsOrder:ce.optional(),grading:e.boolean().optional(),lessonDownload:e.boolean().optional(),allowNewStudent:e.boolean().optional(),sectionDisplay:e.record(e.string(),e.boolean()).optional(),isContentGroupingEnabled:e.boolean().optional()}),ln=e.object({title:e.string().min(1).optional(),description:e.string().min(1).optional(),type:e.enum(["LIVE_CLASS","SELF_PACED"]).optional(),logo:e.string().optional(),slug:e.string().optional(),isPublished:e.boolean().optional(),overview:e.string().optional(),metadata:Z.optional(),cost:e.number().int().min(0).optional(),currency:e.enum(["NGN","USD"]).optional(),isCertificateDownloadable:e.boolean().optional(),certificateTheme:e.string().optional(),tagIds:e.array(e.uuid()).max(100).optional()}),dn=e.object({courseId:e.string().min(1)}),pe=e.object({title:e.string().min(1).optional(),description:e.string().min(1).optional(),overview:e.string().optional(),cost:e.number().int().min(0).optional(),currency:e.enum(["NGN","USD"]).optional(),imageUrl:e.url().optional(),generateImage:e.boolean().optional(),imageQuery:e.string().min(1).max(120).optional(),metadata:lt.optional()}),mn=e.object({courseId:e.string().min(1)}),gn=e.object({courseId:e.string().min(1)}),zn=e.object({profileId:e.string().uuid()}),fn=e.object({courseId:e.string().min(1),userId:e.string().uuid()});var le=r.enum(["en","hi","fr","pt","de","vi","ru","es","pl","da"]),dt="Lesson body HTML only. Do not include the lesson title. Do not use h1 or h2 anywhere in lessonLanguages[].content. Start headings at h3 because that is the highest heading level allowed in lesson content.",de=r.object({code:r.string().min(1),message:r.string().min(1),severity:r.enum(["info","warning","error"])}),mt=r.object({type:r.enum(["prompt","pdf","course"]),label:r.string().min(1),pageStart:r.number().int().min(1).optional(),pageEnd:r.number().int().min(1).optional()}),gt=r.object({title:r.string().min(1),description:r.string().min(1),type:r.enum(["LIVE_CLASS","SELF_PACED"]),locale:le.default("en"),metadata:Z.optional()}),zt=r.object({externalId:r.string().min(1),title:r.string().min(1),order:r.number().int().min(0)}),ft=r.object({externalId:r.string().min(1),sectionExternalId:r.string().min(1),title:r.string().min(1),order:r.number().int().min(0),isUnlocked:r.boolean().optional(),public:r.boolean().optional()}),It=r.object({lessonExternalId:r.string().min(1),locale:le,content:r.string().min(1).describe(dt)}),Ct=r.object({label:r.string().min(1),isCorrect:r.boolean(),settings:r.record(r.string(),r.unknown()).optional()}),xt=r.object({question:r.string().min(1),questionTypeId:L.optional(),points:r.number().int().min(0).optional(),order:r.number().int().min(0).optional(),settings:r.record(r.string(),r.unknown()).optional(),options:r.array(Ct).optional()}),Et=r.object({externalId:r.string().min(1),lessonExternalId:r.string().min(1).optional(),sectionExternalId:r.string().min(1).optional(),title:r.string().min(1),description:r.string().optional(),order:r.number().int().min(0).optional(),dueBy:r.string().optional(),questions:r.array(xt).optional()}),me=r.object({course:gt,tags:r.array(r.string().trim().min(1).max(80)).max(100).default([]),sections:r.array(zt).min(1),lessons:r.array(ft).min(1),lessonLanguages:r.array(It).min(1),exercises:r.array(Et).optional(),sourceReferences:r.array(mt).optional(),warnings:r.array(de).default([])}).superRefine((o,t)=>{let n=new Set;o.tags.forEach((g,E)=>{let H=g.trim().toLowerCase();n.has(H)&&t.addIssue({code:r.ZodIssueCode.custom,path:["tags",E],message:"Draft tags must be unique"}),n.add(H)});let a=new Set;o.sections.forEach((g,E)=>{a.has(g.externalId)&&t.addIssue({code:r.ZodIssueCode.custom,path:["sections",E,"externalId"],message:"Section externalId must be unique"}),a.add(g.externalId)});let u=new Set;o.lessons.forEach((g,E)=>{u.has(g.externalId)&&t.addIssue({code:r.ZodIssueCode.custom,path:["lessons",E,"externalId"],message:"Lesson externalId must be unique"}),u.add(g.externalId),a.has(g.sectionExternalId)||t.addIssue({code:r.ZodIssueCode.custom,path:["lessons",E,"sectionExternalId"],message:"Lesson sectionExternalId must reference an existing section"})}),o.lessonLanguages.forEach((g,E)=>{u.has(g.lessonExternalId)||t.addIssue({code:r.ZodIssueCode.custom,path:["lessonLanguages",E,"lessonExternalId"],message:"Lesson language must reference an existing lesson"})});let d=new Set;o.exercises?.forEach((g,E)=>{d.has(g.externalId)&&t.addIssue({code:r.ZodIssueCode.custom,path:["exercises",E,"externalId"],message:"Exercise externalId must be unique"}),d.add(g.externalId),g.lessonExternalId&&!u.has(g.lessonExternalId)&&t.addIssue({code:r.ZodIssueCode.custom,path:["exercises",E,"lessonExternalId"],message:"Exercise lessonExternalId must reference an existing lesson"}),g.sectionExternalId&&!a.has(g.sectionExternalId)&&t.addIssue({code:r.ZodIssueCode.custom,path:["exercises",E,"sectionExternalId"],message:"Exercise sectionExternalId must reference an existing section"})})}),F=r.object({sourceType:r.enum(["prompt","pdf","course"]),idempotencyKey:r.string().min(1).optional(),summary:r.record(r.string(),r.unknown()).optional(),sourceArtifacts:r.array(r.record(r.string(),r.unknown())).optional(),draft:me}),T=r.object({courseId:r.string().min(1)}),M=r.object({courseId:r.string().min(1),idempotencyKey:r.string().min(1).optional(),summary:r.record(r.string(),r.unknown()).optional(),sourceArtifacts:r.array(r.record(r.string(),r.unknown())).optional()}),O=r.object({draftId:r.string().uuid()}),ge=r.object({summary:r.record(r.string(),r.unknown()).optional(),sourceArtifacts:r.array(r.record(r.string(),r.unknown())).optional(),warnings:r.array(de).optional(),draft:me.optional()}),K=r.object({title:r.string().min(1).optional(),description:r.string().min(1).optional(),type:r.enum(["LIVE_CLASS","SELF_PACED"]).optional(),metadata:Z.optional(),bannerImageUrl:r.string().url().optional(),bannerImageQuery:r.string().min(1).max(120).optional(),generateBannerImage:r.boolean().optional()}),ze=K.extend({courseId:r.string().min(1)});import*as f from"zod";var Tn=f.object({title:f.string().min(1),courseId:f.string().min(1),order:f.number().int().min(0).optional()}),hn=f.object({title:f.string().min(1).optional(),order:f.number().int().min(0).optional()}),Sn=f.object({title:f.string().min(1)}),On=f.object({sectionId:f.string().min(1)}),An=f.object({sections:f.array(f.object({id:f.string().min(1),order:f.number().int().min(0)})).min(1)});import*as l from"zod";var Pn=l.object({courseId:l.string().min(1)}),wn=l.object({courseId:l.string().min(1),inviteId:l.string().uuid()}),Rn=l.object({courseId:l.string().min(1),inviteId:l.string().uuid()}),Ln=l.object({token:l.string().min(1)}),Zn=l.object({courseId:l.uuid()}),yt=l.enum(["ONE_TIME_24H","MULTI_USE_7D","MULTI_USE_30D","CUSTOM"]),Dn=l.object({preset:yt.default("MULTI_USE_30D"),expiresAt:l.string().min(1).optional(),maxUses:l.number().int().min(1).max(1e3).optional(),allowedEmails:l.array(l.string().email()).max(100).optional(),allowedDomains:l.array(l.string().min(1)).max(100).optional(),recipientEmails:l.array(l.string().email()).max(500).optional(),recipientCsv:l.string().max(25e3).optional(),sendEmail:l.boolean().default(!1),metadata:l.record(l.string(),l.unknown()).optional()}).refine(o=>{let t=(o.recipientEmails?.length??0)>0,n=typeof o.recipientCsv=="string"&&o.recipientCsv.trim().length>0;return t||n},{message:"recipientEmails or recipientCsv is required",path:["recipientEmails"]});var Ce=ge.extend({draftId:O.shape.draftId}),xe=D.extend({draftId:Q.shape.draftId}),Ee=te.extend({courseId:T.shape.courseId}),ye=G.extend({courseId:T.shape.courseId}),be=W.omit({courseId:!0}).extend({courseId:T.shape.courseId}),Te=oe.extend({courseId:T.shape.courseId}),he=ee.extend({courseId:T.shape.courseId,exerciseId:G.shape.exerciseId}),Se=pe.extend({courseId:T.shape.courseId}),Oe=k.extend({courseId:T.shape.courseId}),Ae=K.extend({draftId:O.shape.draftId}),_e=ze.extend({draftId:O.shape.draftId}),bt=F.shape,Tt=M.shape,ht=U.shape,St=T.shape,Ot=O.shape,At=Ce.shape,_t=xe.shape,Pt=Ee.shape,wt=ye.shape,Rt=be.shape,Lt=Te.shape,Zt=he.shape,Dt=Se.shape,Nt=Oe.shape,Ut=Ae.shape,jt=_e.shape,vt=N.shape,fe="Supported questionTypeId values: RADIO=1 (Single answer), CHECKBOX=2 (Multiple answers), TEXTAREA=3 (Paragraph), TRUE_FALSE=4 (True/False), SHORT_ANSWER=5 (Short answer), NUMERIC=6 (Numeric answer), FILL_BLANK=7 (Fill in the blank), FILE_UPLOAD=8 (File upload), MATCHING=9 (Matching), ORDERING=10 (Ordering), HOTSPOT=11 (Hotspot), LINK=12 (Links). Use the numeric ids in exercise payloads.",Ie="For draft lesson HTML, put only the lesson body in lessonLanguages[].content. Do not include the lesson title. Do not use h1 or h2 anywhere in lesson HTML. Start headings at h3 because that is the highest heading level allowed in lesson content.";function Pe(o,t){o.tool("list_org_courses","List all courses in the authenticated organization. Use this when the user refers to a course by name and you need to discover the available course IDs first.",ht,async n=>{let a=U.parse(n),u=await t.listOrganizationCourses(a);return C(u)}),o.tool("get_course_structure","Read the current live course structure. Use this before creating a fresh draft for post-publish edits.",St,async n=>{let{courseId:a}=T.parse(n),u=await t.getCourseStructure(a);return C(u)}),o.tool("update_course_landing_page","Update landing-page-facing course fields on a live course, including headline copy, overview, requirements, goals, pricing, reviews, instructor information, and the course image. Use imageUrl to set an explicit cover image, or set generateImage/imageQuery to fetch a random Unsplash-based image.",Dt,async n=>{let{courseId:a,...u}=Se.parse(n),d=await t.updateCourseLandingPage(a,u);return C(d)}),o.tool("reorder_course_content","Batch reorder live course sections and lesson or exercise content without creating a draft. Use this for section ordering or moving/reordering lessons and exercises between sections. For items, pass type as LESSON or EXERCISE and use sectionId to move between sections or null for ungrouped content.",Nt,async n=>{let{courseId:a,...u}=Oe.parse(n),d=ne.parse(u),g=await t.reorderCourseContent(a,d);return C(g)}),o.tool("create_course_draft",`Create a new unpublished course draft from structured course JSON. Use this before the first publish. ${Ie}`,bt,async n=>{let a=F.parse(n),u=await t.createCourseDraft(a);return C(u)}),o.tool("create_course_draft_from_course","Create a fresh draft from an existing live course. Use this after a course has already been published and the user wants more changes.",Tt,async n=>{let a=M.parse(n),u=await t.createCourseDraftFromCourse(a);return C(u)}),o.tool("get_course_draft","Fetch an existing draft. Draft responses include status and publishedCourseId so you can decide whether to keep editing the draft or seed a fresh one from the live course.",Ot,async n=>{let{draftId:a}=O.parse(n),u=await t.getCourseDraft(a);return C(u)}),o.tool("list_course_exercises","List exercises for a live course, optionally filtered by lessonId or sectionId.",Pt,async n=>{let{courseId:a,...u}=Ee.parse(n),d=await t.listCourseExercises(a,u);return C(d)}),o.tool("get_course_exercise","Fetch a single exercise from a live course, including its questions and options.",wt,async n=>{let{courseId:a,exerciseId:u}=ye.parse(n),d=await t.getCourseExercise(a,u);return C(d)}),o.tool("create_course_exercise",`Create a new exercise directly on a live course. Use this for adding exercises after a course has already been published. ${fe}`,Rt,async n=>{let{courseId:a,...u}=be.parse(n),d=await t.createCourseExercise(a,u);return C(d)}),o.tool("create_course_exercise_from_template","Create a new exercise on a live course from an existing template.",Lt,async n=>{let{courseId:a,...u}=Te.parse(n),d=await t.createCourseExerciseFromTemplate(a,u);return C(d)}),o.tool("update_course_exercise",`Update an existing exercise on a live course, including questions and options. ${fe}`,Zt,async n=>{let{courseId:a,exerciseId:u,...d}=he.parse(n),g=await t.updateCourseExercise(a,u,d);return C(g)}),o.tool("update_course_draft",`Update an unpublished draft. If the draft is already published, do not keep editing it blindly; create a fresh draft from the live course first. ${Ie}`,At,async n=>{let{draftId:a,...u}=Ce.parse(n),d=await t.updateCourseDraft(a,u);return C(d)}),o.tool("tag_course_draft","Attach human-readable tags to a draft before publish. The tags are stored on the draft and applied during publish.",_t,async n=>{let{draftId:a,...u}=xe.parse(n),d=await t.tagCourseDraft(a,u);return C(d)}),o.tool("publish_course_draft","Publish an unpublished draft as a new live course. The result includes courseId and courseUrl. After publish, future major edits should usually start from a fresh draft created from the live course.",Ut,async n=>{let{draftId:a,...u}=Ae.parse(n),d=await t.publishCourseDraft(a,u);return C(d)}),o.tool("publish_course_draft_to_existing_course","Apply a fresh draft back onto an existing live course. Use this after reading the live course and creating a new draft from it.",jt,async n=>{let{draftId:a,...u}=_e.parse(n),d=await t.publishCourseDraftToExistingCourse(a,u);return C(d)}),o.tool("tag_courses","Assign tags directly to one or more live courses by name. Default mode is merge, so existing tags are preserved unless replace is requested.",vt,async n=>{let a=N.parse(n),u=await t.tagCourses(a);return C(u)})}function C(o){return{content:[{type:"text",text:JSON.stringify(o)}]}}async function Ft(){let o=q(),t=new A(o),n=new Gt({name:"classroomio-course-authoring",version:"0.0.1"});Pe(n,t);let a=new kt;await n.connect(a),process.stdin.on("close",()=>{n.close()})}Ft().catch(o=>{o instanceof S&&(console.error(JSON.stringify({error:o.message,status:o.status,code:o.code,field:o.field})),process.exit(1)),console.error(o),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classroomio/mcp",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",