Compare commits

..
221 changed files with 11498 additions and 54978 deletions

50
.githooks/commit-msg Executable file
View file

@ -0,0 +1,50 @@
#!/bin/sh
# Verify commit message follows conventional commit format
# https://www.conventionalcommits.org/
#
# Uses npx commitlint if Node.js is available (same as CI),
# otherwise falls back to basic shell regex validation.
commit_msg_file="$1"
commit_msg=$(cat "$commit_msg_file")
# Skip merge commits
if echo "$commit_msg" | grep -qE "^Merge "; then
exit 0
fi
# Try to use commitlint via npx if Node.js is available
if command -v npx >/dev/null 2>&1; then
# Run commitlint with config-conventional rules (same as CI)
echo "$commit_msg" | npx --yes @commitlint/cli@latest --extends @commitlint/config-conventional
exit $?
fi
# Fallback: basic shell regex validation if Node.js is not available
echo "Note: Node.js not found, using basic commit message validation."
echo " Install Node.js for full commitlint validation (same as CI)."
echo ""
# Conventional commit types from @commitlint/config-conventional
types="build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test"
# Pattern: type(optional-scope): description
# The description must start with lowercase and not end with period
pattern="^($types)(\(.+\))?(!)?: .+"
if ! echo "$commit_msg" | head -1 | grep -qE "$pattern"; then
echo "ERROR: Commit message does not follow conventional commit format."
echo ""
echo "Expected format: <type>(<scope>): <description>"
echo ""
echo "Valid types: build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test"
echo ""
echo "Examples:"
echo " feat: add new feature"
echo " fix(parser): resolve parsing issue"
echo " docs: update README"
echo ""
echo "Your commit message:"
echo " $(head -1 "$commit_msg_file")"
exit 1
fi

View file

@ -16,15 +16,11 @@ updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
interval: "daily"
labels:
# Mark PRs as CI related change.
- T-CI
open-pull-requests-limit: 3
groups:
codeql-action:
patterns:
- "github/codeql-action*"
commit-message:
prefix: "chore"
include: "scope"
include: "scope"

View file

@ -3,9 +3,6 @@ on:
# Runs workflow when activity on a PR in the workflow's repository occurs.
pull_request_target:
permissions:
contents: read
jobs:
auto-label:
permissions:
@ -23,7 +20,7 @@ jobs:
PR_URL: ${{ github.event.pull_request.html_url }}
steps:
- uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # v7
- uses: actions/labeler@v6
with:
# Auto-include paths starting with dot (e.g. .github)
dot: true

View file

@ -12,35 +12,34 @@ env:
CARGO_TERM_COLOR: always
ARTIFACT_DIR: release-artifacts
permissions:
contents: read
jobs:
commit-lint:
name: Lint Commit Messages
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
uses: actions/setup-node@v6
with:
node-version: '22'
node-version: '20'
- name: Install commitlint
run: npm install --global @commitlint/cli@20.4.3 @commitlint/config-conventional@20.4.3
run: |
npm install --save-dev @commitlint/cli @commitlint/config-conventional
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
- name: Lint commit messages
run: commitlint --extends @commitlint/config-conventional --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} --verbose
run: npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} --verbose
fmt:
name: Code Formatting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/checkout@v6
- name: Install Rust fmt
run: rustup toolchain install nightly --component rustfmt
@ -52,209 +51,41 @@ jobs:
name: Lint with Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
- uses: Swatinem/rust-cache@v2
- name: Run clippy (all features)
- name: Run clippy
run: cargo clippy --all-targets --all-features -- -D warnings
- name: Run clippy (all features except local)
run: |
FEATURES=$(cargo metadata --no-deps --format-version 1 \
| jq -r '[.packages[] | select(.name == "rmcp") | .features | keys[]
| select(startswith("__") | not)
| select(. != "local")] | join(",")')
cargo clippy --package rmcp --all-targets --no-default-features --features "$FEATURES" -- -D warnings
semver:
name: SemVer Check
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Install Rust
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
- name: Install cargo-semver-checks
uses: taiki-e/install-action@3f74d7c16a4242f1c95561e98edc25d36adb4375 # v2.87.12
with:
tool: cargo-semver-checks
- name: Determine semver release type
run: |
if git log --format=%B \
${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} \
| grep -Eq '(^[A-Za-z0-9_-]+(\([^)]*\))?!:|^BREAKING[ -]CHANGE:)'; then
echo "SEMVER_RELEASE_TYPE=major" >> "$GITHUB_ENV"
else
echo "SEMVER_RELEASE_TYPE=minor" >> "$GITHUB_ENV"
fi
- name: Check rmcp (default features)
run: |
cargo semver-checks \
--package rmcp \
--baseline-rev ${{ github.event.pull_request.base.sha }} \
--release-type "$SEMVER_RELEASE_TYPE" \
--only-explicit-features \
--features default
- name: Check rmcp (all features except local)
run: |
FEATURES=$(cargo metadata --no-deps --format-version 1 \
| jq -r '[.packages[] | select(.name == "rmcp") | .features | keys[]
| select(startswith("__") | not)
| select(. != "local")] | join(",")')
cargo semver-checks \
--package rmcp \
--baseline-rev ${{ github.event.pull_request.base.sha }} \
--release-type "$SEMVER_RELEASE_TYPE" \
--only-explicit-features \
--features "$FEATURES"
public-api:
name: Public API Check
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
# cargo-public-api builds rustdoc JSON, which requires a nightly toolchain
# to be installed (it does not need to be the default; the tool invokes it
# via `cargo +nightly`).
- name: Install Rust
uses: dtolnay/rust-toolchain@7c8d7d138f5c09cef361f8214cf96882cd029cdb # nightly
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
- name: Install cargo-public-api
uses: taiki-e/install-action@3f74d7c16a4242f1c95561e98edc25d36adb4375 # v2.87.12
with:
tool: cargo-public-api
# Mirror the SemVer Check job's release-type detection: a breaking-change
# commit marker (`!:` or `BREAKING CHANGE:`) means a major release (any API
# change is allowed); otherwise a minor release (additions allowed, but
# changed/removed public items are denied). This catches breaking changes
# that cargo-semver-checks cannot yet detect, such as a change to a
# function's return type or a field's type.
# See https://github.com/obi1kenobi/cargo-semver-checks/issues/5
- name: Determine release type and deny flags
run: |
if git log --format=%B \
${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} \
| grep -Eq '(^[A-Za-z0-9_-]+(\([^)]*\))?!:|^BREAKING[ -]CHANGE:)'; then
SEMVER_RELEASE_TYPE=major
else
SEMVER_RELEASE_TYPE=minor
fi
case "$SEMVER_RELEASE_TYPE" in
major) DENY="" ;;
patch) DENY="--deny added --deny changed --deny removed" ;;
*) DENY="--deny changed --deny removed" ;;
esac
echo "SEMVER_RELEASE_TYPE=$SEMVER_RELEASE_TYPE" >> "$GITHUB_ENV"
echo "DENY=$DENY" >> "$GITHUB_ENV"
- name: Check rmcp (default features)
run: |
cargo public-api \
--package rmcp \
-ss \
diff \
$DENY \
--force \
${{ github.event.pull_request.base.sha }}..${{ github.sha }}
- name: Check rmcp (all features except local)
run: |
BASE_SHA=${{ github.event.pull_request.base.sha }}
# `cargo public-api diff` builds both revisions with the same feature
# set, so a feature that exists on only one side (e.g. a feature added
# or removed by this PR) would fail to build the other revision. Diff
# only the features present in BOTH the base and the head; features
# unique to one side are necessarily pure additions/removals, which the
# release-type deny flags already govern.
list_features() {
cargo metadata --no-deps --format-version 1 --manifest-path "$1/Cargo.toml" \
| jq -r '.packages[] | select(.name == "rmcp") | .features | keys[]
| select(startswith("__") | not)
| select(. != "local")'
}
list_features "." | sort -u > "$RUNNER_TEMP/head_features"
git worktree add --detach "$RUNNER_TEMP/rmcp-base" "$BASE_SHA"
list_features "$RUNNER_TEMP/rmcp-base" | sort -u > "$RUNNER_TEMP/base_features"
git worktree remove --force "$RUNNER_TEMP/rmcp-base"
FEATURES=$(comm -12 "$RUNNER_TEMP/head_features" "$RUNNER_TEMP/base_features" | paste -sd, -)
cargo public-api \
--package rmcp \
--features "$FEATURES" \
-ss \
diff \
$DENY \
--force \
"$BASE_SHA"..${{ github.sha }}
spelling:
name: spell check with typos
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/checkout@v6
- name: Spell Check Repo
uses: crate-ci/typos@d43b6c087ac471e2ea7b8af622ff15f05c0c365b # master
msrv:
name: Check MSRV
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Read MSRV from Cargo.toml
run: |
MSRV=$(cargo metadata --no-deps --format-version 1 \
| jq -r '.packages[] | select(.name == "rmcp") | .rust_version')
echo "MSRV=$MSRV" >> "$GITHUB_ENV"
- name: Install MSRV Rust
uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # master
with:
toolchain: ${{ env.MSRV }}
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
- name: Check default workspace members with MSRV
run: cargo +${{ env.MSRV }} check --all-targets --all-features
uses: crate-ci/typos@master
test:
name: Run Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/checkout@v6
# install nodejs
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
uses: actions/setup-node@v6
with:
node-version: '22'
node-version: '20'
- name: Install uv
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
uses: astral-sh/setup-uv@v7
- name: Install Rust
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
uses: dtolnay/rust-toolchain@stable
- name: Set up Python
run: uv python install
@ -262,7 +93,7 @@ jobs:
- name: Create venv for python
run: uv venv
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
- uses: Swatinem/rust-cache@v2
- name: Run tests
run: cargo test --all-features
@ -271,19 +102,19 @@ jobs:
name: Run Tests (no local feature)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/checkout@v6
# install nodejs
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
uses: actions/setup-node@v6
with:
node-version: '22'
node-version: '20'
- name: Install uv
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
uses: astral-sh/setup-uv@v7
- name: Install Rust
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
uses: dtolnay/rust-toolchain@stable
- name: Set up Python
run: uv python install
@ -291,7 +122,7 @@ jobs:
- name: Create venv for python
run: uv venv
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
- uses: Swatinem/rust-cache@v2
- name: Run tests without local feature
run: |
@ -305,21 +136,21 @@ jobs:
name: Code Coverage
runs-on: ubuntu-latest
permissions:
contents: read
contents: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/checkout@v6
# install nodejs
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
uses: actions/setup-node@v6
with:
node-version: '22'
node-version: '20'
- name: Install uv
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
uses: astral-sh/setup-uv@v7
- name: Install Rust
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
uses: dtolnay/rust-toolchain@stable
- name: Set up Python
run: uv python install
@ -327,7 +158,7 @@ jobs:
- name: Create venv for python
run: uv venv
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
- uses: Swatinem/rust-cache@v2
- name: Install cargo-llvm-cov
run: cargo install cargo-llvm-cov
@ -342,19 +173,19 @@ jobs:
name: Example test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/checkout@v6
# install nodejs
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
uses: actions/setup-node@v6
with:
node-version: '22'
node-version: '20'
- name: Install uv
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
uses: astral-sh/setup-uv@v7
- name: Install Rust
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
uses: dtolnay/rust-toolchain@stable
- name: Set up Python
run: uv python install
@ -362,7 +193,7 @@ jobs:
- name: Create venv for python
run: uv venv
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
- uses: Swatinem/rust-cache@v2
- name: Add target WASI preview 2
run: |
@ -389,7 +220,7 @@ jobs:
if [ -f "$dir/Cargo.toml" ]; then
if [[ "$dir" != *"wasi"* ]]; then
echo "Testing $dir"
cargo test --manifest-path "$dir/Cargo.toml" --all-features --all-targets
cargo test --manifest-path "$dir/Cargo.toml" --all-features
fi
fi
done
@ -398,12 +229,12 @@ jobs:
name: Security Audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
- uses: Swatinem/rust-cache@v2
- name: Install cargo-audit
run: cargo install cargo-audit
@ -415,12 +246,12 @@ jobs:
name: Generate Documentation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@7c8d7d138f5c09cef361f8214cf96882cd029cdb # nightly
uses: dtolnay/rust-toolchain@nightly
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
- uses: Swatinem/rust-cache@v2
- name: Generate documentation
run: |
@ -441,7 +272,7 @@ jobs:
# This happened recently in the attack on `tj-actions/changed-files`, but
# has happened many times before as well.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/checkout@v6
- name: Update Rust
run: |

View file

@ -8,9 +8,6 @@ on:
schedule:
- cron: '0 0 * * 1' # Weekly on Monday
permissions:
contents: read
jobs:
analyze:
name: Analyze
@ -24,16 +21,16 @@ jobs:
language: [rust, javascript-typescript, python, actions]
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v6
- name: Initialize CodeQL
uses: github/codeql-action/init@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
config-file: ./.github/codeql/codeql-config.yml
- name: Autobuild
uses: github/codeql-action/autobuild@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
uses: github/codeql-action/autobuild@v4
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
uses: github/codeql-action/analyze@v4

View file

@ -1,149 +0,0 @@
name: Conformance
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
concurrency:
group: conformance-${{ github.ref }}
cancel-in-progress: true
env:
CONFORMANCE_VERSION: "0.2.0-alpha.10"
permissions:
contents: read
jobs:
server:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
- name: Build conformance binaries
run: cargo build -p mcp-conformance
- name: Test conformance server
run: cargo test -p mcp-conformance --bin conformance-server
- name: Start conformance server
run: |
PORT=8001 ./target/debug/conformance-server &
echo $! > server.pid
for _ in $(seq 1 30); do
if curl -s -o /dev/null http://127.0.0.1:8001/mcp; then
exit 0
fi
sleep 1
done
echo "conformance server did not become ready" >&2
exit 1
- name: Run 2025-11-25 server suite
run: |
npx -y "@modelcontextprotocol/conformance@${CONFORMANCE_VERSION}" server \
--url http://127.0.0.1:8001/mcp \
--suite all \
--spec-version 2025-11-25 \
-o conformance-results/2025-11-25
- name: Run 2026-07-28 server suite
run: |
npx -y "@modelcontextprotocol/conformance@${CONFORMANCE_VERSION}" server \
--url http://127.0.0.1:8001/mcp \
--suite all \
--spec-version 2026-07-28 \
-o conformance-results/2026-07-28
- name: Run extension server scenarios (informational)
run: |
scenarios=(
tasks-lifecycle
tasks-capability-negotiation
tasks-wire-fields
tasks-request-state-removal
tasks-mrtr-input
tasks-request-headers
tasks-dispatch-and-envelope
tasks-status-notifications
tasks-required-task-error
tasks-mrtr-composition
)
for scenario in "${scenarios[@]}"; do
npx -y "@modelcontextprotocol/conformance@${CONFORMANCE_VERSION}" server \
--url http://127.0.0.1:8001/mcp \
--scenario "$scenario" \
--expected-failures conformance/expected-failures-extensions.yaml \
-o conformance-extension-results
done
- name: Stop conformance server
if: always()
run: kill "$(cat server.pid)" 2>/dev/null || true
- name: Upload results
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: conformance-server-results
path: |
conformance-results
conformance-extension-results
client:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
- name: Build conformance binaries
run: cargo build -p mcp-conformance
- name: Run 2025-11-25 client suite
run: |
npx -y "@modelcontextprotocol/conformance@${CONFORMANCE_VERSION}" client \
--command "$(pwd)/target/debug/conformance-client" \
--suite all \
--spec-version 2025-11-25 \
-o conformance-client-results/2025-11-25
- name: Run 2026-07-28 client suite
run: |
npx -y "@modelcontextprotocol/conformance@${CONFORMANCE_VERSION}" client \
--command "$(pwd)/target/debug/conformance-client" \
--suite all \
--spec-version 2026-07-28 \
-o conformance-client-results/2026-07-28
- name: Run extension client suite (informational)
run: |
npx -y "@modelcontextprotocol/conformance@${CONFORMANCE_VERSION}" client \
--command "$(pwd)/target/debug/conformance-client" \
--suite extensions \
--expected-failures conformance/expected-failures-extensions.yaml \
-o conformance-client-results/extensions
- name: Upload results
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: conformance-client-results
path: conformance-client-results

View file

@ -1,6 +1,8 @@
name: Release-plz
permissions: {}
permissions:
pull-requests: write
contents: write
on:
push:
@ -16,24 +18,18 @@ jobs:
if: ${{ github.repository_owner == 'modelcontextprotocol' }}
permissions:
contents: write
pull-requests: read
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
uses: dtolnay/rust-toolchain@stable
- name: Run release-plz
uses: release-plz/action@v0.5
with:
toolchain: "1.92"
# Using fork until semver_check_features support is released upstream.
# See: https://github.com/release-plz/release-plz/pull/2757
- name: Install release-plz from fork
run: cargo install --locked --git https://github.com/DaleSeo/release-plz --rev c2ea743bacf9bbd56e344df68219e60b0ad35d04 release-plz
- name: Run release-plz release
run: release-plz release
command: release
env:
GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
@ -50,20 +46,15 @@ jobs:
cancel-in-progress: false
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
uses: dtolnay/rust-toolchain@stable
- name: Run release-plz
uses: release-plz/action@v0.5
with:
toolchain: "1.92"
# Using fork until semver_check_features support is released upstream.
# See: https://github.com/release-plz/release-plz/pull/2757
- name: Install release-plz from fork
run: cargo install --locked --git https://github.com/DaleSeo/release-plz --rev c2ea743bacf9bbd56e344df68219e60b0ad35d04 release-plz
- name: Run release-plz release-pr
run: release-plz release-pr
command: release-pr
env:
GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

View file

