# Phase 4: Distribution - Research

**Researched:** 2026-04-12
**Domain:** Claude Code plugin packaging, cplugs marketplace publishing, MCP server auto-registration
**Confidence:** HIGH

## Summary

This phase packages the screen-timelapse MCP server as a cplugs marketplace plugin. Research focused on two questions: (1) what is the exact plugin directory structure and manifest format, and (2) how does Claude Code discover and auto-register MCP servers from installed plugins.

By examining the existing `spt` plugin on the cplugs marketplace and the `mempalace` plugin (which registers an MCP server), the required structure is clear. A `.claude-plugin/plugin.json` manifest defines the plugin metadata and can include an `mcpServers` field for automatic MCP server registration. Alternatively, a `.mcp.json` file at the plugin root achieves the same thing (this is the dominant pattern in the official marketplace). Both approaches are verified to work.

**Primary recommendation:** Use the `mcpServers` field in `.claude-plugin/plugin.json` (mempalace pattern) since it keeps all plugin metadata in one file, and add a `.mcp.json` file as a secondary mechanism for broader compatibility.

<user_constraints>
## User Constraints (from CONTEXT.md)

### Locked Decisions
- **D-01:** Follow cplugs plugin convention: `.claude-plugin/plugin.json` manifest at plugin root
- **D-02:** Plugin directory structure: `.claude-plugin/plugin.json`, `dist/` (built output), `package.json`, `node_modules/` (production deps only)
- **D-03:** plugin.json contains: name ("screen-timelapse"), version (from package.json), description, author, keywords
- **D-04:** The plugin root IS the project directory -- no separate plugin package needed. The project builds in place and the marketplace points to it.
- **D-05:** tsup bundles TypeScript to `dist/index.js` (already configured in package.json)
- **D-06:** Build step: `npm run build` produces the dist/ output ready for plugin use
- **D-07:** Production install: `npm install --omit=dev` for minimal node_modules in distributed plugin
- **D-08:** Native dependencies (sharp, node-screenshots) ship prebuilt binaries -- no build tools needed at install time
- **D-09:** `/plugin install screen-timelapse@cplugs` copies the plugin from marketplace to cache
- **D-10:** Post-install, the plugin's MCP server is available via the registered command
- **D-11:** No interactive prompts during install -- fully automated
- **D-12:** Plugin provides an MCP server configuration that registers automatically
- **D-13:** MCP server command: `node {plugin_path}/dist/index.js` using stdio transport
- **D-14:** Server name in MCP config: "screen-timelapse"
- **D-15:** No environment variables or manual configuration required for basic functionality
- **D-16:** Plugin published to cplugs marketplace at `~/.claude/plugins/marketplaces/cplugs/plugins/screen-timelapse/`
- **D-17:** Marketplace entry includes: .claude-plugin/plugin.json, dist/, package.json, node_modules/
- **D-18:** README.md in marketplace root updated to list screen-timelapse

### Claude's Discretion
- Exact tsup configuration details (format, target, etc.)
- Whether to include a post-install script or rely on pre-built artifacts
- Plugin icon or additional metadata fields
- Whether to add a CHANGELOG.md to the plugin

### Deferred Ideas (OUT OF SCOPE)
None -- discussion stayed within phase scope.
</user_constraints>

<phase_requirements>
## Phase Requirements

| ID | Description | Research Support |
|----|-------------|------------------|
| DIST-01 | Tool is packaged as a cplugs marketplace plugin | Plugin structure verified from spt reference; plugin.json schema verified from mempalace |
| DIST-02 | Plugin installs via `/plugin install screen-timelapse@cplugs` | Installation record format verified from installed_plugins.json; marketplace.json entry format verified from cplugs marketplace |
| DIST-03 | Plugin includes MCP server configuration for automatic registration | mcpServers field in plugin.json verified from mempalace plugin; .mcp.json alternative verified from official marketplace plugins |
</phase_requirements>

## Standard Stack

No new libraries needed. This phase uses existing project infrastructure (tsup for build, existing dependencies).

### Build Tool Configuration
| Tool | Version | Purpose | Status |
|------|---------|---------|--------|
| tsup | ^8.0.0 | Bundle TypeScript to dist/ | Already in devDependencies |
| npm | system | Production install for plugin packaging | Available |

## Architecture Patterns

### Plugin Directory Structure (Verified)

