Skip to content

Commit 314a219

Browse files
committed
Merge branch 'master' of github.com:opgginc/laravel-mcp-server
2 parents 398a406 + 6bed481 commit 314a219

33 files changed

+2009
-3
lines changed

README.md

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,8 +485,159 @@ public function annotations(): array
485485
'requires_permission' => 'analytics.read',
486486
];
487487
}
488+
489+
### Working with Resources
490+
491+
Resources expose data from your server that can be read by MCP clients. They are
492+
**application-controlled**, meaning the client decides when and how to use them.
493+
Create concrete resources or URI templates in `app/MCP/Resources` and
494+
`app/MCP/ResourceTemplates` using the Artisan helpers:
495+
496+
```bash
497+
php artisan make:mcp-resource SystemLogResource
498+
php artisan make:mcp-resource-template UserLogTemplate
499+
```
500+
501+
Register the generated classes in `config/mcp-server.php` under the `resources`
502+
and `resource_templates` arrays. Each resource class extends the base
503+
`Resource` class and implements a `read()` method that returns either `text` or
504+
`blob` content. Templates extend `ResourceTemplate` and describe dynamic URI
505+
patterns clients can use. A resource is identified by a URI such as
506+
`file:///logs/app.log` and may optionally define metadata like `mimeType` or
507+
`size`.
508+
509+
List available resources using the `resources/list` endpoint and read their
510+
contents with `resources/read`. The `resources/list` endpoint returns both
511+
concrete resources and resource templates in a single response:
512+
513+
```json
514+
{
515+
"resources": [...], // Array of concrete resources
516+
"resourceTemplates": [...] // Array of URI templates
517+
}
518+
```
519+
520+
Resource templates allow clients to construct dynamic resource identifiers
521+
using URI templates (RFC 6570). You can also list templates separately using
522+
the `resources/templates/list` endpoint:
523+
524+
```bash
525+
# List only resource templates
526+
curl -X POST https://your-server.com/mcp \
527+
-H "Content-Type: application/json" \
528+
-d '{"jsonrpc":"2.0","id":1,"method":"resources/templates/list"}'
529+
```
530+
531+
When running your Laravel MCP server remotely, the HTTP transport works with
532+
standard JSON-RPC requests. Here is a simple example using `curl` to list and
533+
read resources:
534+
535+
```bash
536+
# List resources
537+
curl -X POST https://your-server.com/mcp \
538+
-H "Content-Type: application/json" \
539+
-d '{"jsonrpc":"2.0","id":1,"method":"resources/list"}'
540+
541+
# Read a specific resource
542+
curl -X POST https://your-server.com/mcp \
543+
-H "Content-Type: application/json" \
544+
-d '{"jsonrpc":"2.0","id":2,"method":"resources/read","params":{"uri":"file:///logs/app.log"}}'
545+
```
546+
547+
The server responds with JSON messages streamed over the HTTP connection, so
548+
`curl --no-buffer` can be used if you want to see incremental output.
549+
550+
### Working with Prompts
551+
552+
Prompts provide reusable text snippets with argument support that your tools or users can request.
553+
Create prompt classes in `app/MCP/Prompts` using:
554+
555+
```bash
556+
php artisan make:mcp-prompt WelcomePrompt
557+
```
558+
559+
Register them in `config/mcp-server.php` under `prompts`. Each prompt class
560+
extends the `Prompt` base class and defines:
561+
- `name`: Unique identifier (e.g., "welcome-user")
562+
- `description`: Optional human-readable description
563+
- `arguments`: Array of argument definitions with name, description, and required fields
564+
- `text`: The prompt template with placeholders like `{username}`
565+
566+
List prompts via the `prompts/list` endpoint and fetch them using
567+
`prompts/get` with arguments:
568+
569+
```bash
570+
# Fetch a welcome prompt with arguments
571+
curl -X POST https://your-server.com/mcp \
572+
-H "Content-Type: application/json" \
573+
-d '{"jsonrpc":"2.0","id":1,"method":"prompts/get","params":{"name":"welcome-user","arguments":{"username":"Alice","role":"admin"}}}'
574+
```
575+
576+
### MCP Prompts
577+
578+
When crafting prompts that reference your tools or resources, consult the [official prompt guidelines](https://modelcontextprotocol.io/docs/concepts/prompts). Prompts are reusable templates that can accept arguments, include resource context and even describe multi-step workflows.
579+
580+
**Prompt structure**
581+
582+
```json
583+
{
584+
"name": "string",
585+
"description": "string",
586+
"arguments": [
587+
{
588+
"name": "string",
589+
"description": "string",
590+
"required": true
591+
}
592+
]
593+
}
594+
```
595+
596+
Clients discover prompts via `prompts/list` and request specific ones with `prompts/get`:
597+
598+
```json
599+
{
600+
"method": "prompts/get",
601+
"params": {
602+
"name": "analyze-code",
603+
"arguments": {
604+
"language": "php"
605+
}
606+
}
607+
}
608+
```
609+
610+
**Example Prompt Class**
611+
612+
```php
613+
use OPGG\LaravelMcpServer\Services\PromptService\Prompt;
614+
615+
class WelcomePrompt extends Prompt
616+
{
617+
public string $name = 'welcome-user';
618+
619+
public ?string $description = 'A customizable welcome message for users';
620+
621+
public array $arguments = [
622+
[
623+
'name' => 'username',
624+
'description' => 'The name of the user to welcome',
625+
'required' => true,
626+
],
627+
[
628+
'name' => 'role',
629+
'description' => 'The role of the user (optional)',
630+
'required' => false,
631+
],
632+
];
633+
634+
public string $text = 'Welcome, {username}! You are logged in as {role}.';
635+
}
488636
```
489637

638+
Prompts can embed resources and return sequences of messages to guide an LLM. See the official documentation for advanced examples and best practices.
639+
640+
490641
### Testing MCP Tools
491642

492643
The package includes a special command for testing your MCP tools without needing a real MCP client:

config/mcp-server.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,35 @@
144144
// Register your custom tools here
145145
// App\MCP\Tools\YourCustomTool::class,
146146
],
147+
148+
/*
149+
|--------------------------------------------------------------------------
150+
| MCP Resources Registry
151+
|--------------------------------------------------------------------------
152+
|
153+
| Register resources and templates to expose to clients.
154+
|
155+
*/
156+
'resources' => [
157+
\OPGG\LaravelMcpServer\Services\ResourceService\Examples\LogFileResource::class,
158+
// App\MCP\Resources\YourResource::class,
159+
],
160+
161+
'resource_templates' => [
162+
\OPGG\LaravelMcpServer\Services\ResourceService\Examples\LogFileTemplate::class,
163+
// App\MCP\ResourceTemplates\YourTemplate::class,
164+
],
165+
166+
/*
167+
|--------------------------------------------------------------------------
168+
| MCP Prompts Registry
169+
|--------------------------------------------------------------------------
170+
|
171+
| Register prompts available to clients.
172+
|
173+
*/
174+
'prompts' => [
175+
\OPGG\LaravelMcpServer\Services\PromptService\Examples\WelcomePrompt::class,
176+
// App\MCP\Prompts\YourPrompt::class,
177+
],
147178
];

