Skip to content

Commit d7f0e35

Browse files
authored
10.2.0 (#669)
* feat: upgrade to laravel mcp 0.2 * Fix styling * fix: wip * fix: wip * fix: laravel12 * fix: wip * fix: wip * fix: wip * fix: php 8.3 * fix: lara11 * fix: wip * fix: test --------- Co-authored-by: binaryk <binaryk@users.noreply.github.com>
1 parent 13164c9 commit d7f0e35

31 files changed

+565
-488
lines changed

.github/workflows/psalm.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
- name: Setup PHP
1414
uses: shivammathur/setup-php@v2
1515
with:
16-
php-version: '8.2'
16+
php-version: '8.3'
1717
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick
1818
coverage: none
1919

.github/workflows/tests.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ on: [push, pull_request]
55
jobs:
66
test:
77
runs-on: ${{ matrix.os }}
8+
timeout-minutes: 30
89
strategy:
9-
fail-fast: true
10+
fail-fast: false
1011
matrix:
1112
os: [ubuntu-latest, windows-latest]
12-
php: [8.2, 8.3, 8.4]
13+
php: [8.3, 8.4]
1314
laravel: [11.*, 12.*]
14-
stability: [prefer-lowest, prefer-stable]
15+
stability: [prefer-stable]
1516
include:
1617
- laravel: 11.*
1718
testbench: 9.*
@@ -48,4 +49,4 @@ jobs:
4849
run: sleep 5
4950

5051
- name: Execute tests
51-
run: ./vendor/bin/testbench package:test --no-coverage
52+
run: ./vendor/bin/testbench package:test --parallel --no-coverage

composer.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@
1818
}
1919
],
2020
"require": {
21-
"php": "^8.2|^8.3",
21+
"php": "^8.3",
2222
"illuminate/contracts": "^11.0|^12.0",
23-
"laravel/pint": "^1.0",
24-
"laravel/mcp": "^0.1.0",
23+
"laravel/framework": "^11.0|^12.0",
24+
"laravel/mcp": "^0.2.0",
25+
"laravel/pint": "^1.25.1",
2526
"spatie/laravel-data": "^4.4",
2627
"spatie/laravel-package-tools": "^1.12",
2728
"spatie/once": "^3.0"

src/Commands/stubs/mcp-tool.stub

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
namespace DummyNamespace;
44

5-
use Generator;
5+
use Illuminate\JsonSchema\JsonSchema;
6+
use Laravel\Mcp\Request;
7+
use Laravel\Mcp\Response;
68
use Laravel\Mcp\Server\Tool;
7-
use Laravel\Mcp\Server\Tools\ToolInputSchema;
8-
use Laravel\Mcp\Server\Tools\ToolResult;
99

1010
class DummyClass extends Tool
1111
{
@@ -19,23 +19,25 @@ class DummyClass extends Tool
1919
return 'Description of what this tool does';
2020
}
2121

22-
public function schema(ToolInputSchema $schema): ToolInputSchema
22+
public function schema(JsonSchema $schema): array
2323
{
2424
// Define your tool's input parameters here
2525
// Example:
26-
// $schema->string('input')
27-
// ->description('The input parameter')
28-
// ->required();
26+
// return [
27+
// 'input' => $schema->string()
28+
// ->description('The input parameter')
29+
// ->required(),
30+
// ];
2931

30-
return $schema;
32+
return [];
3133
}
3234

33-
public function handle(array $arguments): ToolResult|Generator
35+
public function handle(Request $request): Response
3436
{
3537
// Implement your tool's logic here
36-
// Access input parameters via $arguments array
37-
38-
return ToolResult::json([
38+
// Access input parameters via $request->input('parameter_name')
39+
40+
return Response::json([
3941
'success' => true,
4042
'message' => 'Tool executed successfully',
4143
]);

src/Fields/Field.php

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -774,13 +774,6 @@ public function image(): Image
774774
*/
775775
public function guessFieldType(): string
776776
{
777-
// Check field class type first
778-
$fieldType = $this->guessTypeFromFieldClass();
779-
if ($fieldType) {
780-
return $fieldType;
781-
}
782-
783-
// Check validation rules
784777
$ruleType = $this->guessTypeFromValidationRules();
785778
if ($ruleType) {
786779
return $ruleType;
@@ -796,28 +789,6 @@ public function guessFieldType(): string
796789
return 'string';
797790
}
798791

799-
/**
800-
* Guess type from field class name.
801-
*/
802-
protected function guessTypeFromFieldClass(): ?string
803-
{
804-
$className = class_basename(static::class);
805-
806-
return match ($className) {
807-
'Boolean', 'BooleanField' => 'boolean',
808-
'Number', 'Integer', 'Decimal', 'Float' => 'number',
809-
'Email' => 'string',
810-
'Password' => 'string',
811-
'Textarea' => 'string',
812-
'Text', 'TextField' => 'string',
813-
'Date', 'DateTime' => 'string',
814-
'File', 'Image' => 'string',
815-
'Select', 'MultiSelect' => 'array',
816-
'BelongsTo', 'HasOne', 'HasMany', 'BelongsToMany' => 'object',
817-
default => null
818-
};
819-
}
820-
821792
/**
822793
* Guess type from validation rules.
823794
*/
@@ -842,7 +813,7 @@ protected function guessTypeFromValidationRules(): ?string
842813
return 'boolean';
843814
}
844815

845-
if ($this->hasAnyRule($ruleStrings, ['email', 'url', 'ip', 'uuid', 'string', 'regex', 'in', 'array'])) {
816+
if ($this->hasAnyRule($ruleStrings, ['email', 'url', 'ip', 'uuid', 'string', 'regex', 'array'])) {
846817
return 'string';
847818
}
848819

src/MCP/Concerns/FieldMcpSchemaDetection.php

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,39 @@
44

55
use Binaryk\LaravelRestify\Fields\File;
66
use Binaryk\LaravelRestify\Repositories\Repository;
7-
use Laravel\Mcp\Server\Tools\ToolInputSchema;
7+
use Illuminate\JsonSchema\JsonSchema;
8+
use Illuminate\JsonSchema\Types\Type;
89

910
trait FieldMcpSchemaDetection
1011
{
1112
/**
12-
* Resolve the tool schema for this field to be used in MCP tools.
13+
* Resolve the JSON schema for this field to be used in MCP tools.
1314
*/
14-
public function resolveToolSchema(ToolInputSchema $schema, Repository $repository): self
15+
public function resolveJsonSchema(JsonSchema $schema, Repository $repository): ?Type
1516
{
1617
// Check if there's a custom callback defined
1718
if (is_callable($this->toolInputSchemaCallback)) {
18-
call_user_func($this->toolInputSchemaCallback, $schema, $repository, $this);
19-
20-
return $this;
19+
$result = call_user_func($this->toolInputSchemaCallback, $schema, $repository, $this);
20+
if ($result instanceof Type) {
21+
return $result;
22+
}
2123
}
2224

23-
// Skip computed fields for default implementation
24-
if ($this->computed()) {
25-
return $this;
25+
// For MCP tools, we include computed fields that have resolve callbacks
26+
// since they represent storable fields in MCP contexts
27+
// Only skip truly computed fields without resolve callbacks
28+
if ($this->computed() && ! $this->resolveCallback) {
29+
return null;
2630
}
2731

28-
$attribute = $this->label ?? $this->attribute;
2932
$fieldType = $this->guessFieldType();
3033

31-
// Add the field to schema based on its type
34+
// Create the field schema based on its type
3235
$schemaField = match ($fieldType) {
33-
'boolean' => $schema->boolean($attribute),
34-
'number' => $schema->number($attribute),
35-
'array' => $schema->string($attribute), // Arrays are typically sent as JSON strings
36-
default => $schema->string($attribute)
36+
'boolean' => $schema->boolean(),
37+
'number' => $schema->number(),
38+
'array' => $schema->string(), // Arrays are typically sent as JSON strings
39+
default => $schema->string()
3740
};
3841

3942
// Add description
@@ -45,7 +48,7 @@ public function resolveToolSchema(ToolInputSchema $schema, Repository $repositor
4548
$schemaField->required();
4649
}
4750

48-
return $this;
51+
return $schemaField;
4952
}
5053

5154
/**

src/MCP/Concerns/McpActionTool.php

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,15 @@
44

55
use Binaryk\LaravelRestify\Actions\Action;
66
use Binaryk\LaravelRestify\MCP\Requests\McpActionRequest;
7-
use Laravel\Mcp\Server\Tools\ToolInputSchema;
7+
use Illuminate\JsonSchema\JsonSchema;
88

99
/**
1010
* @mixin \Binaryk\LaravelRestify\Repositories\Repository
1111
*/
1212
trait McpActionTool
1313
{
14-
public function actionTool(Action $action, array $arguments, McpActionRequest $actionRequest): array
14+
public function actionTool(Action $action, McpActionRequest $actionRequest): array
1515
{
16-
$actionRequest->merge($arguments);
17-
18-
$this->sanitizeToolRequest($actionRequest, $arguments);
19-
2016
if ($id = $actionRequest->input('id')) {
2117
if (! $action->authorizedToRun($actionRequest, $actionRequest->findModelOrFail($id))) {
2218
return [
@@ -26,17 +22,6 @@ public function actionTool(Action $action, array $arguments, McpActionRequest $a
2622
}
2723
}
2824

29-
// Set up the action request context based on action type
30-
if (! $action->isStandalone()) {
31-
if (isset($arguments['id'])) {
32-
// Single model action (show context)
33-
$actionRequest->merge(['id' => $arguments['id']]);
34-
} elseif (isset($arguments['repositories'])) {
35-
// Multiple models action (index context)
36-
$actionRequest->merge(['repositories' => $arguments['repositories']]);
37-
}
38-
}
39-
4025
// Check authorization
4126
if (! $action->authorizedToSee($actionRequest)) {
4227
return [
@@ -61,9 +46,10 @@ public function actionTool(Action $action, array $arguments, McpActionRequest $a
6146
}
6247
}
6348

64-
public static function actionToolSchema(Action $action, ToolInputSchema $schema, McpActionRequest $mcpRequest): void
49+
public static function actionToolSchema(Action $action, JsonSchema $schema, McpActionRequest $mcpRequest): array
6550
{
6651
$modelName = class_basename(static::guessModelClassName());
52+
$properties = [];
6753

6854
// Add action-specific validation rules
6955
$actionRules = $action->rules();
@@ -73,26 +59,27 @@ public static function actionToolSchema(Action $action, ToolInputSchema $schema,
7359

7460
// Determine field type based on rules
7561
if (in_array('boolean', $rulesArray)) {
76-
$fieldSchema = $schema->boolean($field);
62+
$fieldSchema = $schema->boolean();
7763
} elseif (in_array('integer', $rulesArray) || in_array('numeric', $rulesArray)) {
78-
$fieldSchema = $schema->number($field);
64+
$fieldSchema = $schema->number();
7965
} elseif (in_array('array', $rulesArray)) {
80-
$fieldSchema = $schema->string($field);
66+
$fieldSchema = $schema->string();
8167
} else {
82-
$fieldSchema = $schema->string($field);
68+
$fieldSchema = $schema->string();
8369
}
8470

8571
if ($isRequired) {
8672
$fieldSchema->required();
8773
}
8874

8975
$fieldSchema->description("Action parameter: {$field}");
76+
$properties[$field] = $fieldSchema;
9077
}
9178

9279
// Add context-specific fields based on action type
9380
if ($action->isStandalone()) {
9481
// Standalone actions don't need ID or repositories
95-
$schema->string('include')
82+
$properties['include'] = $schema->string()
9683
->description('Comma-separated list of relationships to include in response');
9784
} else {
9885
// Check if it's primarily a show action or index action
@@ -101,21 +88,23 @@ public static function actionToolSchema(Action $action, ToolInputSchema $schema,
10188

10289
if ($shownOnShow && ! $shownOnIndex) {
10390
// Show action - requires single ID
104-
$schema->string('id')
91+
$properties['id'] = $schema->string()
10592
->description("The ID of the {$modelName} to perform the action on")
10693
->required();
10794

108-
$schema->string('include')
95+
$properties['include'] = $schema->string()
10996
->description('Comma-separated list of relationships to include');
11097
} else {
11198
// Index action - requires repositories array
112-
$schema->string('repositories')
99+
$properties['repositories'] = $schema->string()
113100
->description("Array of {$modelName} IDs to perform the action on. e.g. repositories=[1,2,3]")
114101
->required();
115102

116-
$schema->string('include')
103+
$properties['include'] = $schema->string()
117104
->description('Comma-separated list of relationships to include');
118105
}
119106
}
107+
108+
return $properties;
120109
}
121110
}

src/MCP/Concerns/McpDestroyTool.php

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,31 @@
33
namespace Binaryk\LaravelRestify\MCP\Concerns;
44

55
use Binaryk\LaravelRestify\MCP\Requests\McpDestroyRequest;
6-
use Laravel\Mcp\Server\Tools\ToolInputSchema;
6+
use Illuminate\JsonSchema\JsonSchema;
77

88
/**
99
* @mixin \Binaryk\LaravelRestify\Repositories\Repository
1010
*/
1111
trait McpDestroyTool
1212
{
13-
public function deleteTool(array $arguments, McpDestroyRequest $request): array
13+
public function deleteTool(McpDestroyRequest $request): array
1414
{
15-
$id = $arguments['id'] ?? null;
16-
unset($arguments['id']);
17-
$request->merge($arguments);
18-
$this->sanitizeToolRequest($request, $arguments);
15+
$id = $request->input('id');
1916

2017
$model = static::query($request)->findOrFail($id);
2118

2219
return static::resolveWith($model)->destroy($request, $id);
2320
}
2421

25-
public static function destroyToolSchema(ToolInputSchema $schema): void
22+
public static function destroyToolSchema(JsonSchema $schema): array
2623
{
2724
$key = static::uriKey();
2825
$modelName = class_basename(static::guessModelClassName());
2926

30-
$schema->string('id')
31-
->description("The ID of the $modelName to delete")
32-
->required();
27+
return [
28+
'id' => $schema->string()
29+
->description("The ID of the $modelName to delete")
30+
->required(),
31+
];
3332
}
3433
}

0 commit comments

Comments
 (0)