ext-apps/CONTRIBUTING.md
Olivier Chafik 3e9950062b
build: add npm run bump; parallel npm-publish approval (#568)
* ci: automate release process with workflow-driven version bumps and PR flow

Adds a Release workflow that bumps all package versions, generates release
notes, and opens a PR. Merging the PR auto-tags and creates the GitHub
Release, which triggers npm-publish. Also adds `npm run bump` for local use
and a release.yml config for categorized auto-generated notes.

* ci: simplify release workflow and flatten npm-publish approval chain

- Drop auto-generated RELEASES.md prepending (notes are written manually in PR)
- Flatten npm-publish job dependencies so all publish jobs wait for approval
  together instead of sequentially, reducing 3 approval clicks to 1
- Remove Release environment from publish-mcpb (only needs GH_TOKEN)
- Use --generate-notes for GitHub Release creation

* fix: address review findings in release automation

- release.yml: use RELEASE_TOKEN PAT for gh release create so the
  release:published event triggers npm-publish (GITHUB_TOKEN events
  don't cascade to other workflows)
- release.yml: checkout merge_commit_sha in tag job instead of ref:main
  to avoid tagging later commits
- release.yml: checkout ref:main in prepare job so dispatching from a
  feature branch doesn't pollute the release PR
- release.yml: constrain preid to type:choice[beta] to match
  npm-publish.yml's tag detection
- release.yml: drop -f on push so re-runs fail loudly instead of
  clobbering manual RELEASES.md edits; drop pr-edit fallback
- release.yml: ensure release label exists before gh pr create
- release.yml: make tag push idempotent for re-runs after partial
  failure
- bump-version.mjs: update workspace dependency ranges so major bumps
  don't leave examples pointing at the old major
- bump-version.mjs: run npm install --package-lock-only so npm ci
  on the release PR doesn't fail on lockfile mismatch
- npm-publish.yml: add npm-tag detection to publish-examples so beta
  example releases don't overwrite latest
- .github/release.yml: use [bot] suffix in author excludes so
  dependabot PRs are actually filtered from release notes
- CONTRIBUTING.md: document RELEASE_TOKEN setup; fix npm run -- syntax

* style: auto-fix prettier formatting

* build: add npm run bump; parallel npm-publish approval

- scripts/bump-version.mjs (+ npm run bump): bumps root and all workspace
  package versions, updates examples' ^X.Y.0 dependency range on the root
  package (matters on major bumps), and refreshes package-lock so npm ci
  accepts the release PR.
- npm-publish.yml: all publish jobs now depend directly on [build, test]
  instead of chaining, so they enter "waiting for approval" together and a
  single Review-deployments click approves the lot. Prerelease versions
  (any X.Y.Z-…) publish under --tag beta so they don't take latest.
- CONTRIBUTING.md: release steps now use npm run bump.

Dropped from the original change: the workflow_dispatch release.yml
(required a long-lived PAT to cascade events), label-based
.github/release.yml auto-notes (PRs aren't labeled today), and the
release-X.Y npm-tag for non-main branches (no backport branches yet).

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-04-21 16:03:27 +01:00

582 lines
16 KiB
Markdown

# Contributing to MCP Apps SDK
We welcome contributions to the MCP Apps SDK! This document outlines the process for contributing to the project.
## Getting Started
1. Fork the repository
2. Clone your fork: `git clone https://github.com/YOUR-USERNAME/ext-apps.git`
3. Install dependencies: `npm install`
4. Build the project: `npm run build`
5. Run tests: `npm test`
## Development Process
1. Create a new branch for your changes
2. Make your changes
3. Run `npm run prettier` to ensure code style compliance
4. Run `npm test` to verify all tests pass
5. Submit a pull request
## Pull Request Guidelines
- Follow the existing code style
- Include tests for new functionality
- Update documentation as needed
- Keep changes focused and atomic
- Provide a clear description of changes
- **Keep "Allow edits by maintainers" checked** when opening your PR — this lets maintainers rebase your branch and lets the [`/update-snapshots` workflow](#updating-snapshots-in-ci) push updated screenshots to it
## Examples Policy
The examples in `examples/` are maintained by the core team. **We do not accept pull requests that add new example servers from outside contributors.** Each example carries an ongoing maintenance cost — keeping it building, tested, and up to date with SDK changes — so we are deliberate about what we add.
If you have an idea for an example that would provide significant educational value, please [open an issue](https://github.com/modelcontextprotocol/ext-apps/issues) describing what the example would demonstrate and why it would be useful, and maintainers will evaluate the request.
PRs that add new examples without a prior approved issue may be closed.
## Running Examples
Start the development environment with hot reloading:
```bash
npm run examples:dev
```
Or build and run examples:
```bash
npm run examples:start
```
### With MCP Clients
To use these examples with MCP clients that support the stdio transport (such as Claude Desktop or VS Code), add this MCP server configuration to your client's settings:
<details>
<summary>MCP client configuration for all examples (using stdio)</summary>
```json
{
"mcpServers": {
"basic-react": {
"command": "npx",
"args": [
"-y",
"--silent",
"--registry=https://registry.npmjs.org/",
"@modelcontextprotocol/server-basic-react",
"--stdio"
]
},
"basic-vanillajs": {
"command": "npx",
"args": [
"-y",
"--silent",
"--registry=https://registry.npmjs.org/",
"@modelcontextprotocol/server-basic-vanillajs",
"--stdio"
]
},
"basic-vue": {
"command": "npx",
"args": [
"-y",
"--silent",
"--registry=https://registry.npmjs.org/",
"@modelcontextprotocol/server-basic-vue",
"--stdio"
]
},
"basic-svelte": {
"command": "npx",
"args": [
"-y",
"--silent",
"--registry=https://registry.npmjs.org/",
"@modelcontextprotocol/server-basic-svelte",
"--stdio"
]
},
"basic-preact": {
"command": "npx",
"args": [
"-y",
"--silent",
"--registry=https://registry.npmjs.org/",
"@modelcontextprotocol/server-basic-preact",
"--stdio"
]
},
"basic-solid": {
"command": "npx",
"args": [
"-y",
"--silent",
"--registry=https://registry.npmjs.org/",
"@modelcontextprotocol/server-basic-solid",
"--stdio"
]
},
"budget-allocator": {
"command": "npx",
"args": [
"-y",
"--silent",
"--registry=https://registry.npmjs.org/",
"@modelcontextprotocol/server-budget-allocator",
"--stdio"
]
},
"cohort-heatmap": {
"command": "npx",
"args": [
"-y",
"--silent",
"--registry=https://registry.npmjs.org/",
"@modelcontextprotocol/server-cohort-heatmap",
"--stdio"
]
},
"customer-segmentation": {
"command": "npx",
"args": [
"-y",
"--silent",
"--registry=https://registry.npmjs.org/",
"@modelcontextprotocol/server-customer-segmentation",
"--stdio"
]
},
"map": {
"command": "npx",
"args": [
"-y",
"--silent",
"--registry=https://registry.npmjs.org/",
"@modelcontextprotocol/server-map",
"--stdio"
]
},
"pdf": {
"command": "npx",
"args": [
"-y",
"--silent",
"--registry=https://registry.npmjs.org/",
"@modelcontextprotocol/server-pdf",
"--stdio"
]
},
"scenario-modeler": {
"command": "npx",
"args": [
"-y",
"--silent",
"--registry=https://registry.npmjs.org/",
"@modelcontextprotocol/server-scenario-modeler",
"--stdio"
]
},
"shadertoy": {
"command": "npx",
"args": [
"-y",
"--silent",
"--registry=https://registry.npmjs.org/",
"@modelcontextprotocol/server-shadertoy",
"--stdio"
]
},
"sheet-music": {
"command": "npx",
"args": [
"-y",
"--silent",
"--registry=https://registry.npmjs.org/",
"@modelcontextprotocol/server-sheet-music",
"--stdio"
]
},
"system-monitor": {
"command": "npx",
"args": [
"-y",
"--silent",
"--registry=https://registry.npmjs.org/",
"@modelcontextprotocol/server-system-monitor",
"--stdio"
]
},
"threejs": {
"command": "npx",
"args": [
"-y",
"--silent",
"--registry=https://registry.npmjs.org/",
"@modelcontextprotocol/server-threejs",
"--stdio"
]
},
"transcript": {
"command": "npx",
"args": [
"-y",
"--silent",
"--registry=https://registry.npmjs.org/",
"@modelcontextprotocol/server-transcript",
"--stdio"
]
},
"video-resource": {
"command": "npx",
"args": [
"-y",
"--silent",
"--registry=https://registry.npmjs.org/",
"@modelcontextprotocol/server-video-resource",
"--stdio"
]
},
"wiki-explorer": {
"command": "npx",
"args": [
"-y",
"--silent",
"--registry=https://registry.npmjs.org/",
"@modelcontextprotocol/server-wiki-explorer",
"--stdio"
]
},
"qr": {
"command": "uv",
"args": [
"run",
"/path/to/ext-apps/examples/qr-server/server.py",
"--stdio"
]
},
"say": {
"command": "uv",
"args": [
"run",
"--default-index",
"https://pypi.org/simple",
"https://raw.githubusercontent.com/modelcontextprotocol/ext-apps/refs/heads/main/examples/say-server/server.py",
"--stdio"
]
}
}
}
```
</details>
> [!NOTE]
> The `qr` server requires cloning the repository first. See [qr-server README](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/qr-server) for details.
### Local Development
To test local modifications with MCP clients, first clone and install the repository:
```bash
git clone https://github.com/modelcontextprotocol/ext-apps.git
cd ext-apps
npm install
```
Then configure your MCP client to build and run the local server. Replace `~/src/ext-apps` with your actual clone path.
Most example servers have a `start:stdio` script that builds and launches in stdio mode:
<details>
<summary>MCP client configuration for local development (all examples)</summary>
```json
{
"mcpServers": {
"basic-react": {
"command": "bash",
"args": [
"-c",
"cd ~/src/ext-apps/examples/basic-server-react && npm --silent run start:stdio"
]
},
"basic-vanillajs": {
"command": "bash",
"args": [
"-c",
"cd ~/src/ext-apps/examples/basic-server-vanillajs && npm --silent run start:stdio"
]
},
"basic-vue": {
"command": "bash",
"args": [
"-c",
"cd ~/src/ext-apps/examples/basic-server-vue && npm --silent run start:stdio"
]
},
"basic-svelte": {
"command": "bash",
"args": [
"-c",
"cd ~/src/ext-apps/examples/basic-server-svelte && npm --silent run start:stdio"
]
},
"basic-preact": {
"command": "bash",
"args": [
"-c",
"cd ~/src/ext-apps/examples/basic-server-preact && npm --silent run start:stdio"
]
},
"basic-solid": {
"command": "bash",
"args": [
"-c",
"cd ~/src/ext-apps/examples/basic-server-solid && npm --silent run start:stdio"
]
},
"budget-allocator": {
"command": "bash",
"args": [
"-c",
"cd ~/src/ext-apps/examples/budget-allocator-server && npm --silent run start:stdio"
]
},
"cohort-heatmap": {
"command": "bash",
"args": [
"-c",
"cd ~/src/ext-apps/examples/cohort-heatmap-server && npm --silent run start:stdio"
]
},
"customer-segmentation": {
"command": "bash",
"args": [
"-c",
"cd ~/src/ext-apps/examples/customer-segmentation-server && npm --silent run start:stdio"
]
},
"map": {
"command": "bash",
"args": [
"-c",
"cd ~/src/ext-apps/examples/map-server && npm --silent run start:stdio"
]
},
"pdf": {
"command": "bash",
"args": [
"-c",
"cd ~/src/ext-apps/examples/pdf-server && npm --silent run start:stdio"
]
},
"scenario-modeler": {
"command": "bash",
"args": [
"-c",
"cd ~/src/ext-apps/examples/scenario-modeler-server && npm --silent run start:stdio"
]
},
"shadertoy": {
"command": "bash",
"args": [
"-c",
"cd ~/src/ext-apps/examples/shadertoy-server && npm --silent run start:stdio"
]
},
"sheet-music": {
"command": "bash",
"args": [
"-c",
"cd ~/src/ext-apps/examples/sheet-music-server && npm --silent run start:stdio"
]
},
"system-monitor": {
"command": "bash",
"args": [
"-c",
"cd ~/src/ext-apps/examples/system-monitor-server && npm --silent run start:stdio"
]
},
"threejs": {
"command": "bash",
"args": [
"-c",
"cd ~/src/ext-apps/examples/threejs-server && npm --silent run start:stdio"
]
},
"transcript": {
"command": "bash",
"args": [
"-c",
"cd ~/src/ext-apps/examples/transcript-server && npm --silent run start:stdio"
]
},
"video-resource": {
"command": "bash",
"args": [
"-c",
"cd ~/src/ext-apps/examples/video-resource-server && npm --silent run start:stdio"
]
},
"wiki-explorer": {
"command": "bash",
"args": [
"-c",
"cd ~/src/ext-apps/examples/wiki-explorer-server && npm --silent run start:stdio"
]
},
"qr": {
"command": "bash",
"args": [
"-c",
"uv run ~/src/ext-apps/examples/qr-server/server.py --stdio"
]
},
"say": {
"command": "bash",
"args": [
"-c",
"uv run --index https://pypi.org/simple ~/src/ext-apps/examples/say-server/server.py --stdio"
]
}
}
}
```
</details>
This configuration rebuilds each server on launch, ensuring your local changes are picked up.
## Testing
### Unit Tests
Run unit tests with Bun:
```bash
npm test
```
### E2E Tests
E2E tests use Playwright to verify all example servers work correctly with screenshot comparisons.
```bash
# Run all E2E tests
npm run test:e2e
# Run a specific server's tests
npm run test:e2e -- --grep "Budget Allocator"
# Run tests in interactive UI mode
npm run test:e2e:ui
```
### Updating Golden Screenshots
When UI changes are intentional, update the golden screenshots:
```bash
# Update all screenshots
npm run test:e2e:update
# Update screenshots for a specific server
npm run test:e2e:update -- --grep "Three.js"
```
**Note**: Golden screenshots are platform-agnostic. Tests use canvas masking and tolerance thresholds to handle minor cross-platform rendering differences.
#### Updating Snapshots in CI
If E2E tests fail in CI due to screenshot mismatches, you can update snapshots directly from your PR:
1. Comment `/update-snapshots` on the PR
2. The workflow will update snapshots and push to your branch
3. A comment will confirm when complete
Alternatively, use the [workflow dispatch](https://github.com/modelcontextprotocol/ext-apps/actions/workflows/update-snapshots.yml) to manually trigger updates for any branch.
## Code of Conduct
This project follows our [Code of Conduct](CODE_OF_CONDUCT.md). Please review it before contributing.
## Reporting Issues
- Use the [GitHub issue tracker](https://github.com/modelcontextprotocol/ext-apps/issues)
- Search existing issues before creating a new one
- Provide clear reproduction steps
## Security Issues
Please review our [Security Policy](SECURITY.md) for reporting security vulnerabilities.
---
## For Maintainers
### Repository Setup
This repository uses [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/) with OIDC - no secrets required.
Before publishing releases, ensure the following are configured:
1. **Trusted publisher on npm**: Configure the package to trust this GitHub repository
- Go to https://www.npmjs.com/package/@modelcontextprotocol/ext-apps/access
- Under "Trusted Publishers", click "Add trusted publisher"
- Select "GitHub Actions"
- Repository: `modelcontextprotocol/ext-apps`
- Workflow filename: `npm-publish.yml`
- Environment: `Release` (optional, for additional protection)
2. **`Release` environment** (optional): Create a protected environment for additional safeguards
- Go to Settings > Environments > New environment
- Name it `Release`
- Add required reviewers or other protection rules as needed
### Publishing a Release
1. **Bump the version** across the root and all workspace packages:
```bash
npm run bump -- minor # or: patch | major | prerelease --preid=beta | 1.7.0
```
Commit and open a PR with a grouped changelog in the body (see prior `chore: bump …` PRs for the format).
2. **Merge the PR**, then [draft a GitHub Release](https://github.com/modelcontextprotocol/ext-apps/releases/new) — create a `vX.Y.Z` tag on `main`, paste the changelog, publish.
3. **Approve the deployment** — publishing the Release triggers the [npm-publish workflow](https://github.com/modelcontextprotocol/ext-apps/actions/workflows/npm-publish.yml); all publish jobs wait together for a single "Review deployments" approval.
#### npm Tags
The workflow automatically determines the npm dist-tag:
| Version Pattern | npm Tag | Install Command |
| ----------------------------- | ------------- | -------------------------------------------------------- |
| `X.Y.Z` (from main) | `latest` | `npm install @modelcontextprotocol/ext-apps` |
| `X.Y.Z-beta.N` | `beta` | `npm install @modelcontextprotocol/ext-apps@beta` |
| `X.Y.Z` (from release branch) | `release-X.Y` | `npm install @modelcontextprotocol/ext-apps@release-X.Y` |
#### Maintenance Releases
To release a patch for an older version:
1. Create a release branch from the tag: `git checkout -b release-0.1 v0.1.0`
2. Cherry-pick or apply fixes
3. Bump the patch version
4. Create a GitHub Release targeting the release branch
5. The package will be published with tag `release-0.1`
### Testing Pre-releases
Every commit and PR automatically publishes a preview package via [pkg-pr-new](https://github.com/pkg-pr-new/pkg-pr-new). Check the PR comments or workflow logs for the install command.
---
## License
By contributing, you agree that your contributions will be licensed under the MIT License.