scripts/test-setup.sh

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,8 @@ curl -X POST "$HTTP_ENDPOINT" \
166166
"params": {
167167
"protocolVersion": "2024-11-05",
168168
"capabilities": {
169-
"tools": {}
169+
"tools": {},
170+
"resources": {}
170171
},
171172
"clientInfo": {
172173
"name": "test-client",
@@ -224,6 +225,74 @@ curl -X POST "$HTTP_ENDPOINT" \
224225
}
225226
}' | jq '.' 2>/dev/null || echo "Response received (install jq for pretty printing)"
226227
228+
echo ""
229+
echo ""
230+
# Test 5: List resources
231+
echo "📚 Test 5: List available resources"
232+
curl -X POST "$HTTP_ENDPOINT" \
233+
-H "Content-Type: application/json" \
234+
-d '{
235+
"jsonrpc": "2.0",
236+
"id": 5,
237+
"method": "resources/list"
238+
}' | jq '.' 2>/dev/null || echo "Response received (install jq for pretty printing)"
239+
240+
echo ""
241+
echo ""
242+
# Test 6: Read example resource
243+
echo "📖 Test 6: Read example log resource"
244+
curl -X POST "$HTTP_ENDPOINT" \
245+
-H "Content-Type: application/json" \
246+
-d '{
247+
"jsonrpc": "2.0",
248+
"id": 6,
249+
"method": "resources/read",
250+
"params": { "uri": "file:///logs/example.log" }
251+
}' | jq '.' 2>/dev/null || echo "Response received (install jq for pretty printing)"
252+
253+
echo ""
254+
echo ""
255+
# Test 6.5: List resource templates
256+
echo "📑 Test 6.5: List resource templates"
257+
curl -X POST "$HTTP_ENDPOINT" \
258+
-H "Content-Type: application/json" \
259+
-d '{
260+
"jsonrpc": "2.0",
261+
"id": 65,
262+
"method": "resources/templates/list"
263+
}' | jq '.' 2>/dev/null || echo "Response received (install jq for pretty printing)"
264+
265+
echo ""
266+
echo ""
267+
# Test 7: List prompts
268+
echo "📝 Test 7: List available prompts"
269+
curl -X POST "$HTTP_ENDPOINT" \
270+
-H "Content-Type: application/json" \
271+
-d '{
272+
"jsonrpc": "2.0",
273+
"id": 7,
274+
"method": "prompts/list"
275+
}' | jq '.' 2>/dev/null || echo "Response received (install jq for pretty printing)"
276+
277+
echo ""
278+
echo ""
279+
# Test 8: Render prompt
280+
echo "🗒 Test 8: Get welcome prompt"
281+
curl -X POST "$HTTP_ENDPOINT" \
282+
-H "Content-Type: application/json" \
283+
-d '{
284+
"jsonrpc": "2.0",
285+
"id": 8,
286+
"method": "prompts/get",
287+
"params": {
288+
"name": "welcome-user",
289+
"arguments": {
290+
"username": "Test User",
291+
"role": "admin"
292+
}
293+
}
294+
}' | jq '.' 2>/dev/null || echo "Response received (install jq for pretty printing)"
295+
227296
echo ""
228297
echo ""
229298
echo "✅ All HTTP tests completed!"
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
namespace OPGG\LaravelMcpServer\Console\Commands;
4+
5+
use Illuminate\Console\Command;
6+
use Illuminate\Filesystem\Filesystem;
7+
use Illuminate\Support\Str;
8+
9+
class MakeMcpPromptCommand extends Command
10+
{
11+
protected $signature = 'make:mcp-prompt {name : The name of the prompt}';
12+
13+
protected $description = 'Create a new MCP prompt class';
14+
15+
public function __construct(private Filesystem $files)
16+
{
17+
parent::__construct();
18+
}
19+
20+
public function handle(): int
21+
{
22+
$className = $this->getClassName();
23+
$path = $this->getPath($className);
24+
25+
if ($this->files->exists($path)) {
26+
$this->error("❌ MCP prompt {$className} already exists!");
27+
28+
return 1;
29+
}
30+
31+
$this->makeDirectory($path);
32+
$stub = $this->files->get(__DIR__.'/../../stubs/prompt.stub');
33+
$stub = str_replace(['{{ className }}', '{{ namespace }}'], [$className, 'App\\MCP\\Prompts'], $stub);
34+
$this->files->put($path, $stub);
35+
$this->info("✅ Created: {$path}");
36+
37+
return 0;
38+
}
39+
40+
protected function getClassName(): string
41+
{
42+
$name = preg_replace('/[\s\-_]+/', ' ', trim($this->argument('name')));
43+
$name = Str::studly($name);
44+
if (! Str::endsWith($name, 'Prompt')) {
45+
$name .= 'Prompt';
46+
}
47+
48+
return $name;
49+
}
50+
51+
protected function getPath(string $className): string
52+
{
53+
return app_path("MCP/Prompts/{$className}.php");
54+
}
55+
56+
protected function makeDirectory(string $path): void
57+
{
58+
$dir = dirname($path);
59+
if (! $this->files->isDirectory($dir)) {
60+
$this->files->makeDirectory($dir, 0755, true, true);
61+
}
62+
}
63+
}

0 commit comments

Comments
 (0)