feat(mcp): add server resource listing
This commit is contained in:
parent
4ab909f391
commit
6b78f0c137
5 changed files with 130 additions and 1 deletions
|
|
@ -226,7 +226,7 @@ The `McpApiController` exposes five endpoints behind `mcp.auth` middleware:
|
|||
| `GET` | `/servers/{id}.json` | Server details with tool definitions |
|
||||
| `GET` | `/servers/{id}/tools` | List tools for a server |
|
||||
| `POST` | `/tools/call` | Execute a tool |
|
||||
| `GET` | `/resources/{uri}` | Read a resource (not yet implemented -- returns 501) |
|
||||
| `GET` | `/resources/{uri}` | Read a resource |
|
||||
|
||||
`POST /tools/call` accepts:
|
||||
|
||||
|
|
|
|||
|
|
@ -113,6 +113,8 @@ class Boot extends ServiceProvider
|
|||
->where('id', '[a-z0-9-]+');
|
||||
Route::get('servers/{id}/tools', [Controllers\McpApiController::class, 'tools'])->name('servers.tools')
|
||||
->where('id', '[a-z0-9-]+');
|
||||
Route::get('servers/{id}/resources', [Controllers\McpApiController::class, 'resources'])->name('servers.resources')
|
||||
->where('id', '[a-z0-9-]+');
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,6 +82,26 @@ class McpApiController extends Controller
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* List resources for a specific server.
|
||||
*
|
||||
* GET /api/v1/mcp/servers/{id}/resources
|
||||
*/
|
||||
public function resources(Request $request, string $id): JsonResponse
|
||||
{
|
||||
$server = $this->loadServerFull($id);
|
||||
|
||||
if (! $server) {
|
||||
return response()->json(['error' => 'Server not found'], 404);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'server' => $id,
|
||||
'resources' => array_values($server['resources'] ?? []),
|
||||
'count' => count($server['resources'] ?? []),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a tool on an MCP server.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -197,6 +197,35 @@ class OpenApiGenerator
|
|||
],
|
||||
];
|
||||
|
||||
$paths['/servers/{serverId}/resources'] = [
|
||||
'get' => [
|
||||
'tags' => ['Discovery'],
|
||||
'summary' => 'List resources for a server',
|
||||
'operationId' => 'listServerResources',
|
||||
'security' => [['bearerAuth' => []], ['apiKeyAuth' => []]],
|
||||
'parameters' => [
|
||||
[
|
||||
'name' => 'serverId',
|
||||
'in' => 'path',
|
||||
'required' => true,
|
||||
'schema' => ['type' => 'string'],
|
||||
],
|
||||
],
|
||||
'responses' => [
|
||||
'200' => [
|
||||
'description' => 'List of resources',
|
||||
'content' => [
|
||||
'application/json' => [
|
||||
'schema' => [
|
||||
'$ref' => '#/components/schemas/ResourceList',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Execution endpoint
|
||||
$paths['/tools/call'] = [
|
||||
'post' => [
|
||||
|
|
@ -402,6 +431,17 @@ class OpenApiGenerator
|
|||
],
|
||||
],
|
||||
],
|
||||
'ResourceList' => [
|
||||
'type' => 'object',
|
||||
'properties' => [
|
||||
'server' => ['type' => 'string'],
|
||||
'resources' => [
|
||||
'type' => 'array',
|
||||
'items' => ['$ref' => '#/components/schemas/Resource'],
|
||||
],
|
||||
'count' => ['type' => 'integer'],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return $schemas;
|
||||
|
|
|
|||
67
src/php/tests/Unit/McpResourceListTest.php
Normal file
67
src/php/tests/Unit/McpResourceListTest.php
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Mcp\Tests\Unit;
|
||||
|
||||
use Core\Mcp\Controllers\McpApiController;
|
||||
use Core\Mcp\Services\OpenApiGenerator;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Tests\TestCase;
|
||||
|
||||
class McpResourceListTest extends TestCase
|
||||
{
|
||||
public function test_resources_endpoint_returns_server_resources(): void
|
||||
{
|
||||
$controller = new class extends McpApiController {
|
||||
protected function loadServerFull(string $id): ?array
|
||||
{
|
||||
if ($id !== 'demo-server') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => 'demo-server',
|
||||
'resources' => [
|
||||
[
|
||||
'uri' => 'content://workspace/article',
|
||||
'name' => 'Article',
|
||||
'description' => 'Published article',
|
||||
'mimeType' => 'text/markdown',
|
||||
],
|
||||
[
|
||||
'uri' => 'plans://all',
|
||||
'name' => 'Plans',
|
||||
'description' => 'Work plan index',
|
||||
'mimeType' => 'text/markdown',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
$response = $controller->resources(Request::create('/api/v1/mcp/servers/demo-server/resources', 'GET'), 'demo-server');
|
||||
|
||||
$this->assertInstanceOf(JsonResponse::class, $response);
|
||||
$this->assertSame(200, $response->getStatusCode());
|
||||
|
||||
$data = $response->getData(true);
|
||||
$this->assertSame('demo-server', $data['server']);
|
||||
$this->assertSame(2, $data['count']);
|
||||
$this->assertSame('content://workspace/article', $data['resources'][0]['uri']);
|
||||
$this->assertSame('plans://all', $data['resources'][1]['uri']);
|
||||
}
|
||||
|
||||
public function test_openapi_includes_resource_list_endpoint(): void
|
||||
{
|
||||
$schema = (new OpenApiGenerator)->generate();
|
||||
|
||||
$this->assertArrayHasKey('/servers/{serverId}/resources', $schema['paths']);
|
||||
$this->assertArrayHasKey('ResourceList', $schema['components']['schemas']);
|
||||
$this->assertSame(
|
||||
'#/components/schemas/ResourceList',
|
||||
$schema['paths']['/servers/{serverId}/resources']['get']['responses']['200']['content']['application/json']['schema']['$ref']
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue