Skip to content

Add LMS MCP write tools - #122

Merged
scottgrayson merged 13 commits into
4.xfrom
CU-868kuzw7c-lms-mcp
Sep 29, 2026
Merged

scottgrayson merged 13 commits into
4.xfrom
CU-868kuzw7c-lms-mcp

Conversation

@scottgrayson

@scottgrayson scottgrayson commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Add a Laravel MCP server so hosts can create and edit Course → Lesson → video Step data over stdio or HTTP, and fix null Filament URLs and global Spatie sort order on new courses.

Design decisions

The package registers POST /mcp/lms on by default

Why

Hosts should not copy a route after an LMS update. The route is Sanctum, throttle:mcp, and isLmsAdmin(). The leftover is HasApiTokens, the tokens table, a real isLmsAdmin(), and pasting the bearer into Claude.

High impact

Every app that upgrades this package gets a public write endpoint unless they set filament-lms.mcp.web to false. Hosts whose isLmsAdmin() stays the default false get 403.

Files

src/FilamentLmsServiceProvider.php

src/Http/Middleware/EnsureLmsMcpAdmin.php

src/Console/Commands/LmsMcpTokenCommand.php

Set the Filament panel before generating MCP URLs

Why

HTTP MCP requests have no current panel, so CourseResource::getUrl and StepPage::getUrlForStep threw and safeUrl() returned null. Tool payloads listed urls.admin and urls.learner as null.

Files

src/Mcp/LmsTool.php

Scope Spatie sort order to the parent course or lesson

Why

SortableTrait used a global max(order) + 1. A new course inherited the highest lesson and step order in the database. buildSortQuery() scopes Lesson to course_id and Step to lesson_id.

Files

src/Models/Lesson.php

src/Models/Step.php

Testing

Already verified on a local Filament-LMS clone and against CHECK staging (https://check.webuildawesomesoftware.com/mcp/lms).

  1. POST /mcp/lms with no token → 401. A Sanctum user whose isLmsAdmin() is false → 403.
  2. Call list_courses or get_course as an LMS admin. urls.admin and urls.learner are Filament paths, not null.
  3. create_video_course for a private throwaway. First lesson and first step are order: 1. Delete that course.

Note

High Risk
Upgrading hosts get a default public MCP write route unless filament-lms.mcp.web is false; compromise of an LMS-admin Sanctum token allows course structure mutations over HTTP.

Overview
Adds a Laravel MCP server so AI clients can read and write LMS structure (Course → Lesson → YouTube/Vimeo video Step) over local stdio (php artisan mcp:start filament-lms) or HTTP at POST /mcp/lms. The package now requires laravel/mcp and laravel/sanctum, registers both transports by default (filament-lms.mcp.enabled / mcp.web), and protects HTTP with Sanctum, throttle:mcp, and isLmsAdmin(). php artisan lms:mcp-token mints bearer tokens and prints Claude Desktop config; README documents Cursor/Claude Code setup.

v1 tools include create_video_course (nested create, private by default) plus granular list/get/update/delete for courses, lessons, and video steps. Shared LmsTool logic handles tenancy, Filament-aligned validation (certificate_template_id, unique fields), embed URL conversion, and admin/learner URLs by temporarily setting the correct Filament panel.

Model fixes scope Spatie lesson/step ordering to course_id / lesson_id, prefix auto step slugs with the course slug, and shift lesson order when inserting at an explicit position. MCP update_step validates video URLs and refuses to turn non-video steps into video steps.

Reviewed by Cursor Bugbot for commit 83e0b8c. Bugbot is set up for automated code reviews on this repo. Configure here.

scottgrayson and others added 5 commits August 24, 2026 05:32
Give AI clients a package-local stdio server to create and edit Course → Lesson → Step data from hosted YouTube/Vimeo URLs, with new courses private by default.

Co-authored-by: Cursor <cursoragent@cursor.com>
Keep the MCP Unreleased entry and the released v4.7.7 dashboard changelog.

Co-authored-by: Cursor <cursoragent@cursor.com>
Require isLmsAdmin for authenticated MCP writes, guard tenant context when
tenancy is enabled, prefix auto external_ids that start with a digit, and
assert on JSON-safe video fields in LmsMcpTest.

Co-authored-by: Cursor <cursoragent@cursor.com>
Assert on the video id and provider name instead of exact JSON key spacing.

Co-authored-by: Cursor <cursoragent@cursor.com>

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread src/Mcp/LmsTool.php
Comment thread src/Mcp/Tools/CreateLesson.php
Comment thread src/Mcp/Tools/UpdateStep.php Outdated
Comment thread src/Mcp/Tools/UpdateStep.php Outdated
HTTP MCP requests have no Filament panel, and Spatie sortable was using a global max, so new courses got null admin/learner links and order 34/83.

Co-authored-by: Cursor <cursoragent@cursor.com>

@cursor cursor Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread src/Mcp/Tools/CreateLesson.php
Keep MCP registration through the certificate-template and user-group work on 4.x, and fix slug collisions, lesson order shifts, and update_step validation.

Co-authored-by: Cursor <cursoragent@cursor.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5f83df5. Configure here.

Comment thread src/Mcp/LmsTool.php Outdated
Comment thread src/Mcp/Tools/UpdateLesson.php Outdated
prefer-stable laravel/mcp leaves slashes unescaped, so the old admin\/lms path check failed CI.

Co-authored-by: Cursor <cursoragent@cursor.com>
…pdate.

Co-authored-by: Cursor <cursoragent@cursor.com>
scottgrayson and others added 2 commits September 28, 2026 12:50
Co-authored-by: Cursor <cursoragent@cursor.com>
Hosts still add HasApiTokens and the tokens table; the package now mounts the route, admin gate, rate limit, and token command.

Co-authored-by: Cursor <cursoragent@cursor.com>
Package README and tests should describe any host, not one project.

Co-authored-by: Cursor <cursoragent@cursor.com>
@scottgrayson

Copy link
Copy Markdown
Contributor Author

@swilla I've updated this package to minimize the amount of work that projects need to do to start using the MCP:

  1. Install sanctum (migrations and user trait)
  2. implement isLmsAdmin() method on user
  3. mint a token with artisan command php artisan lms:mcp-token admin@example.com

they no longer need to define and auth an /mcp/lms route. its published by the package and uses sanctum and isLmsAdmin for authorization

Co-authored-by: Cursor <cursoragent@cursor.com>
@scottgrayson scottgrayson changed the title Add optional LMS MCP write tools Add LMS MCP write tools Sep 29, 2026
@scottgrayson
scottgrayson merged commit 4a4ac42 into 4.x Sep 29, 2026
1 check passed
@scottgrayson
scottgrayson deleted the CU-868kuzw7c-lms-mcp branch September 29, 2026 20:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant