Merge pull request #1 from modelcontextprotocol/chore/scaffolding

Initial project structure for MCP variants
This commit is contained in:
Kurt Degiorgio 2026-01-27 20:10:28 +00:00 • committed by GitHub
commit 4f98e0aab3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 855 additions and 3 deletions

90
.github/workflows/comment-trigger.yml vendored Normal file
View file

@ -0,0 +1,90 @@
name: Comment Trigger
on:
issue_comment:
types: [created]
permissions:
contents: read
pull-requests: write
actions: write
jobs:
trigger-checks:
name: Trigger Checks from Comment
if: |
github.event.issue.pull_request &&
(github.event.comment.body == '/test all' ||
github.event.comment.body == '/test-all' ||
github.event.comment.body == '/run-all-checks') &&
(github.event.comment.author_association == 'OWNER' ||
github.event.comment.author_association == 'MEMBER' ||
github.event.comment.author_association == 'COLLABORATOR')
runs-on: ubuntu-latest
steps:
- name: Get PR branch
uses: actions/github-script@v7
id: get-pr
with:
script: |
const pr = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number
});
return pr.data.head.ref;
- name: Add reaction to comment
uses: actions/github-script@v7
with:
script: |
await github.rest.reactions.createForIssueComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: context.payload.comment.id,
content: 'rocket'
});
- name: Trigger Python CI
uses: actions/github-script@v7
with:
script: |
await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'python.yml',
ref: '${{ fromJSON(steps.get-pr.outputs.result) }}'
});
- name: Trigger Go CI
uses: actions/github-script@v7
with:
script: |
await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'go.yml',
ref: '${{ fromJSON(steps.get-pr.outputs.result) }}'
});
- name: Trigger TypeScript CI
uses: actions/github-script@v7
with:
script: |
await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'typescript.yml',
ref: '${{ fromJSON(steps.get-pr.outputs.result) }}'
});
- name: Comment on PR
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: '🚀 All CI checks triggered! Check the Actions tab for progress.'
});

80
.github/workflows/go.yml vendored Normal file
View file

@ -0,0 +1,80 @@
name: Go CI
on:
push:
branches: [main]
paths:
- 'go/**'
- '.github/workflows/go.yml'
pull_request:
paths:
- 'go/**'
- '.github/workflows/go.yml'
workflow_dispatch:
permissions:
contents: read
jobs:
go-lint:
name: "Linting"
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Go
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
with:
go-version: "^1.25"
- name: Check formatting
run: |
cd go/sdk
unformatted=$(gofmt -l .)
if [ -n "$unformatted" ]; then
echo "The following files are not properly formatted:"
echo "$unformatted"
exit 1
fi
echo "All Go files are properly formatted"
- name: Run Go vet
run: |
cd go/sdk
go vet ./...
- name: Run staticcheck
uses: dominikh/staticcheck-action@288b4e28bae83c59f35f73651aeb5cab746a06fc # v1.4.0
with:
install-go: false
version: "v0.6.1"
working-directory: go/sdk
go-test:
name: "Unit Tests"
runs-on: ubuntu-latest
strategy:
matrix:
go: ["1.23", "1.24", "1.25"]
steps:
- name: Check out code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Go
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
with:
go-version: ${{ matrix.go }}
- name: Test
run: |
cd go/sdk
go test -v ./...
go-race-test:
name: "Race Detection"
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- name: Set up Go
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
with:
go-version: "1.24"
- name: Test with -race
run: |
cd go/sdk
go test -v -race ./...

67
.github/workflows/python.yml vendored Normal file
View file

@ -0,0 +1,67 @@
name: Python CI
on:
push:
branches: [main]
paths:
- 'python/**'
- '.github/workflows/python.yml'
pull_request:
paths:
- 'python/**'
- '.github/workflows/python.yml'
workflow_dispatch:
permissions:
contents: read
jobs:
python-lint:
name: "Linting"
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install dependencies
run: |
cd python/sdk
pip install -e .
pip install ruff mypy
- name: Run Ruff formatter check
run: |
cd python/sdk
ruff format --check .
- name: Run Ruff linter
run: |
cd python/sdk
ruff check .
- name: Run mypy
run: |
cd python/sdk
mypy src/mcp_ext_variants --ignore-missing-imports
python-test:
name: "Unit Tests"
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
cd python/sdk
pip install -e .
pip install pytest pytest-cov
- name: Run tests
run: |
cd python/sdk
pytest -v --cov=src/mcp_ext_variants

138
.github/workflows/status-check.yml vendored Normal file
View file

@ -0,0 +1,138 @@
name: Status Check
# This workflow enforces that all relevant CI checks pass based on which files changed.
# It's the only required status check in branch protection settings.
on:
pull_request:
types: [opened, synchronize, reopened]
workflow_run:
workflows: ["Python CI", "Go CI", "TypeScript CI"]
types: [completed]
permissions:
contents: read
statuses: write
checks: write
pull-requests: read
jobs:
verify-required-checks:
name: Verify Required Checks
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' || github.event.workflow_run.conclusion != 'cancelled'
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Get PR number
id: pr
run: |
if [ "${{ github.event_name }}" == "workflow_run" ]; then
PR_NUMBER=$(gh pr list --head "${{ github.event.workflow_run.head_branch }}" --json number --jq '.[0].number')
echo "number=$PR_NUMBER" >> $GITHUB_OUTPUT
else
echo "number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get changed files
id: changed-files
uses: dorny/paths-filter@v3
with:
filters: |
python:
- 'python/**'
- '.github/workflows/python.yml'
go:
- 'go/**'
- '.github/workflows/go.yml'
typescript:
- 'typescript/**'
- '.github/workflows/typescript.yml'
- name: Check required statuses
uses: actions/github-script@v7
with:
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const ref = context.payload.pull_request?.head.sha || context.payload.workflow_run?.head_sha;
// Define required checks per language
const requiredChecks = {
python: [
'Python CI / Linting',
'Python CI / Unit Tests (3.10)',
'Python CI / Unit Tests (3.11)',
'Python CI / Unit Tests (3.12)'
],
go: [
'Go CI / Linting',
'Go CI / Unit Tests (1.23)',
'Go CI / Unit Tests (1.24)',
'Go CI / Unit Tests (1.25)',
'Go CI / Race Detection'
],
typescript: [
'TypeScript CI / Linting',
'TypeScript CI / Unit Tests (20)',
'TypeScript CI / Unit Tests (22)'
]
};
// Get changed files from previous step
const changedFiles = {
python: ${{ steps.changed-files.outputs.python }} === 'true',
go: ${{ steps.changed-files.outputs.go }} === 'true',
typescript: ${{ steps.changed-files.outputs.typescript }} === 'true'
};
// Collect all required checks based on changes
let allRequiredChecks = [];
for (const [lang, changed] of Object.entries(changedFiles)) {
if (changed) {
allRequiredChecks = allRequiredChecks.concat(requiredChecks[lang]);
}
}
if (allRequiredChecks.length === 0) {
console.log('No language files changed, no checks required');
return;
}
// Get all check runs for this commit
const { data: checkRuns } = await github.rest.checks.listForRef({
owner,
repo,
ref
});
// Verify all required checks have passed
const checkStatuses = {};
for (const check of checkRuns.check_runs) {
checkStatuses[check.name] = check.conclusion;
}
let allPassed = true;
let failureMessage = '';
for (const requiredCheck of allRequiredChecks) {
const status = checkStatuses[requiredCheck];
if (!status) {
allPassed = false;
failureMessage += `❌ ${requiredCheck}: Not started\\n`;
} else if (status !== 'success') {
allPassed = false;
failureMessage += `❌ ${requiredCheck}: ${status}\\n`;
} else {
failureMessage += `✅ ${requiredCheck}: ${status}\\n`;
}
}
if (!allPassed) {
core.setFailed(`Required checks have not passed:\\n${failureMessage}`);
} else {
console.log(`All required checks passed:\\n${failureMessage}`);
}

61
.github/workflows/typescript.yml vendored Normal file
View file

@ -0,0 +1,61 @@
name: TypeScript CI
on:
push:
branches: [main]
paths:
- 'typescript/**'
- '.github/workflows/typescript.yml'
pull_request:
paths:
- 'typescript/**'
- '.github/workflows/typescript.yml'
workflow_dispatch:
permissions:
contents: read
jobs:
typescript-lint:
name: "Linting"
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v6
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: "20"
- name: Install dependencies
run: |
cd typescript/sdk
npm install
- name: Run TypeScript compiler check
run: |
cd typescript/sdk
npm run build
- name: Run ESLint
run: |
cd typescript/sdk
npm run lint
typescript-test:
name: "Unit Tests"
runs-on: ubuntu-latest
strategy:
matrix:
node-version: ["20", "22"]
steps:
- name: Check out code
uses: actions/checkout@v6
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: |
cd typescript/sdk
npm install
- name: Run tests
run: |
cd typescript/sdk
npm test

44
.gitignore vendored
View file

@ -1,6 +1,3 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
@ -30,3 +27,44 @@ go.work.sum
# Editor/IDE
# .idea/
# .vscode/
# Node.js / TypeScript
node_modules/
dist/
*.tsbuildinfo
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
.venv/
venv/
ENV/
.pytest_cache/
.mypy_cache/
.ruff_cache/
htmlcov/
.coverage
.coverage.*
coverage.xml
*.cover

View file

