From 0653c821487add821965fb48df104c688877204d Mon Sep 17 00:00:00 2001 From: Snider Date: Tue, 3 Mar 2026 21:39:36 +0000 Subject: [PATCH] feat: MCP domain as native endpoint with OpenBrain support The mcp.* domain now serves both the human-readable portal AND the functional API. Agents POST to /tools/call with an API key, browsers get the HTML docs as a fallback. No separate api.* bridge needed. - Add POST /tools/call, GET /resources/{uri} routes to mcp domain - Add JSON server list/detail routes (servers.json, servers/{id}.json) - Add OpenBrain icon to landing, index, and show views - Replace all api.* domain references with mcp.* (request()->getSchemeAndHttpHost()) - Rewrite connect.blade.php as HTTP-only documentation - Update ApiExplorer base URL to use current mcp domain - Remove stdio/Docker configuration from all views Co-Authored-By: Virgil --- src/Website/Mcp/Routes/web.php | 27 ++- .../Mcp/View/Blade/web/api-explorer.blade.php | 4 +- .../View/Blade/web/api-key-manager.blade.php | 9 +- .../Mcp/View/Blade/web/connect.blade.php | 183 +++++++++--------- .../Mcp/View/Blade/web/index.blade.php | 3 + .../Mcp/View/Blade/web/landing.blade.php | 16 +- src/Website/Mcp/View/Blade/web/show.blade.php | 37 ++-- src/Website/Mcp/View/Modal/ApiExplorer.php | 4 +- 8 files changed, 159 insertions(+), 124 deletions(-) diff --git a/src/Website/Mcp/Routes/web.php b/src/Website/Mcp/Routes/web.php index 6103e84..7d2fe1f 100644 --- a/src/Website/Mcp/Routes/web.php +++ b/src/Website/Mcp/Routes/web.php @@ -1,7 +1,9 @@ name('mcp.')->group(function () { - // Agent discovery endpoint (always JSON) + // Agent discovery endpoint (always JSON, no auth) Route::get('.well-known/mcp-servers.json', [McpRegistryController::class, 'registry']) ->name('registry'); - // Landing page + // ── Functional API (authenticated) ──────────────────────────── + Route::middleware(McpApiKeyAuth::class)->group(function () { + Route::post('tools/call', [McpApiController::class, 'callTool'])->name('tools.call'); + Route::get('resources/{uri}', [McpApiController::class, 'resource'])->name('resources.read') + ->where('uri', '.+'); + Route::get('servers.json', [McpApiController::class, 'servers'])->name('servers.json'); + Route::get('servers/{id}.json', [McpApiController::class, 'server'])->name('servers.json.show') + ->where('id', '[a-z0-9-]+'); + Route::get('servers/{id}/tools', [McpApiController::class, 'tools'])->name('servers.tools') + ->where('id', '[a-z0-9-]+'); + }); + + // ── Human-readable portal (optional auth) ──────────────────── Route::get('/', [McpRegistryController::class, 'landing']) ->middleware(McpAuthenticate::class.':optional') ->name('landing'); - // Server list (HTML/JSON based on Accept header) Route::get('servers', [McpRegistryController::class, 'index']) ->middleware(McpAuthenticate::class.':optional') ->name('servers.index'); - // Server detail (supports .json extension) Route::get('servers/{id}', [McpRegistryController::class, 'show']) ->middleware(McpAuthenticate::class.':optional') ->name('servers.show') - ->where('id', '[a-z0-9-]+(?:\.json)?'); + ->where('id', '[a-z0-9-]+'); - // Connection config page Route::get('connect', [McpRegistryController::class, 'connect']) ->middleware(McpAuthenticate::class.':optional') ->name('connect'); diff --git a/src/Website/Mcp/View/Blade/web/api-explorer.blade.php b/src/Website/Mcp/View/Blade/web/api-explorer.blade.php index 17595d7..cb87e4f 100644 --- a/src/Website/Mcp/View/Blade/web/api-explorer.blade.php +++ b/src/Website/Mcp/View/Blade/web/api-explorer.blade.php @@ -26,8 +26,8 @@ wire:model="baseUrl" class="rounded-md border-yellow-300 shadow-sm focus:border-yellow-500 focus:ring-yellow-500 text-sm" > - - + + diff --git a/src/Website/Mcp/View/Blade/web/api-key-manager.blade.php b/src/Website/Mcp/View/Blade/web/api-key-manager.blade.php index 340a4eb..cdfcc3f 100644 --- a/src/Website/Mcp/View/Blade/web/api-key-manager.blade.php +++ b/src/Website/Mcp/View/Blade/web/api-key-manager.blade.php @@ -149,13 +149,14 @@

Call an MCP tool via HTTP POST:

-
curl -X POST https://mcp.host.uk.com/api/v1/tools/call \
+            @php $mcpUrl = request()->getSchemeAndHttpHost(); @endphp
+            
curl -X POST {{ $mcpUrl }}/tools/call \
   -H "Authorization: Bearer YOUR_API_KEY" \
   -H "Content-Type: application/json" \
   -d '{
-    "server": "commerce",
-    "tool": "product_list",
-    "arguments": {}
+    "server": "openbrain",
+    "tool": "brain_recall",
+    "arguments": { "query": "recent decisions" }
   }'
diff --git a/src/Website/Mcp/View/Blade/web/connect.blade.php b/src/Website/Mcp/View/Blade/web/connect.blade.php index 67ba144..c3a7999 100644 --- a/src/Website/Mcp/View/Blade/web/connect.blade.php +++ b/src/Website/Mcp/View/Blade/web/connect.blade.php @@ -1,11 +1,15 @@ Setup Guide + @php + $mcpUrl = request()->getSchemeAndHttpHost(); + @endphp +

Setup Guide

- Connect to MCP servers via HTTP API or stdio. + Connect AI agents to MCP servers via HTTP.

@@ -28,7 +32,7 @@
- +
@@ -37,143 +41,118 @@

HTTP API

- Recommended + All platforms
-

- Call MCP tools from any language or platform using standard HTTP requests. - Perfect for external integrations, webhooks, and remote agents. +

+ Call MCP tools from any language, platform, or AI agent using standard HTTP requests. + Works with Claude Code, Cursor, custom agents, webhooks, and any HTTP client.

1. Get your API key

- Sign in to your account to create an API key from the admin dashboard. + Create an API key from your admin dashboard. Keys use the hk_ prefix.

-

2. Call a tool

-
curl -X POST https://mcp.host.uk.com/api/v1/mcp/tools/call \
+            

2. Discover available servers

+
curl {{ $mcpUrl }}/servers.json \
+  -H "Authorization: Bearer YOUR_API_KEY"
+ +

3. Call a tool

+
curl -X POST {{ $mcpUrl }}/tools/call \
   -H "Authorization: Bearer YOUR_API_KEY" \
   -H "Content-Type: application/json" \
   -d '{
-    "server": "commerce",
-    "tool": "product_list",
-    "arguments": { "category": "hosting" }
+    "server": "openbrain",
+    "tool": "brain_recall",
+    "arguments": { "query": "authentication decisions" }
   }'
