Files
turnstone/docs/mcp-registry.md
T
Patrick Buckley 471d1a3311 docs: audit documentation for 1.4 / 1.5 state
Systematic pass over every doc under docs/, the root-level README /
QUICKSTART / CONTRIBUTING, and the PlantUML diagrams.  Memory and docs
had drifted against the code since 1.2 — this catches them up to the
1.4.0 release and the 1.5.0a1 experimental line.

User-facing fixes
- README: fix broken docs/mcp.md link (→ mcp-registry.md); channel
  gateway entry reflects shipped Discord + Slack adapters instead of
  "Slack/Teams planned"; diagrams table mentions both.
- QUICKSTART: docs/*.md relative links were wrong from the repo root;
  wizard version bumped from 0.5.4.
- CONTRIBUTING: add dev extra plus the ruff / mypy / pytest commands
  we actually expect before push.

Reference docs
- architecture.md: 19 tool schemas (was 15), 18 admin tabs (was 14),
  turnstone-bootstrap added to entry-points table, OpenAI provider
  file split (chat/responses/common) documented, 38 SDK event
  dataclasses (was 27 and referenced deleted mq/protocol.py), Slack
  adapter + multi-adapter gateway, plan_agent/task_agent naming,
  governance admin-panel rewrite.
- api-reference.md: full attachment endpoints (POST/GET/content/
  DELETE on /v1/api/workstreams/{ws_id}/attachments) plus the
  multipart mode on POST /v1/api/workstreams/new.
- channels.md: Slack Setup section (Socket Mode app creation, OAuth
  scopes, tokens), Slack CLI/env reference in config table, combined-
  adapter architecture diagram.
- console.md: 18-tab listing (was 13) with Channels/Models/Nodes/TLS
  descriptions and ConfigStore live-edit note.
- docker.md: Slack env vars block; image entry-point list now
  includes turnstone / turnstone-bootstrap.
- sdk.md: attachments methods on the server client, attachments
  example (upload-then-send and at-creation), event count fixed.
- releasing.md: four-track table (stable/1.0, 1.3, 1.4 + main 1.5);
  promotion workflow uses 1.5 / 1.6 numbering.
- settings.md: plan_model / task_model / plan_effort / task_effort
  overrides section.
- governance.md: skill naming (/skill, `skill` field — not /template),
  Prompts/Judge tabs called out.
- security.md: two-token-types wording; src claim values match the
  AuthResult source strings actually emitted.
- mcp-registry.md: SDK package name is @turnstone/sdk.
- tools.md: plan / task renamed to plan_agent / task_agent in the
  section headings and summary table; primary-key table matched.
- design/consistent-hash-ring.md: dead direct-http-transport.md
  pointer redirected to architecture.md.

Diagrams
- 02-package-structure: drop phantom chat.py entry point, add admin
  and bootstrap, add slack/bot.py, rename channels/gateway.py →
  channels/cli.py.
- 16-channel-architecture: Slack is no longer "(future)", add a
  SlackBot class and the slack-bolt Socket Mode edges; wire the new
  bot into ChannelService.  PNGs regenerated from both puml sources.
2026-04-16 16:01:24 -07:00

194 lines
7.0 KiB
Markdown

# MCP Registry Integration
Turnstone integrates with the [official MCP Registry](https://registry.modelcontextprotocol.io) to let administrators discover and install MCP servers directly from the console admin panel.
## Overview
The MCP Registry is maintained by the [Agentic AI Foundation](https://www.linuxfoundation.org/press/linux-foundation-announces-the-formation-of-the-agentic-ai-foundation) (Linux Foundation) and serves as the canonical discovery layer for MCP servers. Turnstone queries its REST API (v0.1) for server metadata and provides a one-click install flow.
Three sources of MCP servers coexist in Turnstone:
| Source | Badge | Description |
|--------|-------|-------------|
| **Config** | `CONFIG` (magenta) | Imported from `config.toml` or JSON file. Read-only in admin UI. |
| **Manual** | `MANUAL` (cyan) | Added through the admin UI or API. Full CRUD. |
| **Registry** | `REGISTRY` (green) | Installed from the MCP Registry. Tracked by `registry_name`. |
## Admin UI
The MCP admin tab has two views, toggled by a pill selector:
### Servers View
Lists all installed MCP servers regardless of source. Each server shows:
- **Source badge** — CONFIG, MANUAL, or REGISTRY
- **Transport badge** — stdio or streamable-http
- **Tool/resource/prompt counts** — aggregated across cluster nodes
- **Per-node connection status** — connected (magenta dot), error (red), disabled (gray)
- **Actions** — Edit / Delete (DB-managed servers only)
Clicking a server name opens the detail modal. For registry-installed servers, the detail modal includes a **Registry** section showing the registry name, installed version, description, and website link.
### Registry View
Search and browse the MCP Registry. Switching to this view auto-loads a listing. Type a query and press Enter or click Search to filter.
Each result card shows:
- **Server name and description**
- **Source type badges** — remote (streamable-http), npm, pypi
- **Version number**
- **Install / Installed / Update button**
#### Install flow
- **One-click**: Remote servers with no required headers or URL variables install immediately — no modal, no form. The server is added to the database, all cluster nodes are notified, and a toast confirms success.
- **Modal**: Servers that require configuration (API keys, headers, URL template variables) or offer multiple install sources (both remote and package) open an install modal with:
- Source selector (radio group) — only shown when both remote and package are available
- Dynamic form fields for required/optional configuration
- Secret fields rendered as password inputs
## Configuration
### Registry URL
By default, Turnstone queries `https://registry.modelcontextprotocol.io`. Override this for enterprise or private registries:
**Via admin Settings tab:**
Set `mcp.registry_url` to your registry's base URL.
**Via config.toml:**
```toml
[mcp]
registry_url = "https://registry.internal.example.com"
```
The resolution order is: database setting > config.toml > default.
## API Endpoints
Both endpoints require `admin.mcp` permission.
### Search
```
GET /v1/api/admin/mcp-registry/search?search=github&limit=20&cursor=...
```
Query parameters:
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `search` | string | `""` | Search query. Empty returns a browsable listing. |
| `limit` | integer | `20` | Results per page (max 100). |
| `cursor` | string | — | Opaque cursor from `next_cursor` for pagination. |
The response annotates each server with `installed`, `installed_server_id`, `installed_version`, and `update_available` by cross-referencing the `mcp_servers` table.
### Install
```
POST /v1/api/admin/mcp-registry/install
```
```json
{
"registry_name": "io.example/mcp-server",
"source": "remote",
"index": 0,
"name": "",
"variables": {},
"env": {"API_KEY": "sk-..."},
"headers": {"Authorization": "Bearer ..."}
}
```
| Field | Required | Description |
|-------|----------|-------------|
| `registry_name` | Yes | Server name from registry search results. |
| `source` | Yes | `"remote"` (streamable-http) or `"package"` (npm/pypi). |
| `index` | No | Which remote or package entry to use (default `0`). |
| `name` | No | Custom server name. Auto-derived from registry name if empty. |
| `variables` | No | Values for URL template `{var}` placeholders. |
| `env` | No | Environment variable values for package servers. |
| `headers` | No | Header values for remote servers. |
On success, the server is created in the database and all cluster nodes are automatically reloaded. Returns the created `McpServerDetail`.
Errors: `400` (validation), `404` (not found in registry), `409` (already installed or name collision), `502` (registry unreachable).
## SDK
### Python
```python
from turnstone.sdk.console import TurnstoneConsole
with TurnstoneConsole("http://localhost:8081", token="...") as client:
# Search
results = client.search_mcp_registry(q="github", limit=10)
for srv in results.servers:
print(f"{srv.name} v{srv.version} - {srv.description}")
# Install a remote server
detail = client.install_from_registry(
"io.example/mcp-server",
"remote",
headers={"Authorization": "Bearer sk-..."},
)
print(f"Installed: {detail.name}")
```
### TypeScript
```typescript
import { TurnstoneConsole } from "@turnstone/sdk";
const client = new TurnstoneConsole({
baseUrl: "http://localhost:8081",
token: "...",
});
// Search
const results = await client.searchMcpRegistry({ q: "github", limit: 10 });
for (const srv of results.servers) {
console.log(`${srv.name} v${srv.version} - ${srv.description}`);
}
// Install
const detail = await client.installFromRegistry({
registry_name: "io.example/mcp-server",
source: "remote",
headers: { Authorization: "Bearer sk-..." },
});
```
## Storage
Registry-installed servers are stored in the existing `mcp_servers` table with three additional columns (migration 019):
| Column | Type | Description |
|--------|------|-------------|
| `registry_name` | TEXT (nullable, unique) | Reverse-DNS name from the registry (e.g. `io.example/mcp-server`). |
| `registry_version` | TEXT | Version at time of install. |
| `registry_meta` | TEXT (JSON) | Snapshot of description, title, website, icons for display. |
The partial unique index on `registry_name` prevents duplicate installs while allowing multiple non-registry servers with `NULL` registry_name.
## Package Type Support
| Registry Type | Transport | Command | Status |
|--------------|-----------|---------|--------|
| Remote (streamable-http) | `streamable-http` | — (URL-based) | Supported |
| `npm` | `stdio` | `npx -y @scope/package@version` | Supported |
| `pypi` | `stdio` | `uvx package==version` | Supported |
| `oci` | — | — | Not supported (no runtime available) |
| `nuget` | — | — | Not supported |
| `mcpb` | — | — | Not supported |
For `npm` and `pypi` packages, the corresponding runtime (`node`/`npx` or `python`/`uvx`) must be available on the cluster nodes. Connection failures due to missing runtimes appear in the per-node MCP status display.