@ -1,48 +0,0 @@
# OpenSSF Scorecard supply-chain security analysis.
name: Scorecard supply-chain security
on:
branch_protection_rule:
schedule:
- cron: '5 23 * * 5'
push:
branches: [main]
pull_request:
workflow_dispatch:
permissions: read-all
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
security-events: write
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Run analysis
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
with:
results_file: results.sarif
results_format: sarif
# Publishing only succeeds on the default branch. PR and manual runs
# still validate the analysis and produce SARIF without publishing.
publish_results: ${{ github.event_name != 'pull_request' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
- name: Upload SARIF artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: scorecard-results
path: results.sarif
retention-days: 5
- name: Upload to code scanning
uses: github/codeql-action/upload-sarif@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
with:
sarif_file: results.sarif

View file

@ -1,38 +0,0 @@
name: Clean Up Stale PRs and Issues
on:
schedule:
- cron: "0 0 * * *"
workflow_dispatch:
permissions:
contents: read
jobs:
stale:
name: Clean Up
runs-on: ubuntu-24.04
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@4391f3da665fdf50b6810c1a66712fb9ba21aa93 # v11
with:
days-before-stale: 60
days-before-close: 14
exempt-issue-labels: keep,security,pinned
exempt-pr-labels: keep,security,pinned
exempt-draft-pr: true
stale-issue-message: >
This issue has had no activity for 60 days and is now marked as stale.
It will be closed in 14 days if there is no further activity. Add the
`keep` label to keep it open.
close-issue-message: >
Closing this issue after 14 days of inactivity since it was marked stale.
Feel free to reopen if it is still relevant.
stale-pr-message: >
This pull request has had no activity for 60 days and is now marked as stale.
It will be closed in 14 days if there is no further activity. Add the
`keep` label to keep it open.
close-pr-message: >
Closing this pull request after 14 days of inactivity since it was marked stale.
Feel free to reopen if you plan to continue the work.

View file

@ -39,7 +39,7 @@ jobs:
TRIAGE_MODEL: ${{ vars.TRIAGE_MODEL || 'gpt-4o-mini' }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/checkout@v6
- name: Install jq
run: sudo apt-get install -y jq

2
.gitignore vendored
View file

@ -27,5 +27,3 @@ __pycache__/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
node_modules/
.DS_Store

View file

@ -4,13 +4,12 @@ default-members = ["crates/rmcp", "crates/rmcp-macros"]
resolver = "2"
[workspace.dependencies]
rmcp = { version = "3.4.0", path = "./crates/rmcp" }
rmcp-macros = { version = "3.4.0", path = "./crates/rmcp-macros" }
rmcp = { version = "1.3.0", path = "./crates/rmcp" }
rmcp-macros = { version = "1.3.0", path = "./crates/rmcp-macros" }
[workspace.package]
edition = "2024"
rust-version = "1.88"
version = "3.4.0"
version = "1.3.0"
authors = ["4t145 <u4t145@163.com>"]
license = "Apache-2.0"
repository = "https://github.com/modelcontextprotocol/rust-sdk/"

View file

@ -1,65 +0,0 @@
# Dependency Policy
This document describes how the Rust MCP SDK (`rmcp`, `rmcp-macros`) selects,
updates, and maintains its dependencies. It applies to all crates in the workspace.
## Goals
- Keep dependencies current and secure without churn that destabilizes the public API.
- Minimize the dependency footprint we impose on downstream users.
- Make dependency updates reviewable and traceable.
## Choosing dependencies
Before adding a new dependency we consider:
- **Necessity** — whether the functionality is worth an added dependency, or is small
enough to implement directly.
- **Maintenance & trust** — active maintenance, a healthy release history, and a
permissive, compatible license (the workspace is `Apache-2.0`).
- **Footprint** — transitive dependency count and compile-time cost.
- **Optionality** — anything not needed by every user should sit behind a Cargo
feature so downstream builds stay lean. The SDK is heavily feature-gated for this
reason (transports, `auth`, `schemars`, `reqwest`, etc.).
Version requirements are specified with caret (`^`, the Cargo default) constraints so
compatible upgrades are picked up automatically.
## Automated updates
Dependency updates are automated with [Dependabot](https://docs.github.com/en/code-security/dependabot),
configured in [`.github/dependabot.yml`](.github/dependabot.yml):
| Ecosystem | Cadence | PR label |
| -------------- | ------- | ----------------- |
| Cargo crates | Weekly | `T-dependencies` |
| GitHub Actions | Daily | `T-CI` |
Dependabot opens a limited number of PRs at a time (currently 3 per ecosystem) to
keep the review queue manageable. Update PRs run the full CI suite (build, format,
clippy, tests, and conformance) and must be green before merge.
## Review and merge
- Patch and minor updates that pass CI are reviewed and merged promptly by maintainers.
- Major (breaking) updates are evaluated individually; they may require code changes
and are scheduled deliberately rather than merged automatically.
- Any dependency change that affects the **public API** (for example a type re-exported
from a dependency) is treated as a potential breaking change and follows
[`VERSIONING.md`](VERSIONING.md).
## Security updates
- Security fixes take priority over routine updates and are expedited.
- Vulnerabilities in the SDK itself are handled per [`SECURITY.md`](SECURITY.md) via
GitHub's private Security Advisory process.
- Advisories affecting our dependencies (e.g. via the [RustSec Advisory Database](https://rustsec.org/))
are addressed as soon as a fixed version is available; if a released version of the
SDK is impacted, a patched release is published and, where warranted, the affected
versions are yanked from crates.io.
## Minimum Supported Rust Version
Dependency upgrades must not raise the SDK's declared MSRV (`rust-version` in
`Cargo.toml`) without an explicit, documented MSRV bump — which is itself treated as a
breaking change per [`VERSIONING.md`](VERSIONING.md).

1346
README.md

File diff suppressed because it is too large Load diff

View file

@ -1,112 +1,71 @@
# RMCP Roadmap
This roadmap tracks the path to [SEP-1730](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1730) Tier 1 for the Rust MCP SDK.
This roadmap tracks the path to SEP-1730 Tier 1 for the Rust MCP SDK.
**Status: all SEP-1730 Tier 1 requirements are met.** Conformance is
100% across every date-versioned suite, a stable ≥1.0.0 release has shipped
([Releases](https://github.com/modelcontextprotocol/rust-sdk/releases)),
issue triage and critical-bug (P0) resolution are within the Tier 1 SLAs, the
governance documents (`VERSIONING.md`, `DEPENDENCY_POLICY.md`, this `ROADMAP.md`) are
published, and all 48 non-experimental features are documented with examples. This
document now serves as the ongoing tracker for spec conformance and SDK health.
| SEP-1730 Tier 1 requirement | Status | Evidence |
| --------------------------------------------------- | ------ | -------- |
| Server conformance 100% (date-versioned) | ✅ | 30/30 — see below |
| Client conformance 100% (date-versioned) | ✅ | 39/39 scored — see below |
| Issue triage ≥90% within 2 business days | ✅ | 95.2% (20/21) |
| All P0 bugs resolved within 7 days | ✅ | 0 open; last P0 (#741) resolved in 3 days |
| Stable release ≥1.0.0 (no pre-release suffix) | ✅ | [latest release](https://github.com/modelcontextprotocol/rust-sdk/releases/latest) (see tooling note below) |
| Clear versioning + breaking-change policy | ✅ | [`VERSIONING.md`](VERSIONING.md) |
| All non-experimental features documented w/ examples| ✅ | 48/48 in [`README.md`](README.md) |
| Published dependency update policy | ✅ | [`DEPENDENCY_POLICY.md`](DEPENDENCY_POLICY.md) + [`.github/dependabot.yml`](.github/dependabot.yml) |
| Published roadmap tracking spec components | ✅ | this document |
| Suite (date-versioned) | Server | Client |
| ---------------------- | ------------- | ------------- |
| 2025-11-25 | 100% (30/30) | 100% |
| 2026-07-28 | 100% (30/30) | 100% |
Only date-versioned scenarios count toward SDK tiering. `draft` (2026-07-28 draft)
and `extension` scenarios are informational and reported separately below.
> **Tooling note — `stable_release`:** the SEP-1730 `tier-check` CLI may report
> `stable_release` as failing because it does not parse the workspace tag prefix
> `rmcp-v` (as in `rmcp-vX.Y.Z`). The published releases are genuine stable,
> non-pre-release versions ≥1.0.0
> ([Releases](https://github.com/modelcontextprotocol/rust-sdk/releases)); the
> flag is a tooling artifact, not an unmet requirement.
Server conformance: 87.5% (28/32) · Client conformance: 80.0% (16/20)
---
## Conformance
## Tier 2 → Tier 1
Per-scenario status for the current spec is tracked in the epic issue
[#977 — Tracking: 2026-07-28 spec conformance](https://github.com/modelcontextprotocol/rust-sdk/issues/977),
under the [`2026-07-28 spec` milestone](https://github.com/modelcontextprotocol/rust-sdk/milestone/3).
### Conformance
CI (`.github/workflows/conformance.yml`) runs the full `2025-11-25` and `2026-07-28`
server and client suites on every push and PR. Both suites are fully green.
#### Server (87.5% → 100%)
### Informational (not scored for tiering)
- [ ] Fix `prompts-get-with-args` — prompt argument handling returns incorrect result (arg1/arg2 not substituted)
- [ ] Fix `prompts-get-embedded-resource` — embedded resource content in prompt responses (invalid content union)
- [ ] Fix `elicitation-sep1330-enums` — enum inference handling per SEP-1330 (missing enumNames for legacy titled enum)
- [ ] Fix `dns-rebinding-protection` — validate `Host` / `Origin` headers on Streamable HTTP transport (accepts invalid headers with 200)
Extension-tagged scenarios are excluded by `--spec-version` filters, so CI runs them
in separate steps against `conformance/expected-failures-extensions.yaml`:
#### Client (80.0% → 100%)
| Scenario | Tag | Status |
| --------------------------------------- | ---------- | ------ |
| `auth/client-credentials-basic` | extension | ✅ Pass |
| `auth/client-credentials-jwt` | extension | ✅ Pass |
| `auth/enterprise-managed-authorization` | extension | ❌ Expected failure (not implemented by the conformance client) |
| `auth/wif-jwt-bearer` | 2026-07-28 draft | ❌ Expected failure (WIF / SEP-1933, draft) |
| `tasks-*` (SEP-2663) | extension | ❌ 9 expected failures · ⏭️ 1 upstream-skipped |
- [ ] Fix `auth/metadata-var3` — AS metadata discovery variant 3 (no authorization support detected)
- [ ] Fix `auth/scope-from-www-authenticate` — use scope parameter from WWW-Authenticate header on 403 insufficient_scope
- [ ] Fix `auth/scope-step-up` — handle 403 `insufficient_scope` and re-authorize with upgraded scopes
- [ ] Fix `auth/2025-03-26-oauth-endpoint-fallback` — legacy OAuth endpoint fallback for pre-2025-06-18 servers (no authorization support detected)
### Spec features without conformance scenarios
### Governance & Policy
Conformance does not cover the entire spec surface. Remaining feature work tracked via
the milestone:
- [ ] Create `VERSIONING.md` — document semver scheme, what constitutes a breaking change, and how breaking changes are communicated
- SEP-2567 sessionless MCP via explicit state handles (#870)
- SEP-2260 server requests must associate with a client request (#873)
- SEP-2549 follow-up: client-side TTL-honoring cache (#974)
### Documentation (26/48 → 48/48 features with prose + examples)
#### Undocumented features (14)
- [ ] Tools — image results
- [ ] Tools — audio results
- [ ] Tools — embedded resources
- [ ] Prompts — embedded resources
- [ ] Prompts — image content
- [ ] Elicitation — URL mode
- [ ] Elicitation — default values
- [ ] Elicitation — complete notification
- [ ] Ping
- [ ] SSE transport — legacy (client)
- [ ] SSE transport — legacy (server)
- [ ] Pagination
- [ ] Protocol version negotiation
- [ ] JSON Schema 2020-12 support *(upgrade from partial)*
#### Partially documented features (7)
- [ ] Tools — error handling *(add dedicated prose + example)*
- [ ] Resources — reading binary *(add dedicated example)*
- [ ] Elicitation — form mode *(add prose docs, not just example README)*
- [ ] Elicitation — schema validation *(add prose docs)*
- [ ] Elicitation — enum values *(add prose docs)*
- [ ] Capability negotiation *(add dedicated prose explaining the builder API)*
- [ ] Protocol version negotiation *(document version negotiation behavior)*
---
## Completed
## Informational (not scored for tiering)
### Tier 1 requirements
These draft/extension scenarios are tracked but do not count toward tier advancement:
- [x] **v3.0.0 stable released** (2026-07-28) — MRTR, SEP-2549 cache hints, SEP-2243
standard headers, SEP-2575 stateless MCP, and SEP-2106 relaxations
- [x] 2025-11-25 server conformance 100% (30/30)
- [x] 2025-11-25 client conformance 100%
- [x] 2026-07-28 server conformance 100% (30/30 dated)
- [x] 2026-07-28 client conformance 100% (dated)
- [x] Issue triage ≥90% within 2 business days (95.2%, 20/21) with the full SEP-1730
label taxonomy (bug, enhancement, question, needs confirmation, needs repro,
ready for work, good first issue, help wanted, P0–P3)
- [x] All P0 bugs resolved within 7 days (0 open; #741 resolved in 3 days). #815 was
reclassified from `P0` to `T-security` — it was a CVE/advisory-coordination task
for an already-shipped fix (PR #764, released in v1.4.0), not a critical-bug fix
- [x] `VERSIONING.md` — semver scheme, breaking-change definition, and communication policy
- [x] `DEPENDENCY_POLICY.md` — published dependency update policy (with `.github/dependabot.yml`)
- [x] `SECURITY.md` and Dependabot configuration
- [x] All 48 non-experimental features documented with examples in the README
(closed the last 6 gaps on 2026-07-30: audio results, resource-template reading,
resource-argument completion, ping, and the legacy HTTP+SSE non-goal writeup)
### Spec implementation
- [x] SEP-2322 MRTR (server scenarios + `sep-2322-client-request-state`)
- [x] SEP-2575 Make MCP Stateless (`server-stateless`)
- [x] SEP-2164 resource not found
- [x] SEP-2549 cache hints (`caching`)
- [x] SEP-2243 HTTP standardization (`http-header-validation`, standard headers)
- [x] DNS rebinding protection
---
## Nice-to-have (scorecard hygiene, not required for Tier 1)
- [ ] Add a top-level `CHANGELOG.md` (release notes are currently managed by release-plz).
- [ ] Add a top-level `CONTRIBUTING.md` (contributor docs currently live at `docs/CONTRIBUTE.MD`).
| Scenario | Tag | Status |
|---|---|---|
| `auth/resource-mismatch` | draft | ❌ Failed |
| `auth/client-credentials-jwt` | extension | ❌ Failed — JWT `aud` claim verification error |
| `auth/client-credentials-basic` | extension | ✅ Passed |
| `auth/cross-app-access-complete-flow` | extension | ❌ Failed — sends `authorization_code` grant instead of `jwt-bearer` |

View file

@ -1,66 +0,0 @@
# Versioning Policy
`rmcp` and `rmcp-macros` follow [Semantic Versioning 2.0.0](https://semver.org/),
the standard versioning scheme for the Rust/Cargo ecosystem. Given a version
`MAJOR.MINOR.PATCH`:
- **MAJOR** — incremented for breaking changes to the public API.
- **MINOR** — incremented for new, backwards-compatible functionality.
- **PATCH** — incremented for backwards-compatible bug fixes.
The `rmcp` and `rmcp-macros` crates are versioned together and released with the
same version number.
## What counts as a breaking change
We treat a change as breaking if it would cause code that compiled against the
previous release to fail to compile, or to change behavior in a way callers could
not reasonably anticipate. This follows the Cargo SemVer guidelines
([The Cargo Book — SemVer Compatibility](https://doc.rust-lang.org/cargo/reference/semver.html)).
Examples include:
- Removing or renaming a public item (function, type, trait, module, variant, field).
- Changing a public function signature, trait method, or trait bound.
- Adding a required method to a public trait, or a field to a struct that callers
construct directly. (Public structs and enums are marked `#[non_exhaustive]` where
practical so that additive changes remain non-breaking.)
- Raising the Minimum Supported Rust Version (MSRV) — see below.
Additive changes — new functions, new types, new enum variants on
`#[non_exhaustive]` enums, new optional Cargo features — are **not** breaking and
ship in MINOR releases.
### Cargo features
The public API is feature-gated. SemVer compatibility is evaluated against the
crate's **default features** and all features except `local`.
## Pre-1.0 crates in the workspace
The core crates (`rmcp`, `rmcp-macros`) are `>= 1.0.0` and follow the rules above.
Any workspace crate still in `0.x` follows Cargo's pre-1.0 convention, where the
`MINOR` version acts as the breaking-change signal (`0.MINOR.PATCH`).
## Minimum Supported Rust Version (MSRV)
The MSRV is declared via `rust-version` in `Cargo.toml`. Raising the MSRV is
considered a breaking change and is only done in a MINOR or MAJOR release with a
note in the release notes.
## How breaking changes are communicated
- Release automation is handled by [release-plz](https://release-plz.dev/), which
derives version bumps from [Conventional Commits](https://www.conventionalcommits.org/)
(`feat!:` / `fix!:` and `BREAKING CHANGE:` footers trigger a MAJOR bump) and runs
`cargo-semver-checks` to catch unintended API breaks.
- Every release is published as a tagged [GitHub Release](https://github.com/modelcontextprotocol/rust-sdk/releases)
with generated notes.
- Significant migrations (for example the 2.x → 3.x upgrade) are accompanied by a
migration guide linked from the release notes and the README.
## Yanking
A published version that is later found to be broken or to contain a security issue
may be [yanked](https://doc.rust-lang.org/cargo/commands/cargo-yank.html) from
crates.io. Yanking prevents new dependents from selecting the version but does not
delete it; a fixed PATCH release is published alongside.

View file

@ -3,6 +3,5 @@
"redirect_uris": ["http://127.0.0.1:8080/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "none",
"application_type": "native"
"token_endpoint_auth_method": "none"
}

View file

@ -1,2 +1,3 @@
msrv = "1.85"
too-many-arguments-threshold = 10
check-private-items = false

View file

@ -18,9 +18,6 @@ rmcp = { path = "../crates/rmcp", features = [
"client",
"elicitation",
"auth",
"auth-client-credentials-jwt",
"auth-enterprise-managed",
"request-state",
"transport-streamable-http-server",
"transport-streamable-http-client-reqwest",
] }
@ -32,7 +29,8 @@ tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
axum = { version = "0.8", features = ["macros"] }
anyhow = "1"
oauth2 = { version = "5.0", default-features = false }
reqwest = { version = "0.13", features = ["json"] }
urlencoding = "2"
url = "2"
p256 = { version = "0.13", features = ["ecdsa"] }
base64 = "0.22"

View file

@ -1,26 +0,0 @@
# Known failures for informational extension scenarios in
# @modelcontextprotocol/conformance CONFORMANCE_VERSION.
#
# Extensions are not selected by a `--spec-version` run and do not count toward
# SDK tiering. CI runs them separately so a green versioned-spec suite does not
# hide extension gaps.
#
# This is a strict baseline:
# - an unlisted failure fails the build
# - a listed scenario that starts passing fails the build as a stale entry
#
# When bumping CONFORMANCE_VERSION, review the available extension and
# pending scenarios and update this file deliberately.
# The SEP-2663 Tasks Extension server scenarios (tracked in #868) all pass and
# were removed from this baseline. `tasks-status-notifications` remains
# upstream-skipped pending the subscriptions/listen rewrite; CI will fail if it
# becomes active but does not pass.
server: []
client:
# Informational OAuth extension scenarios.
- auth/enterprise-managed-authorization
- auth/dpop
- auth/dpop-nonce
- auth/wif-jwt-bearer

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