The plugin root IS the project directory. The marketplace entry is a copy/symlink of the project.

```
screen-timelapse/                    # Plugin root = project root
  .claude-plugin/
    plugin.json                      # Plugin manifest with mcpServers
  .mcp.json                          # MCP server config (redundant but compatible)
  dist/
    index.js                         # Built entry point
  package.json                       # Standard npm package
  node_modules/                      # Production deps only in marketplace copy
```

[VERIFIED: ~/.claude/plugins/marketplaces/cplugs/plugins/spt/ directory structure]

### Pattern 1: plugin.json Manifest with mcpServers

**What:** The `.claude-plugin/plugin.json` file defines plugin metadata AND MCP server registration in one file.
**When to use:** For plugins that provide MCP servers.
**Verified from:** mempalace plugin at `~/.claude/plugins/cache/mempalace/mempalace/3.0.14/plugin.json`

```json
{
  "name": "screen-timelapse",
  "version": "0.1.0",
  "description": "MCP server for capturing screen timelapse sequences and compiling grid images",
  "author": {
    "name": "Reavo End"
  },
  "mcpServers": {
    "screen-timelapse": {
      "command": "node",
      "args": ["dist/index.js"]
    }
  },
  "keywords": ["screenshot", "timelapse", "mcp", "grid", "debugging"]
}
```

[VERIFIED: mempalace plugin.json at ~/.claude/plugins/cache/mempalace/mempalace/3.0.14/plugin.json]

### Pattern 2: .mcp.json File (Alternative/Supplementary)

**What:** A `.mcp.json` file at the plugin root following the standard Claude Code MCP config format.
**When to use:** The dominant pattern in official marketplace plugins. Provides compatibility with standard MCP discovery.

```json
{
  "screen-timelapse": {
    "command": "node",
    "args": ["dist/index.js"]
  }
}
```

[VERIFIED: context7, firebase, and other official plugins at ~/.claude/plugins/marketplaces/claude-plugins-official/external_plugins/]

### Pattern 3: Marketplace Registration (marketplace.json)

**What:** The cplugs marketplace.json must list the new plugin for discovery.
**File:** `~/.claude/plugins/marketplaces/cplugs/.claude-plugin/marketplace.json`

Current content needs a new entry in the `plugins` array:

```json
{
  "name": "screen-timelapse",
  "description": "MCP server for capturing screen timelapse sequences and compiling grid images",
  "author": {
    "name": "Reavo End"
  },
  "source": "./plugins/screen-timelapse",
  "category": "development"
}
```

[VERIFIED: existing cplugs marketplace.json structure at ~/.claude/plugins/marketplaces/cplugs/.claude-plugin/marketplace.json]

### Pattern 4: installed_plugins.json Record

**What:** After `/plugin install`, Claude Code writes an entry to `~/.claude/plugins/installed_plugins.json`.
**This is NOT something we create** -- Claude Code manages it. But we need to understand its structure to verify successful installation.

```json
{
  "screen-timelapse@cplugs": [
    {
      "scope": "user",
      "installPath": "C:\\Users\\decid\\.claude\\plugins\\cache\\cplugs\\screen-timelapse\\0.1.0",
      "version": "0.1.0",
      "installedAt": "...",
      "lastUpdated": "..."
    }
  ]
}
```

[VERIFIED: structure from ~/.claude/plugins/installed_plugins.json]

### Anti-Patterns to Avoid
- **Building at install time:** Pre-build the dist/ directory. End users should not need TypeScript tooling. [VERIFIED: D-06 decision, and spt plugin ships a pre-built owl.exe]
- **Including devDependencies:** The marketplace copy should only have production node_modules. Use `npm install --omit=dev`. [VERIFIED: D-07 decision]
- **Hardcoding absolute paths in mcpServers:** The `command` args should use relative paths (e.g., `dist/index.js`). Claude Code resolves them relative to the plugin install path. [VERIFIED: mempalace uses relative module path `-m mempalace.mcp_server`]

## Don't Hand-Roll

| Problem | Don't Build | Use Instead | Why |
|---------|-------------|-------------|-----|
| Plugin packaging | Custom zip/tar scripts | Direct directory copy to marketplace | cplugs is a local filesystem marketplace -- no packaging format needed |
| MCP server registration | Manual claude_desktop_config.json editing | mcpServers in plugin.json | Claude Code auto-discovers from plugin manifest |
| Build bundling | Custom esbuild/rollup config | tsup (already configured) | tsup handles ESM output with zero config for this project |

