582 lines
16 KiB
Markdown
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](https://github.com/modelcontextprotocol/.github/blob/main/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/stackblitz-labs/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.
|