MCP Servers
Ship a governed MCP server definition inside a Scrydon extension and understand its approval, availability, and transport lifecycle
This artifact ships inside a Extension. For the shared lifecycle — extension build, upload, install — see Extensions & Authoring SDK.
An MCP server content entry lets an extension ship a fully-configured Model Context Protocol server — its transport, endpoint (or launch command), and advertised tools — as JSON-serializable configuration. Applying the extension registers the server at organization scope. An explicit admin install or a signed source registers it as approved; a non-interactive unsigned source sync registers it as pending for review. No one hand-enters URLs or commands.
Unlike an extension, an MCP server carries no code artifact — the manifest is the whole definition, so it travels inline in the extension with no archive.tar.gz.
Author with defineMcpServer
import { defineMcpServer } from "@scrydon/sdk-authoring/mcp-servers/define";
export default defineMcpServer({
// Stable kebab slug — the idempotency key. Renaming `name` later is safe;
// changing `id` registers a new server.
id: "github-mcp",
name: "GitHub MCP",
description: "Issue and PR tools for GitHub repositories.",
transport: "streamable-http",
url: "https://mcp.example.com/github",
// Advertised tools → the registry allowlist. Live discovery still happens at
// connect time; this is the governance surface.
tools: ["search_issues", "create_issue"],
});stdio is currently registration-only. It cannot become organization-default, be installed or enabled in a workspace, or execute. Runtime support requires the governed MCP plane introduced by PR #2140 and a separately reviewed stdio dispatcher on that plane; merging the plane alone does not enable stdio. Registered stdio entries remain visible and removable.
A stdio server declares a command (and optional args / envVarKeys) instead of a url:
export default defineMcpServer({
id: "everything",
name: "Everything",
transport: "stdio",
command: "npx",
args: ["-y", "@modelcontextprotocol/server-everything"],
envVarKeys: ["API_KEY"],
});Fields
| Field | Required | Notes |
|---|---|---|
id | ✅ | Stable lowercase kebab slug. The idempotency key — keep it stable across versions. |
name | ✅ | Display name. Must be unique within the org. |
description | — | Shown in the registry UI. |
transport | ✅ | "streamable-http" or "stdio". |
url | for HTTP | Endpoint for streamable-http. |
command / args | for stdio | Launch command for stdio. |
envVarKeys | — | Names reserved for the future governed stdio dispatcher. Values are never inlined; the current runtime does not execute stdio. |
tools | — | Advertised tool names → the registry allowlist. |
auth | — | Reference-only auth config — see below. Omit it and the server registers with no special auth. |
Authentication (auth)
Authentication ships by reference, never as an embedded token. The auth field is a small subset of the registry's auth model for packaged streamable HTTP servers that run headless or from a workflow with no user present:
kind | Use |
|---|---|
"none" | No auth header. |
"static_header" | Send the configured static headers map, whose {{secret}} placeholders are resolved from the workspace secret store at run time. (envVarKeys is the separate stdio-env mechanism, not header values.) |
"oauth_client_credentials" | Service-to-service OAuth 2.1. Requires tokenUrl, clientId, and clientSecretRef (a workspace-secret reference, never the secret itself); optional scopes, audience, resource, useBasicAuth. |
defineMcpServer({
id: "github-mcp",
name: "GitHub MCP",
transport: "streamable-http",
url: "https://mcp.example.com/github",
auth: {
kind: "oauth_client_credentials",
tokenUrl: "https://idp.example.com/oauth/token",
clientId: "scrydon-github-mcp",
clientSecretRef: "github-mcp-client-secret", // resolved from the workspace secret store
scopes: ["repo"],
},
});Do not put secrets in the manifest. The delegated, per-user modes (oauth_authorization_code, managed_ema) are intentionally not available to extensions — they need a user present, which a triggered/background run does not have.
Extension layout
Place each server in its own subdir; reference it from extension.json:
{
"contents": [
{ "kind": "mcp-server", "path": "mcp-server-github", "version": "1.0.0", "required": true }
],
"installOrder": ["mcp-server"]
}mcp-server-github/
manifest.json # the defineMcpServer output (no archive.tar.gz)An extension may ship several MCP servers — give each its own mcp-server-<slug>/ subdir.
Lifecycle
extension build, then install the extension or synchronize its source. An explicit admin install or signed source creates an approved registry row. A non-interactive unsigned sync creates a pending row.streamable-http server. A signed-in organization admin may instead make it available by default across the organization. A workspace can remove a default server to create an explicit opt-out, then add it again to reverse that choice. Configuring a default server materializes the workspace's overrides.(organization, extension, source id) updates one registry row in place and preserves its governance status. Renaming name while keeping id updates that row; changing id registers a different source entry.An extension cannot overwrite a server it does not own: if the org already has a server with the same name that was hand-created or shipped by a different extension, the install refuses that entry rather than clobbering it.
Registered servers surface on their extension's Includes tab under MCP servers. Approved streamable HTTP servers can then appear on Settings → Platform → Extensions and on each workspace's Extensions page according to their availability policy.