## Common Pitfalls

### Pitfall 1: Native Dependencies Missing Prebuilt Binaries
**What goes wrong:** sharp or node-screenshots fail to load after copying to marketplace because prebuilt binaries were not included in the copy.
**Why it happens:** `node_modules` is copied incompletely, or `npm install --omit=dev` is run on a different platform.
**How to avoid:** Run `npm install --omit=dev` in the marketplace target directory on the same platform (Windows 11). Verify sharp and node-screenshots load correctly from the installed path.
**Warning signs:** `Error: Cannot find module '../build/Release/...'` at runtime.

### Pitfall 2: Relative Path Resolution in mcpServers
**What goes wrong:** The MCP server command `node dist/index.js` fails because the working directory is not the plugin root.
**Why it happens:** Claude Code may invoke the command from a different cwd.
**How to avoid:** Use the same pattern as existing plugins. If needed, the args can include the full path. Test by running `/plugin install` and verifying the MCP server starts.
**Warning signs:** `Error: Cannot find module './dist/index.js'`

### Pitfall 3: ESM Shebang and Module Format
**What goes wrong:** `node dist/index.js` fails with ESM/CJS mismatch errors.
**Why it happens:** package.json has `"type": "module"` but tsup default output may be CJS.
**How to avoid:** Configure tsup to output ESM format (`format: ['esm']`) since package.json declares `"type": "module"`. Ensure dist/index.js has proper ESM syntax.
**Warning signs:** `SyntaxError: Cannot use import statement in a module` or `ERR_REQUIRE_ESM`

### Pitfall 4: Forgetting to Update marketplace.json
**What goes wrong:** Plugin is correctly placed in the directory but `/plugin install screen-timelapse@cplugs` says plugin not found.
**Why it happens:** The marketplace.json `plugins` array was not updated to include the new plugin.
**How to avoid:** Add the entry to marketplace.json as part of the publishing step.
**Warning signs:** Plugin directory exists but install command fails.

### Pitfall 5: Version Mismatch Between plugin.json and package.json
**What goes wrong:** Plugin shows wrong version or update detection breaks.
**Why it happens:** Version in `.claude-plugin/plugin.json` differs from `package.json`.
**How to avoid:** Keep a single source of truth (package.json) and either manually sync or use a build script to copy the version.
**Warning signs:** Confusing version numbers in `/plugin list`.

## Code Examples

### tsup Configuration for ESM Output

Since package.json has `"type": "module"`, tsup should output ESM:

```typescript
// tsup.config.ts
import { defineConfig } from 'tsup';

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['esm'],
  target: 'node20',
  clean: true,
  sourcemap: false,
  // Don't bundle node_modules -- they'll be in the plugin's node_modules
  external: [
    '@modelcontextprotocol/sdk',
    'sharp',
    'node-screenshots',
    '@skyra/gifenc',
    'zod'
  ],
});
```