-

3. List available tools

-
curl https://mcp.host.uk.com/api/v1/mcp/servers \
+            

4. Read a resource

+
curl {{ $mcpUrl }}/resources/plans://all \
   -H "Authorization: Bearer YOUR_API_KEY"
-

API Endpoints

+

Endpoints

View OpenAPI Spec →
- GET /api/v1/mcp/servers + GET /.well-known/mcp-servers.json + Agent discovery +
+
+ GET /servers List all servers
- GET /api/v1/mcp/servers/{id} - Server details + GET /servers/{id} + Server details + tools
- GET /api/v1/mcp/servers/{id}/tools - List tools -
-
- POST /api/v1/mcp/tools/call + POST /tools/call Execute a tool
- GET /api/v1/mcp/resources/{uri} + GET /resources/{uri} Read a resource
- +
- +
-

Stdio (Local)

- - For local development - +

Code Examples

-

- Direct stdio connection for Claude Code and other local AI agents. - Ideal for OSS framework users running their own Host Hub instance. -

+
+ +
+

Python

+
import requests
 
-            
- - Show stdio configuration - - -
- -
-

Claude Code

-

- Add to ~/.claude/claude_code_config.json: -

-
{
-  "mcpServers": {
-@foreach($servers as $server)
-    "{{ $server['id'] }}": {
-      "command": "{{ $server['connection']['command'] ?? 'php' }}",
-      "args": {!! json_encode($server['connection']['args'] ?? ['artisan', 'mcp:agent-server']) !!},
-      "cwd": "{{ $server['connection']['cwd'] ?? '/path/to/host.uk.com' }}"
-    }{{ !$loop->last ? ',' : '' }}
-@endforeach
-  }
-}
-
- - -
-

Cursor

-

- Add to .cursor/mcp.json: -

-
{
-  "mcpServers": {
-@foreach($servers as $server)
-    "{{ $server['id'] }}": {
-      "command": "{{ $server['connection']['command'] ?? 'php' }}",
-      "args": {!! json_encode($server['connection']['args'] ?? ['artisan', 'mcp:agent-server']) !!}
-    }{{ !$loop->last ? ',' : '' }}
-@endforeach
-  }
-}
-
- - -
-

Docker

