Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dbd2382755 |
||
|
|
9f7ace357a |
||
|
|
011f281ced |
||
|
|
0cfbe7f453 |
||
|
|
fd7811fdaa |
||
|
|
0550e13c1b |
||
|
|
b037c0fa88 |
||
|
|
d481cb0eec |
||
|
|
47fcdf4b57 |
||
|
|
165ff92df1 |
||
|
|
fbfc5cc96e |
||
|
|
3075dc9152 |
||
|
|
a5d1169e20 |
||
|
|
4fa84a5130 |
||
|
|
b86995aebd |
||
|
|
e27c5d1c24 |
||
|
|
3e636cab26 |
||
|
|
b2c90b1edc |
||
|
|
46db531df9 |
||
|
|
744b9f904c |
||
|
|
e8a0d7099a |
||
|
|
6fa40536de |
||
|
|
f9a68bfa5c |
||
|
|
7d14f3a8a2 |
||
|
|
04c2f3fac9 |
||
|
|
add9cbeab5 |
||
|
|
302319861a |
||
|
|
3b5ca4dd3f |
||
|
|
42cf8d9fa3 |
||
|
|
51ccb42993 |
||
|
|
ad9832ec21 |
||
|
|
4e74499783 |
||
|
|
3a2ebbc920 |
||
|
|
1afde5b4fc |
||
|
|
ec041b7430 |
||
|
|
3ef34e64e7 |
||
|
|
12db02833f |
||
|
|
c8bb1c6f7d |
||
|
|
3501f3e646 |
||
|
|
103bf239b3 |
||
|
|
78cf9f1cb6 |
||
|
|
02decfb682 |
||
|
|
697c497724 |
||
|
|
4a738b9dd9 |
||
|
|
6f8dcde04f |
||
|
|
d5c46b3fe0 |
||
|
|
38428f66bc |
||
|
|
018ab24fac |
||
|
|
f9b39f3c13 |
||
|
|
0f164cd67e |
||
|
|
fd14830a19 |
||
|
|
0282172f33 |
||
|
|
248cbca688 |
||
|
|
f713ebd1a6 |
||
|
|
a50a73fda2 |
||
|
|
d81bfa8854 |
||
|
|
3f5a3f6a28 |
||
|
|
a0e103c970 |
||
|
|
b5cf34eee7 |
||
|
|
22be2cc5ad |
||
|
|
02c62aef2e |
||
|
|
c345078867 |
||
|
|
f8e63824d3 |
||
|
|
8fb3e046a1 |
||
|
|
3c8fb2a7e4 |
||
|
|
e150d4f3a6 |
||
|
|
9a3168af87 |
||
|
|
baac607e52 |
||
|
|
f57d585168 |
||
|
|
07bcda2973 |
||
|
|
f8f9607d0b |
||
|
|
c4def55422 |
||
|
|
7e14020cb8 |
||
|
|
9cbb69d9e9 |
||
|
|
5b216605f2 |
||
|
|
684ff5a51e |
||
|
|
830e088d73 |
116 changed files with 12109 additions and 1592 deletions
8
.github/dependabot.yml
vendored
8
.github/dependabot.yml
vendored
|
|
@ -16,11 +16,15 @@ updates:
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "daily"
|
interval: "weekly"
|
||||||
labels:
|
labels:
|
||||||
# Mark PRs as CI related change.
|
# Mark PRs as CI related change.
|
||||||
- T-CI
|
- T-CI
|
||||||
open-pull-requests-limit: 3
|
open-pull-requests-limit: 3
|
||||||
|
groups:
|
||||||
|
codeql-action:
|
||||||
|
patterns:
|
||||||
|
- "github/codeql-action*"
|
||||||
commit-message:
|
commit-message:
|
||||||
prefix: "chore"
|
prefix: "chore"
|
||||||
include: "scope"
|
include: "scope"
|
||||||
|
|
|
||||||
5
.github/workflows/auto-label-pr.yml
vendored
5
.github/workflows/auto-label-pr.yml
vendored
|
|
@ -3,6 +3,9 @@ on:
|
||||||
# Runs workflow when activity on a PR in the workflow's repository occurs.
|
# Runs workflow when activity on a PR in the workflow's repository occurs.
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
auto-label:
|
auto-label:
|
||||||
permissions:
|
permissions:
|
||||||
|
|
@ -20,7 +23,7 @@ jobs:
|
||||||
PR_URL: ${{ github.event.pull_request.html_url }}
|
PR_URL: ${{ github.event.pull_request.html_url }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/labeler@v7
|
- uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # v7
|
||||||
with:
|
with:
|
||||||
# Auto-include paths starting with dot (e.g. .github)
|
# Auto-include paths starting with dot (e.g. .github)
|
||||||
dot: true
|
dot: true
|
||||||
|
|
|
||||||
115
.github/workflows/ci.yml
vendored
115
.github/workflows/ci.yml
vendored
|
|
@ -12,34 +12,35 @@ env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
ARTIFACT_DIR: release-artifacts
|
ARTIFACT_DIR: release-artifacts
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
commit-lint:
|
commit-lint:
|
||||||
name: Lint Commit Messages
|
name: Lint Commit Messages
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v7
|
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||||
with:
|
with:
|
||||||
node-version: '22'
|
node-version: '22'
|
||||||
|
|
||||||
- name: Install commitlint
|
- name: Install commitlint
|
||||||
run: |
|
run: npm install --global @commitlint/cli@20.4.3 @commitlint/config-conventional@20.4.3
|
||||||
npm install --save-dev @commitlint/cli @commitlint/config-conventional
|
|
||||||
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
|
|
||||||
|
|
||||||
- name: Lint commit messages
|
- name: Lint commit messages
|
||||||
run: npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} --verbose
|
run: commitlint --extends @commitlint/config-conventional --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} --verbose
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
name: Code Formatting
|
name: Code Formatting
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: Install Rust fmt
|
- name: Install Rust fmt
|
||||||
run: rustup toolchain install nightly --component rustfmt
|
run: rustup toolchain install nightly --component rustfmt
|
||||||
|
|
@ -51,32 +52,40 @@ jobs:
|
||||||
name: Lint with Clippy
|
name: Lint with Clippy
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
||||||
|
|
||||||
- name: Run clippy
|
- name: Run clippy (all features)
|
||||||
run: cargo clippy --all-targets --all-features -- -D warnings
|
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:
|
semver:
|
||||||
name: SemVer Check
|
name: SemVer Check
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
||||||
|
|
||||||
- name: Install cargo-semver-checks
|
- name: Install cargo-semver-checks
|
||||||
uses: taiki-e/install-action@v2.85.2
|
uses: taiki-e/install-action@3f74d7c16a4242f1c95561e98edc25d36adb4375 # v2.87.12
|
||||||
with:
|
with:
|
||||||
tool: cargo-semver-checks
|
tool: cargo-semver-checks
|
||||||
|
|
||||||
|
|
@ -117,7 +126,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
|
@ -125,12 +134,12 @@ jobs:
|
||||||
# to be installed (it does not need to be the default; the tool invokes it
|
# to be installed (it does not need to be the default; the tool invokes it
|
||||||
# via `cargo +nightly`).
|
# via `cargo +nightly`).
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@nightly
|
uses: dtolnay/rust-toolchain@7c8d7d138f5c09cef361f8214cf96882cd029cdb # nightly
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
||||||
|
|
||||||
- name: Install cargo-public-api
|
- name: Install cargo-public-api
|
||||||
uses: taiki-e/install-action@v2.85.2
|
uses: taiki-e/install-action@3f74d7c16a4242f1c95561e98edc25d36adb4375 # v2.87.12
|
||||||
with:
|
with:
|
||||||
tool: cargo-public-api
|
tool: cargo-public-api
|
||||||
|
|
||||||
|
|
@ -201,9 +210,9 @@ jobs:
|
||||||
name: spell check with typos
|
name: spell check with typos
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
- name: Spell Check Repo
|
- name: Spell Check Repo
|
||||||
uses: crate-ci/typos@master
|
uses: crate-ci/typos@d43b6c087ac471e2ea7b8af622ff15f05c0c365b # master
|
||||||
|
|
||||||
msrv:
|
msrv:
|
||||||
name: Check MSRV
|
name: Check MSRV
|
||||||
|
|
@ -211,7 +220,7 @@ jobs:
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: Read MSRV from Cargo.toml
|
- name: Read MSRV from Cargo.toml
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -220,11 +229,11 @@ jobs:
|
||||||
echo "MSRV=$MSRV" >> "$GITHUB_ENV"
|
echo "MSRV=$MSRV" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
- name: Install MSRV Rust
|
- name: Install MSRV Rust
|
||||||
uses: dtolnay/rust-toolchain@master
|
uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # master
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ env.MSRV }}
|
toolchain: ${{ env.MSRV }}
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
||||||
|
|
||||||
- name: Check default workspace members with MSRV
|
- name: Check default workspace members with MSRV
|
||||||
run: cargo +${{ env.MSRV }} check --all-targets --all-features
|
run: cargo +${{ env.MSRV }} check --all-targets --all-features
|
||||||
|
|
@ -233,19 +242,19 @@ jobs:
|
||||||
name: Run Tests
|
name: Run Tests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
# install nodejs
|
# install nodejs
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v7
|
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||||
with:
|
with:
|
||||||
node-version: '22'
|
node-version: '22'
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@v7
|
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
run: uv python install
|
run: uv python install
|
||||||
|
|
@ -253,7 +262,7 @@ jobs:
|
||||||
- name: Create venv for python
|
- name: Create venv for python
|
||||||
run: uv venv
|
run: uv venv
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: cargo test --all-features
|
run: cargo test --all-features
|
||||||
|
|
@ -262,19 +271,19 @@ jobs:
|
||||||
name: Run Tests (no local feature)
|
name: Run Tests (no local feature)
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
# install nodejs
|
# install nodejs
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v7
|
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||||
with:
|
with:
|
||||||
node-version: '22'
|
node-version: '22'
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@v7
|
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
run: uv python install
|
run: uv python install
|
||||||
|
|
@ -282,7 +291,7 @@ jobs:
|
||||||
- name: Create venv for python
|
- name: Create venv for python
|
||||||
run: uv venv
|
run: uv venv
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
||||||
|
|
||||||
- name: Run tests without local feature
|
- name: Run tests without local feature
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -296,21 +305,21 @@ jobs:
|
||||||
name: Code Coverage
|
name: Code Coverage
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
# install nodejs
|
# install nodejs
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v7
|
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||||
with:
|
with:
|
||||||
node-version: '22'
|
node-version: '22'
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@v7
|
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
run: uv python install
|
run: uv python install
|
||||||
|
|
@ -318,7 +327,7 @@ jobs:
|
||||||
- name: Create venv for python
|
- name: Create venv for python
|
||||||
run: uv venv
|
run: uv venv
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
||||||
|
|
||||||
- name: Install cargo-llvm-cov
|
- name: Install cargo-llvm-cov
|
||||||
run: cargo install cargo-llvm-cov
|
run: cargo install cargo-llvm-cov
|
||||||
|
|
@ -333,19 +342,19 @@ jobs:
|
||||||
name: Example test
|
name: Example test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
# install nodejs
|
# install nodejs
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v7
|
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||||
with:
|
with:
|
||||||
node-version: '22'
|
node-version: '22'
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@v7
|
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
run: uv python install
|
run: uv python install
|
||||||
|
|
@ -353,7 +362,7 @@ jobs:
|
||||||
- name: Create venv for python
|
- name: Create venv for python
|
||||||
run: uv venv
|
run: uv venv
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
||||||
|
|
||||||
- name: Add target WASI preview 2
|
- name: Add target WASI preview 2
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -389,12 +398,12 @@ jobs:
|
||||||
name: Security Audit
|
name: Security Audit
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
||||||
|
|
||||||
- name: Install cargo-audit
|
- name: Install cargo-audit
|
||||||
run: cargo install cargo-audit
|
run: cargo install cargo-audit
|
||||||
|
|
@ -406,12 +415,12 @@ jobs:
|
||||||
name: Generate Documentation
|
name: Generate Documentation
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: dtolnay/rust-toolchain@nightly
|
uses: dtolnay/rust-toolchain@7c8d7d138f5c09cef361f8214cf96882cd029cdb # nightly
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
||||||
|
|
||||||
- name: Generate documentation
|
- name: Generate documentation
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -432,7 +441,7 @@ jobs:
|
||||||
# This happened recently in the attack on `tj-actions/changed-files`, but
|
# This happened recently in the attack on `tj-actions/changed-files`, but
|
||||||
# has happened many times before as well.
|
# has happened many times before as well.
|
||||||
|
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: Update Rust
|
- name: Update Rust
|
||||||
run: |
|
run: |
|
||||||
|
|
|
||||||
11
.github/workflows/codeql.yml
vendored
11
.github/workflows/codeql.yml
vendored
|
|
@ -8,6 +8,9 @@ on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * 1' # Weekly on Monday
|
- cron: '0 0 * * 1' # Weekly on Monday
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
name: Analyze
|
name: Analyze
|
||||||
|
|
@ -21,16 +24,16 @@ jobs:
|
||||||
language: [rust, javascript-typescript, python, actions]
|
language: [rust, javascript-typescript, python, actions]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v4.37.3
|
uses: github/codeql-action/init@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
config-file: ./.github/codeql/codeql-config.yml
|
config-file: ./.github/codeql/codeql-config.yml
|
||||||
|
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v4.37.3
|
uses: github/codeql-action/autobuild@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v4.37.3
|
uses: github/codeql-action/analyze@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
|
||||||
|
|
|
||||||
19
.github/workflows/conformance.yml
vendored
19
.github/workflows/conformance.yml
vendored
|
|
@ -14,6 +14,9 @@ concurrency:
|
||||||
env:
|
env:
|
||||||
CONFORMANCE_VERSION: "0.2.0-alpha.10"
|
CONFORMANCE_VERSION: "0.2.0-alpha.10"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
server:
|
server:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
@ -21,12 +24,12 @@ jobs:
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: Install Rust toolchain
|
- name: Install Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
||||||
|
|
||||||
- name: Build conformance binaries
|
- name: Build conformance binaries
|
||||||
run: cargo build -p mcp-conformance
|
run: cargo build -p mcp-conformance
|
||||||
|
|
@ -91,7 +94,7 @@ jobs:
|
||||||
|
|
||||||
- name: Upload results
|
- name: Upload results
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: conformance-server-results
|
name: conformance-server-results
|
||||||
path: |
|
path: |
|
||||||
|
|
@ -104,12 +107,12 @@ jobs:
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: Install Rust toolchain
|
- name: Install Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
|
||||||
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2
|
||||||
|
|
||||||
- name: Build conformance binaries
|
- name: Build conformance binaries
|
||||||
run: cargo build -p mcp-conformance
|
run: cargo build -p mcp-conformance
|
||||||
|
|
@ -140,7 +143,7 @@ jobs:
|
||||||
|
|
||||||
- name: Upload results
|
- name: Upload results
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
with:
|
with:
|
||||||
name: conformance-client-results
|
name: conformance-client-results
|
||||||
path: conformance-client-results
|
path: conformance-client-results
|
||||||
|
|
|
||||||
17
.github/workflows/release-plz.yml
vendored
17
.github/workflows/release-plz.yml
vendored
|
|
@ -1,8 +1,6 @@
|
||||||
name: Release-plz
|
name: Release-plz
|
||||||
|
|
||||||
permissions:
|
permissions: {}
|
||||||
pull-requests: write
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
|
@ -18,19 +16,20 @@ jobs:
|
||||||
if: ${{ github.repository_owner == 'modelcontextprotocol' }}
|
if: ${{ github.repository_owner == 'modelcontextprotocol' }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
pull-requests: read
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Install Rust toolchain
|
- name: Install Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
|
||||||
with:
|
with:
|
||||||
toolchain: "1.92"
|
toolchain: "1.92"
|
||||||
# Using fork until semver_check_features support is released upstream.
|
# Using fork until semver_check_features support is released upstream.
|
||||||
# See: https://github.com/release-plz/release-plz/pull/2757
|
# See: https://github.com/release-plz/release-plz/pull/2757
|
||||||
- name: Install release-plz from fork
|
- name: Install release-plz from fork
|
||||||
run: cargo install --locked --git https://github.com/DaleSeo/release-plz --branch feat/semver-check-features release-plz
|
run: cargo install --locked --git https://github.com/DaleSeo/release-plz --rev c2ea743bacf9bbd56e344df68219e60b0ad35d04 release-plz
|
||||||
- name: Run release-plz release
|
- name: Run release-plz release
|
||||||
run: release-plz release
|
run: release-plz release
|
||||||
env:
|
env:
|
||||||
|
|
@ -51,17 +50,17 @@ jobs:
|
||||||
cancel-in-progress: false
|
cancel-in-progress: false
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Install Rust toolchain
|
- name: Install Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
|
||||||
with:
|
with:
|
||||||
toolchain: "1.92"
|
toolchain: "1.92"
|
||||||
# Using fork until semver_check_features support is released upstream.
|
# Using fork until semver_check_features support is released upstream.
|
||||||
# See: https://github.com/release-plz/release-plz/pull/2757
|
# See: https://github.com/release-plz/release-plz/pull/2757
|
||||||
- name: Install release-plz from fork
|
- name: Install release-plz from fork
|
||||||
run: cargo install --locked --git https://github.com/DaleSeo/release-plz --branch feat/semver-check-features release-plz
|
run: cargo install --locked --git https://github.com/DaleSeo/release-plz --rev c2ea743bacf9bbd56e344df68219e60b0ad35d04 release-plz
|
||||||
- name: Run release-plz release-pr
|
- name: Run release-plz release-pr
|
||||||
run: release-plz release-pr
|
run: release-plz release-pr
|
||||||
env:
|
env:
|
||||||
|
|
|
||||||
48
.github/workflows/scorecard.yml
vendored
Normal file
48
.github/workflows/scorecard.yml
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
# 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
|
||||||
5
.github/workflows/stale.yml
vendored
5
.github/workflows/stale.yml
vendored
|
|
@ -4,6 +4,9 @@ on:
|
||||||
- cron: "0 0 * * *"
|
- cron: "0 0 * * *"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
stale:
|
stale:
|
||||||
name: Clean Up
|
name: Clean Up
|
||||||
|
|
@ -12,7 +15,7 @@ jobs:
|
||||||
issues: write
|
issues: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v11
|
- uses: actions/stale@4391f3da665fdf50b6810c1a66712fb9ba21aa93 # v11
|
||||||
with:
|
with:
|
||||||
days-before-stale: 60
|
days-before-stale: 60
|
||||||
days-before-close: 14
|
days-before-close: 14
|
||||||
|
|
|
||||||
2
.github/workflows/triage.yml
vendored
2
.github/workflows/triage.yml
vendored
|
|
@ -39,7 +39,7 @@ jobs:
|
||||||
TRIAGE_MODEL: ${{ vars.TRIAGE_MODEL || 'gpt-4o-mini' }}
|
TRIAGE_MODEL: ${{ vars.TRIAGE_MODEL || 'gpt-4o-mini' }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||||
|
|
||||||
- name: Install jq
|
- name: Install jq
|
||||||
run: sudo apt-get install -y jq
|
run: sudo apt-get install -y jq
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@ default-members = ["crates/rmcp", "crates/rmcp-macros"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
rmcp = { version = "3.1.0", path = "./crates/rmcp" }
|
rmcp = { version = "3.4.0", path = "./crates/rmcp" }
|
||||||
rmcp-macros = { version = "3.1.0", path = "./crates/rmcp-macros" }
|
rmcp-macros = { version = "3.4.0", path = "./crates/rmcp-macros" }
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
rust-version = "1.88"
|
rust-version = "1.88"
|
||||||
version = "3.1.0"
|
version = "3.4.0"
|
||||||
authors = ["4t145 <u4t145@163.com>"]
|
authors = ["4t145 <u4t145@163.com>"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
repository = "https://github.com/modelcontextprotocol/rust-sdk/"
|
repository = "https://github.com/modelcontextprotocol/rust-sdk/"
|
||||||
|
|
|
||||||
76
README.md
76
README.md
|
|
@ -99,10 +99,10 @@ Use [`ClientServiceExt::serve_with_lifecycle`](crates/rmcp/src/service/client.rs
|
||||||
select another lifecycle explicitly:
|
select another lifecycle explicitly:
|
||||||
|
|
||||||
```rust, ignore
|
```rust, ignore
|
||||||
use rmcp::{ClientInfo, ClientLifecycleMode, ClientServiceExt, ProtocolVersion};
|
use rmcp::{ClientLifecycleMode, ClientServiceExt, ProtocolVersion, model::ClientConfig};
|
||||||
|
|
||||||
// Start directly with server/discover and include client metadata on every request.
|
// Start directly with server/discover and include client metadata on every request.
|
||||||
let client = ClientInfo::default()
|
let client = ClientConfig::default()
|
||||||
.serve_with_lifecycle(
|
.serve_with_lifecycle(
|
||||||
transport,
|
transport,
|
||||||
ClientLifecycleMode::Discover {
|
ClientLifecycleMode::Discover {
|
||||||
|
|
@ -112,8 +112,8 @@ let client = ClientInfo::default()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Or probe the discover lifecycle and fall back when a legacy server reports
|
// Or probe the discover lifecycle and fall back when a legacy server reports
|
||||||
// that server/discover is not implemented.
|
// that server/discover is not implemented or does not respond within 10 seconds.
|
||||||
let client = ClientInfo::default()
|
let client = ClientConfig::default()
|
||||||
.serve_with_lifecycle(
|
.serve_with_lifecycle(
|
||||||
transport,
|
transport,
|
||||||
ClientLifecycleMode::Auto {
|
ClientLifecycleMode::Auto {
|
||||||
|
|
@ -369,8 +369,8 @@ use serde_json::json;
|
||||||
struct MyServer;
|
struct MyServer;
|
||||||
|
|
||||||
impl ServerHandler for MyServer {
|
impl ServerHandler for MyServer {
|
||||||
fn get_info(&self) -> ServerInfo {
|
fn get_info(&self) -> ServerConfig {
|
||||||
ServerInfo::new(
|
ServerConfig::new(
|
||||||
ServerCapabilities::builder()
|
ServerCapabilities::builder()
|
||||||
.enable_resources()
|
.enable_resources()
|
||||||
.build(),
|
.build(),
|
||||||
|
|
@ -582,8 +582,8 @@ impl MyServer {
|
||||||
|
|
||||||
#[prompt_handler]
|
#[prompt_handler]
|
||||||
impl ServerHandler for MyServer {
|
impl ServerHandler for MyServer {
|
||||||
fn get_info(&self) -> ServerInfo {
|
fn get_info(&self) -> ServerConfig {
|
||||||
ServerInfo::new(ServerCapabilities::builder().enable_prompts().build())
|
ServerConfig::new(ServerCapabilities::builder().enable_prompts().build())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
@ -934,8 +934,8 @@ Enable the logging capability, handle level changes from the client, and send lo
|
||||||
use rmcp::{ServerHandler, model::*, service::RequestContext};
|
use rmcp::{ServerHandler, model::*, service::RequestContext};
|
||||||
|
|
||||||
impl ServerHandler for MyServer {
|
impl ServerHandler for MyServer {
|
||||||
fn get_info(&self) -> ServerInfo {
|
fn get_info(&self) -> ServerConfig {
|
||||||
ServerInfo::new(
|
ServerConfig::new(
|
||||||
ServerCapabilities::builder()
|
ServerCapabilities::builder()
|
||||||
.enable_logging()
|
.enable_logging()
|
||||||
.build(),
|
.build(),
|
||||||
|
|
@ -1007,8 +1007,8 @@ Enable the completions capability and implement the `complete()` handler. Use `r
|
||||||
use rmcp::{ErrorData as McpError, ServerHandler, model::*, service::RequestContext, RoleServer};
|
use rmcp::{ErrorData as McpError, ServerHandler, model::*, service::RequestContext, RoleServer};
|
||||||
|
|
||||||
impl ServerHandler for MyServer {
|
impl ServerHandler for MyServer {
|
||||||
fn get_info(&self) -> ServerInfo {
|
fn get_info(&self) -> ServerConfig {
|
||||||
ServerInfo::new(
|
ServerConfig::new(
|
||||||
ServerCapabilities::builder()
|
ServerCapabilities::builder()
|
||||||
.enable_completions()
|
.enable_completions()
|
||||||
.enable_prompts()
|
.enable_prompts()
|
||||||
|
|
@ -1253,8 +1253,8 @@ use rmcp::{
|
||||||
};
|
};
|
||||||
|
|
||||||
impl ServerHandler for MyServer {
|
impl ServerHandler for MyServer {
|
||||||
fn get_info(&self) -> ServerInfo {
|
fn get_info(&self) -> ServerConfig {
|
||||||
ServerInfo::new(
|
ServerConfig::new(
|
||||||
ServerCapabilities::builder()
|
ServerCapabilities::builder()
|
||||||
.enable_tools()
|
.enable_tools()
|
||||||
.enable_tool_list_changed()
|
.enable_tool_list_changed()
|
||||||
|
|
@ -1363,11 +1363,21 @@ async fn call_tool(&self, request: CallToolRequestParams, _ctx: RequestContext<R
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> **`requestState` is untrusted.** The client echoes it back verbatim, so a
|
> **`requestState` is untrusted.** [SEP-2322 requires servers to validate
|
||||||
> stateless server that stores meaningful data in it MUST verify integrity
|
> it](https://modelcontextprotocol.io/seps/2322-MRTR#protocol-requirements-for-ephemeral-workflow)
|
||||||
> first. Enable the `request-state` feature and use `RequestStateCodec` to seal
|
> because the client echoes it back verbatim. A stateless server that stores
|
||||||
> and open it (HMAC-tagged), or keep state server-side and use `requestState`
|
> meaningful data in it MUST verify integrity first. Enable the `request-state`
|
||||||
> only as an opaque handle.
|
> feature and use `RequestStateCodec` to seal and open it (HMAC-tagged), or keep
|
||||||
|
> state server-side and use `requestState` only as an opaque handle.
|
||||||
|
|
||||||
|
For multi-replica deployments, use `RequestStateCodec::new_with_keyring` to
|
||||||
|
rotate signing keys without invalidating in-flight requests:
|
||||||
|
|
||||||
|
1. Deploy the old and new keys everywhere, continuing to emit `rs1` with the
|
||||||
|
old key via `with_rs1_signing("old")`.
|
||||||
|
2. Start emitting `rs2` with the new key while retaining the old key via
|
||||||
|
`with_rs1_fallback("old")`.
|
||||||
|
3. After the maximum `requestState` lifetime has elapsed, remove the old key.
|
||||||
|
|
||||||
### Client-side
|
### Client-side
|
||||||
|
|
||||||
|
|
@ -1574,7 +1584,7 @@ use rmcp::transport::StreamableHttpClientTransport;
|
||||||
|
|
||||||
// Defaults are stateless-friendly.
|
// Defaults are stateless-friendly.
|
||||||
let transport = StreamableHttpClientTransport::from_uri("http://localhost:8000/mcp");
|
let transport = StreamableHttpClientTransport::from_uri("http://localhost:8000/mcp");
|
||||||
let client = ClientInfo::default().serve(transport).await?;
|
let client = ClientConfig::default().serve(transport).await?;
|
||||||
```
|
```
|
||||||
|
|
||||||
**Example:** [`examples/servers/src/counter_streamhttp.rs`](examples/servers/src/counter_streamhttp.rs) (server), [`examples/clients/src/streamable_http.rs`](examples/clients/src/streamable_http.rs) (client)
|
**Example:** [`examples/servers/src/counter_streamhttp.rs`](examples/servers/src/counter_streamhttp.rs) (server), [`examples/clients/src/streamable_http.rs`](examples/clients/src/streamable_http.rs) (client)
|
||||||
|
|
@ -1628,9 +1638,27 @@ server example). The client transport connects with a single URI:
|
||||||
use rmcp::transport::StreamableHttpClientTransport;
|
use rmcp::transport::StreamableHttpClientTransport;
|
||||||
|
|
||||||
let transport = StreamableHttpClientTransport::from_uri("http://localhost:8000/mcp");
|
let transport = StreamableHttpClientTransport::from_uri("http://localhost:8000/mcp");
|
||||||
let client = ClientInfo::default().serve(transport).await?;
|
let client = ClientConfig::default().serve(transport).await?;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The client allows up to 16 ordinary http POSTs at once. Configure this with
|
||||||
|
`StreamableHttpClientTransportConfig::with_uri(url).max_concurrent_requests(n)`;
|
||||||
|
`1` keeps ordinary POSTs serial, and `0` is treated as `1`. An open sse response
|
||||||
|
stream does not count against this limit. Cancellation and replies use a
|
||||||
|
separate queue with one extra POST slot. Configure their timeout with
|
||||||
|
`control_request_timeout` (default: five seconds). The timeout starts when the
|
||||||
|
POST starts, excluding time in the queue. Cancellation stops a queued or active
|
||||||
|
POST immediately.
|
||||||
|
For an open legacy response stream, the client stops reading but keeps the stream
|
||||||
|
alive until the cancellation send finishes or is dropped. This lets custom http
|
||||||
|
adapters handle cancellation before their stream state is removed.
|
||||||
|
|
||||||
|
Session recovery waits up to five seconds for old POSTs, then stops any that
|
||||||
|
remain. Those POSTs are not retried because the server may have processed them.
|
||||||
|
Configure this wait and the separate
|
||||||
|
reinitialization timeout with `session_recovery_timeout`. Callers still decide
|
||||||
|
which tools may run at the same time and which need approval.
|
||||||
|
|
||||||
#### Server-Sent Events (SSE)
|
#### Server-Sent Events (SSE)
|
||||||
|
|
||||||
Streamable HTTP responses arrive as either a single `application/json` body or a
|
Streamable HTTP responses arrive as either a single `application/json` body or a
|
||||||
|
|
@ -1714,10 +1742,10 @@ knows what the other supports. Declare yours with the `ServerCapabilities`
|
||||||
builder in `get_info()`:
|
builder in `get_info()`:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
use rmcp::model::{ServerCapabilities, ServerInfo};
|
use rmcp::model::{ServerCapabilities, ServerConfig};
|
||||||
|
|
||||||
fn get_info(&self) -> ServerInfo {
|
fn get_info(&self) -> ServerConfig {
|
||||||
ServerInfo::new(
|
ServerConfig::new(
|
||||||
ServerCapabilities::builder()
|
ServerCapabilities::builder()
|
||||||
.enable_tools()
|
.enable_tools()
|
||||||
.enable_prompts()
|
.enable_prompts()
|
||||||
|
|
|
||||||
15
ROADMAP.md
15
ROADMAP.md
|
|
@ -2,8 +2,9 @@
|
||||||
|
|
||||||
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](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1730) Tier 1 for the Rust MCP SDK.
|
||||||
|
|
||||||
**Status (2026-07-30): all SEP-1730 Tier 1 requirements are met.** Conformance is
|
**Status: all SEP-1730 Tier 1 requirements are met.** Conformance is
|
||||||
100% across every date-versioned suite, the stable **v3.0.1** release has shipped,
|
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
|
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
|
governance documents (`VERSIONING.md`, `DEPENDENCY_POLICY.md`, this `ROADMAP.md`) are
|
||||||
published, and all 48 non-experimental features are documented with examples. This
|
published, and all 48 non-experimental features are documented with examples. This
|
||||||
|
|
@ -15,7 +16,7 @@ document now serves as the ongoing tracker for spec conformance and SDK health.
|
||||||
| Client conformance 100% (date-versioned) | ✅ | 39/39 scored — see below |
|
| Client conformance 100% (date-versioned) | ✅ | 39/39 scored — see below |
|
||||||
| Issue triage ≥90% within 2 business days | ✅ | 95.2% (20/21) |
|
| 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 |
|
| 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) | ✅ | `rmcp-v3.0.1` (see tooling note below) |
|
| 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) |
|
| Clear versioning + breaking-change policy | ✅ | [`VERSIONING.md`](VERSIONING.md) |
|
||||||
| All non-experimental features documented w/ examples| ✅ | 48/48 in [`README.md`](README.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 dependency update policy | ✅ | [`DEPENDENCY_POLICY.md`](DEPENDENCY_POLICY.md) + [`.github/dependabot.yml`](.github/dependabot.yml) |
|
||||||
|
|
@ -31,8 +32,9 @@ and `extension` scenarios are informational and reported separately below.
|
||||||
|
|
||||||
> **Tooling note — `stable_release`:** the SEP-1730 `tier-check` CLI may report
|
> **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
|
> `stable_release` as failing because it does not parse the workspace tag prefix
|
||||||
> `rmcp-v` (as in `rmcp-v3.0.1`). `rmcp-v3.0.1` is a genuine stable, non-pre-release
|
> `rmcp-v` (as in `rmcp-vX.Y.Z`). The published releases are genuine stable,
|
||||||
> release ([Releases](https://github.com/modelcontextprotocol/rust-sdk/releases)); the
|
> 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.
|
> flag is a tooling artifact, not an unmet requirement.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -75,8 +77,7 @@ the milestone:
|
||||||
### Tier 1 requirements
|
### Tier 1 requirements
|
||||||
|
|
||||||
- [x] **v3.0.0 stable released** (2026-07-28) — MRTR, SEP-2549 cache hints, SEP-2243
|
- [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; **v3.0.1**
|
standard headers, SEP-2575 stateless MCP, and SEP-2106 relaxations
|
||||||
is the current stable release
|
|
||||||
- [x] 2025-11-25 server conformance 100% (30/30)
|
- [x] 2025-11-25 server conformance 100% (30/30)
|
||||||
- [x] 2025-11-25 client conformance 100%
|
- [x] 2025-11-25 client conformance 100%
|
||||||
- [x] 2026-07-28 server conformance 100% (30/30 dated)
|
- [x] 2026-07-28 server conformance 100% (30/30 dated)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ rmcp = { path = "../crates/rmcp", features = [
|
||||||
"elicitation",
|
"elicitation",
|
||||||
"auth",
|
"auth",
|
||||||
"auth-client-credentials-jwt",
|
"auth-client-credentials-jwt",
|
||||||
|
"auth-enterprise-managed",
|
||||||
"request-state",
|
"request-state",
|
||||||
"transport-streamable-http-server",
|
"transport-streamable-http-server",
|
||||||
"transport-streamable-http-client-reqwest",
|
"transport-streamable-http-client-reqwest",
|
||||||
|
|
@ -31,6 +32,7 @@ tracing = "0.1"
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
axum = { version = "0.8", features = ["macros"] }
|
axum = { version = "0.8", features = ["macros"] }
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
|
oauth2 = { version = "5.0", default-features = false }
|
||||||
reqwest = { version = "0.13", features = ["json"] }
|
reqwest = { version = "0.13", features = ["json"] }
|
||||||
urlencoding = "2"
|
urlencoding = "2"
|
||||||
url = "2"
|
url = "2"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,10 @@
|
||||||
|
#![expect(
|
||||||
|
deprecated,
|
||||||
|
reason = "The conformance suite still exercises deprecated sampling scenarios"
|
||||||
|
)]
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
|
use oauth2::{ClientSecret, RefreshToken};
|
||||||
use rmcp::{
|
use rmcp::{
|
||||||
ClientHandler, ClientLifecycleMode, ClientServiceExt, ErrorData, RoleClient, ServiceExt,
|
ClientHandler, ClientLifecycleMode, ClientServiceExt, ErrorData, RoleClient, ServiceExt,
|
||||||
model::*,
|
model::*,
|
||||||
|
|
@ -6,7 +13,8 @@ use rmcp::{
|
||||||
AuthClient, AuthorizationManager, StreamableHttpClientTransport,
|
AuthClient, AuthorizationManager, StreamableHttpClientTransport,
|
||||||
auth::{
|
auth::{
|
||||||
AuthorizationCallback, AuthorizationRequest, ClientCredentialsConfig,
|
AuthorizationCallback, AuthorizationRequest, ClientCredentialsConfig,
|
||||||
InMemoryCredentialStore, JwtSigningAlgorithm, OAuthState,
|
InMemoryCredentialStore, JwtSigningAlgorithm, OAuthState, default_oauth_http_client,
|
||||||
|
enterprise::{EmaAuthorizationServer, EmaClientAuthentication, EmaExchangeRequest},
|
||||||
},
|
},
|
||||||
streamable_http_client::StreamableHttpClientTransportConfig,
|
streamable_http_client::StreamableHttpClientTransportConfig,
|
||||||
},
|
},
|
||||||
|
|
@ -36,6 +44,17 @@ struct ConformanceContext {
|
||||||
private_key_pem: Option<String>,
|
private_key_pem: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
signing_algorithm: Option<String>,
|
signing_algorithm: Option<String>,
|
||||||
|
// enterprise-managed-authorization-refresh-token
|
||||||
|
#[serde(default)]
|
||||||
|
idp_client_id: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
idp_client_secret: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
idp_refresh_token: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
idp_issuer: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
idp_token_endpoint: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_context() -> ConformanceContext {
|
fn load_context() -> ConformanceContext {
|
||||||
|
|
@ -55,8 +74,8 @@ impl ClientHandler for BasicClientHandler {}
|
||||||
struct ElicitationDefaultsClientHandler;
|
struct ElicitationDefaultsClientHandler;
|
||||||
|
|
||||||
impl ClientHandler for ElicitationDefaultsClientHandler {
|
impl ClientHandler for ElicitationDefaultsClientHandler {
|
||||||
fn get_info(&self) -> ClientInfo {
|
fn get_info(&self) -> ClientConfig {
|
||||||
let mut info = ClientInfo::default();
|
let mut info = ClientConfig::default();
|
||||||
info.capabilities.elicitation = Some(
|
info.capabilities.elicitation = Some(
|
||||||
ElicitationCapability::new()
|
ElicitationCapability::new()
|
||||||
.with_form(FormElicitationCapability::new().with_schema_validation(true)),
|
.with_form(FormElicitationCapability::new().with_schema_validation(true)),
|
||||||
|
|
@ -144,8 +163,8 @@ impl ClientHandler for ElicitationDefaultsClientHandler {
|
||||||
struct FullClientHandler;
|
struct FullClientHandler;
|
||||||
|
|
||||||
impl ClientHandler for FullClientHandler {
|
impl ClientHandler for FullClientHandler {
|
||||||
fn get_info(&self) -> ClientInfo {
|
fn get_info(&self) -> ClientConfig {
|
||||||
let mut info = ClientInfo::default();
|
let mut info = ClientConfig::default();
|
||||||
info.capabilities.elicitation = Some(
|
info.capabilities.elicitation = Some(
|
||||||
ElicitationCapability::new()
|
ElicitationCapability::new()
|
||||||
.with_form(FormElicitationCapability::new().with_schema_validation(true)),
|
.with_form(FormElicitationCapability::new().with_schema_validation(true)),
|
||||||
|
|
@ -760,6 +779,66 @@ async fn run_client_credentials_jwt(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Exchange the fixture's IdP refresh token, then exercise authenticated MCP access.
|
||||||
|
async fn run_ema_refresh_token_client(
|
||||||
|
server_url: &str,
|
||||||
|
ctx: &ConformanceContext,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let manager = AuthorizationManager::new(server_url).await?;
|
||||||
|
let metadata = manager.resolve_metadata().await?.metadata;
|
||||||
|
let idp = EmaAuthorizationServer::new(
|
||||||
|
ctx.idp_issuer.as_deref().context("Missing idp_issuer")?,
|
||||||
|
ctx.idp_token_endpoint
|
||||||
|
.as_deref()
|
||||||
|
.context("Missing idp_token_endpoint")?,
|
||||||
|
ctx.idp_client_id
|
||||||
|
.as_deref()
|
||||||
|
.context("Missing idp_client_id")?,
|
||||||
|
)
|
||||||
|
.with_client_authentication(EmaClientAuthentication::ClientSecretBasic(
|
||||||
|
ClientSecret::new(
|
||||||
|
ctx.idp_client_secret
|
||||||
|
.clone()
|
||||||
|
.context("Missing idp_client_secret")?,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
let resource_as = EmaAuthorizationServer::new(
|
||||||
|
metadata
|
||||||
|
.issuer
|
||||||
|
.context("Missing authorization server issuer")?,
|
||||||
|
metadata.token_endpoint,
|
||||||
|
ctx.client_id.as_deref().context("Missing client_id")?,
|
||||||
|
)
|
||||||
|
.with_client_authentication(EmaClientAuthentication::ClientSecretBasic(
|
||||||
|
ClientSecret::new(ctx.client_secret.clone().context("Missing client_secret")?),
|
||||||
|
));
|
||||||
|
let refresh_token = RefreshToken::new(
|
||||||
|
ctx.idp_refresh_token
|
||||||
|
.clone()
|
||||||
|
.context("Missing idp_refresh_token")?,
|
||||||
|
);
|
||||||
|
let http = default_oauth_http_client()?;
|
||||||
|
let token = EmaExchangeRequest::new(idp, resource_as, server_url, &refresh_token)
|
||||||
|
.with_scopes(manager.select_scopes(None, &[]))
|
||||||
|
.exchange(&http, &http)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let transport = StreamableHttpClientTransport::from_config(
|
||||||
|
StreamableHttpClientTransportConfig::with_uri(server_url)
|
||||||
|
.auth_header(token.access_token.secret()),
|
||||||
|
);
|
||||||
|
let client = BasicClientHandler
|
||||||
|
.serve_with_lifecycle(transport, conformance_lifecycle())
|
||||||
|
.await?;
|
||||||
|
let tools = client.list_tools(Default::default()).await?;
|
||||||
|
for tool in tools.tools {
|
||||||
|
let args = build_tool_arguments(&tool);
|
||||||
|
client.call_tool(call_tool_params(tool.name, args)).await?;
|
||||||
|
}
|
||||||
|
client.cancel().await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Cross-app access flow (SEP-1046 extension).
|
/// Cross-app access flow (SEP-1046 extension).
|
||||||
async fn run_cross_app_access_client(
|
async fn run_cross_app_access_client(
|
||||||
server_url: &str,
|
server_url: &str,
|
||||||
|
|
@ -1110,6 +1189,11 @@ async fn run_scenario(
|
||||||
"auth/client-credentials-basic" => run_client_credentials_basic(server_url, ctx).await?,
|
"auth/client-credentials-basic" => run_client_credentials_basic(server_url, ctx).await?,
|
||||||
"auth/client-credentials-jwt" => run_client_credentials_jwt(server_url, ctx).await?,
|
"auth/client-credentials-jwt" => run_client_credentials_jwt(server_url, ctx).await?,
|
||||||
|
|
||||||
|
// Auth - enterprise-managed authorization with a refresh-token subject
|
||||||
|
"auth/enterprise-managed-authorization-refresh-token" => {
|
||||||
|
run_ema_refresh_token_client(server_url, ctx).await?
|
||||||
|
}
|
||||||
|
|
||||||
// Auth - cross-app access
|
// Auth - cross-app access
|
||||||
"auth/cross-app-access-complete-flow" => {
|
"auth/cross-app-access-complete-flow" => {
|
||||||
run_cross_app_access_client(server_url, ctx).await?
|
run_cross_app_access_client(server_url, ctx).await?
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,10 @@ fn custom_header_tool() -> Tool {
|
||||||
/// Signing key for SEP-2322 `requestState` sealing. A fixed key is fine for a
|
/// Signing key for SEP-2322 `requestState` sealing. A fixed key is fine for a
|
||||||
/// conformance harness; real servers must load a secret out of clients' reach.
|
/// conformance harness; real servers must load a secret out of clients' reach.
|
||||||
const REQUEST_STATE_KEY: &[u8] = b"rust-sdk-conformance-request-state-key!!";
|
const REQUEST_STATE_KEY: &[u8] = b"rust-sdk-conformance-request-state-key!!";
|
||||||
|
const _: () = assert!(
|
||||||
|
REQUEST_STATE_KEY.len() >= RequestStateCodec::MIN_KEY_LENGTH,
|
||||||
|
"REQUEST_STATE_KEY is shorter than RequestStateCodec::MIN_KEY_LENGTH",
|
||||||
|
);
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct ConformanceServer {
|
struct ConformanceServer {
|
||||||
|
|
@ -70,7 +74,7 @@ impl ConformanceServer {
|
||||||
subscriptions: Arc::new(Mutex::new(HashMap::new())),
|
subscriptions: Arc::new(Mutex::new(HashMap::new())),
|
||||||
next_subscription: Arc::new(AtomicU64::new(0)),
|
next_subscription: Arc::new(AtomicU64::new(0)),
|
||||||
log_level: Arc::new(Mutex::new(LoggingLevel::Debug)),
|
log_level: Arc::new(Mutex::new(LoggingLevel::Debug)),
|
||||||
request_state_codec: RequestStateCodec::new(REQUEST_STATE_KEY),
|
request_state_codec: RequestStateCodec::new_unchecked(REQUEST_STATE_KEY),
|
||||||
tasks: TaskManager::new(),
|
tasks: TaskManager::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -700,8 +704,8 @@ impl ServerHandler for ConformanceServer {
|
||||||
(name == "test_custom_header").then(custom_header_tool)
|
(name == "test_custom_header").then(custom_header_tool)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_info(&self) -> ServerInfo {
|
fn get_info(&self) -> ServerConfig {
|
||||||
ServerInfo::new(
|
ServerConfig::new(
|
||||||
ServerCapabilities::builder()
|
ServerCapabilities::builder()
|
||||||
.enable_prompts()
|
.enable_prompts()
|
||||||
.enable_prompts_list_changed()
|
.enable_prompts_list_changed()
|
||||||
|
|
@ -1740,7 +1744,9 @@ async fn main() -> anyhow::Result<()> {
|
||||||
tracing::info!("Starting conformance server on {}", bind_addr);
|
tracing::info!("Starting conformance server on {}", bind_addr);
|
||||||
|
|
||||||
let server = ConformanceServer::new();
|
let server = ConformanceServer::new();
|
||||||
let config = StreamableHttpServerConfig::default();
|
let config = StreamableHttpServerConfig::default()
|
||||||
|
.with_allowed_origins([format!("http://{bind_addr}")])
|
||||||
|
.enforce_origin_validation();
|
||||||
let service = StreamableHttpService::new(
|
let service = StreamableHttpService::new(
|
||||||
move || Ok(server.clone()),
|
move || Ok(server.clone()),
|
||||||
LocalSessionManager::default().into(),
|
LocalSessionManager::default().into(),
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,48 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [3.4.0](https://github.com/modelcontextprotocol/rust-sdk/compare/rmcp-macros-v3.3.0...rmcp-macros-v3.4.0) - 2026-09-15
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- *(model)* add ServerConfig and ClientConfig ([#1266](https://github.com/modelcontextprotocol/rust-sdk/pull/1266))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- *(model)* deprecate ServerInfo and ClientInfo aliases ([#1156](https://github.com/modelcontextprotocol/rust-sdk/pull/1156))
|
||||||
|
|
||||||
|
## [3.3.0](https://github.com/modelcontextprotocol/rust-sdk/compare/rmcp-macros-v3.2.0...rmcp-macros-v3.3.0) - 2026-09-10
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- *(macros)* reject empty tool_router ([#1233](https://github.com/modelcontextprotocol/rust-sdk/pull/1233))
|
||||||
|
|
||||||
|
## [3.2.0](https://github.com/modelcontextprotocol/rust-sdk/compare/rmcp-macros-v3.1.4...rmcp-macros-v3.2.0) - 2026-08-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- add request-state key rotation ([#1128](https://github.com/modelcontextprotocol/rust-sdk/pull/1128))
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- allow concurrent streamable http requests ([#1186](https://github.com/modelcontextprotocol/rust-sdk/pull/1186))
|
||||||
|
|
||||||
|
## [3.1.3](https://github.com/modelcontextprotocol/rust-sdk/compare/rmcp-macros-v3.1.2...rmcp-macros-v3.1.3) - 2026-08-17
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- time out auto discovery probe ([#1149](https://github.com/modelcontextprotocol/rust-sdk/pull/1149))
|
||||||
|
|
||||||
|
## [3.1.1](https://github.com/modelcontextprotocol/rust-sdk/compare/rmcp-macros-v3.1.0...rmcp-macros-v3.1.1) - 2026-08-05
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- emit cache hints from handler macros ([#1120](https://github.com/modelcontextprotocol/rust-sdk/pull/1120))
|
||||||
|
|
||||||
|
### Other
|
||||||
|
|
||||||
|
- upgrade darling and syn ([#1138](https://github.com/modelcontextprotocol/rust-sdk/pull/1138))
|
||||||
|
|
||||||
## [3.1.0](https://github.com/modelcontextprotocol/rust-sdk/compare/rmcp-macros-v3.0.1...rmcp-macros-v3.1.0) - 2026-07-31
|
## [3.1.0](https://github.com/modelcontextprotocol/rust-sdk/compare/rmcp-macros-v3.0.1...rmcp-macros-v3.1.0) - 2026-07-31
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,11 @@ documentation = "https://docs.rs/rmcp-macros"
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
syn = {version = "2", features = ["full"]}
|
syn = {version = "3", features = ["full"]}
|
||||||
quote = "1"
|
quote = "1"
|
||||||
proc-macro2 = "1"
|
proc-macro2 = "1"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
darling = { version = "0.23" }
|
darling = { version = "0.24" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
local = []
|
local = []
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ pub fn tool(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
/// | `router` | `Ident` | The name of the router function to be generated. Defaults to `tool_router`. |
|
/// | `router` | `Ident` | The name of the router function to be generated. Defaults to `tool_router`. |
|
||||||
/// | `vis` | `Visibility` | The visibility of the generated router function. Defaults to empty. |
|
/// | `vis` | `Visibility` | The visibility of the generated router function. Defaults to empty. |
|
||||||
/// | `server_handler` | `flag` | When set, also emits `#[::rmcp::tool_handler]` on `impl ServerHandler for Self` so you can omit a separate `#[tool_handler]` block. |
|
/// | `server_handler` | `flag` | When set, also emits `#[::rmcp::tool_handler]` on `impl ServerHandler for Self` so you can omit a separate `#[tool_handler]` block. |
|
||||||
|
/// | `allow_empty` | `flag` | When set, accepts an impl block with no `#[tool]` fn. Without it, an empty router is a compile error. |
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
///
|
///
|
||||||
|
|
@ -122,6 +123,37 @@ pub fn tool(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// ### Empty routers
|
||||||
|
///
|
||||||
|
/// Collecting tools is this attribute's whole purpose, so an impl block with no `#[tool]` fn is a
|
||||||
|
/// compile error rather than a router that silently serves nothing. Pass `allow_empty` when that
|
||||||
|
/// is what you want:
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// #[tool_router(allow_empty)]
|
||||||
|
/// impl MyToolHandler {}
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// The usual way to hit this by accident is a `macro_rules!` helper *inside* the impl block. An
|
||||||
|
/// attribute macro receives the unexpanded item, so `#[tool]` fns produced by such a helper are
|
||||||
|
/// invisible to `#[tool_router]`. Let the `macro_rules!` emit the whole annotated impl instead:
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// macro_rules! define_tools {
|
||||||
|
/// ($($name:ident => $description:literal),* $(,)?) => {
|
||||||
|
/// #[tool_router]
|
||||||
|
/// impl MyToolHandler {
|
||||||
|
/// $(
|
||||||
|
/// #[tool(description = $description)]
|
||||||
|
/// async fn $name(&self) -> String { stringify!($name).to_owned() }
|
||||||
|
/// )*
|
||||||
|
/// }
|
||||||
|
/// };
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// define_tools!(my_tool => "what my tool does");
|
||||||
|
/// ```
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn tool_router(attr: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn tool_router(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
tool_router::tool_router(attr.into(), input.into())
|
tool_router::tool_router(attr.into(), input.into())
|
||||||
|
|
@ -185,8 +217,8 @@ pub fn tool_router(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
/// ```rust,ignore
|
/// ```rust,ignore
|
||||||
/// #[tool_handler]
|
/// #[tool_handler]
|
||||||
/// impl ServerHandler for MyToolHandler {
|
/// impl ServerHandler for MyToolHandler {
|
||||||
/// fn get_info(&self) -> ServerInfo {
|
/// fn get_info(&self) -> ServerConfig {
|
||||||
/// ServerInfo::new(ServerCapabilities::builder().enable_tools().build())
|
/// ServerConfig::new(ServerCapabilities::builder().enable_tools().build())
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ pub fn prompt(attr: TokenStream, input: TokenStream) -> syn::Result<TokenStream>
|
||||||
let new_output = syn::parse2::<ReturnType>({
|
let new_output = syn::parse2::<ReturnType>({
|
||||||
let mut lt = quote! { 'static };
|
let mut lt = quote! { 'static };
|
||||||
if let Some(receiver) = fn_item.sig.receiver()
|
if let Some(receiver) = fn_item.sig.receiver()
|
||||||
&& let Some((_, receiver_lt)) = receiver.reference.as_ref()
|
&& let syn::ReceiverKind::Reference(_, receiver_lt, _) = &receiver.kind
|
||||||
{
|
{
|
||||||
if let Some(receiver_lt) = receiver_lt {
|
if let Some(receiver_lt) = receiver_lt {
|
||||||
lt = quote! { #receiver_lt };
|
lt = quote! { #receiver_lt };
|
||||||
|
|
@ -259,6 +259,20 @@ mod test {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_async_prompt_preserves_receiver_lifetime() -> syn::Result<()> {
|
||||||
|
let attr = quote! {};
|
||||||
|
let input = quote! {
|
||||||
|
async fn test_prompt_with_lifetime<'a>(&'a self) -> String {
|
||||||
|
"ok".to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let result = prompt(attr, input)?;
|
||||||
|
|
||||||
|
assert!(result.to_string().contains("+ 'a"));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_async_prompt_local_omits_send() -> syn::Result<()> {
|
fn test_async_prompt_local_omits_send() -> syn::Result<()> {
|
||||||
let attr = quote! { local };
|
let attr = quote! { local };
|
||||||
|
|
|
||||||
|
|
@ -57,16 +57,20 @@ pub fn prompt_handler(attr: TokenStream, input: TokenStream) -> syn::Result<Toke
|
||||||
async fn list_prompts(
|
async fn list_prompts(
|
||||||
&self,
|
&self,
|
||||||
_request: Option<rmcp::model::PaginatedRequestParams>,
|
_request: Option<rmcp::model::PaginatedRequestParams>,
|
||||||
_context: rmcp::service::RequestContext<rmcp::RoleServer>,
|
context: rmcp::service::RequestContext<rmcp::RoleServer>,
|
||||||
) -> Result<rmcp::model::ListPromptsResult, rmcp::ErrorData> {
|
) -> Result<rmcp::model::ListPromptsResult, rmcp::ErrorData> {
|
||||||
let prompts = #router_expr.list_all();
|
let prompts = #router_expr.list_all();
|
||||||
|
let supports_cache_hints = context.protocol_version().is_some_and(|version| {
|
||||||
|
version >= rmcp::model::ProtocolVersion::V_2026_07_28
|
||||||
|
});
|
||||||
Ok(rmcp::model::ListPromptsResult {
|
Ok(rmcp::model::ListPromptsResult {
|
||||||
result_type: Some(rmcp::model::ResultType::COMPLETE),
|
result_type: Some(rmcp::model::ResultType::COMPLETE),
|
||||||
prompts,
|
prompts,
|
||||||
meta: #meta,
|
meta: #meta,
|
||||||
next_cursor: None,
|
next_cursor: None,
|
||||||
ttl_ms: None,
|
ttl_ms: supports_cache_hints.then_some(0),
|
||||||
cache_scope: None,
|
cache_scope: supports_cache_hints
|
||||||
|
.then_some(rmcp::model::CacheScope::Public),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue