Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 151 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -444,8 +444,159 @@ public function annotations(): array
'requires_permission' => 'analytics.read',
];
}

### Working with Resources

Resources expose data from your server that can be read by MCP clients. They are
**application-controlled**, meaning the client decides when and how to use them.
Create concrete resources or URI templates in `app/MCP/Resources` and
`app/MCP/ResourceTemplates` using the Artisan helpers:

```bash
php artisan make:mcp-resource SystemLogResource
php artisan make:mcp-resource-template UserLogTemplate
```

Register the generated classes in `config/mcp-server.php` under the `resources`
and `resource_templates` arrays. Each resource class extends the base
`Resource` class and implements a `read()` method that returns either `text` or
`blob` content. Templates extend `ResourceTemplate` and describe dynamic URI
patterns clients can use. A resource is identified by a URI such as
`file:///logs/app.log` and may optionally define metadata like `mimeType` or
`size`.

List available resources using the `resources/list` endpoint and read their
contents with `resources/read`. The `resources/list` endpoint returns both
concrete resources and resource templates in a single response:

```json
{
"resources": [...], // Array of concrete resources
"resourceTemplates": [...] // Array of URI templates
}
```

Resource templates allow clients to construct dynamic resource identifiers
using URI templates (RFC 6570). You can also list templates separately using
the `resources/templates/list` endpoint:

```bash
# List only resource templates
curl -X POST https://your-server.com/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"resources/templates/list"}'
```

When running your Laravel MCP server remotely, the HTTP transport works with
standard JSON-RPC requests. Here is a simple example using `curl` to list and
read resources:

```bash
# List resources
curl -X POST https://your-server.com/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"resources/list"}'

# Read a specific resource
curl -X POST https://your-server.com/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"resources/read","params":{"uri":"file:///logs/app.log"}}'
```

The server responds with JSON messages streamed over the HTTP connection, so
`curl --no-buffer` can be used if you want to see incremental output.

### Working with Prompts

Prompts provide reusable text snippets with argument support that your tools or users can request.
Create prompt classes in `app/MCP/Prompts` using:

```bash
php artisan make:mcp-prompt WelcomePrompt
```

Register them in `config/mcp-server.php` under `prompts`. Each prompt class
extends the `Prompt` base class and defines:
- `name`: Unique identifier (e.g., "welcome-user")
- `description`: Optional human-readable description
- `arguments`: Array of argument definitions with name, description, and required fields
- `text`: The prompt template with placeholders like `{username}`

List prompts via the `prompts/list` endpoint and fetch them using
`prompts/get` with arguments:

```bash
# Fetch a welcome prompt with arguments
curl -X POST https://your-server.com/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"prompts/get","params":{"name":"welcome-user","arguments":{"username":"Alice","role":"admin"}}}'
```

### MCP Prompts

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.

**Prompt structure**

```json
{
"name": "string",
"description": "string",
"arguments": [
{
"name": "string",
"description": "string",
"required": true
}
]
}
```

Clients discover prompts via `prompts/list` and request specific ones with `prompts/get`:

```json
{
"method": "prompts/get",
"params": {
"name": "analyze-code",
"arguments": {
"language": "php"
}
}
}
```

**Example Prompt Class**

```php
use OPGG\LaravelMcpServer\Services\PromptService\Prompt;

class WelcomePrompt extends Prompt
{
public string $name = 'welcome-user';

public ?string $description = 'A customizable welcome message for users';

public array $arguments = [
[
'name' => 'username',
'description' => 'The name of the user to welcome',
'required' => true,
],
[
'name' => 'role',
'description' => 'The role of the user (optional)',
'required' => false,
],
];

public string $text = 'Welcome, {username}! You are logged in as {role}.';
}
```

Prompts can embed resources and return sequences of messages to guide an LLM. See the official documentation for advanced examples and best practices.


### Testing MCP Tools

The package includes a special command for testing your MCP tools without needing a real MCP client:
Expand Down
31 changes: 31 additions & 0 deletions config/mcp-server.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,35 @@
// Register your custom tools here
// App\MCP\Tools\YourCustomTool::class,
],

/*
|--------------------------------------------------------------------------
| MCP Resources Registry
|--------------------------------------------------------------------------
|
| Register resources and templates to expose to clients.
|
*/
'resources' => [
\OPGG\LaravelMcpServer\Services\ResourceService\Examples\LogFileResource::class,
// App\MCP\Resources\YourResource::class,
],

'resource_templates' => [
\OPGG\LaravelMcpServer\Services\ResourceService\Examples\LogFileTemplate::class,
// App\MCP\ResourceTemplates\YourTemplate::class,
],

/*
|--------------------------------------------------------------------------
| MCP Prompts Registry
|--------------------------------------------------------------------------
|
| Register prompts available to clients.
|
*/
'prompts' => [
\OPGG\LaravelMcpServer\Services\PromptService\Examples\WelcomePrompt::class,
// App\MCP\Prompts\YourPrompt::class,
],
];
71 changes: 70 additions & 1 deletion scripts/test-setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,8 @@ curl -X POST "$HTTP_ENDPOINT" \
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {
"tools": {}
"tools": {},
"resources": {}
},
"clientInfo": {
"name": "test-client",
Expand Down Expand Up @@ -224,6 +225,74 @@ curl -X POST "$HTTP_ENDPOINT" \
}
}' | jq '.' 2>/dev/null || echo "Response received (install jq for pretty printing)"

echo ""
echo ""
# Test 5: List resources
echo "📚 Test 5: List available resources"
curl -X POST "$HTTP_ENDPOINT" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 5,
"method": "resources/list"
}' | jq '.' 2>/dev/null || echo "Response received (install jq for pretty printing)"

echo ""
echo ""
# Test 6: Read example resource
echo "📖 Test 6: Read example log resource"
curl -X POST "$HTTP_ENDPOINT" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 6,
"method": "resources/read",
"params": { "uri": "file:///logs/example.log" }
}' | jq '.' 2>/dev/null || echo "Response received (install jq for pretty printing)"

echo ""
echo ""
# Test 6.5: List resource templates
echo "📑 Test 6.5: List resource templates"
curl -X POST "$HTTP_ENDPOINT" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 65,
"method": "resources/templates/list"
}' | jq '.' 2>/dev/null || echo "Response received (install jq for pretty printing)"

echo ""
echo ""
# Test 7: List prompts
echo "📝 Test 7: List available prompts"
curl -X POST "$HTTP_ENDPOINT" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 7,
"method": "prompts/list"
}' | jq '.' 2>/dev/null || echo "Response received (install jq for pretty printing)"

echo ""
echo ""
# Test 8: Render prompt
echo "🗒 Test 8: Get welcome prompt"
curl -X POST "$HTTP_ENDPOINT" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 8,
"method": "prompts/get",
"params": {
"name": "welcome-user",
"arguments": {
"username": "Test User",
"role": "admin"
}
}
}' | jq '.' 2>/dev/null || echo "Response received (install jq for pretty printing)"

echo ""
echo ""
echo "✅ All HTTP tests completed!"
Expand Down
63 changes: 63 additions & 0 deletions src/Console/Commands/MakeMcpPromptCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

namespace OPGG\LaravelMcpServer\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Str;

class MakeMcpPromptCommand extends Command
{
protected $signature = 'make:mcp-prompt {name : The name of the prompt}';

protected $description = 'Create a new MCP prompt class';

public function __construct(private Filesystem $files)
{
parent::__construct();
}

public function handle(): int
{
$className = $this->getClassName();
$path = $this->getPath($className);

if ($this->files->exists($path)) {
$this->error("❌ MCP prompt {$className} already exists!");

return 1;
}

$this->makeDirectory($path);
$stub = $this->files->get(__DIR__.'/../../stubs/prompt.stub');
$stub = str_replace(['{{ className }}', '{{ namespace }}'], [$className, 'App\\MCP\\Prompts'], $stub);
$this->files->put($path, $stub);
$this->info("✅ Created: {$path}");

return 0;
}

protected function getClassName(): string
{
$name = preg_replace('/[\s\-_]+/', ' ', trim($this->argument('name')));
$name = Str::studly($name);
if (! Str::endsWith($name, 'Prompt')) {
$name .= 'Prompt';
}

return $name;
}

protected function getPath(string $className): string
{
return app_path("MCP/Prompts/{$className}.php");
}

protected function makeDirectory(string $path): void
{
$dir = dirname($path);
if (! $this->files->isDirectory($dir)) {
$this->files->makeDirectory($dir, 0755, true, true);
}
}
}
Loading