docs: add human-friendly documentation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9e2d77ed2a
commit
8b9cc2e3d6
3 changed files with 879 additions and 0 deletions
410
docs/architecture.md
Normal file
410
docs/architecture.md
Normal file
|
|
@ -0,0 +1,410 @@
|
|||
---
|
||||
title: Architecture
|
||||
description: Internals of the Go MCP server and PHP Laravel package -- types, data flow, subsystems, and security model.
|
||||
---
|
||||
|
||||
# Architecture
|
||||
|
||||
Core MCP is split into two cooperating halves: a Go binary that speaks the
|
||||
native MCP protocol over stdio/TCP/Unix sockets, and a PHP Laravel package
|
||||
that exposes an HTTP MCP API with multi-tenant auth, quotas, and analytics.
|
||||
|
||||
## Go server (`pkg/mcp/`)
|
||||
|
||||
### Service
|
||||
|
||||
`mcp.Service` is the central type. It owns an `mcp.Server` from the official
|
||||
Go SDK, a sandboxed filesystem `Medium`, optional subsystems, and an ordered
|
||||
slice of `ToolRecord` metadata that powers the REST bridge.
|
||||
|
||||
```go
|
||||
svc, err := mcp.New(
|
||||
mcp.WithWorkspaceRoot("/home/user/project"),
|
||||
mcp.WithSubsystem(mcp.NewMLSubsystem(mlService)),
|
||||
mcp.WithProcessService(processService),
|
||||
mcp.WithWSHub(wsHub),
|
||||
)
|
||||
```
|
||||
|
||||
Options follow the functional-options pattern. `WithWorkspaceRoot` creates a
|
||||
sandboxed `io.Medium` that confines all file operations to a single directory
|
||||
tree. Passing an empty string disables sandboxing (not recommended for
|
||||
untrusted clients).
|
||||
|
||||
### Tool registration
|
||||
|
||||
Every tool is registered through a single generic function:
|
||||
|
||||
```go
|
||||
func addToolRecorded[In, Out any](
|
||||
s *Service,
|
||||
server *mcp.Server,
|
||||
group string,
|
||||
t *mcp.Tool,
|
||||
h mcp.ToolHandlerFor[In, Out],
|
||||
)
|
||||
```
|
||||
|
||||
This function does three things in one call:
|
||||
|
||||
1. Registers the handler with the MCP server for native protocol calls.
|
||||
2. Reflects on the `In` and `Out` type parameters to build JSON Schemas.
|
||||
3. Creates a `RESTHandler` closure that unmarshals raw JSON into the concrete
|
||||
`In` type, calls the handler, and returns the `Out` value -- enabling the
|
||||
REST bridge without any per-tool glue code.
|
||||
|
||||
The resulting `ToolRecord` structs are stored in `Service.tools` and exposed
|
||||
via `Tools()` and `ToolsSeq()` (the latter returns a Go 1.23 `iter.Seq`).
|
||||
|
||||
### Built-in tool groups
|
||||
|
||||
The service registers the following tools at startup:
|
||||
|
||||
| Group | Tools | Source |
|
||||
|-------|-------|--------|
|
||||
| **files** | `file_read`, `file_write`, `file_delete`, `file_rename`, `file_exists`, `file_edit`, `dir_list`, `dir_create` | `mcp.go` |
|
||||
| **language** | `lang_detect`, `lang_list` | `mcp.go` |
|
||||
| **metrics** | `metrics_record`, `metrics_query` | `tools_metrics.go` |
|
||||
| **rag** | `rag_query`, `rag_ingest`, `rag_collections` | `tools_rag.go` |
|
||||
| **process** | `process_start`, `process_stop`, `process_kill`, `process_list`, `process_output`, `process_input` | `tools_process.go` |
|
||||
| **webview** | `webview_connect`, `webview_disconnect`, `webview_navigate`, `webview_click`, `webview_type`, `webview_query`, `webview_console`, `webview_eval`, `webview_screenshot`, `webview_wait` | `tools_webview.go` |
|
||||
| **ws** | `ws_start`, `ws_info` | `tools_ws.go` |
|
||||
|
||||
Process and WebSocket tools are conditionally registered -- they require
|
||||
`WithProcessService` and `WithWSHub` respectively.
|
||||
|
||||
### Subsystem interface
|
||||
|
||||
Additional tool groups are plugged in via the `Subsystem` interface:
|
||||
|
||||
```go
|
||||
type Subsystem interface {
|
||||
Name() string
|
||||
RegisterTools(server *mcp.Server)
|
||||
}
|
||||
```
|
||||
|
||||
Subsystems that need teardown implement `SubsystemWithShutdown`:
|
||||
|
||||
```go
|
||||
type SubsystemWithShutdown interface {
|
||||
Subsystem
|
||||
Shutdown(ctx context.Context) error
|
||||
}
|
||||
```
|
||||
|
||||
Two subsystems ship with this repo:
|
||||
|
||||
#### ML subsystem (`tools_ml.go`)
|
||||
|
||||
`MLSubsystem` wraps a `go-ml.Service` and exposes five tools:
|
||||
|
||||
- `ml_generate` -- text generation via any registered inference backend.
|
||||
- `ml_score` -- heuristic and semantic scoring of prompt/response pairs.
|
||||
- `ml_probe` -- capability probes (predefined prompts run through the model).
|
||||
- `ml_status` -- training and generation progress from InfluxDB.
|
||||
- `ml_backends` -- lists registered inference backends and their availability.
|
||||
|
||||
#### IDE subsystem (`pkg/mcp/ide/`)
|
||||
|
||||
Bridges the desktop IDE to a Laravel `core-agentic` backend over WebSocket.
|
||||
Registers tools in three groups:
|
||||
|
||||
- **Chat**: `ide_chat_send`, `ide_chat_history`, `ide_session_list`,
|
||||
`ide_session_create`, `ide_plan_status`
|
||||
- **Build**: `ide_build_status`, `ide_build_list`, `ide_build_logs`
|
||||
- **Dashboard**: `ide_dashboard_overview`, `ide_dashboard_activity`,
|
||||
`ide_dashboard_metrics`
|
||||
|
||||
The IDE bridge (`Bridge`) maintains a persistent WebSocket connection to the
|
||||
Laravel backend with exponential-backoff reconnection. Messages are forwarded
|
||||
from Laravel to a local `ws.Hub` for real-time streaming to the IDE frontend.
|
||||
|
||||
#### Brain subsystem (`pkg/mcp/brain/`)
|
||||
|
||||
Proxies OpenBrain knowledge-store operations to the Laravel backend via the
|
||||
IDE bridge. Four tools:
|
||||
|
||||
- `brain_remember` -- store a memory (decision, observation, bug, etc.).
|
||||
- `brain_recall` -- semantic search across stored memories.
|
||||
- `brain_forget` -- permanently delete a memory.
|
||||
- `brain_list` -- list memories with filtering (no vector search).
|
||||
|
||||
### Transports
|
||||
|
||||
The Go server supports three transports, all using line-delimited JSON-RPC:
|
||||
|
||||
| Transport | Activation | Default address |
|
||||
|-----------|-----------|-----------------|
|
||||
| **Stdio** | No `MCP_ADDR` env var | stdin/stdout |
|
||||
| **TCP** | `MCP_ADDR=host:port` | `127.0.0.1:9100` |
|
||||
| **Unix** | `ServeUnix(ctx, path)` | caller-specified socket path |
|
||||
|
||||
TCP binds to `127.0.0.1` by default when the host component is empty. Binding
|
||||
to `0.0.0.0` emits a security warning. Each accepted connection spawns a
|
||||
fresh `mcp.Server` instance with its own tool set.
|
||||
|
||||
### REST bridge
|
||||
|
||||
`BridgeToAPI` populates a `go-api.ToolBridge` from the recorded tool
|
||||
metadata. Each tool becomes a `POST` endpoint that:
|
||||
|
||||
1. Reads and size-limits the JSON body (10 MB max).
|
||||
2. Calls the tool's `RESTHandler` (which deserialises to the correct input
|
||||
type).
|
||||
3. Wraps the result in a standard `api.Response` envelope.
|
||||
|
||||
JSON parse errors return 400; all other errors return 500. This allows any
|
||||
MCP tool to be called over plain HTTP without additional code.
|
||||
|
||||
### Data flow (Go)
|
||||
|
||||
```
|
||||
AI Client (Claude Code, IDE)
|
||||
|
|
||||
| JSON-RPC over stdio / TCP / Unix
|
||||
v
|
||||
mcp.Server (go-sdk)
|
||||
|
|
||||
| typed handler dispatch
|
||||
v
|
||||
Service.readFile / Service.writeFile / ...
|
||||
|
|
||||
| sandboxed I/O
|
||||
v
|
||||
io.Medium (go-io)
|
||||
|
||||
--- or via REST ---
|
||||
|
||||
HTTP Client
|
||||
|
|
||||
| POST /api/tools/{name}
|
||||
v
|
||||
gin.Router -> BridgeToAPI -> RESTHandler -> typed handler
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## PHP package (`src/php/`)
|
||||
|
||||
### Namespace structure
|
||||
|
||||
The PHP side is split into three namespace roots, each serving a different
|
||||
stage of the Laravel request lifecycle:
|
||||
|
||||
| Namespace | Path | Purpose |
|
||||
|-----------|------|---------|
|
||||
| `Core\Front\Mcp` | `src/Front/Mcp/` | **Frontage** -- defines the `mcp` middleware group, fires `McpRoutesRegistering` and `McpToolsRegistering` lifecycle events |
|
||||
| `Core\Mcp` | `src/Mcp/` | **Module** -- service provider, models, services, middleware, tools, admin panel, migrations |
|
||||
| `Core\Website\Mcp` | `src/Website/Mcp/` | **Website** -- public-facing Livewire pages (playground, API explorer, metrics dashboard) |
|
||||
|
||||
### Boot sequence
|
||||
|
||||
1. **`Core\Front\Mcp\Boot`** (auto-discovered via `composer.json` extra) --
|
||||
registers the `mcp` middleware group with throttling and route-model
|
||||
binding, then fires `McpRoutesRegistering` and `McpToolsRegistering`
|
||||
lifecycle events.
|
||||
|
||||
2. **`Core\Mcp\Boot`** listens to those events via the `$listens` array:
|
||||
- `McpRoutesRegistering` -- registers MCP API routes under the configured
|
||||
domain with `mcp.auth` middleware.
|
||||
- `McpToolsRegistering` -- hook for other modules to register tool
|
||||
handlers.
|
||||
- `AdminPanelBooting` -- loads admin views and Livewire components.
|
||||
- `ConsoleBooting` -- registers artisan commands.
|
||||
|
||||
3. Services are bound as singletons: `ToolRegistry`, `ToolAnalyticsService`,
|
||||
`McpQuotaService`, `ToolDependencyService`, `AuditLogService`,
|
||||
`ToolVersionService`, `QueryAuditService`, `QueryExecutionService`.
|
||||
|
||||
### HTTP API
|
||||
|
||||
The `McpApiController` exposes five endpoints behind `mcp.auth` middleware:
|
||||
|
||||
| Method | Path | Handler |
|
||||
|--------|------|---------|
|
||||
| `GET` | `/servers.json` | List all MCP servers from YAML registry |
|
||||
| `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) |
|
||||
|
||||
`POST /tools/call` accepts:
|
||||
|
||||
```json
|
||||
{
|
||||
"server": "hosthub-agent",
|
||||
"tool": "brain_remember",
|
||||
"arguments": { "content": "...", "type": "observation" }
|
||||
}
|
||||
```
|
||||
|
||||
The controller validates arguments against the tool's JSON Schema, executes
|
||||
via the `AgentToolRegistry`, logs the call, records quota usage, and
|
||||
dispatches webhooks.
|
||||
|
||||
### Authentication
|
||||
|
||||
`McpApiKeyAuth` middleware extracts an API key from either:
|
||||
|
||||
- `Authorization: Bearer hk_xxx_yyy`
|
||||
- `X-API-Key: hk_xxx_yyy`
|
||||
|
||||
It checks expiry, per-server access scopes, and records usage. The resolved
|
||||
`ApiKey` model is attached to the request for downstream use.
|
||||
|
||||
### McpToolHandler contract
|
||||
|
||||
Tool handlers implement `Core\Front\Mcp\Contracts\McpToolHandler`:
|
||||
|
||||
```php
|
||||
interface McpToolHandler
|
||||
{
|
||||
public static function schema(): array;
|
||||
public function handle(array $args, McpContext $context): array;
|
||||
}
|
||||
```
|
||||
|
||||
`McpContext` abstracts the transport layer (stdio vs HTTP), providing:
|
||||
session tracking, plan context, notification sending, and session logging.
|
||||
|
||||
### Tool registry
|
||||
|
||||
`Core\Mcp\Services\ToolRegistry` loads server and tool definitions from
|
||||
YAML files in `resources/mcp/`. It provides:
|
||||
|
||||
- Server discovery (`getServers()`)
|
||||
- Tool listing with optional version info (`getToolsForServer()`)
|
||||
- Category grouping and search (`getToolsByCategory()`, `searchTools()`)
|
||||
- Example input generation from JSON Schema
|
||||
- 5-minute cache with manual invalidation
|
||||
|
||||
### SQL security
|
||||
|
||||
`QueryDatabase` is the most security-hardened tool. It implements seven
|
||||
layers of defence:
|
||||
|
||||
1. **Keyword blocking** -- `INSERT`, `UPDATE`, `DELETE`, `DROP`, `ALTER`,
|
||||
`GRANT`, `SET`, and 20+ other dangerous keywords are rejected outright.
|
||||
2. **Dangerous pattern detection** -- stacked queries, UNION injection,
|
||||
hex encoding, `SLEEP()`, `BENCHMARK()`, comment obfuscation, and
|
||||
`INFORMATION_SCHEMA` access are blocked before comment stripping.
|
||||
3. **Whitelist matching** -- only queries matching predefined regex patterns
|
||||
(simple SELECT, COUNT, explicit column lists) are allowed.
|
||||
4. **Blocked table list** -- configurable list of tables that cannot appear
|
||||
in FROM or JOIN clauses.
|
||||
5. **Tier-based row limits** -- results are truncated with a warning when
|
||||
they exceed the configured limit.
|
||||
6. **Query timeout** -- per-query time limit prevents runaway queries.
|
||||
7. **Audit logging** -- every query attempt (allowed, blocked, or errored)
|
||||
is recorded with workspace, user, IP, and session context.
|
||||
|
||||
### Circuit breaker
|
||||
|
||||
`CircuitBreaker` provides fault tolerance for external service dependencies.
|
||||
It implements the standard three-state pattern:
|
||||
|
||||
- **Closed** -- requests pass through normally; failures are counted.
|
||||
- **Open** -- requests fail fast; a configurable timeout triggers transition
|
||||
to half-open.
|
||||
- **Half-Open** -- a single trial request is allowed (with a lock to prevent
|
||||
concurrent trials); success closes the circuit, failure re-opens it.
|
||||
|
||||
Configuration is per-service via `config('mcp.circuit_breaker.{service}.*')`.
|
||||
|
||||
### Metrics and analytics
|
||||
|
||||
`McpMetricsService` provides dashboard data from `McpToolCallStat` aggregate
|
||||
records:
|
||||
|
||||
- Overview stats with period-over-period trend comparison
|
||||
- Daily call trends for charting
|
||||
- Top tools by call count
|
||||
- Per-tool performance percentiles (p50, p95, p99)
|
||||
- Hourly distribution heatmap
|
||||
- Error breakdown by tool and error code
|
||||
- Plan activity tracking
|
||||
|
||||
`ToolAnalyticsService` and `ToolVersionService` handle deeper per-tool
|
||||
analytics and schema versioning respectively.
|
||||
|
||||
### Quota management
|
||||
|
||||
`McpQuotaService` enforces per-workspace usage limits tracked in the
|
||||
`mcp_usage_quotas` table. The `CheckMcpQuota` middleware blocks requests
|
||||
when the quota is exhausted.
|
||||
|
||||
### Audit logging
|
||||
|
||||
`AuditLogService` records tamper-evident audit logs in the `mcp_audit_logs`
|
||||
table. The `VerifyAuditLogCommand` artisan command checks log integrity.
|
||||
|
||||
### Admin panel
|
||||
|
||||
The module registers nine Livewire components for the admin panel:
|
||||
|
||||
- **ApiKeyManager** -- create, revoke, and scope API keys
|
||||
- **Playground / McpPlayground** -- interactive tool testing
|
||||
- **RequestLog** -- full request/response replay
|
||||
- **ToolAnalyticsDashboard / ToolAnalyticsDetail** -- visual metrics
|
||||
- **QuotaUsage** -- per-workspace quota status
|
||||
- **AuditLogViewer** -- searchable audit trail
|
||||
- **ToolVersionManager** -- schema versioning and deprecation
|
||||
|
||||
### Data flow (PHP)
|
||||
|
||||
```
|
||||
AI Agent / External Client
|
||||
|
|
||||
| POST /tools/call (Bearer hk_xxx_yyy)
|
||||
v
|
||||
McpApiKeyAuth middleware
|
||||
|
|
||||
| auth + scope check
|
||||
v
|
||||
CheckMcpQuota middleware
|
||||
|
|
||||
| quota enforcement
|
||||
v
|
||||
McpApiController::callTool()
|
||||
|
|
||||
| schema validation
|
||||
v
|
||||
AgentToolRegistry::execute()
|
||||
|
|
||||
| permission + dependency check
|
||||
v
|
||||
Tool handler (e.g. QueryDatabase, brain_remember)
|
||||
|
|
||||
| result
|
||||
v
|
||||
Log (McpToolCall, McpApiRequest, AuditLog, Webhook)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Brain-seed utility (`cmd/brain-seed/`)
|
||||
|
||||
A standalone Go program that bulk-imports knowledge into OpenBrain via the
|
||||
PHP MCP HTTP API. It discovers three sources:
|
||||
|
||||
1. **MEMORY.md** files from `~/.claude/projects/*/memory/`
|
||||
2. **Plan documents** from `~/Code/*/docs/plans/`
|
||||
3. **CLAUDE.md** files from `~/Code/` (up to 4 levels deep)
|
||||
|
||||
Each markdown file is split by headings into sections. Each section becomes a
|
||||
`brain_remember` API call with inferred type (architecture, convention,
|
||||
decision, bug, plan, research, observation), project tag, and confidence
|
||||
level. Content is truncated to 3,800 characters to fit within embedding model
|
||||
limits.
|
||||
|
||||
```bash
|
||||
# Dry run (preview without storing)
|
||||
go run ./cmd/brain-seed -dry-run
|
||||
|
||||
# Import memories
|
||||
go run ./cmd/brain-seed -api-key YOUR_KEY
|
||||
|
||||
# Also import plans and CLAUDE.md files
|
||||
go run ./cmd/brain-seed -api-key YOUR_KEY -plans -claude-md
|
||||
```
|
||||
372
docs/development.md
Normal file
372
docs/development.md
Normal file
|
|
@ -0,0 +1,372 @@
|
|||
---
|
||||
title: Development
|
||||
description: How to build, test, and contribute to the core/mcp repository.
|
||||
---
|
||||
|
||||
# Development
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Go 1.26+** -- the module uses Go 1.26 features (range-over-func
|
||||
iterators, `reflect.Type.Fields()`)
|
||||
- **PHP 8.2+** -- required by the Laravel package
|
||||
- **Composer** -- for PHP dependency management
|
||||
- **Core CLI** -- `core build`, `core go test`, etc. (built from
|
||||
`forge.lthn.ai/core/cli`)
|
||||
- **Go workspace** -- this module is part of the workspace at `~/Code/go.work`
|
||||
|
||||
## Building
|
||||
|
||||
### Go binary
|
||||
|
||||
```bash
|
||||
# From the repo root
|
||||
core build # produces ./core-mcp (arm64 by default)
|
||||
|
||||
# Or with Go directly
|
||||
go build -o core-mcp ./cmd/core-mcp/
|
||||
```
|
||||
|
||||
Build configuration lives in `.core/build.yaml`:
|
||||
|
||||
```yaml
|
||||
project:
|
||||
name: core-mcp
|
||||
binary: core-mcp
|
||||
```
|
||||
|
||||
### PHP package
|
||||
|
||||
The PHP code is consumed as a Composer package. There is no standalone build
|
||||
step. To develop locally, symlink or use a Composer path repository in your
|
||||
Laravel application:
|
||||
|
||||
```json
|
||||
{
|
||||
"repositories": [
|
||||
{
|
||||
"type": "path",
|
||||
"url": "../core/mcp"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Then run `composer require lthn/mcp:@dev`.
|
||||
|
||||
## Testing
|
||||
|
||||
### Go tests
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
core go test
|
||||
|
||||
# Run a single test
|
||||
core go test --run TestBridgeToAPI
|
||||
|
||||
# With coverage
|
||||
core go cov
|
||||
core go cov --open # opens HTML report in browser
|
||||
|
||||
# Full QA (format + vet + lint + test)
|
||||
core go qa
|
||||
core go qa full # also runs race detector, vuln scan, security audit
|
||||
```
|
||||
|
||||
Test files follow the `_Good`, `_Bad`, `_Ugly` suffix convention:
|
||||
|
||||
| Suffix | Meaning |
|
||||
|--------|---------|
|
||||
| `_Good` | Happy path -- expected behaviour with valid inputs |
|
||||
| `_Bad` | Error paths -- expected failures with invalid inputs |
|
||||
| `_Ugly` | Edge cases -- panics, nil pointers, concurrent access |
|
||||
|
||||
Key test files:
|
||||
|
||||
| File | What it covers |
|
||||
|------|----------------|
|
||||
| `mcp_test.go` | Service creation, workspace sandboxing, file operations |
|
||||
| `registry_test.go` | Tool recording, schema extraction, REST handler creation |
|
||||
| `bridge_test.go` | `BridgeToAPI`, JSON error classification, 10 MB body limit |
|
||||
| `subsystem_test.go` | Subsystem registration and shutdown |
|
||||
| `transport_tcp_test.go` | TCP transport, loopback default, `0.0.0.0` warning |
|
||||
| `transport_e2e_test.go` | End-to-end TCP client/server round-trip |
|
||||
| `tools_metrics_test.go` | Duration parsing, metrics record/query |
|
||||
| `tools_ml_test.go` | ML subsystem tool registration |
|
||||
| `tools_process_test.go` | Process start/stop/kill/list/output/input |
|
||||
| `tools_process_ci_test.go` | CI-safe process tests (no external binaries) |
|
||||
| `tools_rag_test.go` | RAG query/ingest/collections |
|
||||
| `tools_rag_ci_test.go` | CI-safe RAG tests (no Qdrant required) |
|
||||
| `tools_webview_test.go` | Webview tool registration and error handling |
|
||||
| `tools_ws_test.go` | WebSocket start/info tools |
|
||||
| `iter_test.go` | Iterator helpers (`SubsystemsSeq`, `ToolsSeq`) |
|
||||
| `integration_test.go` | Cross-subsystem integration |
|
||||
| `ide/bridge_test.go` | IDE bridge connection, message dispatch |
|
||||
| `ide/tools_test.go` | IDE tool registration |
|
||||
| `brain/brain_test.go` | Brain subsystem registration and bridge-nil handling |
|
||||
|
||||
### PHP tests
|
||||
|
||||
```bash
|
||||
# From the repo root (or src/php/)
|
||||
composer test
|
||||
|
||||
# Single test
|
||||
composer test -- --filter=SqlQueryValidatorTest
|
||||
```
|
||||
|
||||
PHP tests use Pest syntax. Key test files:
|
||||
|
||||
| File | What it covers |
|
||||
|------|----------------|
|
||||
| `SqlQueryValidatorTest.php` | Blocked keywords, injection patterns, whitelist |
|
||||
| `McpQuotaServiceTest.php` | Quota recording and enforcement |
|
||||
| `QueryAuditServiceTest.php` | Audit log recording |
|
||||
| `QueryExecutionServiceTest.php` | Query execution with limits and timeouts |
|
||||
| `ToolAnalyticsServiceTest.php` | Analytics aggregation |
|
||||
| `ToolDependencyServiceTest.php` | Dependency validation |
|
||||
| `ToolVersionServiceTest.php` | Version management |
|
||||
| `ValidateWorkspaceContextMiddlewareTest.php` | Workspace context validation |
|
||||
| `WorkspaceContextSecurityTest.php` | Multi-tenant isolation |
|
||||
|
||||
## Code style
|
||||
|
||||
### Go
|
||||
|
||||
- Format with `core go fmt` (uses `gofmt`)
|
||||
- Lint with `core go lint` (uses `golangci-lint`)
|
||||
- Vet with `core go vet`
|
||||
- All three run automatically via `core go qa`
|
||||
|
||||
### PHP
|
||||
|
||||
- Format with `composer lint` (uses Laravel Pint, PSR-12)
|
||||
- Format only changed files: `./vendor/bin/pint --dirty`
|
||||
|
||||
### General conventions
|
||||
|
||||
- **UK English** in all user-facing strings and documentation (colour,
|
||||
organisation, centre, normalise, serialise).
|
||||
- **Strict types** in every PHP file: `declare(strict_types=1);`
|
||||
- **SPDX headers** in Go files: `// SPDX-License-Identifier: EUPL-1.2`
|
||||
- **Type hints** on all PHP parameters and return types.
|
||||
- Conventional commits: `type(scope): description`
|
||||
|
||||
## Project structure
|
||||
|
||||
```
|
||||
core/mcp/
|
||||
+-- .core/
|
||||
| +-- build.yaml # Build configuration
|
||||
+-- cmd/
|
||||
| +-- core-mcp/
|
||||
| | +-- main.go # Binary entry point
|
||||
| +-- mcpcmd/
|
||||
| | +-- cmd_mcp.go # CLI command registration
|
||||
| +-- brain-seed/
|
||||
| +-- main.go # OpenBrain import utility
|
||||
+-- pkg/
|
||||
| +-- mcp/
|
||||
| +-- mcp.go # Service, file tools, Run()
|
||||
| +-- registry.go # ToolRecord, addToolRecorded, schema extraction
|
||||
| +-- subsystem.go # Subsystem interface, WithSubsystem option
|
||||
| +-- bridge.go # BridgeToAPI (MCP-to-REST adapter)
|
||||
| +-- transport_stdio.go
|
||||
| +-- transport_tcp.go
|
||||
| +-- transport_unix.go
|
||||
| +-- tools_metrics.go # Metrics record/query
|
||||
| +-- tools_ml.go # MLSubsystem (generate, score, probe, status, backends)
|
||||
| +-- tools_process.go # Process management tools
|
||||
| +-- tools_rag.go # RAG query/ingest/collections
|
||||
| +-- tools_webview.go # Chrome DevTools automation
|
||||
| +-- tools_ws.go # WebSocket server tools
|
||||
| +-- brain/
|
||||
| | +-- brain.go # Brain subsystem
|
||||
| | +-- tools.go # remember/recall/forget/list tools
|
||||
| +-- ide/
|
||||
| +-- ide.go # IDE subsystem
|
||||
| +-- config.go # Config, options, defaults
|
||||
| +-- bridge.go # Laravel WebSocket bridge
|
||||
| +-- tools_chat.go
|
||||
| +-- tools_build.go
|
||||
| +-- tools_dashboard.go
|
||||
+-- src/
|
||||
| +-- php/
|
||||
| +-- src/
|
||||
| | +-- Front/Mcp/ # Frontage (middleware group, contracts)
|
||||
| | +-- Mcp/ # Module (services, models, tools, admin)
|
||||
| | +-- Website/Mcp/ # Public pages (playground, explorer)
|
||||
| +-- tests/
|
||||
| +-- config/
|
||||
| +-- routes/
|
||||
+-- composer.json
|
||||
+-- go.mod
|
||||
+-- go.sum
|
||||
```
|
||||
|
||||
## Running locally
|
||||
|
||||
### MCP server (stdio, for Claude Code)
|
||||
|
||||
Add to your Claude Code MCP configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"core": {
|
||||
"command": "/path/to/core-mcp",
|
||||
"args": ["mcp", "serve", "--workspace", "/path/to/project"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### MCP server (TCP, for multi-client)
|
||||
|
||||
```bash
|
||||
MCP_ADDR=127.0.0.1:9100 ./core-mcp mcp serve
|
||||
```
|
||||
|
||||
Connect with any JSON-RPC client over TCP. Each line is a complete JSON-RPC
|
||||
message. Maximum message size is 10 MB.
|
||||
|
||||
### PHP development server
|
||||
|
||||
Use Laravel Valet or the built-in server:
|
||||
|
||||
```bash
|
||||
cd /path/to/laravel-app
|
||||
php artisan serve
|
||||
```
|
||||
|
||||
The MCP API is available at the configured domain under the routes registered
|
||||
by `Core\Mcp\Boot::onMcpRoutes`.
|
||||
|
||||
### Brain-seed
|
||||
|
||||
```bash
|
||||
# Preview what would be imported
|
||||
go run ./cmd/brain-seed -dry-run
|
||||
|
||||
# Import with API key
|
||||
go run ./cmd/brain-seed \
|
||||
-api-key YOUR_KEY \
|
||||
-api https://lthn.sh/api/v1/mcp \
|
||||
-plans \
|
||||
-claude-md
|
||||
```
|
||||
|
||||
## Adding a new Go tool
|
||||
|
||||
1. Define input and output structs with `json` tags:
|
||||
|
||||
```go
|
||||
type MyToolInput struct {
|
||||
Query string `json:"query"`
|
||||
Limit int `json:"limit,omitempty"`
|
||||
}
|
||||
|
||||
type MyToolOutput struct {
|
||||
Results []string `json:"results"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
```
|
||||
|
||||
2. Write the handler function:
|
||||
|
||||
```go
|
||||
func (s *Service) myTool(
|
||||
ctx context.Context,
|
||||
req *mcp.CallToolRequest,
|
||||
input MyToolInput,
|
||||
) (*mcp.CallToolResult, MyToolOutput, error) {
|
||||
// Implementation here
|
||||
return nil, MyToolOutput{Results: results, Total: len(results)}, nil
|
||||
}
|
||||
```
|
||||
|
||||
3. Register in `registerTools()`:
|
||||
|
||||
```go
|
||||
addToolRecorded(s, server, "mygroup", &mcp.Tool{
|
||||
Name: "my_tool",
|
||||
Description: "Does something useful",
|
||||
}, s.myTool)
|
||||
```
|
||||
|
||||
The `addToolRecorded` generic function automatically generates JSON Schemas
|
||||
from the struct tags and creates a REST-compatible handler. No additional
|
||||
wiring is needed.
|
||||
|
||||
## Adding a new Go subsystem
|
||||
|
||||
1. Create a new package under `pkg/mcp/`:
|
||||
|
||||
```go
|
||||
package mysubsystem
|
||||
|
||||
type Subsystem struct{}
|
||||
|
||||
func (s *Subsystem) Name() string { return "mysubsystem" }
|
||||
|
||||
func (s *Subsystem) RegisterTools(server *mcp.Server) {
|
||||
mcp.AddTool(server, &mcp.Tool{
|
||||
Name: "my_subsystem_tool",
|
||||
Description: "...",
|
||||
}, s.handler)
|
||||
}
|
||||
```
|
||||
|
||||
2. Register when creating the service:
|
||||
|
||||
```go
|
||||
mcp.New(mcp.WithSubsystem(&mysubsystem.Subsystem{}))
|
||||
```
|
||||
|
||||
## Adding a new PHP tool
|
||||
|
||||
1. Create a tool class implementing `McpToolHandler`:
|
||||
|
||||
```php
|
||||
namespace Core\Mcp\Tools;
|
||||
|
||||
use Core\Front\Mcp\Contracts\McpToolHandler;
|
||||
use Core\Front\Mcp\McpContext;
|
||||
|
||||
class MyTool implements McpToolHandler
|
||||
{
|
||||
public static function schema(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'my_tool',
|
||||
'description' => 'Does something useful',
|
||||
'inputSchema' => [
|
||||
'type' => 'object',
|
||||
'properties' => [
|
||||
'query' => ['type' => 'string'],
|
||||
],
|
||||
'required' => ['query'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function handle(array $args, McpContext $context): array
|
||||
{
|
||||
return ['result' => 'done'];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. Register via the `McpToolsRegistering` lifecycle event in your module's
|
||||
Boot class.
|
||||
|
||||
## Contributing
|
||||
|
||||
- All changes must pass `core go qa` (Go) and `composer test` (PHP) before
|
||||
committing.
|
||||
- Use conventional commits: `feat(mcp): add new tool`, `fix(mcp): handle nil
|
||||
input`, `docs(mcp): update architecture`.
|
||||
- Include `Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>` when
|
||||
pair-programming with Claude.
|
||||
- Licence: EUPL-1.2. All new files must include the appropriate SPDX header.
|
||||
97
docs/index.md
Normal file
97
docs/index.md
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
---
|
||||
title: Core MCP
|
||||
description: Model Context Protocol server and tooling for AI agents -- Go binary + Laravel PHP package.
|
||||
---
|
||||
|
||||
# Core MCP
|
||||
|
||||
`forge.lthn.ai/core/mcp` provides a complete Model Context Protocol (MCP)
|
||||
implementation spanning two languages:
|
||||
|
||||
- **Go** -- a standalone MCP server binary (`core-mcp`) with file operations,
|
||||
ML inference, RAG, process management, webview automation, and WebSocket
|
||||
streaming.
|
||||
- **PHP** -- a Laravel package (`lthn/mcp`) that adds an HTTP MCP API, tool
|
||||
registry, SQL query tools, quota enforcement, circuit breakers, audit
|
||||
logging, and an admin panel to any Host UK application.
|
||||
|
||||
Both halves speak the same protocol and can bridge to one another via REST or
|
||||
WebSocket.
|
||||
|
||||
## Quick start
|
||||
|
||||
### Go binary
|
||||
|
||||
```bash
|
||||
# Build
|
||||
core build # produces ./core-mcp
|
||||
|
||||
# Start on stdio (for Claude Code / IDE integration)
|
||||
./core-mcp mcp serve
|
||||
|
||||
# Start on TCP
|
||||
MCP_ADDR=127.0.0.1:9100 ./core-mcp mcp serve
|
||||
|
||||
# Restrict file operations to a directory
|
||||
./core-mcp mcp serve --workspace /path/to/project
|
||||
```
|
||||
|
||||
### PHP package
|
||||
|
||||
Add the Composer dependency to a Laravel application:
|
||||
|
||||
```bash
|
||||
composer require lthn/mcp
|
||||
```
|
||||
|
||||
The package auto-registers via `Core\Front\Mcp\Boot`. No manual provider
|
||||
registration is needed. Run migrations, then visit the admin panel at
|
||||
`/admin/mcp`.
|
||||
|
||||
## Package layout
|
||||
|
||||
| Path | Language | Purpose |
|
||||
|------|----------|---------|
|
||||
| `pkg/mcp/` | Go | Core MCP server: `Service`, transports, tool registry, REST bridge |
|
||||
| `pkg/mcp/brain/` | Go | OpenBrain knowledge-store subsystem (remember/recall/forget/list) |
|
||||
| `pkg/mcp/ide/` | Go | IDE subsystem: Laravel WebSocket bridge, chat, builds, dashboard |
|
||||
| `cmd/core-mcp/` | Go | Binary entry point (`core-mcp`) |
|
||||
| `cmd/mcpcmd/` | Go | CLI command registration (`mcp serve`) |
|
||||
| `cmd/brain-seed/` | Go | Utility to import CLAUDE.md / MEMORY.md files into OpenBrain |
|
||||
| `src/php/src/Mcp/` | PHP | Laravel service provider, models, services, middleware, tools |
|
||||
| `src/php/src/Front/Mcp/` | PHP | MCP frontage: middleware group, `McpToolHandler` contract, `McpContext` |
|
||||
| `src/php/src/Website/Mcp/` | PHP | Public-facing MCP pages: playground, API explorer, metrics |
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Go
|
||||
|
||||
| Module | Role |
|
||||
|--------|------|
|
||||
| `forge.lthn.ai/core/go` | DI container and service lifecycle |
|
||||
| `forge.lthn.ai/core/cli` | CLI framework (bubbletea TUI) |
|
||||
| `forge.lthn.ai/core/go-io` | Filesystem abstraction (`Medium`, sandboxing) |
|
||||
| `forge.lthn.ai/core/go-log` | Structured logger |
|
||||
| `forge.lthn.ai/core/go-ai` | AI event metrics |
|
||||
| `forge.lthn.ai/core/go-ml` | ML inference, scoring, capability probes |
|
||||
| `forge.lthn.ai/core/go-inference` | Backend registry (Ollama, MLX, ROCm) |
|
||||
| `forge.lthn.ai/core/go-rag` | Qdrant vector search and document ingestion |
|
||||
| `forge.lthn.ai/core/go-process` | External process lifecycle management |
|
||||
| `forge.lthn.ai/core/go-webview` | Chrome DevTools Protocol automation |
|
||||
| `forge.lthn.ai/core/go-ws` | WebSocket hub and channel messaging |
|
||||
| `forge.lthn.ai/core/go-api` | REST API framework and `ToolBridge` |
|
||||
| `github.com/modelcontextprotocol/go-sdk` | Official MCP Go SDK |
|
||||
| `github.com/gin-gonic/gin` | HTTP router (REST bridge) |
|
||||
| `github.com/gorilla/websocket` | WebSocket client for IDE bridge |
|
||||
|
||||
### PHP
|
||||
|
||||
| Package | Role |
|
||||
|---------|------|
|
||||
| `lthn/php` (core/php) | Foundation framework: lifecycle events, modules, actions |
|
||||
| Laravel 12 | Application framework |
|
||||
| Livewire / Flux Pro | Admin panel components |
|
||||
|
||||
## Licence
|
||||
|
||||
EUPL-1.2. See the `SPDX-License-Identifier` headers in each source file.
|
||||
Loading…
Add table
Reference in a new issue