[ASSUMED: tsup config pattern based on project's package.json `"type": "module"` and standard tsup usage]

### Plugin Publishing Script (Conceptual)

```bash
# Build the project
npm run build

# Create marketplace plugin directory
MARKETPLACE=~/.claude/plugins/marketplaces/cplugs/plugins/screen-timelapse
mkdir -p "$MARKETPLACE/.claude-plugin"
mkdir -p "$MARKETPLACE/dist"

# Copy required files
cp .claude-plugin/plugin.json "$MARKETPLACE/.claude-plugin/"
cp .mcp.json "$MARKETPLACE/"
cp -r dist/* "$MARKETPLACE/dist/"
cp package.json "$MARKETPLACE/"
cp package-lock.json "$MARKETPLACE/"

# Install production deps in marketplace
cd "$MARKETPLACE"
npm install --omit=dev
```

[ASSUMED: publishing flow based on observed marketplace directory structure]

### Verification Commands

```bash
# Verify plugin structure
ls -la ~/.claude/plugins/marketplaces/cplugs/plugins/screen-timelapse/.claude-plugin/
cat ~/.claude/plugins/marketplaces/cplugs/plugins/screen-timelapse/.claude-plugin/plugin.json

# Verify MCP server starts from plugin directory
cd ~/.claude/plugins/marketplaces/cplugs/plugins/screen-timelapse
node dist/index.js  # Should start and wait for MCP messages on stdin

# Verify native deps load
cd ~/.claude/plugins/marketplaces/cplugs/plugins/screen-timelapse
node -e "require('sharp'); console.log('sharp OK')"
node -e "require('node-screenshots'); console.log('node-screenshots OK')"

# After /plugin install, verify registration
cat ~/.claude/plugins/installed_plugins.json | grep screen-timelapse
```

## State of the Art

| Old Approach | Current Approach | When Changed | Impact |
|--------------|------------------|--------------|--------|
| Manual .mcp.json editing for MCP servers | mcpServers in plugin.json auto-registration | ~2026 Q1 (plugin system) | Zero-config MCP server setup via plugins |

## Assumptions Log

| # | Claim | Section | Risk if Wrong |
|---|-------|---------|---------------|
| A1 | tsup needs explicit `format: ['esm']` config for ESM output | Code Examples | Build could produce CJS, causing runtime errors. Verify by testing `npm run build` output. |
| A2 | Plugin publishing is a manual directory copy (no `cplugs publish` CLI) | Code Examples | If cplugs has a publish command, the manual copy approach still works but is unnecessarily manual. |
| A3 | Claude Code resolves mcpServers args relative to the plugin install path | Architecture Patterns | If paths are resolved differently, the MCP server command would fail. Mitigated by testing. |
| A4 | Both mcpServers-in-plugin.json and .mcp.json work for cplugs plugins | Architecture Patterns | If only one mechanism works for cplugs, the other would be ignored. Low risk since both are verified patterns. |

## Open Questions

1. **How does Claude Code resolve the cwd for MCP server commands from plugins?**
   - What we know: mempalace uses `python3 -m mempalace.mcp_server` (module-based, no path), official plugins use `npx` (global resolution). Both avoid relative file paths.
   - What's unclear: Whether `node dist/index.js` resolves relative to the plugin install directory.
   - Recommendation: Use `node dist/index.js` as planned; if cwd resolution is wrong, fall back to having the entry script resolve `__dirname`. Test during implementation.

2. **Does cplugs have a CLI for publishing, or is it manual directory management?**
   - What we know: The cplugs marketplace is a local directory at `~/.claude/plugins/marketplaces/cplugs/`. The spt plugin was placed there manually (no publish tooling observed).
   - What's unclear: Whether there is a `/plugin publish` or similar command.
   - Recommendation: Use manual directory copy approach. This is verified to work based on existing spt plugin.

## Environment Availability

| Dependency | Required By | Available | Version | Fallback |
|------------|------------|-----------|---------|----------|
| Node.js | MCP server runtime | Needs verification | 20 LTS expected | -- |
| npm | Dependency installation | Needs verification | -- | -- |
| tsup | Build bundling | In devDependencies | ^8.0.0 | -- |
| cplugs marketplace | Plugin distribution | Verified exists | -- | -- |

**Missing dependencies with no fallback:** None identified.

## Sources

### Primary (HIGH confidence)
- `~/.claude/plugins/marketplaces/cplugs/plugins/spt/` - Reference plugin structure for cplugs marketplace
- `~/.claude/plugins/marketplaces/cplugs/.claude-plugin/marketplace.json` - Marketplace registration format
- `~/.claude/plugins/cache/mempalace/mempalace/3.0.14/plugin.json` - MCP server registration via mcpServers field
- `~/.claude/plugins/installed_plugins.json` - Installation record format
- `~/.claude/plugins/marketplaces/claude-plugins-official/external_plugins/context7/.mcp.json` - .mcp.json pattern for MCP registration
- `~/.claude/plugins/marketplaces/claude-plugins-official/external_plugins/firebase/.mcp.json` - .mcp.json pattern confirmation

### Secondary (MEDIUM confidence)
- tsup documentation for ESM output configuration (training knowledge, not live-verified)

## Metadata

**Confidence breakdown:**
- Plugin structure: HIGH - verified from existing spt and mempalace plugins on local filesystem
- MCP registration: HIGH - verified from mempalace plugin.json mcpServers field and official .mcp.json files
- Marketplace publishing: HIGH - verified from cplugs marketplace.json and directory structure
- Build configuration: MEDIUM - tsup ESM config is assumed based on package.json type:module

**Research date:** 2026-04-12
**Valid until:** 2026-05-12 (stable -- plugin system unlikely to change drastically)
