@github/mcp-registry
v0.1.7
Published
TypeScript SDK for the MCP Registry API — includes types, a ready-to-use client, and deterministic fingerprint computation for enterprise allowlist enforcement.
Readme
@github/mcp-registry
TypeScript SDK for the MCP Registry — includes a registry API client, generated types, and deterministic fingerprint computation for enterprise allowlist enforcement.
Installation
npm install @github/mcp-registryRegistry Client
import { RegistryClient } from '@github/mcp-registry';
const client = new RegistryClient();
// Search for servers
const servers = await client.searchServers({ query: 'github', limit: 5 });
for (const s of servers) {
console.log(`${s.server.name}@${s.server.version}`);
}
// Get a specific server
const server = await client.getServer({ name: 'io.github.user/repo' });
// Get a specific version
const pinned = await client.getServer({
name: 'io.github.user/repo',
version: '1.2.0',
});
// Parse "name@version" format
const entry = await client.getDsl('io.github.user/[email protected]');Configuration
const client = new RegistryClient({
baseUrl: 'https://api.mcp.github.com/v0.1', // default
cacheTtlMs: 5 * 60 * 1000, // 5 minutes (default)
timeoutMs: 30_000, // 30 seconds (default)
fetch: customFetch, // optional custom fetch
});Error Handling
The client returns null for 404s and throws RegistryError for other HTTP errors:
import { RegistryClient, RegistryError } from '@github/mcp-registry';
const server = await client.getServer({ name: 'example/server' });
if (!server) {
console.log('Not found');
}Fingerprint Computation
Deterministic SHA-256 fingerprints for enterprise allowlist enforcement — computes the same fingerprints as the registry backend.
import { computeFingerprint, verifyFingerprint, commandToRegistryType } from '@github/mcp-registry';
// Remote server
const fp = computeFingerprint({
remotes: [{ url: 'https://mcp.example.com/sse' }],
});
// Local package
const registryType = commandToRegistryType('npx'); // → "npm"
const fp2 = computeFingerprint({
packages: [{ registryType: registryType!, identifier: '@playwright/mcp' }],
});
// Verify against the enterprise allowlist
const matches = verifyFingerprint(
{ packages: [{ registryType: 'npm', identifier: '@playwright/mcp' }] },
allowlistEntry.server.fingerprints ?? {},
);Command Mapping
| Command | Registry Type |
| ------- | ------------- |
| npx, npm | npm |
| uvx, pip | pypi |
| dotnet | nuget |
| docker | docker |
| brew | homebrew |
DSL Engine
The DSL engine extracts configurable fields from server definitions, validates user-provided configuration, and computes launch plans (the command + env + headers needed to start a server).
Quick Start — Registry Client + DSL Engine
import { RegistryClient, DslEngine } from '@github/mcp-registry';
// 1. Fetch a server definition from the registry
const client = new RegistryClient();
const response = await client.getServer({ name: 'io.github.user/repo' });
if (!response) throw new Error('Server not found');
// 2. Create a DSL engine from the response
const engine = DslEngine.fromServerResponse(response);
// 3. Discover what configuration the server needs
const requiredFields = engine.getRequiredFields();
for (const field of requiredFields) {
console.log(`${field.key} (${field.type})${field.isSecret ? ' [secret]' : ''}`);
}
// 4. Validate user-provided configuration
const validation = engine.validate({
environmentVariables: { API_KEY: 'sk-...' },
});
if (!validation.valid) {
console.log('Missing:', validation.missingFields.map(f => f.key));
}
// 5. Resolve a launch plan
const result = engine.resolve({
environmentVariables: { API_KEY: 'sk-...' },
});
if (result.complete && result.plan) {
if (result.plan.type === 'package') {
// Start a local process
console.log('Command:', result.plan.command.join(' '));
console.log('Env:', result.plan.env);
} else {
// Connect to a remote server
console.log('Endpoint:', result.plan.endpoint);
console.log('Headers:', result.plan.headers);
}
}Extracting Fields
Use extractConfigurableFields directly when you don't need the full engine:
import { extractConfigurableFields } from '@github/mcp-registry';
import type { ServerDetail } from '@github/mcp-registry';
const fields = extractConfigurableFields(server);
// fields: FieldSpec[] — key, type, required, isSecret, enumValues, etc.
// Extract fields for a specific remote instead of the default package
const remoteFields = extractConfigurableFields(server, { remoteIndex: 0 });Resolving Launch Plans
import { resolveLaunchPlan } from '@github/mcp-registry';
// Package-based (local process)
const plan = resolveLaunchPlan({
spec: server,
config: { environmentVariables: { API_KEY: 'sk-...' } },
});
// plan.type === 'package'
// plan.command → ['npx', '-y', '@scope/[email protected]']
// plan.env → { API_KEY: 'sk-...' }
// Remote-based (HTTP connection)
const remotePlan = resolveLaunchPlan({
spec: server,
config: { headers: { Authorization: 'Bearer token' } },
remoteIndex: 0,
});
// remotePlan.type === 'remote'
// remotePlan.endpoint → 'https://api.example.com/mcp'
// remotePlan.headers → { Authorization: 'Bearer token' }Enumerating Installation Choices
Use one engine call to inspect every advertised package and remote without constructing an engine for each array index:
const result = engine.enumerateInstallChoices(
{ headers: { Authorization: 'Bearer ...' } },
{
policy: choice => choice.transport === 'sse'
? { decision: 'denied', code: 'sse-disabled', reason: 'SSE is disabled by policy' }
: { decision: 'allowed' },
},
);
// Packages first, then remotes, preserving each source array's order.
for (const choice of result.advertisedChoices) {
console.log(choice.choiceId, choice.transport, choice.validation, choice.policy);
if (choice.eligible) {
console.log(choice.configurableFields, choice.launchPlan);
} else {
console.log(choice.ineligibility);
}
}
console.log(result.eligibleChoices);The exact method signature is:
enumerateInstallChoices(
config?: ConfigValues,
options?: EnumerateInstallChoicesOptions,
): InstallChoicesResultChoice IDs are deterministic for a descriptor: package:<index> and
remote:<index>. Supply remote URL template values through
ConfigValues.urlVariables. Configuration errors do not hide structurally
eligible choices, so callers can present required fields while retaining the
inert launch plan. Invalid, unsupported, and policy-denied advertisements
remain in advertisedChoices with typed explanations.
License
MIT