-
{
-  "mcpServers": {
-    "hosthub-agent": {
-      "command": "docker",
-      "args": ["exec", "-i", "hosthub-app", "php", "artisan", "mcp:agent-server"]
+resp = requests.post(
+    "{{ $mcpUrl }}/tools/call",
+    headers={"Authorization": "Bearer hk_your_key"},
+    json={
+        "server": "openbrain",
+        "tool": "brain_recall",
+        "arguments": {"query": "recent decisions"}
     }
-  }
-}
-
+) +print(resp.json())
- + + +
+

JavaScript

+
const resp = await fetch("{{ $mcpUrl }}/tools/call", {
+  method: "POST",
+  headers: {
+    "Authorization": "Bearer hk_your_key",
+    "Content-Type": "application/json",
+  },
+  body: JSON.stringify({
+    server: "openbrain",
+    tool: "brain_recall",
+    arguments: { query: "recent decisions" },
+  }),
+});
+const data = await resp.json();
+
+
@@ -199,6 +178,28 @@ check your key's server scopes in your admin dashboard.

+ +
+

Rate limiting

+

+ Requests are rate limited to 120 per minute. Rate limit headers + (X-RateLimit-Limit, X-RateLimit-Remaining) + are included in all responses. +

+
+ + + +
+

Discovery

+

+ Agents discover available servers automatically via the well-known endpoint: +

+
curl {{ $mcpUrl }}/.well-known/mcp-servers.json
+

+ Returns the server registry with capabilities and connection details. + No authentication required for discovery. +

@@ -208,8 +209,8 @@ Browse Servers - - Contact Support + + OpenAPI Spec diff --git a/src/Website/Mcp/View/Blade/web/index.blade.php b/src/Website/Mcp/View/Blade/web/index.blade.php index 9f7a8bc..bb149d3 100644 --- a/src/Website/Mcp/View/Blade/web/index.blade.php +++ b/src/Website/Mcp/View/Blade/web/index.blade.php @@ -32,6 +32,9 @@ @case('supporthost') @break + @case('openbrain') + + @break @case('analyticshost') @break diff --git a/src/Website/Mcp/View/Blade/web/landing.blade.php b/src/Website/Mcp/View/Blade/web/landing.blade.php index 2f5e995..7963814 100644 --- a/src/Website/Mcp/View/Blade/web/landing.blade.php +++ b/src/Website/Mcp/View/Blade/web/landing.blade.php @@ -93,6 +93,9 @@ @case('supporthost') @break + @case('openbrain') + + @break @case('analyticshost') @break @@ -180,18 +183,21 @@ @endif + @php + $mcpUrl = request()->getSchemeAndHttpHost(); + @endphp

Quick Start

- Call MCP tools via HTTP API with your API key: + Call MCP tools via HTTP with your API key:

-
curl -X POST https://mcp.host.uk.com/api/v1/mcp/tools/call \
+        
curl -X POST {{ $mcpUrl }}/tools/call \
   -H "Authorization: Bearer YOUR_API_KEY" \
   -H "Content-Type: application/json" \
   -d '{
-    "server": "commerce",
-    "tool": "product_list",
-    "arguments": {}
+    "server": "openbrain",
+    "tool": "brain_recall",
+    "arguments": { "query": "recent decisions" }
   }'
diff --git a/src/Website/Mcp/View/Blade/web/show.blade.php b/src/Website/Mcp/View/Blade/web/show.blade.php index 9c2b9e7..7d3316a 100644 --- a/src/Website/Mcp/View/Blade/web/show.blade.php +++ b/src/Website/Mcp/View/Blade/web/show.blade.php @@ -29,6 +29,9 @@ @case('supporthost') @break + @case('openbrain') + + @break @case('analyticshost') @break @@ -96,18 +99,28 @@
- @if(!empty($server['connection'])) -
-

Connection

-
{
-  "{{ $server['id'] }}": {
-    "command": "{{ $server['connection']['command'] ?? 'php' }}",
-    "args": {!! json_encode($server['connection']['args'] ?? ['artisan', 'mcp:agent-server']) !!},
-    "cwd": "{{ $server['connection']['cwd'] ?? '/path/to/project' }}"
-  }
-}
-
- @endif + @php + $mcpUrl = request()->getSchemeAndHttpHost(); + @endphp +
+

Connection

+

+ Call tools on this server via HTTP: +

+
curl -X POST {{ $mcpUrl }}/tools/call \
+  -H "Authorization: Bearer YOUR_API_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{
+    "server": "{{ $server['id'] }}",
+    "tool": "{{ !empty($server['tools']) ? $server['tools'][0]['name'] : 'tool_name' }}",
+    "arguments": {}
+  }'
+

+ + Full setup guide → + +

+
@if(!empty($server['tools'])) diff --git a/src/Website/Mcp/View/Modal/ApiExplorer.php b/src/Website/Mcp/View/Modal/ApiExplorer.php index 1c7bd24..60d79c6 100644 --- a/src/Website/Mcp/View/Modal/ApiExplorer.php +++ b/src/Website/Mcp/View/Modal/ApiExplorer.php @@ -108,8 +108,8 @@ class ApiExplorer extends Component public function mount(): void { - // Set base URL from config - $this->baseUrl = config('api.base_url', config('app.url')); + // Set base URL from current request (mcp domain) + $this->baseUrl = request()->getSchemeAndHttpHost(); // Pre-select first endpoint if (! empty($this->endpoints)) {