Repository navigation
feat(mcp): laravel/mcp 1.x and the MCP 2026-07-28 protocol - #3957
Merged
Merged
Conversation
Moves the MCP stack from laravel/mcp v0.1.1 to 1.x (1.1.0). The McpServer plugin's side (server
class, endpoint, call tracking) ships in Leantime/plugins and must be merged with this.
- composer: laravel/mcp ^1.0 (brings nette/utils 4.1.6). illuminate/json-schema ships with
Laravel 12.
- The 57 core tools in app/Domain/*/Tools move to the 1.x tool API:
- schema(JsonSchema): array instead of the chained ToolInputSchema
- handle(Request): Response instead of handle(array): ToolResult; the handlers read
$request->all() and are otherwise unchanged
- The tool contract is unchanged (diffed tools/list before and after): same names, descriptions,
arguments and required lists. Intended additions only:
- array arguments declare their items (integer for id lists, object for record lists, string
for search types), which strict clients require
- every tool has a title
- tools that only add data are marked destructiveHint: false, deleteEvent destructiveHint:
true, edit tools idempotentHint
- getICalUrl's empty schema is a JSON object again (v0.1.1 serialized it as [])
- McpCest:
- keeps the legacy initialize tests (2025-06-18, 2025-11-25)
- adds 2026-07-28 tests: server/discover, a single-page tools/list with a private cache hint
and the full expected tool list, tools/call with Mcp-Name, header mismatch → 400/-32020
- an unknown tool is now a JSON-RPC error (400/-32602) instead of an isError result
- tests/Httprequests/MCP.http covers both client generations.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
Coordinated private-plugin compatibility remains unverified here, and the MCP acceptance suite has not been run.
No open issues 🎉
What changed in this PR
Upgrades Leantime’s MCP integration to laravel/mcp 1.x following the Laravel 12 upgrade, adding MCP 2026-07-28 support while retaining legacy handshakes.
Changes:
- Migrates 57 core tools to the new schema, request and response APIs.
- Adds tool titles, operation annotations and array item types.
- Updates unit tests, protocol acceptance tests and HTTP examples.
| File | Description |
|---|---|
| tests/Unit/app/Domain/Tickets/Tools/GetStatusLabelsToolTest.php | Updates request and response assertions. |
| tests/Unit/app/Domain/Tickets/Tools/BulkAddTasksToolTest.php | Updates bulk-result assertions. |
| tests/Unit/app/Domain/Projects/Tools/GetUsersAssignedToProjectToolTest.php | Updates request and response assertions. |
| tests/Unit/app/Domain/Goalcanvas/Tools/GetParentKPIsToolTest.php | Updates request and response assertions. |
| tests/Httprequests/MCP.http | Documents both client generations. |
| tests/Acceptance/API/McpCest.php | Adds modern protocol contract checks. |
| composer.lock | Locks MCP 1.1.0 and updated dependencies. |
| composer.json | Requires MCP 1.x. |
| app/Domain/Timesheets/Tools/StopTimerTool.php | Migrates timer-stop tool API. |
| app/Domain/Timesheets/Tools/StartTimerTool.php | Migrates timer-start tool API. |
| app/Domain/Timesheets/Tools/LogTimeTool.php | Migrates API; marks logging non-destructive. |
| app/Domain/Timesheets/Tools/GetWeeklyTimesheetsTool.php | Migrates weekly timesheet tool API. |
| app/Domain/Timesheets/Tools/GetUserTimesheetsTool.php | Migrates user timesheet tool API. |
| app/Domain/Timesheets/Tools/GetTimesheetSummaryTool.php | Migrates timesheet summary tool API. |
| app/Domain/Timesheets/Tools/GetProjectTimesheetsTool.php | Migrates project timesheet tool API. |
| app/Domain/Tickets/Tools/GetTaskTool.php | Migrates task retrieval tool API. |
| app/Domain/Tickets/Tools/GetTaskStatusSummaryTool.php | Migrates status summary tool API. |
| app/Domain/Tickets/Tools/GetStatusLabelsTool.php | Migrates status-label tool API. |
| app/Domain/Tickets/Tools/GetMilestoneTool.php | Migrates milestone retrieval tool API. |
| app/Domain/Tickets/Tools/GetAllStatusLabelsByUserIdTool.php | Migrates user status-label tool API. |
| app/Domain/Tickets/Tools/FindTasksTool.php | Migrates API; types project ID items. |
| app/Domain/Tickets/Tools/FindMilestonesTool.php | Migrates milestone search tool API. |
| app/Domain/Tickets/Tools/EditTaskTool.php | Migrates API; adds idempotency hint. |
| app/Domain/Tickets/Tools/EditMilestoneTool.php | Migrates API; adds idempotency hint. |
| app/Domain/Tickets/Tools/CreateSubtasksForTaskTool.php | Migrates API and subtask array schema. |
| app/Domain/Tickets/Tools/BulkScheduleTasksTool.php | Migrates API and schedule array schema. |
| app/Domain/Tickets/Tools/BulkEditTasksTool.php | Migrates API and update array schema. |
| app/Domain/Tickets/Tools/BulkAddTasksTool.php | Migrates API and task array schema. |
| app/Domain/Tickets/Tools/AddTaskTool.php | Migrates API; preserves status union. |
| app/Domain/Tickets/Tools/AddSubtaskTool.php | Migrates API; preserves status union. |
| app/Domain/Tickets/Tools/AddMilestoneTool.php | Migrates API; marks creation non-destructive. |
| app/Domain/Tickets/Tools/AddMilestonesForProjectTool.php | Migrates API and milestone array schema. |
| app/Domain/Search/Tools/SearchTool.php | Migrates API; preserves dynamic descriptions. |
| app/Domain/Projects/Tools/PatchProjectTool.php | Migrates API; adds idempotency hint. |
| app/Domain/Projects/Tools/GetUsersAssignedToProjectTool.php | Migrates project-member tool API. |
| app/Domain/Projects/Tools/GetProjectTool.php | Migrates project retrieval tool API. |
| app/Domain/Projects/Tools/GetProjectsAssignedToUserTool.php | Migrates assigned-project tool API. |
| app/Domain/Projects/Tools/GetFullProjectOverviewTool.php | Migrates project overview tool API. |
| app/Domain/Projects/Tools/GetAllProjectsTool.php | Migrates project listing tool API. |
| app/Domain/Projects/Tools/FindProjectTool.php | Migrates project search tool API. |
| app/Domain/Projects/Tools/EditProjectTool.php | Migrates API; adds idempotency hint. |
| app/Domain/Projects/Tools/AddProjectTool.php | Migrates API; marks creation non-destructive. |
| app/Domain/Goalcanvas/Tools/GetParentKPIsTool.php | Migrates parent KPI tool API. |
| app/Domain/Goalcanvas/Tools/GetGoalTool.php | Migrates goal retrieval tool API. |
| app/Domain/Goalcanvas/Tools/GetGoalsByMilestoneTool.php | Migrates milestone-goal tool API. |
| app/Domain/Goalcanvas/Tools/GetChildGoalsTool.php | Migrates child-goal tool API. |
| app/Domain/Goalcanvas/Tools/GetAllGoalsTool.php | Migrates goal listing tool API. |
| app/Domain/Goalcanvas/Tools/EditGoalTool.php | Migrates API; adds idempotency hint. |
| app/Domain/Goalcanvas/Tools/CreateGoalTool.php | Migrates API; marks creation non-destructive. |
| app/Domain/Goalcanvas/Tools/CreateGoalboardTool.php | Migrates API; marks creation non-destructive. |
| app/Domain/Comments/Tools/PollCommentsTool.php | Migrates comment polling tool API. |
| app/Domain/Comments/Tools/GetCommentsTool.php | Migrates comment retrieval tool API. |
| app/Domain/Comments/Tools/GetAllProjectCommentsTool.php | Migrates project-comment tool API. |
| app/Domain/Comments/Tools/AddProjectStatusUpdateTool.php | Migrates API; marks additions non-destructive. |
| app/Domain/Comments/Tools/AddCommentTool.php | Migrates API; marks additions non-destructive. |
| app/Domain/Calendar/Tools/ScheduleTaskOnCalendarTool.php | Migrates API; adds idempotency hint. |
| app/Domain/Calendar/Tools/ScheduleDayTool.php | Migrates API; types event and ID arrays. |
| app/Domain/Calendar/Tools/GetICalUrlTool.php | Uses default schema and new response API. |
| app/Domain/Calendar/Tools/GetCalendarTool.php | Migrates calendar retrieval tool API. |
| app/Domain/Calendar/Tools/EditCalendarEventTool.php | Migrates API; adds idempotency hint. |
| app/Domain/Calendar/Tools/DeleteCalendarEventTool.php | Migrates API; marks deletion destructive. |
| app/Domain/Calendar/Tools/BulkEditCalendarEventsTool.php | Migrates API and update array schema. |
| app/Domain/Calendar/Tools/BulkAddCalendarEventsTool.php | Migrates API and event array schema. |
| app/Domain/Calendar/Tools/BreakdownTaskTool.php | Migrates API and subtask array schema. |
| app/Domain/Calendar/Tools/AddCalendarEventTool.php | Migrates API; marks creation non-destructive. |
Review effort: Balanced
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 1 of the MCP protocol plan: laravel/mcp v0.1.1 → 1.x (1.1.0), which brings the MCP 2026-07-28 protocol and keeps the
initializehandshake for older clients. Builds on the Laravel 12 upgrade (#3956).Plugins counterpart: Leantime/plugins#132 (server class, endpoint, call tracking, plugin tools). Merge both together, then bump
app/Pluginsto pluginsmainhere.What changes
laravel/mcp ^1.0, which bringsnette/utils4.1.6.illuminate/json-schemaalready ships with Laravel 12.app/Domain/*/Tools) move to the 1.x tool API:schema(JsonSchema): arrayreplaces the chainedToolInputSchema.handle(Request): Responsereplaceshandle(array): ToolResult.$request->all()into the same$argumentsarray, so the bodies are unchanged apart fromToolResult::→Response::.SearchTool, whose description is built at runtime, was ported by hand, and every file was reviewed.The tool contract
I snapshotted
tools/liston master before the port and diffed it against the ported server. Names, descriptions, arguments and required lists are unchanged. The only differences are intended:items: integer for id lists (findTasks.projectIds,scheduleDay.taskIds), object for record lists, and string forsearch.types. Strict clients (OpenAI- and Gemini-style tool schemas) requireitems.title(e.g. "Get iCal URL", "Get Parent KPIs"). Without one, 1.x would derive "Add Comment Tool" from the class name.destructiveHint: false(add*,create*,bulkAdd*,logTime,breakdownTask)deleteEventgetsdestructiveHint: trueidempotentHintreadOnlyHinttools are unchanged.readOnlyHintis presumed destructive, so clients would otherwise ask for confirmation on everyaddTask.getICalUrl's emptypropertiesis a JSON object again; v0.1.1 sent[].Behaviour changes for clients
initializewith 2025-11-25, 2025-06-18 or 2025-03-26 gets that revision back. Anything older, e.g. 2024-11-05, is offered 2025-11-25 instead of echoed, as the spec requires.isError: true.MCP-Protocol-Version,Mcp-Methodand (fortools/call)Mcp-Name, matching the body-32020resultType: "complete".tools/listandserver/discovercarryttlMs: 900000andcacheScope: "private".Tests
McpCest(groupmcp):server/discovertools/listwith the private cache hint and the full expected tool listtools/callwithMcp-Name-32020-32602with HTTP 400.handle(new Request([...])).tests/Httprequests/MCP.httpis rewritten for both client generations.Verification
ConsoleKernelPluginLoadingTest×2,MenuRepositoryTest×2) fail only when plugins are checked out locally. They fail the same way on master in that setup, and pass with plugins absent, as in CI.make phpstanclean; Pint clean.LEAN_PLUGINS, sincerequires: >=3.13drops McpServer on core 3.12.0):initialize, a notification returning 202, legacy and 2026-07-28tools/list(59 tools, nonextCursor, private 15-minute hint).server/discover,tools/callwithMcp-Name, a header mismatch (400 /-32020), an unknown tool (400 /-32602).x-api-keyand Bearer tokens work.addTask→editTask→getTicket→search, andgetCalendar.php bin/leantime mcp:start leantimeanswersinitializeandtools/list.addComment→getComments.mcpacceptance group. It isn't in CI (OSS checkout has no plugins), and locally it needs the 3.13 version bump: pluginsmainalready requires>=3.13, so/mcpreturns 404 on core 3.12.0.After merge
app/Pluginsto pluginsmain(after the plugins PR is squash-merged).major).🤖 Generated with Claude Code