@ -3,3 +3,73 @@
> #### **Status:** Experimental. This work is for prototyping and feedback only, and is not an accepted or official MCP extension.
This repository provides a multi-language reference implementation of the variants proposal for the Model Context Protocol (MCP), as described in [SEP-2053](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2053).
## Implementations
| Language | Directory | Package | Status |
|----------|-----------|---------|--------|
| Go | `go/sdk/` | `github.com/modelcontextprotocol/experimental-ext-variants/go/sdk` | Planned |
| Python | `python/sdk/` | `mcp-ext-variants` | Planned |
| TypeScript | `typescript/sdk/` | `@ext-modelcontextprotocol/variants` | Planned |
## CI/CD
This monorepo uses **path-based CI workflows** to efficiently test only what changes:
### How It Works
1. **Language-specific workflows** (`python.yml`, `go.yml`, `typescript.yml`)
- Only trigger when their language directory or workflow file changes
- Run all tests, linting, and checks for that language
2. **Status check workflow** (`status-check.yml`)
- Runs on every PR to verify required checks passed
- Determines what needs to pass based on which files changed
- This is the only required check in branch protection
### Examples
- Change `python/sdk/file.py` → Only Python CI runs → PR requires Python checks to pass
- Change both Go and TypeScript files → Both CIs run → PR requires both to pass
- Change only `README.md` → No language CIs run → PR can merge immediately
### Forcing All Checks
To run all language checks regardless of changed files:
- **In a PR**: Comment `/test all` (only works for repo owners/members/collaborators)
- **Manually**: Use GitHub Actions UI or CLI to trigger individual workflows
### Adding New Required Checks
1. **Add your check** to the appropriate language workflow (e.g., `python.yml`):
```yaml
python-security-scan:
name: "Security Scan"
runs-on: ubuntu-latest
steps:
- name: Run security checks
run: # your commands here
```
2. **Update the status check** in `.github/workflows/status-check.yml`:
```javascript
const requiredChecks = {
python: [
'Python CI / Linting',
'Python CI / Unit Tests (3.10)',
// ... existing checks ...
'Python CI / Security Scan' // ← Add your new check
],
```
3. **Submit PR** - Your new check is now required for all relevant changes!
## License
Apache License 2.0 - See LICENSE file for details
## Resources
- [Variants Proposal Specification (SEP-2053)](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2053) - Full specification and design details
- [Model Context Protocol](https://modelcontextprotocol.io/specification)

0
conformance/.gitkeep Normal file
View file

3
go/sdk/README.md Normal file
View file

@ -0,0 +1,3 @@
# MCP Variants - Go Implementation
This will contain the Go implementation of the MCP Variants based on [SEP-2053](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2053).

0
go/sdk/examples/.gitkeep Normal file
View file

3
go/sdk/go.mod Normal file
View file

@ -0,0 +1,3 @@
module github.com/modelcontextprotocol/experimental-ext-variants/go/sdk
go 1.23.0

0
go/sdk/internal/.gitkeep Normal file
View file

View file

@ -0,0 +1,6 @@
// Copyright 2025 The MCP Variants Authors. All rights reserved.
// Use of this source code is governed by a Apache-2.0
// license that can be found in the LICENSE file.
// Package variants implements MCP Variants for experimental extensions.
package variants

View file

@ -0,0 +1,14 @@
// Copyright 2025 The MCP Variants Authors. All rights reserved.
// Use of this source code is governed by a Apache-2.0
// license that can be found in the LICENSE file.
package variants
import "testing"
func TestPlaceholder(t *testing.T) {
// Placeholder test to ensure go test runs successfully
if false {
t.Error("This should not happen")
}
}

31
python/sdk/README.md Normal file
View file

@ -0,0 +1,31 @@
# MCP Variants Python SDK
Python implementation of the Model Context Protocol (MCP) variants framework.
## Installation
```bash
pip install mcp-ext-variants
```
## Usage
```python
from mcp_ext_variants import Variant
# Example usage will be added as the implementation progresses
```
## Development
```bash
# Install in development mode
pip install -e .
# Run tests (when available)
pytest
```
## License
Apache License 2.0 - See LICENSE file in the root directory for details.

71
python/sdk/pyproject.toml Normal file
View file

@ -0,0 +1,71 @@
[project]
name = "mcp-ext-variants"
version = "0.1.0"
description = "Python SDK for Model Context Protocol variants"
authors = [{name = "The MCP Variants Authors"}]
readme = "README.md"
license = {text = "Apache-2.0"}
requires-python = ">=3.10"
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
dependencies = [
"mcp>=1.0.0",
]
[project.urls]
"Homepage" = "https://github.com/modelcontextprotocol/experimental-ext-variants"
"Bug Reports" = "https://github.com/modelcontextprotocol/experimental-ext-variants/issues"
"Source" = "https://github.com/modelcontextprotocol/experimental-ext-variants"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/mcp_ext_variants"]
[tool.ruff]
line-length = 120
target-version = "py310"
[tool.ruff.lint]
select = [
"E", # pycodestyle
"F", # pyflakes
"I", # isort
"UP", # pyupgrade
"C4", # flake8-comprehensions
"PERF", # Perflint
]
[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"] # Allow unused imports in __init__.py
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
[tool.mypy]
python_version = "3.10"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
ignore_missing_imports = true
[tool.pyright]
typeCheckingMode = "basic"
include = ["src/mcp_ext_variants", "tests"]
venvPath = "."
venv = ".venv"
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
addopts = "--strict-markers --verbose"

View file

@ -0,0 +1,11 @@
# Copyright 2025 The MCP Variants Authors. All rights reserved.
# Use of this source code is governed by a Apache-2.0
# license that can be found in the LICENSE file.
"""
MCP Variants Python SDK
Implements experimental MCP Variants for extended functionality.
"""
__all__: list[str] = [] # Export list will be populated as the SDK develops

View file

View file

@ -0,0 +1,9 @@
# Copyright 2025 The MCP Variants Authors. All rights reserved.
# Use of this source code is governed by a Apache-2.0
# license that can be found in the LICENSE file.
"""
Package variants implements experimental MCP Variants for extended functionality.
"""
__all__ = [] # Export list will be populated as the SDK develops

View file

@ -0,0 +1 @@
# Tests for mcp-ext-variants

Some files were not shown because too many files have changed in this diff Show more