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.
7.0 KiB
MCP Registry Integration
Turnstone integrates with the official MCP Registry 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 (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:
[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
{
"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
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
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.