MCP Servers
Ship a governed MCP server definition inside a Scrydon Pack and understand its approval, availability, and transport lifecycle
This artifact ships inside a Pack. For the shared lifecycle — pack build, upload, install — see Packs & Authoring SDK.
An MCP server content entry lets a pack ship a fully-configured Model Context Protocol server — its transport, endpoint (or launch command), and advertised tools — as JSON-serializable configuration. Applying the pack 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 integration, an MCP server carries no code artifact — the manifest is the whole definition, so it travels inline in the pack with no bundle.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 packs — they need a user present, which a triggered/background run does not have.
Pack layout
Place each server in its own subdir; reference it from pack.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 bundle.tar.gz)A pack may ship several MCP servers — give each its own mcp-server-<slug>/ subdir.
Lifecycle
pack build, then install the pack 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, pack, 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.A pack 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 pack, the install refuses that entry rather than clobbering it.
Registered servers surface on the Packs page under Contributes → MCP servers. Approved streamable HTTP servers can then appear in organization settings and the workspace Marketplace according to their availability policy.