From 092d152cf7bea4cf30e467cec8a74eb8e792c2af Mon Sep 17 00:00:00 2001 From: Juan Antonio Osorio Date: Tue, 20 Jan 2026 13:46:16 +0200 Subject: [PATCH] feat: add package-mcp-server skill for Claude Code Add an Agent Skill to help users package MCP servers for Dockyard: - SKILL.md with step-by-step workflow using task commands - references/SPEC-YAML-REFERENCE.md with full spec.yaml documentation The skill guides users through: 1. Gathering package information 2. Creating directory structure 3. Writing spec.yaml configuration 4. Building dockhand CLI (task build-setup) 5. Verifying provenance 6. Testing locally (task build, task scan, task test-build) 7. Committing changes Co-Authored-By: Claude Opus 4.5 --- .claude/skills/package-mcp-server/SKILL.md | 175 ++++++++++++ .../references/SPEC-YAML-REFERENCE.md | 250 ++++++++++++++++++ 2 files changed, 425 insertions(+) create mode 100644 .claude/skills/package-mcp-server/SKILL.md create mode 100644 .claude/skills/package-mcp-server/references/SPEC-YAML-REFERENCE.md diff --git a/.claude/skills/package-mcp-server/SKILL.md b/.claude/skills/package-mcp-server/SKILL.md new file mode 100644 index 0000000..e78ebeb --- /dev/null +++ b/.claude/skills/package-mcp-server/SKILL.md @@ -0,0 +1,175 @@ +--- +name: package-mcp-server +description: Creates spec.yaml configurations for packaging MCP servers as containers. Use when adding a new MCP server to Dockyard, creating a spec.yaml file, or packaging npm/PyPI/Go MCP servers. +--- + +# Package MCP Server for Dockyard + +This skill helps you package MCP servers for distribution via Dockyard containers. + +## When to Use This Skill + +Use this skill when: +- Adding a new MCP server to Dockyard +- Creating a spec.yaml configuration file +- Packaging an npm (npx), PyPI (uvx), or Go MCP server +- The user mentions "package", "add server", "spec.yaml", or "dockyard" + +## Prerequisites + +- The MCP server must be published to npm, PyPI, or as a Go module +- Know the exact package name and version + +## Workflow + +### Step 1: Gather Information + +Ask the user for: +1. **Package name** - The exact registry name (e.g., `@upstash/context7-mcp`, `mcp-clickhouse`) +2. **Package type** - npm (npx), PyPI (uvx), or Go +3. **Version** - Specific version to package (not "latest") +4. **Source repository** - GitHub URL for the source code + +If not provided, look up the package: + +```bash +# For npm packages +npm view {package-name} version +npm view {package-name} repository.url + +# For PyPI packages +curl -s https://pypi.org/pypi/{package-name}/json | jq -r '.info.version, .info.project_urls.Homepage' +``` + +### Step 2: Determine Protocol Directory + +| Package Type | Directory | Registry | +|--------------|-----------|----------| +| Node.js/npm | `npx/` | npm | +| Python/PyPI | `uvx/` | PyPI | +| Go module | `go/` | Go modules | + +### Step 3: Create Directory Structure + +```bash +mkdir -p {protocol}/{server-name} +``` + +The server name should be: +- Lowercase with hyphens +- Based on the package name (without scope) +- Example: `@upstash/context7-mcp` becomes `context7` + +### Step 4: Create spec.yaml + +Write the configuration file to `{protocol}/{server-name}/spec.yaml`: + +```yaml +# {Server Name} MCP Server Configuration +# Package: {package-registry-url} +# Repository: {source-repository-url} +# Will build as: ghcr.io/stacklok/dockyard/{protocol}/{name}:{version} + +metadata: + name: {server-name} + description: "{brief-description}" + version: "{version}" + protocol: {npx|uvx|go} + +spec: + package: "{full-package-name}" + version: "{exact-version}" + # args: # Optional: CLI arguments + # - "start" # Some packages need specific commands + +provenance: + repository_uri: "{github-repo-url}" + repository_ref: "refs/tags/v{version}" + + # Optional: Document attestations if available + # attestations: + # available: true + # verified: true + # publisher: + # kind: "GitHub" + # repository: "{owner/repo}" +``` + +### Step 5: Build dockhand CLI (First Time Only) + +```bash +# Build the dockhand CLI tool +task build-setup +``` + +### Step 6: Verify Provenance (Recommended) + +Check if the package has provenance attestations: + +```bash +# Check provenance +./build/dockhand verify-provenance -c {protocol}/{server-name}/spec.yaml -v +``` + +If provenance exists, update the spec.yaml with attestation information. + +### Step 7: Test Locally + +```bash +# Setup scanner (first time only) +task scan-setup + +# Validate spec and generate Dockerfile +task build -- {protocol}/{server-name} + +# Run security scan +task scan -- {protocol}/{server-name} + +# Optional: Full build test +task test-build -- {protocol}/{server-name} +``` + +### Step 8: Commit + +```bash +git add {protocol}/{server-name}/spec.yaml +git commit -m "feat: add {server-name} MCP server + +Add packaging for {server-name} v{version}. +Package: {package-url} +Repository: {repo-url}" +``` + +## Protocol-Specific Notes + +### npm (npx) + +- Package name may include scope: `@org/package-name` +- Some packages require CLI args (e.g., `args: ["start"]`) +- Check if package has npm provenance signatures + +### PyPI (uvx) + +- Package name is usually lowercase with hyphens +- Check for PEP 740 attestations +- AWS Labs packages have verified attestations + +### Go + +- Package is the full module path: `github.com/org/repo` +- Version must include `v` prefix: `v0.3.1` + +## Common Issues + +| Issue | Solution | +|-------|----------| +| Package not found | Verify exact name in registry | +| Version doesn't exist | Check available versions with `npm view` or PyPI API | +| Security scan fails | Review issues, add allowlist if false positive | +| Build fails | Check Dockerfile output with `dockhand build -c spec.yaml` | + +## See Also + +- [SPEC-YAML-REFERENCE.md](references/SPEC-YAML-REFERENCE.md) - Full spec.yaml reference +- [docs/adding-servers.md](../../../docs/adding-servers.md) - Complete contribution guide +- [docs/provenance.md](../../../docs/provenance.md) - Provenance verification details diff --git a/.claude/skills/package-mcp-server/references/SPEC-YAML-REFERENCE.md b/.claude/skills/package-mcp-server/references/SPEC-YAML-REFERENCE.md new file mode 100644 index 0000000..8a7aaf9 --- /dev/null +++ b/.claude/skills/package-mcp-server/references/SPEC-YAML-REFERENCE.md @@ -0,0 +1,250 @@ +# spec.yaml Full Reference + +Complete reference for Dockyard MCP server specification files. + +## File Location + +``` +{protocol}/{server-name}/spec.yaml +``` + +Where `{protocol}` is one of: `npx`, `uvx`, `go` + +## Full Schema + +```yaml +# Comments documenting the package (optional but recommended) +# Package URL: https://... +# Repository: https://... +# Will build as: ghcr.io/stacklok/dockyard/{protocol}/{name}:{version} + +metadata: + name: string # Required: Server identifier (lowercase, hyphens) + description: string # Optional: Brief description + version: string # Optional: Server version (informational) + protocol: string # Required: npx | uvx | go + +spec: + package: string # Required: Package identifier from registry + version: string # Required: Exact version (no ranges) + args: # Optional: CLI arguments array + - string + +provenance: + repository_uri: string # Optional: Expected source repository URL + repository_ref: string # Optional: Git ref (refs/tags/v1.0.0) + + attestations: # Optional: Document provenance status + available: boolean # Whether attestations exist + verified: boolean # Whether you verified them + publisher: + kind: string # Publisher type: GitHub, GitLab + repository: string # Publisher repository (owner/repo) + workflow: string # Publishing workflow file (optional) + +security: + allowed_issues: # Optional: Security scan allowlist + - code: string # Issue code from mcp-scanner + reason: string # Explanation of why it's acceptable +``` + +## Field Details + +### metadata.name + +- **Required**: Yes +- **Format**: Lowercase letters, numbers, hyphens only +- **Purpose**: Unique identifier, used in container image name +- **Example**: `context7`, `aws-documentation-mcp-server` + +### metadata.protocol + +- **Required**: Yes +- **Values**: `npx`, `uvx`, `go` +- **Purpose**: Determines build method and base image + +### spec.package + +- **Required**: Yes +- **Format**: Registry-specific package identifier +- **Examples**: + - npm: `@upstash/context7-mcp` or `my-mcp-server` + - PyPI: `mcp-clickhouse` + - Go: `github.com/org/repo` + +### spec.version + +- **Required**: Yes +- **Format**: Exact version, no ranges +- **Examples**: + - npm/PyPI: `1.0.14`, `0.1.13` + - Go: `v0.3.1` (must include `v` prefix) + +### spec.args + +- **Required**: No +- **Format**: Array of strings +- **Purpose**: Additional CLI arguments passed to entrypoint +- **Example**: + ```yaml + args: + - "start" + - "--port" + - "8080" + ``` + +### provenance.repository_uri + +- **Required**: No (but recommended) +- **Format**: Full HTTPS URL +- **Purpose**: Expected source repository for verification +- **Example**: `https://github.com/upstash/context7-mcp` + +### provenance.attestations + +- **Required**: No +- **Purpose**: Document package provenance status +- **Fields**: + - `available`: Whether the package has attestations + - `verified`: Whether you've verified them cryptographically + - `publisher.kind`: Publisher type (GitHub, GitLab) + - `publisher.repository`: Owner/repo format + - `publisher.workflow`: Workflow file path (optional) + +### security.allowed_issues + +- **Required**: No +- **Purpose**: Allowlist false positives from security scan +- **Fields**: + - `code`: Issue code from mcp-scanner (e.g., `AITech-1.1`) + - `reason`: Clear explanation of why it's acceptable + +## Examples by Protocol + +### npm (npx) + +```yaml +# Context7 MCP Server +# Package: https://www.npmjs.com/package/@upstash/context7-mcp +# Repository: https://github.com/upstash/context7-mcp +# Will build as: ghcr.io/stacklok/dockyard/npx/context7:2.1.0 + +metadata: + name: context7 + description: "Upstash vector search and context management" + version: "1.0.14" + protocol: npx + +spec: + package: "@upstash/context7-mcp" + version: "1.0.14" + +provenance: + repository_uri: "https://github.com/upstash/context7-mcp" + repository_ref: "refs/tags/v1.0.14" +``` + +### npm with args + +```yaml +# LaunchDarkly MCP Server +metadata: + name: launchdarkly-mcp-server + description: "LaunchDarkly feature flag management" + version: "0.4.2" + protocol: npx + +spec: + package: "@launchdarkly/mcp-server" + version: "0.4.2" + args: + - "start" # Required by this package + +provenance: + repository_uri: "https://github.com/launchdarkly/mcp-server" + repository_ref: "refs/tags/v0.4.2" +``` + +### PyPI (uvx) + +```yaml +# ClickHouse MCP Server +# Package: https://pypi.org/project/mcp-clickhouse/ +# Repository: https://github.com/ClickHouse/mcp-clickhouse +# Will build as: ghcr.io/stacklok/dockyard/uvx/mcp-clickhouse:0.1.13 + +metadata: + name: mcp-clickhouse + description: "MCP server for ClickHouse database operations" + version: "0.1.13" + protocol: uvx + +spec: + package: "mcp-clickhouse" + version: "0.1.13" + +provenance: + repository_uri: "https://github.com/ClickHouse/mcp-clickhouse" + repository_ref: "refs/heads/main" + + attestations: + available: true + verified: true + publisher: + kind: "GitHub" + repository: "ClickHouse/mcp-clickhouse" +``` + +### Go + +```yaml +# NetBird MCP Server +# Module: github.com/netbirdio/netbird-mcp +# Will build as: ghcr.io/stacklok/dockyard/go/netbird:v0.1.0 + +metadata: + name: netbird + description: "NetBird network management MCP server" + version: "0.1.0" + protocol: go + +spec: + package: "github.com/netbirdio/netbird-mcp" + version: "v0.1.0" # Note: Go versions must include 'v' prefix + +provenance: + repository_uri: "https://github.com/netbirdio/netbird-mcp" + repository_ref: "refs/tags/v0.1.0" +``` + +### With Security Allowlist + +```yaml +metadata: + name: my-server + protocol: npx + +spec: + package: "my-mcp-server" + version: "1.0.0" + +security: + allowed_issues: + - code: "AITech-1.1" + reason: "Tool description contains imperative instructions necessary for proper AI agent operation" + - code: "AITech-9.1" + reason: "Destructive flow is mitigated by container sandboxing - isolated from host" +``` + +## Container Image Naming + +Images are published to: + +``` +ghcr.io/stacklok/dockyard/{protocol}/{name}:{version} +``` + +Where: +- `{protocol}` matches `metadata.protocol` +- `{name}` matches `metadata.name` +- `{version}` matches `spec.version`