Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16836d4e9a |
||
|
|
ef6bee5531 |
||
|
|
5a64bab1c7 |
||
|
|
e7aa2d77de |
||
|
|
7047019030 |
||
|
|
f39625b257 |
||
|
|
749c0353ef |
||
|
|
1bd7895b5f |
||
|
|
b458008f07 |
||
|
|
ebe067f6b3 |
||
|
|
4630d7424e |
||
|
|
418034f2b0 |
||
|
|
c5dbfb64e5 |
||
|
|
46628fbe6a |
||
|
|
e473c3c115 |
||
|
|
e173367c5f |
||
|
|
ff0b5fadc8 |
||
|
|
312c621417 |
||
|
|
c36e805086 |
||
|
|
d59950f1ac |
||
|
|
1e9520e731 |
||
|
|
d09fe3019e |
||
|
|
32464ec32e |
||
|
|
c882401ee7 |
||
|
|
b1c7272761 |
||
|
|
e0b9c1a26f |
||
|
|
0cd5d56164 |
||
|
|
7fe88677ad |
||
|
|
8c06dcecc6 |
||
|
|
cbceef06eb |
||
|
|
b97389325f |
||
|
|
7e38c94954 |
||
|
|
be9fad4115 |
||
|
|
855011b8dc |
||
|
|
f6a995bc44 |
||
|
|
daac88d626 |
||
|
|
7dd72d82de |
||
|
|
78a8926f17 |
||
|
|
cad686aba7 |
||
|
|
7ffc739fe7 |
||
|
|
dd3783a720 |
||
|
|
5e73683079 |
||
|
|
ef0c1c2808 |
||
|
|
de35ca40bf |
||
|
|
3d596ca75b |
||
|
|
60711951d4 |
||
|
|
5e3e69acc4 |
||
|
|
605df8771f |
||
|
|
cf2c4d4ead |
||
|
|
1961414432 |
||
|
|
5041caf77a |
||
|
|
cfe5e8f570 |
||
|
|
08d332c7ac |
||
|
|
ced762614b |
||
|
|
0751a83658 |
||
|
|
7407661260 |
||
|
|
d5351759d7 |
||
|
|
6f58fe596a |
||
|
|
590afb575b |
||
|
|
1ff9a74521 |
||
|
|
f84d8f5aa9 |
||
|
|
76778a4d9e |
||
|
|
43815e38a8 |
||
|
|
becce29c41 |
||
|
|
82aac972c1 |
||
|
|
d8cc21a345 |
||
|
|
7b12dc4010 |
||
|
|
c4b163b4c4 |
||
|
|
eb20453f49 |
||
|
|
fc456125e2 |
||
|
|
bdc9d86671 |
||
|
|
8bae3c054e |
||
|
|
071476ebd8 |
||
|
|
c8ed07bba2 |
||
|
|
9236824b6d |
||
|
|
a987a3a248 |
||
|
|
d9b3c0ba4e |
||
|
|
785fc3b9b7 |
||
|
|
9a6d24c417 |
||
|
|
9aa18f391a |
||
|
|
529099f463 |
||
|
|
055593f259 |
||
|
|
a6f415578f |
||
|
|
0058d93086 |
||
|
|
1fed089d71 |
||
|
|
d187002d87 |
||
|
|
8e18bfbdf1 |
||
|
|
ff91ca6d60 |
||
|
|
e4a8a224ff |
||
|
|
206aa9da52 |
||
|
|
12f4a78813 |
||
|
|
55f7884368 |
||
|
|
1587ac5bd2 |
||
|
|
df5379c047 |
||
|
|
00a568e8d9 |
||
|
|
30e520a019 |
||
|
|
ca849aa75b |
||
|
|
433c84b58a |
||
|
|
d150772e24 |
||
|
|
0347dc85b4 |
||
|
|
293a5880e4 |
||
|
|
ee40ff523f |
||
|
|
d2fc743553 |
||
|
|
f12ea0ab58 |
||
|
|
3e45b1f486 |
||
|
|
1300d2ee7d |
||
|
|
a820497b62 |
||
|
|
967c5cda4d |
||
|
|
e00450a218 |
||
|
|
1d7100060f |
||
|
|
5e0731f706 |
||
|
|
73414d65f6 |
||
|
|
bfc4728792 |
||
|
|
06917f6af6 |
||
|
|
797472777f |
||
|
|
a929548f33 |
||
|
|
86bd845924 |
||
|
|
fb2c8c2ee4 |
||
|
|
97979ec48d |
||
|
|
abae36e888 |
||
|
|
d5b00cd8ae |
||
|
|
73e4cc2456 |
||
|
|
7af5a345c1 |
||
|
|
863cfc1e78 |
||
|
|
5d57b685a2 |
||
|
|
ea38ef7b62 |
||
|
|
85fddf5f0f |
||
|
|
7a5d5f5cfb |
||
|
|
e8f5a8ba22 |
||
|
|
6a99196ca0 |
||
|
|
faac4a525c |
||
|
|
eac6f01d58 |
||
|
|
591abb4620 |
||
|
|
ee3132a2f9 |
||
|
|
ae320646bf |
||
|
|
a6ed01bec0 |
||
|
|
085e8d9ad6 |
||
|
|
3ef864c444 |
||
|
|
a5a738396d |
||
|
|
528cff269f |
||
|
|
e3f4b88768 |
||
|
|
61c20f1450 |
||
|
|
a98e01a4be |
||
|
|
1f5f7e16a3 |
||
|
|
158e64627f |
||
|
|
04a9a16be2 |
||
|
|
2166b38d56 |
||
|
|
02e86377e4 |
||
|
|
aafac3ddf5 |
||
|
|
0b1d0380c9 |
||
|
|
6e97aafb9c |
||
|
|
8f8af90007 |
||
|
|
43e7a39a21 |
||
|
|
f3ecb2ebd8 |
||
|
|
c19d550c27 |
||
|
|
6503059a4b |
||
|
|
9621039e54 |
||
|
|
77d6433e4b |
||
|
|
940eb90872 |
||
|
|
2fe982505f |
||
|
|
c081b90d74 |
||
|
|
e485fde727 |
||
|
|
ab4f686e18 |
||
|
|
48af7bd547 |
||
|
|
42fe1430ce |
||
|
|
8b57fdf3d0 |
||
|
|
2c16b3e084 |
||
|
|
7cb29125bd |
||
|
|
1de4ea933f |
||
|
|
ed205ef793 |
||
|
|
c0938a19bd |
||
|
|
4c3e5e61c7 |
||
|
|
96ab01712b |
||
|
|
2f29b1dbe5 |
||
|
|
7f02285753 |
||
|
|
abd0ddf13d |
||
|
|
6d74dce073 |
||
|
|
0dc077892e |
604 changed files with 55641 additions and 4404 deletions
9
.gitattributes
vendored
9
.gitattributes
vendored
|
|
@ -1,7 +1,12 @@
|
||||||
/.git* export-ignore
|
/.github export-ignore
|
||||||
|
/.phpdoc export-ignore
|
||||||
|
/docs export-ignore
|
||||||
/examples export-ignore
|
/examples export-ignore
|
||||||
/tests export-ignore
|
/tests export-ignore
|
||||||
/.php-cs-fixer.dist.php export-ignore
|
/.php-cs-fixer.dist.php export-ignore
|
||||||
|
/Makefile export-ignore
|
||||||
|
/mkdocs.yml export-ignore
|
||||||
|
/requirements-docs.txt export-ignore
|
||||||
|
/phpdoc.dist.xml
|
||||||
/phpstan* export-ignore
|
/phpstan* export-ignore
|
||||||
/phpunit.xml.dist export-ignore
|
/phpunit.xml.dist export-ignore
|
||||||
/Makefile export-ignore
|
|
||||||
|
|
|
||||||
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
|
|
@ -1 +1 @@
|
||||||
* @chr-hertel @Nyholm @CodeWithKyrian
|
* @chr-hertel @Nyholm @CodeWithKyrian @soyuka
|
||||||
|
|
|
||||||
11
.github/dependabot.yml
vendored
Normal file
11
.github/dependabot.yml
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: composer
|
||||||
|
directory: /
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
|
||||||
|
- package-ecosystem: github-actions
|
||||||
|
directory: /
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
189
.github/workflows/conformance-weekly.yaml
vendored
Normal file
189
.github/workflows/conformance-weekly.yaml
vendored
Normal file
|
|
@ -0,0 +1,189 @@
|
||||||
|
name: conformance-weekly
|
||||||
|
|
||||||
|
# Tracks the newest published @modelcontextprotocol/conformance; the
|
||||||
|
# on:pull_request pipeline pins an exact version instead. Also publishes
|
||||||
|
# score badges to the orphan `badges` branch, consumed by the README.
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 6 * * 1' # Mondays 06:00 UTC
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
issues: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
server:
|
||||||
|
name: conformance / server (${{ matrix.spec-version }}, ${{ matrix.dist-tag }})
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- spec-version: '2025-11-25'
|
||||||
|
dist-tag: latest
|
||||||
|
path: '/'
|
||||||
|
baseline: conformance-baseline-2025-11-25.yml
|
||||||
|
- spec-version: '2026-07-28'
|
||||||
|
dist-tag: alpha
|
||||||
|
path: '/'
|
||||||
|
baseline: conformance-baseline-2026-07-28.yml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v7
|
||||||
|
- uses: actions/setup-node@v7
|
||||||
|
with:
|
||||||
|
node-version: '22'
|
||||||
|
- run: composer install --prefer-dist --no-progress --no-interaction
|
||||||
|
- name: Start conformance server
|
||||||
|
run: |
|
||||||
|
mkdir -p tests/Conformance/sessions tests/Conformance/logs
|
||||||
|
chmod -R 777 tests/Conformance/sessions tests/Conformance/logs
|
||||||
|
docker compose -f tests/Conformance/Fixtures/docker-compose.yml up -d
|
||||||
|
sleep 5
|
||||||
|
- name: Run conformance tests
|
||||||
|
working-directory: ./tests/Conformance
|
||||||
|
run: >-
|
||||||
|
npx --yes @modelcontextprotocol/conformance@${{ matrix.dist-tag }} server
|
||||||
|
--url http://localhost:8000${{ matrix.path }}
|
||||||
|
--suite all
|
||||||
|
--spec-version ${{ matrix.spec-version }}
|
||||||
|
--expected-failures ${{ matrix.baseline }}
|
||||||
|
--output-dir results
|
||||||
|
- name: Generate score badge
|
||||||
|
if: always()
|
||||||
|
run: php tests/Conformance/score.php server ${{ matrix.spec-version }}
|
||||||
|
- name: Show docker logs on failure
|
||||||
|
if: failure()
|
||||||
|
run: docker compose -f tests/Conformance/Fixtures/docker-compose.yml logs
|
||||||
|
- name: Upload conformance results
|
||||||
|
if: failure()
|
||||||
|
uses: actions/upload-artifact@v7
|
||||||
|
with:
|
||||||
|
name: conformance-server-results-${{ matrix.spec-version }}
|
||||||
|
path: |
|
||||||
|
tests/Conformance/logs
|
||||||
|
tests/Conformance/results
|
||||||
|
- name: Upload score badge
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v7
|
||||||
|
with:
|
||||||
|
name: badge-server-${{ matrix.spec-version }}
|
||||||
|
path: tests/Conformance/server-conformance-${{ matrix.spec-version }}.json
|
||||||
|
|
||||||
|
client:
|
||||||
|
name: conformance / client (${{ matrix.spec-version }}, ${{ matrix.dist-tag }})
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- spec-version: '2025-11-25'
|
||||||
|
dist-tag: latest
|
||||||
|
baseline: conformance-baseline-2025-11-25.yml
|
||||||
|
- spec-version: '2026-07-28'
|
||||||
|
dist-tag: alpha
|
||||||
|
baseline: conformance-baseline-2026-07-28.yml
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v7
|
||||||
|
- uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: '8.4'
|
||||||
|
coverage: none
|
||||||
|
- uses: actions/setup-node@v7
|
||||||
|
with:
|
||||||
|
node-version: '22'
|
||||||
|
- run: composer install --prefer-dist --no-progress --no-interaction
|
||||||
|
- run: mkdir -p tests/Conformance/logs
|
||||||
|
- name: Run conformance tests
|
||||||
|
working-directory: ./tests/Conformance
|
||||||
|
run: >-
|
||||||
|
npx --yes @modelcontextprotocol/conformance@${{ matrix.dist-tag }} client
|
||||||
|
--command "php ${{ github.workspace }}/tests/Conformance/client.php"
|
||||||
|
--suite all
|
||||||
|
--spec-version ${{ matrix.spec-version }}
|
||||||
|
--expected-failures ${{ matrix.baseline }}
|
||||||
|
--output-dir results
|
||||||
|
- name: Generate score badge
|
||||||
|
if: always()
|
||||||
|
run: php tests/Conformance/score.php client ${{ matrix.spec-version }}
|
||||||
|
- name: Upload conformance results
|
||||||
|
if: failure()
|
||||||
|
uses: actions/upload-artifact@v7
|
||||||
|
with:
|
||||||
|
name: conformance-client-results-${{ matrix.spec-version }}
|
||||||
|
path: |
|
||||||
|
tests/Conformance/logs
|
||||||
|
tests/Conformance/results
|
||||||
|
- name: Upload score badge
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v7
|
||||||
|
with:
|
||||||
|
name: badge-client-${{ matrix.spec-version }}
|
||||||
|
path: tests/Conformance/client-conformance-${{ matrix.spec-version }}.json
|
||||||
|
|
||||||
|
notify:
|
||||||
|
name: Open issue on failure
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [server, client]
|
||||||
|
if: failure()
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GH_REPO: ${{ github.repository }}
|
||||||
|
EVENT_NAME: ${{ github.event_name }}
|
||||||
|
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
steps:
|
||||||
|
- name: File or comment tracking issue
|
||||||
|
run: |
|
||||||
|
existing=$(gh issue list --label conformance-weekly --state open --json number --jq '.[0].number // empty')
|
||||||
|
if [ -n "$existing" ]; then
|
||||||
|
gh issue comment "$existing" --body "New failure on $(date -u +%FT%TZ): $RUN_URL"
|
||||||
|
else
|
||||||
|
gh issue create \
|
||||||
|
--title '[conformance] Weekly conformance run failed' \
|
||||||
|
--label conformance-weekly \
|
||||||
|
--body "Conformance against the newest published \`@modelcontextprotocol/conformance\` failed.
|
||||||
|
|
||||||
|
- Run: $RUN_URL
|
||||||
|
- Trigger: $EVENT_NAME
|
||||||
|
- Date: $(date -u +%FT%TZ)
|
||||||
|
|
||||||
|
Upstream likely published a release whose scenarios the SDK does not satisfy. Either fix the SDK, update the conformance fixtures, or add the new failure to the matching \`tests/Conformance/conformance-baseline-<spec-version>.yml\`."
|
||||||
|
fi
|
||||||
|
|
||||||
|
publish:
|
||||||
|
name: Publish conformance badges
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [server, client]
|
||||||
|
if: ${{ !cancelled() && github.repository == 'modelcontextprotocol/php-sdk' }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v7
|
||||||
|
- uses: actions/download-artifact@v8
|
||||||
|
with:
|
||||||
|
pattern: badge-*
|
||||||
|
path: badges-in
|
||||||
|
merge-multiple: true
|
||||||
|
- name: Publish to badges branch
|
||||||
|
run: |
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
|
if git ls-remote --exit-code --heads origin badges >/dev/null 2>&1; then
|
||||||
|
git fetch origin badges
|
||||||
|
git worktree add badges-wt badges
|
||||||
|
else
|
||||||
|
git worktree add --detach badges-wt
|
||||||
|
git -C badges-wt checkout --orphan badges
|
||||||
|
git -C badges-wt rm -rf --quiet . >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f badges-wt/server-conformance.json badges-wt/client-conformance.json
|
||||||
|
cp badges-in/*.json badges-wt/
|
||||||
|
|
||||||
|
cd badges-wt
|
||||||
|
git add -A
|
||||||
|
if git diff --cached --quiet; then
|
||||||
|
echo "Conformance scores unchanged."
|
||||||
|
else
|
||||||
|
git commit -m "Update conformance score badges"
|
||||||
|
git push origin badges
|
||||||
|
fi
|
||||||
63
.github/workflows/docs.yml
vendored
Normal file
63
.github/workflows/docs.yml
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
name: Documentation
|
||||||
|
|
||||||
|
# The site is built by `make docs`: Zensical renders the guides under docs/
|
||||||
|
# (see mkdocs.yml) and phpDocumentor renders the API reference into /api/.
|
||||||
|
# Pull requests build only — the build is strict, so a broken internal link
|
||||||
|
# fails CI instead of shipping a dead link to the site.
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: docs-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
|
- name: Setup PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: '8.4'
|
||||||
|
coverage: "none"
|
||||||
|
|
||||||
|
- name: Install Composer dependencies
|
||||||
|
uses: "ramsey/composer-install@v4"
|
||||||
|
|
||||||
|
- name: Install uv
|
||||||
|
uses: astral-sh/setup-uv@v10.1.0
|
||||||
|
with:
|
||||||
|
enable-cache: true
|
||||||
|
|
||||||
|
- name: Build documentation
|
||||||
|
run: make docs
|
||||||
|
|
||||||
|
- name: Upload Pages artifact
|
||||||
|
uses: actions/upload-pages-artifact@v5
|
||||||
|
with:
|
||||||
|
path: ./site
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
needs: build
|
||||||
|
if: github.event_name != 'pull_request'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
environment:
|
||||||
|
name: github-pages
|
||||||
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
|
steps:
|
||||||
|
- name: Deploy to GitHub Pages
|
||||||
|
id: deployment
|
||||||
|
uses: actions/deploy-pages@v5
|
||||||
170
.github/workflows/pipeline.yaml
vendored
170
.github/workflows/pipeline.yaml
vendored
|
|
@ -5,6 +5,10 @@ permissions:
|
||||||
contents: read
|
contents: read
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
|
|
||||||
|
env:
|
||||||
|
# Pinned exact version; conformance-weekly.yaml tracks upstream releases.
|
||||||
|
CONFORMANCE_PKG: "@modelcontextprotocol/conformance@0.2.0-alpha.11"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
unit:
|
unit:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
@ -23,13 +27,16 @@ jobs:
|
||||||
- symfony-version: '7.4.*'
|
- symfony-version: '7.4.*'
|
||||||
dependencies: 'highest'
|
dependencies: 'highest'
|
||||||
php: '8.4'
|
php: '8.4'
|
||||||
|
- symfony-version: '8.0.*'
|
||||||
|
dependencies: 'highest'
|
||||||
|
php: '8.4'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
SYMFONY_REQUIRE: ${{ matrix.symfony-version || '>=6.4' }}
|
SYMFONY_REQUIRE: ${{ matrix.symfony-version || '>=6.4' }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
|
|
@ -39,18 +46,40 @@ jobs:
|
||||||
coverage: "none"
|
coverage: "none"
|
||||||
|
|
||||||
- name: Install Composer
|
- name: Install Composer
|
||||||
uses: "ramsey/composer-install@v3"
|
uses: "ramsey/composer-install@v4"
|
||||||
with:
|
with:
|
||||||
dependency-versions: "${{ matrix.dependencies }}"
|
dependency-versions: "${{ matrix.dependencies }}"
|
||||||
|
|
||||||
- name: Tests
|
- name: Tests
|
||||||
run: vendor/bin/phpunit --testsuite=unit
|
run: vendor/bin/phpunit --testsuite=unit
|
||||||
|
|
||||||
|
integration:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
php: ['8.1', '8.2', '8.3', '8.4', '8.5']
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
|
- name: Setup PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: ${{ matrix.php }}
|
||||||
|
coverage: "none"
|
||||||
|
|
||||||
|
- name: Install Composer
|
||||||
|
uses: "ramsey/composer-install@v4"
|
||||||
|
|
||||||
|
- name: Tests
|
||||||
|
run: vendor/bin/phpunit --testsuite=integration
|
||||||
|
|
||||||
inspector:
|
inspector:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
|
|
@ -59,21 +88,143 @@ jobs:
|
||||||
coverage: "none"
|
coverage: "none"
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v5
|
uses: actions/setup-node@v7
|
||||||
with:
|
with:
|
||||||
node-version: '22'
|
node-version: '22'
|
||||||
|
|
||||||
- name: Install Composer
|
- name: Install Composer
|
||||||
uses: "ramsey/composer-install@v3"
|
uses: "ramsey/composer-install@v4"
|
||||||
|
|
||||||
- name: Tests
|
- name: Tests
|
||||||
run: vendor/bin/phpunit --testsuite=inspector
|
run: vendor/bin/phpunit --testsuite=inspector
|
||||||
|
|
||||||
|
conformance-server:
|
||||||
|
name: conformance / server (${{ matrix.spec-version }})
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- spec-version: '2025-11-25'
|
||||||
|
path: '/'
|
||||||
|
baseline: conformance-baseline-2025-11-25.yml
|
||||||
|
- spec-version: '2026-07-28'
|
||||||
|
path: '/'
|
||||||
|
baseline: conformance-baseline-2026-07-28.yml
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v7
|
||||||
|
with:
|
||||||
|
node-version: '22'
|
||||||
|
|
||||||
|
- name: Install Composer
|
||||||
|
uses: "ramsey/composer-install@v4"
|
||||||
|
|
||||||
|
- name: Start conformance server
|
||||||
|
run: |
|
||||||
|
mkdir -p tests/Conformance/sessions tests/Conformance/logs
|
||||||
|
chmod -R 777 tests/Conformance/sessions tests/Conformance/logs
|
||||||
|
docker compose -f tests/Conformance/Fixtures/docker-compose.yml up -d
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
- name: Run conformance tests
|
||||||
|
working-directory: ./tests/Conformance
|
||||||
|
run: >-
|
||||||
|
npx --yes ${{ env.CONFORMANCE_PKG }} server
|
||||||
|
--url http://localhost:8000${{ matrix.path }}
|
||||||
|
--suite all
|
||||||
|
--spec-version ${{ matrix.spec-version }}
|
||||||
|
--expected-failures ${{ matrix.baseline }}
|
||||||
|
|
||||||
|
- name: Show logs on failure
|
||||||
|
if: failure()
|
||||||
|
run: |
|
||||||
|
echo "=== Docker Compose Logs ==="
|
||||||
|
docker compose -f tests/Conformance/Fixtures/docker-compose.yml logs
|
||||||
|
echo ""
|
||||||
|
echo "=== Conformance Log ==="
|
||||||
|
cat tests/Conformance/logs/conformance.log 2>/dev/null || echo "No conformance log found"
|
||||||
|
echo ""
|
||||||
|
echo "=== Test Results (first failed test) ==="
|
||||||
|
find tests/Conformance/results -name "checks.json" 2>/dev/null | head -3 | while read f; do
|
||||||
|
echo "--- $f ---"
|
||||||
|
cat "$f"
|
||||||
|
echo ""
|
||||||
|
done || echo "No results found"
|
||||||
|
echo ""
|
||||||
|
echo "=== Directory permissions ==="
|
||||||
|
ls -la tests/Conformance/
|
||||||
|
ls -la tests/Conformance/logs/ 2>/dev/null || echo "logs dir issue"
|
||||||
|
ls -la tests/Conformance/sessions/ 2>/dev/null || echo "sessions dir issue"
|
||||||
|
|
||||||
|
- name: Cleanup
|
||||||
|
if: always()
|
||||||
|
run: docker compose -f tests/Conformance/Fixtures/docker-compose.yml down
|
||||||
|
|
||||||
|
conformance-client:
|
||||||
|
name: conformance / client (${{ matrix.spec-version }})
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- spec-version: '2025-11-25'
|
||||||
|
baseline: conformance-baseline-2025-11-25.yml
|
||||||
|
- spec-version: '2026-07-28'
|
||||||
|
baseline: conformance-baseline-2026-07-28.yml
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
|
- name: Setup PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: '8.4'
|
||||||
|
coverage: "none"
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v7
|
||||||
|
with:
|
||||||
|
node-version: '22'
|
||||||
|
|
||||||
|
- name: Install Composer
|
||||||
|
uses: "ramsey/composer-install@v4"
|
||||||
|
|
||||||
|
- name: Create log directory
|
||||||
|
run: mkdir -p tests/Conformance/logs
|
||||||
|
|
||||||
|
- name: Run client conformance tests
|
||||||
|
working-directory: ./tests/Conformance
|
||||||
|
run: >-
|
||||||
|
npx --yes ${{ env.CONFORMANCE_PKG }} client
|
||||||
|
--command "php ${{ github.workspace }}/tests/Conformance/client.php"
|
||||||
|
--suite all
|
||||||
|
--spec-version ${{ matrix.spec-version }}
|
||||||
|
--expected-failures ${{ matrix.baseline }}
|
||||||
|
|
||||||
|
- name: Show logs on failure
|
||||||
|
if: failure()
|
||||||
|
run: |
|
||||||
|
echo "=== Client Conformance Log ==="
|
||||||
|
cat tests/Conformance/logs/client-conformance.log 2>/dev/null || echo "No client conformance log found"
|
||||||
|
echo ""
|
||||||
|
echo "=== Test Results ==="
|
||||||
|
find tests/Conformance/results -name "checks.json" 2>/dev/null | head -3 | while read f; do
|
||||||
|
echo "--- $f ---"
|
||||||
|
cat "$f"
|
||||||
|
echo ""
|
||||||
|
done || echo "No results found"
|
||||||
|
|
||||||
qa:
|
qa:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v7
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: shivammathur/setup-php@v2
|
uses: shivammathur/setup-php@v2
|
||||||
|
|
@ -85,10 +236,15 @@ jobs:
|
||||||
run: composer validate --strict
|
run: composer validate --strict
|
||||||
|
|
||||||
- name: Install Composer
|
- name: Install Composer
|
||||||
uses: "ramsey/composer-install@v3"
|
uses: "ramsey/composer-install@v4"
|
||||||
|
|
||||||
- name: Code Style PHP
|
- name: Code Style PHP
|
||||||
run: vendor/bin/php-cs-fixer fix --dry-run
|
run: vendor/bin/php-cs-fixer fix --dry-run
|
||||||
|
|
||||||
- name: PHPStan
|
- name: PHPStan
|
||||||
run: vendor/bin/phpstan analyse
|
run: vendor/bin/phpstan analyse
|
||||||
|
|
||||||
|
# Only the phpDocumentor half: this job is PHP-only, and the Zensical
|
||||||
|
# guides are built (strictly) by the Documentation workflow.
|
||||||
|
- name: Documentation
|
||||||
|
run: make docs-api
|
||||||
|
|
|
||||||
13
.gitignore
vendored
13
.gitignore
vendored
|
|
@ -6,3 +6,16 @@ vendor
|
||||||
examples/**/dev.log
|
examples/**/dev.log
|
||||||
examples/**/cache
|
examples/**/cache
|
||||||
examples/**/sessions
|
examples/**/sessions
|
||||||
|
tests/Conformance/client-conformance.json
|
||||||
|
tests/Conformance/server-conformance.json
|
||||||
|
tests/Conformance/results*
|
||||||
|
tests/Conformance/sessions
|
||||||
|
tests/Conformance/logs/*.log
|
||||||
|
|
||||||
|
# phpDocumentor
|
||||||
|
.phpdoc/build/
|
||||||
|
.phpdoc/cache/
|
||||||
|
|
||||||
|
# Documentation site (make docs)
|
||||||
|
/site/
|
||||||
|
/.cache/
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,6 @@ return (new Config())
|
||||||
->setRules([
|
->setRules([
|
||||||
'@Symfony' => true,
|
'@Symfony' => true,
|
||||||
'@Symfony:risky' => true,
|
'@Symfony:risky' => true,
|
||||||
'protected_to_private' => false,
|
|
||||||
'declare_strict_types' => false,
|
|
||||||
'header_comment' => ['header' => $fileHeader],
|
'header_comment' => ['header' => $fileHeader],
|
||||||
'php_unit_test_case_static_method_calls' => ['call_type' => 'this'],
|
'php_unit_test_case_static_method_calls' => ['call_type' => 'this'],
|
||||||
])
|
])
|
||||||
|
|
|
||||||
13
.phpdoc/template/base.html.twig
Normal file
13
.phpdoc/template/base.html.twig
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
{% extends 'layout.html.twig' %}
|
||||||
|
|
||||||
|
{% set topMenu = {
|
||||||
|
"menu": [
|
||||||
|
{ "name": "Guides", "url": "/"},
|
||||||
|
{ "name": "Specification", "url": "https://modelcontextprotocol.io/" }
|
||||||
|
],
|
||||||
|
"social": [
|
||||||
|
{ "iconClass": "fab fa-github", "url": "https://github.com/modelcontextprotocol/php-sdk"},
|
||||||
|
{ "iconClass": "fab fa-discord", "url": "https://discord.gg/6CSzBmMkjX"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
%}
|
||||||
10
.phpdoc/template/components/header-title.html.twig
Normal file
10
.phpdoc/template/components/header-title.html.twig
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<h1 class="phpdocumentor-title">
|
||||||
|
<a href="/" class="phpdocumentor-title__link">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 195 195" fill="none">
|
||||||
|
<path d="M25 97.8528L92.8823 29.9706C102.255 20.598 117.451 20.598 126.823 29.9706V29.9706C136.196 39.3431 136.196 54.5391 126.823 63.9117L75.5581 115.177" stroke="black" stroke-width="12" stroke-linecap="round"/>
|
||||||
|
<path d="M76.2653 114.47L126.823 63.9117C136.196 54.5391 151.392 54.5391 160.765 63.9117L161.118 64.2652C170.491 73.6378 170.491 88.8338 161.118 98.2063L99.7248 159.6C96.6006 162.724 96.6006 167.789 99.7248 170.913L112.331 183.52" stroke="black" stroke-width="12" stroke-linecap="round"/>
|
||||||
|
<path d="M109.853 46.9411L59.6482 97.1457C50.2757 106.518 50.2757 121.714 59.6482 131.087V131.087C69.0208 140.459 84.2168 140.459 93.5894 131.087L143.794 80.8822" stroke="black" stroke-width="12" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
<span style="margin-left: 0.25em;">{{ project.name }}</span>
|
||||||
|
</a>
|
||||||
|
</h1>
|
||||||
122
CHANGELOG.md
122
CHANGELOG.md
|
|
@ -2,6 +2,128 @@
|
||||||
|
|
||||||
All notable changes to `mcp/sdk` will be documented in this file.
|
All notable changes to `mcp/sdk` will be documented in this file.
|
||||||
|
|
||||||
|
0.9.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* [BC Break] Remove the `providerClass` argument of `#[CompletionProvider]`. Use `provider:`, which takes the same class-string and is now the first positional argument.
|
||||||
|
* Add `HttpTransport::getSessionId()` to read the server-minted `Mcp-Session-Id`: a request-scoped caller can persist it and pass it back through the constructor's `$headers` on a later transport. Always `null` on `2026-07-28`, which removed protocol-level sessions.
|
||||||
|
* Fix OIDC discovery rejecting issuers with a trailing slash (e.g. Authentik, Auth0).
|
||||||
|
|
||||||
|
0.8.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Support for MCP spec version 2026-07-28 incl. stateless HTTP transport
|
||||||
|
* Negotiate the protocol revision during `initialize`: the server counter-offers a revision it supports and the client fails the handshake instead of continuing on an unagreed one. Adds `Client::getProtocolVersion()`.
|
||||||
|
* Serve both protocol eras from one endpoint: `StreamableHttpTransport` classifies each request (`InboundClassifier`) and routes it to the matching dispatcher, so one URL answers modern and handshake-era clients alike. `Builder::withoutModernEra()` opts out, `Builder::setModernVersions()` narrows the modern leg.
|
||||||
|
* Speak the lifecycle from the client: `Client` opens with `server/discover`, stamps `_meta` with version, capabilities and client info, and sends the `Mcp-Method`/`Mcp-Name`/`Mcp-Param-*` headers (via `Client\Stateless\ToolCatalog`). `Schema\Wire\McpHeader` holds the shared header names.
|
||||||
|
* Add multi round-trip requests (SEP-2322): a handler returning `InputRequiredResult` yields `resultType: "input_required"` with an opaque, signed `requestState` (`RequestStateCodec`, key via `Builder::setRequestState()`); the client retries with `inputResponses`, read through `RequestContext::getInputContext()` and its typed `elicitResult()`/`samplingResult()`/`rootsResult()`. On the client, `InputRequestResolver` answers such results automatically from the host's elicitation, sampling and roots handlers.
|
||||||
|
* Serve `ClientGateway::elicit()`/`elicitUrl()` on every revision: where the client cannot be asked mid-request, `Server\Stateless\ElicitationReplay` turns the ask into `input_required` and resumes once re-sent — the handler is entered once per ask. `InputRequiredShim` does the reverse for handshake-era clients. `sample()`/`listRoots()` raise a `LogicException` on `2026-07-28`, which removed them.
|
||||||
|
* Run stateless handlers in a fiber so `$gateway->progress()`/`log()` stream over SSE when the handler emits something and the client accepts `text/event-stream`; honour `io.modelcontextprotocol/logLevel` (SEP-2575). Adds `LoggingLevel::severity()`/`isAtLeast()`.
|
||||||
|
* Deliver notifications on `subscriptions/listen` (SEP-2575) via `NotificationBusInterface` — `InMemoryNotificationBus` for persistent runtimes, `Psr16NotificationBus` for PHP-FPM — set with `Builder::setNotificationBus()`; `Builder::setSubscriptionLifetime()` replaces the hard-coded 30s ceiling.
|
||||||
|
* Validate the standard request headers (SEP-2243) with `StandardHeaderValidator` (`Builder::setHeaderValidator()`), answering `-32020` when they contradict the body.
|
||||||
|
* Add `Wire\CachePolicy` (`Builder::setCachePolicy()`) for SEP-2549 caching hints; defaults to `ttlMs: 0, cacheScope: private`. A `ReadResourceResult` may override with its own values.
|
||||||
|
* Carry W3C trace context (SEP-414): `traceparent`/`tracestate`/`baggage` from `_meta` are exposed via `RequestContext::getTraceContext()` and echoed onto the request's notifications.
|
||||||
|
* Close the schema gaps for `2025-06-18`/`2025-11-25` and add the `2026-07-28` surface (SEP-2106): url-mode elicitation (`ClientGateway::elicitUrl()`/`supportsElicitationUrl()`), `Implementation::title`, and `outputSchema`/`structuredContent` accepting any JSON value.
|
||||||
|
* Deprecate Roots, Sampling and Logging (SEP-2577, earliest removal `2027-07-28`); they keep working but trigger a deprecation notice.
|
||||||
|
* [BC Break] Answer a not-found subject with `-32602` instead of `-32002` (SEP-2164): `resources/read` picks the code by revision (`-32602` from `2026-07-28` on), `prompts/get`, `completion/complete` and `tools/call` switch on every revision. Adds `ProtocolVersion::usesInvalidParamsForResourceNotFound()`.
|
||||||
|
* [BC Break] A list-shaped tool result is only sent as `structuredContent` on `2026-07-28`+; older revisions keep the JSON-encoded value in `content`.
|
||||||
|
* [BC Break] Add the extensions framework (SEP-2133) MCP Apps sits on: `ExtensionInterface::getId()` returns an `ExtensionIdentifier` value object, and the interface gains `getMessages()`/`getRequestHandlers()` (extend `AbstractExtension` to skip both). `MessageFactory::make()` takes an `$additional` message list; `RequestHandlerInterface`'s result template is covariant. `ServerExtensionInterface` is replaced by the side-agnostic `Schema\Extension\ExtensionInterface`.
|
||||||
|
* Add client-side extension negotiation: `ClientGateway::supportsExtension()`, `Client\Builder::enableExtension()`, `ClientCapabilities::withExtensions()`.
|
||||||
|
* Add sampling-with-tools: sampling requests can carry tools and tool-choice preferences, messages support tool-use/tool-result blocks, and clients advertise `sampling.context`/`sampling.tools` (`ClientGateway::supportsSamplingTools()`/`supportsSamplingContext()`). Requests violating the tool-flow rules are rejected with a JSON-RPC error.
|
||||||
|
* [BC Break] `SamplingMessage::$content` and `CreateSamplingMessageResult::$content` may be a list of content blocks — use `getContentBlocks()`. `CreateSamplingMessageResult` rejects roles other than `assistant` and empty content.
|
||||||
|
* Add client-side Roots support (`RootsCallbackInterface`, `Client::sendRootsListChanged()`) and server-side `ClientGateway::listRoots()`/`supportsRoots()`/`supportsSampling()`.
|
||||||
|
* Add `Schema\Content\ResourceLink` to reference a resource by URI in tool results and prompt messages.
|
||||||
|
* [BC Break] `Schema\JsonRpc\Error` accepts `null` as `$id`; an unreadable id now omits the member instead of sending `"id": ""`. `MessageFactory` decodes a missing or null id as an id-less error.
|
||||||
|
* Preserve the request `id` on an invalid-but-parseable message (`-32600`) via `InvalidInputMessageException::getRequestId()`.
|
||||||
|
* [BC Break] Drop the SDK-only name pattern on `ResourceDefinition`/`ResourceTemplate` `$name`; the spec allows any string.
|
||||||
|
* Log expected tool failures (`ToolCallException`) at debug level instead of error.
|
||||||
|
* Add `annotations` to `ImageContent`.
|
||||||
|
* Fix empty tool/resource schemas serializing as `[]` instead of `{}`.
|
||||||
|
* Fix `PromptResultFormatter` dropping `annotations`, `_meta` and `mimeType` for plain-array content.
|
||||||
|
|
||||||
|
0.7.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Add client-side elicitation support: `ElicitationCallbackInterface`, `ElicitationRequestHandler`, and `ElicitationException` let clients respond to server elicitation requests.
|
||||||
|
* Defer element loading to the first registry read: loaders now run at request time (first `has*`/`get*` call) instead of eagerly at `Builder::build()`, fixing empty registries under persistent runtimes (e.g. FrankenPHP worker mode) where a loader's data source is not ready at build time. Adds `Builder::setLazyLoading()` (default on), a public `Registry::load()`, and an optional `LoaderInterface` constructor argument on `Registry`.
|
||||||
|
* [BC Break] Element loading is lazy by default: loader failures now surface on the first request rather than at `Builder::build()`, and `initialize` advertises capabilities from the configured sources rather than the loaded registry. Call `Builder::setLazyLoading(false)` to restore eager build-time loading.
|
||||||
|
* Allow `[$instance, 'methodName']` as an element handler in `Builder::addTool()`, `addResource()`, `addResourceTemplate()`, and `addPrompt()`. Unblocks handlers with constructor dependencies that the container-less `new $className()` fallback cannot build.
|
||||||
|
* Always emit an `items` schema for array tool parameters: untyped arrays get `items: {}` and nullable typed arrays (e.g. `string[]|null`) keep their element type. Fixes strict clients rejecting tools with "array type must have items" (#151).
|
||||||
|
* Harden JSON-RPC input parsing: single-message vs batch is now decided from the decoded JSON type (object → single, list array → batch) instead of the raw first byte. Scalars, empty payloads, and non-object batch elements are surfaced as `InvalidInputMessageException` entries instead of triggering warnings or a `TypeError`.
|
||||||
|
* Add `maxBatchSize` (default `100`) to `MessageFactory` — oversized JSON-RPC batches are rejected before any message is constructed, guarding against amplification.
|
||||||
|
* Add `maxBodyBytes` (default 4 MiB) to `StreamableHttpTransport` — POST bodies exceeding the cap are rejected with `413`. Unknown-size/chunked bodies are read incrementally and stopped at the cap so they cannot exhaust memory.
|
||||||
|
* Reject malformed `Mcp-Session-Id` headers with a `400` response: a repeated header or a value that is not a valid UUID is now rejected up front instead of surfacing as an uncaught `Uuid::fromString()` error.
|
||||||
|
* Extract RFC 9728 metadata serving into `ProtectedResourceMetadataHandler`, a transport-neutral PSR-15 `RequestHandlerInterface` that can be mounted directly as a Symfony/Laravel controller; `ProtectedResourceMetadataMiddleware` now delegates to it (no BC break).
|
||||||
|
|
||||||
|
0.6.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Add `Builder::add(Tool|ResourceDefinition|ResourceTemplate|Prompt $definition, ElementHandlerInterface $handler)` for explicit registration of elements whose schema is only known at runtime.
|
||||||
|
* Add handler interfaces `ToolHandlerInterface`, `ResourceHandlerInterface`, `ResourceTemplateHandlerInterface`, `PromptHandlerInterface`, and the `ElementHandlerInterface` marker.
|
||||||
|
* [BC Break] Renamed `Mcp\Schema\Resource` to `Mcp\Schema\ResourceDefinition`. No alias.
|
||||||
|
* [BC Break] Renamed `Mcp\Capability\Registry\Loader\ArrayLoader` to `Mcp\Capability\Registry\Loader\ReflectedElementLoader`.
|
||||||
|
* [BC Break] Bump default protocol version to `2025-11-25`
|
||||||
|
* Add support for MCP Apps extension in schema and server
|
||||||
|
* Add `extensions` to `ServerCapabilities` and `ClientCapabilities` and `Builder::enableExtension()`
|
||||||
|
* Allow overriding the default name pattern for Discovery
|
||||||
|
* Add configurable session garbage collection (`gcProbability`/`gcDivisor`)
|
||||||
|
* Add optional `title` field to `ResourceDefinition` and `ResourceTemplate` for MCP spec compliance
|
||||||
|
* Add `ChainLoader` to compose multiple `LoaderInterface` implementations via explicit ordering.
|
||||||
|
* Add `RegistryInterface::unregisterTool()`, `unregisterResource()`, `unregisterResourceTemplate()`, `unregisterPrompt()` — idempotent removals.
|
||||||
|
* Add `RegistryInterface::hasTool()`, `hasResource()`, `hasResourceTemplate()`, `hasPrompt()` — by-name existence checks.
|
||||||
|
* `DiscoveryLoader` now refreshes only its own previously written entries; manual registrations (via `Builder::addTool()` etc. or runtime `$registry->registerTool()` calls) survive rediscovery, and a same-name manual registration takes precedence over discovery on collision.
|
||||||
|
* [BC Break] Removed `ElementReference::$isManual` public property and the `bool $isManual` parameter from all `*Reference` constructors. Origin tracking is no longer carried on the element; manual-over-discovered precedence is encoded by loader execution order.
|
||||||
|
* [BC Break] `RegistryInterface::registerTool()`, `registerResource()`, `registerResourceTemplate()`, `registerPrompt()` lost their trailing `bool $isManual = false` parameter. Callers using positional arguments must drop the flag.
|
||||||
|
* [BC Break] Removed `RegistryInterface::clear()`, `getDiscoveryState()`, `setDiscoveryState()`. Rediscovery now goes through `DiscoveryLoader::load()` directly.
|
||||||
|
* `Registry::register*()` semantics changed to plain last-write-wins (overwrites silently) and the methods now return the stored `*Reference`. The previous "discovered registration is ignored when a manual one already exists" precedence rule still applies, but is now enforced by `DiscoveryLoader` via reference-identity tracking — and still emits a debug log when a discovery is skipped due to a conflicting registration.
|
||||||
|
* Add optional `title` parameter to `Builder::addResource()` and `Builder::addResourceTemplate()` for MCP spec compliance
|
||||||
|
* [BC Break] `Builder::addResource()` signature changed — `$title` parameter added between `$name` and `$description`. Callers using positional arguments must switch to named arguments.
|
||||||
|
* [BC Break] `Builder::addResourceTemplate()` signature changed — `$title` parameter added between `$name` and `$description`. Callers using positional arguments must switch to named arguments.
|
||||||
|
* Add `CorsMiddleware`, `DnsRebindingProtectionMiddleware`, and `ProtocolVersionMiddleware` for `StreamableHttpTransport`, composed automatically as the default stack via `StreamableHttpTransport::defaultMiddleware()`
|
||||||
|
* [BC BREAK] `StreamableHttpTransport` constructor: `$corsHeaders` parameter removed; CORS is now configured via `CorsMiddleware`. The `$middleware` parameter is nullable — `null` (or omitted) installs the default stack; `[]` disables all defaults. Default `Access-Control-Allow-Origin` is no longer set (was `*`).
|
||||||
|
* [BC Break] `ResourceDefinition::__construct()` signature changed — `$title` parameter added between `$name` and `$description`. Callers using positional arguments must switch to named arguments.
|
||||||
|
* [BC Break] `ResourceTemplate::__construct()` signature changed — `$title` parameter added between `$name` and `$description`. Callers using positional arguments must switch to named arguments.
|
||||||
|
* [BC Break] `McpResource` and `McpResourceTemplate` attribute signatures changed — `$title` parameter added between `$name` and `$description`. Callers using positional arguments must switch to named arguments.
|
||||||
|
|
||||||
|
0.5.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Add built-in authentication middleware for HTTP transport using OAuth
|
||||||
|
* Add client component for building MCP clients
|
||||||
|
* Add `Builder::setReferenceHandler()` to allow custom `ReferenceHandlerInterface` implementations (e.g. authorization decorators)
|
||||||
|
* Add elicitation enum schema types per SEP-1330: `TitledEnumSchemaDefinition`, `MultiSelectEnumSchemaDefinition`, `TitledMultiSelectEnumSchemaDefinition`
|
||||||
|
* [BC break] Make Symfony Finder component optional. Users would need to install `symfony/finder` now themselves
|
||||||
|
* Add `LenientOidcDiscoveryMetadataPolicy` for identity providers that omit `code_challenge_methods_supported` (e.g. FusionAuth, Microsoft Entra ID)
|
||||||
|
* Add OAuth 2.0 Dynamic Client Registration middleware (RFC 7591)
|
||||||
|
* Add optional `title` field to `Prompt` and `McpPrompt` for MCP spec compliance
|
||||||
|
* [BC Break] `Builder::addPrompt()` signature changed — `$title` parameter added between `$name` and `$description`. Callers using positional arguments for `$description` must switch to named arguments.
|
||||||
|
* Add optional `title` field to `Tool` and `McpTool` for MCP spec compliance
|
||||||
|
* [BC Break] `Tool::__construct()` signature changed — `$title` parameter added between `$name` and `$inputSchema`. Callers using positional arguments must switch to named arguments or pass `null` for `$title`.
|
||||||
|
* [BC Break] `McpTool` attribute signature changed — `$title` parameter added between `$name` and `$description`. Callers using positional arguments for `$description` must switch to named arguments.
|
||||||
|
* [BC Break] `Builder::addTool()` signature changed — `$title` parameter added between `$name` and `$description`. Callers using positional arguments for `$description` must switch to named arguments.
|
||||||
|
|
||||||
|
0.4.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Rename `Mcp\Server\Session\Psr16StoreSession` to `Mcp\Server\Session\Psr16SessionStore`
|
||||||
|
* Add missing handlers for resource subscribe/unsubscribe and persist subscriptions via session
|
||||||
|
* Introduce `SessionManager` to encapsulate session handling (replaces `SessionFactory`) and move garbage collection logic from `Protocol`.
|
||||||
|
|
||||||
|
0.3.0
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Add output schema support to MCP tools
|
||||||
|
* Add validation of the input parameters given to a Tool.
|
||||||
|
* Rename `Mcp\Capability\Registry\ResourceReference::$schema` to `Mcp\Capability\Registry\ResourceReference::$resource`.
|
||||||
|
* Introduce `SchemaGeneratorInterface` and `DiscovererInterface` to allow custom schema generation and discovery implementations.
|
||||||
|
* Remove `DocBlockParser::getSummary()` method, use `DocBlockParser::getDescription()` instead.
|
||||||
|
|
||||||
|
0.2.2
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Throw exception when trying to inject parameter with the unsupported names `$_session` or `$_request`.
|
||||||
|
* `Throwable` objects are passed to log context instead of the exception message.
|
||||||
|
|
||||||
0.2.1
|
0.2.1
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
|
|
||||||
84
CONTRIBUTING.md
Normal file
84
CONTRIBUTING.md
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
# Contributing
|
||||||
|
|
||||||
|
Thanks for considering a contribution to the PHP SDK for the Model Context Protocol. This is a
|
||||||
|
collaboration between [the PHP Foundation](https://thephp.foundation/) and the
|
||||||
|
[Symfony project](https://symfony.com/), and it follows Symfony's conventions throughout.
|
||||||
|
|
||||||
|
## Ways to contribute
|
||||||
|
|
||||||
|
- **Report a bug** or **propose a feature** by [opening an issue](https://github.com/modelcontextprotocol/php-sdk/issues).
|
||||||
|
Check existing issues first — many spec-driven changes are already tracked under the relevant
|
||||||
|
`2026-07-28`-style release label.
|
||||||
|
- **Send a pull request** for a fix, a new capability, or a docs improvement.
|
||||||
|
- **Improve the guides** under `docs/` — see [Documentation](#documentation) below for how they're built.
|
||||||
|
- **Help close a conformance gap** — `make conformance-tests` runs the official MCP conformance
|
||||||
|
suite against this SDK; a failing scenario there is a concrete, well-scoped contribution.
|
||||||
|
|
||||||
|
## Development setup
|
||||||
|
|
||||||
|
Requires PHP 8.1+.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
composer install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Before opening a pull request
|
||||||
|
|
||||||
|
Run the full CI suite locally:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make ci
|
||||||
|
```
|
||||||
|
|
||||||
|
This runs, in order: `make cs` (PHP CS Fixer, auto-fixes style), `make phpstan` (static analysis),
|
||||||
|
and `make tests` (unit + inspector tests). All three must pass. If your change touches
|
||||||
|
protocol-observable behavior, also run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make conformance-tests # requires Docker
|
||||||
|
```
|
||||||
|
|
||||||
|
## Coding standards
|
||||||
|
|
||||||
|
This project follows [Symfony's coding standards](https://symfony.com/doc/current/contributing/code/standards.html)
|
||||||
|
and [backward compatibility promise](https://symfony.com/doc/current/contributing/code/bc.html). In short:
|
||||||
|
|
||||||
|
See [CLAUDE.md](CLAUDE.md) for a fuller tour of the codebase's architecture and layout.
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
New capabilities need unit tests (`tests/Unit/`) covering the core logic, and — for anything
|
||||||
|
reachable over the wire — inspector tests (`tests/Inspector/`) for end-to-end coverage. If you're
|
||||||
|
adding a documented pattern, consider adding or updating an example under `examples/`.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
The guides under `docs/` are built with [Zensical](https://zensical.org/) in `--strict` mode,
|
||||||
|
which fails the build on a broken internal link:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make docs-guides
|
||||||
|
```
|
||||||
|
|
||||||
|
Links between guide pages must be relative paths that resolve within `docs/` (e.g.
|
||||||
|
`protocol-versions.md`, `../CLAUDE.md` will *not* resolve — Zensical only follows the `docs/` tree).
|
||||||
|
For anything at the repo root (`CLAUDE.md`, `ROADMAP.md`, `CHANGELOG.md`), link to it by its GitHub
|
||||||
|
URL instead, matching the pattern already used across `docs/`. The class-level API reference is
|
||||||
|
generated separately by phpDocumentor (`make docs-api`) and isn't hand-written.
|
||||||
|
|
||||||
|
## Versioning
|
||||||
|
|
||||||
|
The SDK follows [Semantic Versioning](https://semver.org/) — see
|
||||||
|
[SDK tier target](docs/sdk-tier.md#versioning) for what that means pre- and post-1.0, and how it
|
||||||
|
lines up with Symfony's backward compatibility promise.
|
||||||
|
|
||||||
|
## Licensing
|
||||||
|
|
||||||
|
New contributions are licensed under Apache License, Version 2.0. Existing code predating this
|
||||||
|
policy remains under the MIT License — see [LICENSE](LICENSE) for the details. By opening a pull
|
||||||
|
request, you agree your contribution is provided under those terms.
|
||||||
|
|
||||||
|
## Getting help
|
||||||
|
|
||||||
|
If something is unclear or you want early feedback on an approach before writing code, open an
|
||||||
|
issue or a draft pull request — that's the right place to ask, rather than guessing at scope.
|
||||||
205
LICENSE
205
LICENSE
|
|
@ -1,11 +1,200 @@
|
||||||
|
The MCP project is undergoing a licensing transition from the MIT License to the Apache License, Version 2.0 ("Apache-2.0"). All new code and specification contributions to the project are licensed under Apache-2.0. Documentation contributions (excluding specifications) are licensed under CC-BY-4.0.
|
||||||
|
|
||||||
|
Contributions for which relicensing consent has been obtained are licensed under Apache-2.0. Contributions made by authors who originally licensed their work under the MIT License and who have not yet granted explicit permission to relicense remain licensed under the MIT License.
|
||||||
|
|
||||||
|
No rights beyond those granted by the applicable original license are conveyed for such contributions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to the Licensor for inclusion in the Work by the copyright
|
||||||
|
owner or by an individual or Legal Entity authorized to submit on behalf
|
||||||
|
of the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024-2025 Model Context Protocol a Series of LF Projects, LLC.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
in the Software without restriction, including without limitation the rights
|
in the Software without restriction, including without limitation the rights
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
copies of the Software, and to permit persons to whom the Software is furnished
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
to do so, subject to the following conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
The above copyright notice and this permission notice shall be included in all
|
||||||
copies or substantial portions of the Software.
|
copies or substantial portions of the Software.
|
||||||
|
|
@ -15,5 +204,13 @@ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
THE SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Creative Commons Attribution 4.0 International (CC-BY-4.0)
|
||||||
|
|
||||||
|
Documentation in this project (excluding specifications) is licensed under
|
||||||
|
CC-BY-4.0. See https://creativecommons.org/licenses/by/4.0/legalcode for
|
||||||
|
the full license text.
|
||||||
|
|
|
||||||
67
Makefile
67
Makefile
|
|
@ -1,4 +1,15 @@
|
||||||
.PHONY: deps-stable deps-low cs phpstan tests unit-tests inspector-tests coverage ci ci-stable ci-lowest
|
.PHONY: deps-stable deps-low cs phpstan tests unit-tests integration-tests inspector-tests coverage ci ci-stable ci-lowest conformance-tests conformance-server conformance-client conformance-draft conformance-draft-server conformance-draft-client docs docs-guides docs-api docs-serve
|
||||||
|
|
||||||
|
# The 2026-07-28 scenarios ship on the `alpha` dist-tag; `latest` (0.1.x) has
|
||||||
|
# none of them. Pinned to the same version CI runs (see
|
||||||
|
# .github/workflows/pipeline.yaml), so a local pass means a green pipeline.
|
||||||
|
CONFORMANCE_VERSION ?= 0.2.0-alpha.11
|
||||||
|
CONFORMANCE = npx --yes @modelcontextprotocol/conformance@$(CONFORMANCE_VERSION)
|
||||||
|
|
||||||
|
# The documentation toolchain is Python (Zensical, see requirements-docs.txt),
|
||||||
|
# run through uv so no virtualenv has to be managed by hand:
|
||||||
|
# https://docs.astral.sh/uv/getting-started/installation/
|
||||||
|
DOCS_RUN = uv run --no-project --with-requirements requirements-docs.txt --
|
||||||
|
|
||||||
deps-stable:
|
deps-stable:
|
||||||
composer update --prefer-stable
|
composer update --prefer-stable
|
||||||
|
|
@ -18,9 +29,46 @@ tests:
|
||||||
unit-tests:
|
unit-tests:
|
||||||
vendor/bin/phpunit --testsuite=unit
|
vendor/bin/phpunit --testsuite=unit
|
||||||
|
|
||||||
|
integration-tests:
|
||||||
|
vendor/bin/phpunit --testsuite=integration
|
||||||
|
|
||||||
inspector-tests:
|
inspector-tests:
|
||||||
vendor/bin/phpunit --testsuite=inspector
|
vendor/bin/phpunit --testsuite=inspector
|
||||||
|
|
||||||
|
conformance-tests: conformance-server conformance-client
|
||||||
|
|
||||||
|
conformance-server:
|
||||||
|
docker compose -f tests/Conformance/Fixtures/docker-compose.yml up -d
|
||||||
|
@echo "Waiting for server to start..."
|
||||||
|
@sleep 5
|
||||||
|
rm -rf tests/Conformance/results
|
||||||
|
cd tests/Conformance && $(CONFORMANCE) server --url http://localhost:8000/ --suite all --spec-version 2025-11-25 --expected-failures conformance-baseline-2025-11-25.yml --output-dir results || true
|
||||||
|
php tests/Conformance/score.php server 2025-11-25
|
||||||
|
docker compose -f tests/Conformance/Fixtures/docker-compose.yml down
|
||||||
|
|
||||||
|
conformance-client:
|
||||||
|
rm -rf tests/Conformance/results
|
||||||
|
cd tests/Conformance && $(CONFORMANCE) client --command "php $(CURDIR)/tests/Conformance/client.php" --suite all --spec-version 2025-11-25 --expected-failures conformance-baseline-2025-11-25.yml --output-dir results || true
|
||||||
|
php tests/Conformance/score.php client 2025-11-25
|
||||||
|
|
||||||
|
# --- 2026-07-28 (SEP-2575 stateless lifecycle) ------------------------------
|
||||||
|
|
||||||
|
conformance-draft: conformance-draft-server conformance-draft-client
|
||||||
|
|
||||||
|
conformance-draft-server:
|
||||||
|
docker compose -f tests/Conformance/Fixtures/docker-compose.yml up -d
|
||||||
|
@echo "Waiting for server to start..."
|
||||||
|
@sleep 5
|
||||||
|
rm -rf tests/Conformance/results-2026-07-28
|
||||||
|
cd tests/Conformance && $(CONFORMANCE) server --url http://localhost:8000/ --suite all --spec-version 2026-07-28 --expected-failures conformance-baseline-2026-07-28.yml --output-dir results-2026-07-28 || true
|
||||||
|
php tests/Conformance/score.php server 2026-07-28 results-2026-07-28
|
||||||
|
docker compose -f tests/Conformance/Fixtures/docker-compose.yml down
|
||||||
|
|
||||||
|
conformance-draft-client:
|
||||||
|
rm -rf tests/Conformance/results-2026-07-28
|
||||||
|
cd tests/Conformance && $(CONFORMANCE) client --command "php $(CURDIR)/tests/Conformance/client.php" --suite all --spec-version 2026-07-28 --expected-failures conformance-baseline-2026-07-28.yml --output-dir results-2026-07-28 || true
|
||||||
|
php tests/Conformance/score.php client 2026-07-28 results-2026-07-28
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
XDEBUG_MODE=coverage vendor/bin/phpunit --testsuite=unit --coverage-html=coverage
|
XDEBUG_MODE=coverage vendor/bin/phpunit --testsuite=unit --coverage-html=coverage
|
||||||
|
|
||||||
|
|
@ -29,3 +77,20 @@ ci: ci-stable
|
||||||
ci-stable: deps-stable cs phpstan tests
|
ci-stable: deps-stable cs phpstan tests
|
||||||
|
|
||||||
ci-lowest: deps-low cs phpstan tests
|
ci-lowest: deps-low cs phpstan tests
|
||||||
|
|
||||||
|
# The published site is the guides (Zensical) with the phpDocumentor API
|
||||||
|
# reference mounted at /api/. `zensical build` wipes site/, so it runs first.
|
||||||
|
docs: docs-guides docs-api
|
||||||
|
rm -rf site/api
|
||||||
|
cp -a .phpdoc/build/api site/api
|
||||||
|
|
||||||
|
docs-guides:
|
||||||
|
$(DOCS_RUN) zensical build --strict
|
||||||
|
|
||||||
|
docs-api:
|
||||||
|
vendor/bin/phpdoc --no-interaction
|
||||||
|
@grep -q 'No errors have been found' .phpdoc/build/api/reports/errors.html || \
|
||||||
|
(echo "Documentation errors found. See .phpdoc/build/api/reports/errors.html" && exit 1)
|
||||||
|
|
||||||
|
docs-serve:
|
||||||
|
$(DOCS_RUN) zensical serve
|
||||||
|
|
|
||||||
301
README.md
301
README.md
|
|
@ -1,27 +1,31 @@
|
||||||
# MCP PHP SDK
|
# MCP PHP SDK
|
||||||
|
|
||||||
The official PHP SDK for Model Context Protocol (MCP). It provides a framework-agnostic API for implementing MCP servers
|
[](https://packagist.org/packages/mcp/sdk)
|
||||||
and clients in PHP.
|
[](https://github.com/modelcontextprotocol/php-sdk/actions/workflows/pipeline.yaml)
|
||||||
|
[](https://packagist.org/packages/mcp/sdk)
|
||||||
|
[](LICENSE)
|
||||||
|
|
||||||
> [!IMPORTANT]
|
The official PHP SDK for the Model Context Protocol (MCP). It provides a framework-agnostic API for implementing MCP
|
||||||
> This SDK is currently in active development with ongoing refinement of its architecture and features. While
|
servers and clients in PHP — tools, resources, prompts, STDIO and HTTP transports, sessions, authorization, and both
|
||||||
> functional, the API may experience changes as we work toward stabilization.
|
protocol eras (the `initialize` handshake and the stateless `2026-07-28` revision).
|
||||||
>
|
|
||||||
> If you want to help us stabilize the SDK, please see the [issue tracker](https://github.com/modelcontextprotocol/php-sdk/issues).
|
|
||||||
|
|
||||||
This project represents a collaboration between [the PHP Foundation](https://thephp.foundation/) and the [Symfony project](https://symfony.com/). It adopts
|
This project represents a collaboration between [the PHP Foundation](https://thephp.foundation/) and the [Symfony project](https://symfony.com/). It adopts
|
||||||
development practices and standards from the Symfony project, including [Coding Standards](https://symfony.com/doc/current/contributing/code/standards.html) and the
|
development practices and standards from the Symfony project, including [Coding Standards](https://symfony.com/doc/current/contributing/code/standards.html) and the
|
||||||
[Backward Compatibility Promise](https://symfony.com/doc/current/contributing/code/bc.html).
|
[Backward Compatibility Promise](https://symfony.com/doc/current/contributing/code/bc.html).
|
||||||
|
|
||||||
Until the first major release, this SDK is considered [experimental](https://symfony.com/doc/current/contributing/code/experimental.html).
|
Until the first major release, this SDK is considered [experimental](https://symfony.com/doc/current/contributing/code/experimental.html), please see the [roadmap](./ROADMAP.md) for
|
||||||
|
planned next steps and features.
|
||||||
|
|
||||||
## Roadmap
|
## Protocol Conformance
|
||||||
|
|
||||||
**Features**
|
Measured weekly against the [MCP Conformance Test Framework](https://github.com/modelcontextprotocol/conformance),
|
||||||
- [ ] Stabilize server component with all needed handlers and functional tests
|
as passing checks out of total. The `2026-07-28` scores run against the framework's `alpha` releases, so they move as
|
||||||
- [ ] Extend documentation, including integration guides for popular frameworks
|
upstream publishes new scenarios.
|
||||||
- [ ] Implement Client component
|
|
||||||
- [ ] Support multiple schema versions
|
| Revision | Server | Client |
|
||||||
|
|:--------:|:------:|:------:|
|
||||||
|
| `2026-07-28` | [](https://github.com/modelcontextprotocol/php-sdk/actions/workflows/conformance-weekly.yaml) | [](https://github.com/modelcontextprotocol/php-sdk/actions/workflows/conformance-weekly.yaml) |
|
||||||
|
| `2025-11-25` | [](https://github.com/modelcontextprotocol/php-sdk/actions/workflows/conformance-weekly.yaml) | [](https://github.com/modelcontextprotocol/php-sdk/actions/workflows/conformance-weekly.yaml) |
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|
@ -29,30 +33,20 @@ Until the first major release, this SDK is considered [experimental](https://sym
|
||||||
composer require mcp/sdk
|
composer require mcp/sdk
|
||||||
```
|
```
|
||||||
|
|
||||||
## Quick Start
|
## Build a server
|
||||||
|
|
||||||
This example demonstrates the most common usage pattern - a STDIO server using attribute discovery.
|
A server is a plain PHP class plus three lines of wiring:
|
||||||
|
|
||||||
### 1. Define Your MCP Elements
|
|
||||||
|
|
||||||
Create a class with MCP capabilities using attributes:
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App;
|
|
||||||
|
|
||||||
use Mcp\Capability\Attribute\McpTool;
|
|
||||||
use Mcp\Capability\Attribute\McpResource;
|
use Mcp\Capability\Attribute\McpResource;
|
||||||
|
use Mcp\Capability\Attribute\McpTool;
|
||||||
|
use Mcp\Server;
|
||||||
|
use Mcp\Server\Transport\StdioTransport;
|
||||||
|
|
||||||
class CalculatorElements
|
class Calculator
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Adds two numbers together.
|
* Adds two numbers.
|
||||||
*
|
|
||||||
* @param int $a The first number
|
|
||||||
* @param int $b The second number
|
|
||||||
* @return int The sum of the two numbers
|
|
||||||
*/
|
*/
|
||||||
#[McpTool]
|
#[McpTool]
|
||||||
public function add(int $a, int $b): int
|
public function add(int $a, int $b): int
|
||||||
|
|
@ -60,227 +54,88 @@ class CalculatorElements
|
||||||
return $a + $b;
|
return $a + $b;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
#[McpResource(uri: 'config://calculator/settings')]
|
||||||
* Performs basic arithmetic operations.
|
public function settings(): array
|
||||||
*/
|
|
||||||
#[McpTool(name: 'calculate')]
|
|
||||||
public function calculate(float $a, float $b, string $operation): float|string
|
|
||||||
{
|
{
|
||||||
return match($operation) {
|
return ['precision' => 2];
|
||||||
'add' => $a + $b,
|
|
||||||
'subtract' => $a - $b,
|
|
||||||
'multiply' => $a * $b,
|
|
||||||
'divide' => $b != 0 ? $a / $b : 'Error: Division by zero',
|
|
||||||
default => 'Error: Unknown operation'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[McpResource(
|
|
||||||
uri: 'config://calculator/settings',
|
|
||||||
name: 'calculator_config',
|
|
||||||
mimeType: 'application/json'
|
|
||||||
)]
|
|
||||||
public function getSettings(): array
|
|
||||||
{
|
|
||||||
return ['precision' => 2, 'allow_negative' => true];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exit(Server::builder()
|
||||||
|
->setServerInfo('Calculator', '1.0.0')
|
||||||
|
->setDiscovery(__DIR__, ['.'], excludeDirs: ['vendor'])
|
||||||
|
->build()
|
||||||
|
->run(new StdioTransport()));
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Create the Server Script
|
The walkthrough in [First server](docs/get-started/first-server.md) explains each piece, and
|
||||||
|
[Try it with the Inspector](docs/get-started/inspector.md) shows it running.
|
||||||
|
|
||||||
Create your MCP server:
|
## Build a client
|
||||||
|
|
||||||
```php
|
```php
|
||||||
#!/usr/bin/env php
|
use Mcp\Client;
|
||||||
<?php
|
use Mcp\Client\Transport\StdioTransport;
|
||||||
|
|
||||||
declare(strict_types=1);
|
$client = Client::builder()
|
||||||
|
->setClientInfo('My Application', '1.0.0')
|
||||||
require_once __DIR__ . '/vendor/autoload.php';
|
|
||||||
|
|
||||||
use Mcp\Server;
|
|
||||||
use Mcp\Server\Transport\StdioTransport;
|
|
||||||
|
|
||||||
$server = Server::builder()
|
|
||||||
->setServerInfo('Calculator Server', '1.0.0')
|
|
||||||
->setDiscovery(__DIR__, ['.'])
|
|
||||||
->build();
|
->build();
|
||||||
|
|
||||||
$transport = new StdioTransport();
|
$client->connect(new StdioTransport(command: 'php', args: ['/path/to/server.php']));
|
||||||
|
|
||||||
$server->run($transport);
|
$tools = $client->listTools();
|
||||||
|
$result = $client->callTool('add', ['a' => 5, 'b' => 3]);
|
||||||
|
|
||||||
|
$client->disconnect();
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Configure Your MCP Client
|
See [Connecting to a server](docs/client/connecting.md) for transports, timeouts, and the
|
||||||
|
handlers that answer server-initiated requests.
|
||||||
Add to your client configuration (e.g., Claude Desktop's `mcp.json`):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"mcpServers": {
|
|
||||||
"php-calculator": {
|
|
||||||
"command": "php",
|
|
||||||
"args": ["/absolute/path/to/your/server.php"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Test Your Server
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Test with MCP Inspector
|
|
||||||
npx @modelcontextprotocol/inspector php /path/to/server.php
|
|
||||||
|
|
||||||
# Your AI assistant can now call:
|
|
||||||
# - add: Add two integers
|
|
||||||
# - calculate: Perform arithmetic operations
|
|
||||||
# - Read config://calculator/settings resource
|
|
||||||
```
|
|
||||||
|
|
||||||
## Key Features
|
|
||||||
|
|
||||||
### Attribute-Based Discovery
|
|
||||||
|
|
||||||
Define MCP elements using PHP attributes with automatic discovery:
|
|
||||||
|
|
||||||
```php
|
|
||||||
// Tool with automatic name and description from method
|
|
||||||
#[McpTool]
|
|
||||||
public function generateReport(): string { /* ... */ }
|
|
||||||
|
|
||||||
// Tool with custom name
|
|
||||||
#[McpTool(name: 'custom_name')]
|
|
||||||
public function myMethod(): string { /* ... */ }
|
|
||||||
|
|
||||||
// Resource with URI and metadata
|
|
||||||
#[McpResource(uri: 'config://app/settings', mimeType: 'application/json')]
|
|
||||||
public function getConfig(): array { /* ... */ }
|
|
||||||
```
|
|
||||||
|
|
||||||
### Manual Registration
|
|
||||||
|
|
||||||
Register capabilities programmatically:
|
|
||||||
|
|
||||||
```php
|
|
||||||
$server = Server::builder()
|
|
||||||
->addTool([MyClass::class, 'myMethod'], 'tool_name')
|
|
||||||
->addResource([MyClass::class, 'getData'], 'data://config')
|
|
||||||
->build();
|
|
||||||
```
|
|
||||||
|
|
||||||
### Multiple Transport Options
|
|
||||||
|
|
||||||
**STDIO Transport** (Command-line integration):
|
|
||||||
```php
|
|
||||||
$transport = new StdioTransport();
|
|
||||||
$server->run($transport);
|
|
||||||
```
|
|
||||||
|
|
||||||
**HTTP Transport** (Web-based communication):
|
|
||||||
```php
|
|
||||||
$transport = new StreamableHttpTransport($request, $responseFactory, $streamFactory);
|
|
||||||
$response = $server->run($transport);
|
|
||||||
// Handle $response in your web application
|
|
||||||
```
|
|
||||||
|
|
||||||
### Session Management
|
|
||||||
|
|
||||||
By default, the SDK uses in-memory sessions. You can configure different session stores:
|
|
||||||
|
|
||||||
```php
|
|
||||||
use Mcp\Server\Session\FileSessionStore;
|
|
||||||
use Mcp\Server\Session\InMemorySessionStore;
|
|
||||||
use Mcp\Server\Session\Psr16StoreSession;
|
|
||||||
use Symfony\Component\Cache\Psr16Cache;
|
|
||||||
use Symfony\Component\Cache\Adapter\RedisAdapter;
|
|
||||||
|
|
||||||
// Use default in-memory sessions with custom TTL
|
|
||||||
$server = Server::builder()
|
|
||||||
->setSession(ttl: 7200) // 2 hours
|
|
||||||
->build();
|
|
||||||
|
|
||||||
// Override with file-based storage
|
|
||||||
$server = Server::builder()
|
|
||||||
->setSession(new FileSessionStore(__DIR__ . '/sessions'))
|
|
||||||
->build();
|
|
||||||
|
|
||||||
// Override with in-memory storage and custom TTL
|
|
||||||
$server = Server::builder()
|
|
||||||
->setSession(new InMemorySessionStore(3600))
|
|
||||||
->build();
|
|
||||||
|
|
||||||
// Override with PSR-16 cache-based storage
|
|
||||||
// Requires psr/simple-cache and symfony/cache (or any other PSR-16 implementation)
|
|
||||||
// composer require psr/simple-cache symfony/cache
|
|
||||||
$redisAdapter = new RedisAdapter(
|
|
||||||
RedisAdapter::createConnection('redis://localhost:6379'),
|
|
||||||
'mcp_sessions'
|
|
||||||
);
|
|
||||||
|
|
||||||
$server = Server::builder()
|
|
||||||
->setSession(new Psr16StoreSession(
|
|
||||||
cache: new Psr16Cache($redisAdapter),
|
|
||||||
prefix: 'mcp-',
|
|
||||||
ttl: 3600
|
|
||||||
))
|
|
||||||
->build();
|
|
||||||
```
|
|
||||||
|
|
||||||
### Discovery Caching
|
|
||||||
|
|
||||||
Use any PSR-16 cache implementation to cache discovery results and avoid running discovery on every server start:
|
|
||||||
|
|
||||||
```php
|
|
||||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
|
||||||
use Symfony\Component\Cache\Psr16Cache;
|
|
||||||
|
|
||||||
$cache = new Psr16Cache(new FilesystemAdapter('mcp-discovery'));
|
|
||||||
|
|
||||||
$server = Server::builder()
|
|
||||||
->setDiscovery(
|
|
||||||
basePath: __DIR__,
|
|
||||||
scanDirs: ['.', 'src'], // Default: ['.', 'src']
|
|
||||||
excludeDirs: ['vendor'], // Default: ['vendor', 'node_modules']
|
|
||||||
cache: $cache
|
|
||||||
)
|
|
||||||
->build();
|
|
||||||
```
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
**Core Concepts:**
|
The full documentation is published at **[php.sdk.modelcontextprotocol.io](https://php.sdk.modelcontextprotocol.io/)**.
|
||||||
- [Server Builder](docs/server-builder.md) - Complete ServerBuilder reference and configuration
|
|
||||||
- [Transports](docs/transports.md) - STDIO and HTTP transport setup and usage
|
|
||||||
- [MCP Elements](docs/mcp-elements.md) - Creating tools, resources, and prompts
|
|
||||||
- [Client Communication](docs/client-communication.md) - Communicating back to the client from server-side
|
|
||||||
|
|
||||||
**Learning:**
|
- **[Get started](docs/get-started/index.md)** — Install the SDK and build your first server
|
||||||
- [Examples](docs/examples.md) - Comprehensive example walkthroughs
|
- **[Servers](docs/servers/index.md)** — Tools, resources, resource templates, prompts, and how to register them
|
||||||
|
- **[Inside your handler](docs/handlers/index.md)** — Talking back to the client, logging, and asking for input
|
||||||
|
- **[Running your server](docs/run/index.md)** — Server builder, STDIO and HTTP transports, framework integration, sessions, authorization
|
||||||
|
- **[Clients](docs/client/index.md)** — Client SDK for connecting to and communicating with MCP servers
|
||||||
|
- **[Protocol versions](docs/protocol-versions.md)** — The two protocol eras, and what revision `2026-07-28` changed
|
||||||
|
- **[Advanced](docs/advanced/index.md)** — Events, protocol extensions (including MCP Apps), and custom message handlers
|
||||||
|
- **[Examples](docs/examples.md)** — Runnable server and client examples
|
||||||
|
- **[API Reference](https://php.sdk.modelcontextprotocol.io/api/)** — Generated class reference
|
||||||
|
|
||||||
**External Resources:**
|
## External Resources
|
||||||
- [Model Context Protocol documentation](https://modelcontextprotocol.io)
|
|
||||||
- [Model Context Protocol specification](https://spec.modelcontextprotocol.io)
|
- **[Model Context Protocol Documentation](https://modelcontextprotocol.io)** — Official MCP documentation
|
||||||
- [Officially supported servers](https://github.com/modelcontextprotocol/servers)
|
- **[Model Context Protocol Specification](https://spec.modelcontextprotocol.io)** — Protocol specification
|
||||||
|
- **[Officially Supported Servers](https://github.com/modelcontextprotocol/servers)** — Reference server implementations
|
||||||
|
|
||||||
## PHP Libraries Using the MCP SDK
|
## PHP Libraries Using the MCP SDK
|
||||||
|
|
||||||
* [pronskiy/mcp](https://github.com/pronskiy/mcp) - Additional DX layer
|
- [api-platform/mcp](https://github.com/api-platform/mcp) — MCP integration for API Platform
|
||||||
* [symfony/mcp-bundle](https://github.com/symfony/mcp-bundle) - Symfony integration bundle
|
- [bnomei/kirby-mcp](https://github.com/bnomei/kirby-mcp) — MCP server for the Kirby CMS
|
||||||
* [josbeir/cakephp-synapse](https://github.com/josbeir/cakephp-synapse) - CakePHP integration plugin
|
- [drupal/mcp_server](https://www.drupal.org/project/mcp_server) — MCP server for Drupal exposing configuration and entities as MCP elements
|
||||||
|
- [josbeir/cakephp-synapse](https://github.com/josbeir/cakephp-synapse) — CakePHP plugin exposing application functionality over MCP
|
||||||
|
- [nette/mcp-inspector](https://github.com/nette/mcp-inspector) — MCP server for introspecting Nette applications
|
||||||
|
- [symfony/ai-mate](https://github.com/symfony/ai-mate) — AI development assistant MCP server for Symfony projects
|
||||||
|
- [symfony/mcp-bundle](https://github.com/symfony/mcp-bundle) — Symfony integration bundle
|
||||||
|
|
||||||
|
Building something on top of the SDK? Open a pull request to add it to this list.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
We are passionate about supporting contributors of all levels of experience and would love to see you get involved in
|
We are passionate about supporting contributors of all levels of experience and would love to see you get involved in
|
||||||
the project. See the [contributing guide](CONTRIBUTING.md) to get started before you [report issues](https://github.com/modelcontextprotocol/php-sdk/issues) and [send pull requests](https://github.com/modelcontextprotocol/php-sdk/pulls).
|
the project. Start by [reporting issues](https://github.com/modelcontextprotocol/php-sdk/issues) or
|
||||||
|
[sending pull requests](https://github.com/modelcontextprotocol/php-sdk/pulls). See
|
||||||
|
[CONTRIBUTING.md](CONTRIBUTING.md) for development setup, coding standards, and what to run before
|
||||||
|
opening a PR.
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
The starting point for this SDK was the [PHP-MCP](https://github.com/php-mcp/server) project, initiated by
|
The starting point for this SDK was the [PHP-MCP](https://github.com/php-mcp/server) project, initiated by [Kyrian Obikwelu](https://github.com/CodeWithKyrian), and the [Symfony AI initiative](https://github.com/symfony/ai). We are grateful for the work done by both projects and their contributors, which created a solid foundation for this SDK.
|
||||||
[Kyrian Obikwelu](https://github.com/CodeWithKyrian), and the [Symfony AI initiative](https://github.com/symfony/ai). We are grateful for the work
|
|
||||||
done by both projects and their contributors, which created a solid foundation for this SDK.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
This project is licensed under the Apache License, Version 2.0 for new contributions, with existing code under the MIT License — see the [LICENSE](LICENSE) file for details.
|
||||||
|
|
|
||||||
21
ROADMAP.md
Normal file
21
ROADMAP.md
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Roadmap
|
||||||
|
|
||||||
|
This roadmap is a living document that outlines the planned features and improvements for our project.
|
||||||
|
|
||||||
|
## Goals for the First Major Release
|
||||||
|
|
||||||
|
- **Server**
|
||||||
|
- [x] Implement full support for elicitations
|
||||||
|
- [ ] Implement OAuth2 authentication for server
|
||||||
|
- **Client**
|
||||||
|
- [x] Implement client-side support
|
||||||
|
- [x] Implement client examples and documentation
|
||||||
|
- [ ] Implement OAuth2 authentication for client
|
||||||
|
- **Schema**
|
||||||
|
- [ ] Implement schema generation based on TS or JSON Schema
|
||||||
|
- [ ] Achieve full spec conformance
|
||||||
|
- **Extensions**
|
||||||
|
- [ ] Implement the Tasks extension
|
||||||
|
- **Architecture**
|
||||||
|
- [ ] Split the SDK into multiple packages
|
||||||
|
|
||||||
21
SECURITY.md
Normal file
21
SECURITY.md
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Security Policy
|
||||||
|
|
||||||
|
Thank you for helping keep the Model Context Protocol and its ecosystem secure.
|
||||||
|
|
||||||
|
## Reporting Security Issues
|
||||||
|
|
||||||
|
If you discover a security vulnerability in this repository, please report it through
|
||||||
|
the [GitHub Security Advisory process](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability)
|
||||||
|
for this repository.
|
||||||
|
|
||||||
|
Please **do not** report security vulnerabilities through public GitHub issues, discussions,
|
||||||
|
or pull requests.
|
||||||
|
|
||||||
|
## What to Include
|
||||||
|
|
||||||
|
To help us triage and respond quickly, please include:
|
||||||
|
|
||||||
|
- A description of the vulnerability
|
||||||
|
- Steps to reproduce the issue
|
||||||
|
- The potential impact
|
||||||
|
- Any suggested fixes (optional)
|
||||||
97
adr/0001-oauth-authorization-server-out-of-scope.md
Normal file
97
adr/0001-oauth-authorization-server-out-of-scope.md
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
# 0001 — The MCP server is an OAuth Resource Server, not an Authorization Server
|
||||||
|
|
||||||
|
- Status: Accepted
|
||||||
|
- Date: 2026-06-15
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
OAuth 2.1 defines three distinct roles:
|
||||||
|
|
||||||
|
| Role | What it does | Status in this SDK | Scope |
|
||||||
|
|------|--------------|--------------------|-------|
|
||||||
|
| Resource Server | Validates incoming bearer tokens, serves Protected Resource Metadata (RFC 9728), emits `WWW-Authenticate` | Shipped (`AuthorizationMiddleware`, `JwtTokenValidator`, `ProtectedResourceMetadata`) | **IN scope** |
|
||||||
|
| Delegation / proxy to an upstream AS | Forwards `/authorize` and `/token` to your existing IdP | Shipped (`OAuthProxyMiddleware`) | **IN scope — delegation ONLY** |
|
||||||
|
| Authorization Server / Identity Provider (IdP) | Mints its own tokens, registers clients, runs login and consent | Absent | **OUT of scope** |
|
||||||
|
|
||||||
|
The SDK repeatedly receives pull requests that move it toward becoming a full OAuth 2.1
|
||||||
|
**authorization server** — asking the MCP server to mint its own access and refresh tokens,
|
||||||
|
register clients, and run login and consent flows, that is, to become an Identity Provider.
|
||||||
|
The most explicit example is [#373](https://github.com/modelcontextprotocol/php-sdk/pull/373)
|
||||||
|
("[Server] Add native OAuth 2.1 authorization server", ~3,400 lines across 50+ files), but the
|
||||||
|
direction has crept in incrementally rather than in one PR.
|
||||||
|
|
||||||
|
A contributing factor is that the SDK already exposes the *authorization-server endpoints in
|
||||||
|
proxy form*, and that surface has grown:
|
||||||
|
[#221](https://github.com/modelcontextprotocol/php-sdk/pull/221) added the OAuth resource-server
|
||||||
|
middleware, and [#269](https://github.com/modelcontextprotocol/php-sdk/pull/269) added Dynamic
|
||||||
|
Client Registration (RFC 7591). Today `OAuthProxyMiddleware` answers `/authorize`, `/token`, and
|
||||||
|
`/.well-known/oauth-authorization-server`, and DCR endpoints exist. The shape of that surface
|
||||||
|
invites contributors to "finish the job" by backing those endpoints with a real token issuer.
|
||||||
|
It is not scaffolding to be completed — it is a delegating proxy, and that is the whole of its
|
||||||
|
intent.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
**The MCP server is an OAuth 2.1 Resource Server that MAY delegate to an upstream
|
||||||
|
authorization server. It will NOT issue tokens or act as an Identity Provider.**
|
||||||
|
|
||||||
|
Concretely:
|
||||||
|
|
||||||
|
- The SDK validates bearer tokens, serves RFC 9728 Protected Resource Metadata, and emits
|
||||||
|
`WWW-Authenticate` challenges. This is the Resource Server role and is fully supported.
|
||||||
|
- The SDK MAY delegate `/authorize` and `/token` to an upstream authorization server via
|
||||||
|
`OAuthProxyMiddleware`. This is delegation only: the middleware redirects the browser to
|
||||||
|
the upstream `/authorize` endpoint and proxies `/token` requests to the upstream token
|
||||||
|
endpoint. It never mints, signs, stores, or rotates tokens of its own.
|
||||||
|
- The SDK will NOT implement an authorization server: no token issuance, no token signing or
|
||||||
|
key management, no login UI, no consent UI, no authorization-code or refresh-token storage,
|
||||||
|
no first-party Dynamic Client Registration acting as an issuer.
|
||||||
|
|
||||||
|
Pull requests that add authorization-server / IdP behavior are declined by reference to this
|
||||||
|
ADR.
|
||||||
|
|
||||||
|
## Rationale
|
||||||
|
|
||||||
|
- **Security liability.** Issuing tokens means owning signing-key generation, storage, and
|
||||||
|
rotation; authorization-code and refresh-token persistence; refresh-token rotation and
|
||||||
|
replay detection; and consent. A defect in any of these is a credential-issuance
|
||||||
|
vulnerability affecting every consumer of the SDK. This is precisely the surface an MCP SDK
|
||||||
|
should not own.
|
||||||
|
- **RFC footprint.** A correct authorization server must implement and keep current with
|
||||||
|
RFC 6749 (OAuth 2.0), RFC 7591 (Dynamic Client Registration), RFC 8414 (Authorization
|
||||||
|
Server Metadata), PKCE (RFC 7636), and refresh-token rotation guidance, among others. That
|
||||||
|
is an open-ended maintenance and conformance burden far outside the SDK's purpose.
|
||||||
|
- **Mature implementations already exist.** Token issuance is a solved problem.
|
||||||
|
`league/oauth2-server` provides it as a PHP library, and every production IdP — Keycloak,
|
||||||
|
Auth0, Microsoft Entra ID, Okta — provides it as a service. Re-implementing it inside an
|
||||||
|
MCP SDK adds risk without adding value.
|
||||||
|
|
||||||
|
## Boundary statement
|
||||||
|
|
||||||
|
`OAuthProxyMiddleware` **delegates** to an upstream IdP. It is **not** authorization-server
|
||||||
|
scaffolding to be completed. Backing its `/authorize` and `/token` endpoints with a
|
||||||
|
first-party token issuer is out of scope and will be declined.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- The supported authorization architecture is: an external authorization server (your IdP or
|
||||||
|
`league/oauth2-server` running in your own application) issues tokens; the MCP server
|
||||||
|
validates them as a Resource Server and optionally proxies the OAuth flow to that upstream.
|
||||||
|
- Contributors get a single, citable ruling for why authorization-server PRs are declined,
|
||||||
|
reducing repeated large-PR churn.
|
||||||
|
- Resource Server and proxy/delegation features remain welcome and supported.
|
||||||
|
|
||||||
|
## Alternatives / what to do instead
|
||||||
|
|
||||||
|
If you need an authorization server (token issuance, client registration, login, consent), do
|
||||||
|
**not** add it to this SDK. Instead:
|
||||||
|
|
||||||
|
- **Front the MCP server with an existing IdP** — Keycloak, Auth0, Microsoft Entra ID, or
|
||||||
|
Okta. Point `JwtTokenValidator` and `ProtectedResourceMetadata` at that issuer, and
|
||||||
|
optionally use `OAuthProxyMiddleware` to delegate `/authorize` and `/token` to it.
|
||||||
|
- **Run `league/oauth2-server` in your own application**, behind the SDK's existing proxy and
|
||||||
|
validator seams. The MCP server validates the tokens it issues; it does not issue them
|
||||||
|
itself.
|
||||||
|
|
||||||
|
See [`../docs/run/authorization.md`](../docs/run/authorization.md) for the supported Resource Server
|
||||||
|
and delegation setup.
|
||||||
10
adr/README.md
Normal file
10
adr/README.md
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Architecture Decision Records
|
||||||
|
|
||||||
|
This directory holds Architecture Decision Records (ADRs): short documents that capture a
|
||||||
|
significant architectural decision, the context that led to it, and its consequences. They
|
||||||
|
exist so a decision can be cited once and referenced thereafter — for example, when declining
|
||||||
|
a pull request that conflicts with an accepted decision.
|
||||||
|
|
||||||
|
## Records
|
||||||
|
|
||||||
|
- [0001 — The MCP server is an OAuth Resource Server, not an Authorization Server](0001-oauth-authorization-server-out-of-scope.md)
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"name": "mcp/sdk",
|
"name": "mcp/sdk",
|
||||||
"type": "library",
|
|
||||||
"description": "Model Context Protocol SDK for Client and Server applications in PHP",
|
"description": "Model Context Protocol SDK for Client and Server applications in PHP",
|
||||||
"license": "MIT",
|
"license": "Apache-2.0",
|
||||||
|
"type": "library",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Christopher Hertel",
|
"name": "Christopher Hertel",
|
||||||
|
|
@ -22,27 +22,40 @@
|
||||||
"ext-fileinfo": "*",
|
"ext-fileinfo": "*",
|
||||||
"opis/json-schema": "^2.4",
|
"opis/json-schema": "^2.4",
|
||||||
"php-http/discovery": "^1.20",
|
"php-http/discovery": "^1.20",
|
||||||
"phpdocumentor/reflection-docblock": "^5.6",
|
"phpdocumentor/reflection-docblock": "^5.6 || ^6.0",
|
||||||
"psr/clock": "^1.0",
|
"psr/clock": "^1.0",
|
||||||
"psr/container": "^2.0",
|
"psr/container": "^1.0 || ^2.0",
|
||||||
"psr/event-dispatcher": "^1.0",
|
"psr/event-dispatcher": "^1.0",
|
||||||
|
"psr/http-client": "^1.0",
|
||||||
"psr/http-factory": "^1.1",
|
"psr/http-factory": "^1.1",
|
||||||
"psr/http-message": "^2.0",
|
"psr/http-message": "^1.1 || ^2.0",
|
||||||
|
"psr/http-server-handler": "^1.0",
|
||||||
|
"psr/http-server-middleware": "^1.0",
|
||||||
"psr/log": "^1.0 || ^2.0 || ^3.0",
|
"psr/log": "^1.0 || ^2.0 || ^3.0",
|
||||||
"symfony/finder": "^5.4 || ^6.4 || ^7.3 || ^8.0",
|
"symfony/deprecation-contracts": "^2.5 || ^3.0",
|
||||||
"symfony/uid": "^5.4 || ^6.4 || ^7.3 || ^8.0"
|
"symfony/uid": "^5.4 || ^6.4 || ^7.3 || ^8.0"
|
||||||
},
|
},
|
||||||
|
"suggest": {
|
||||||
|
"symfony/finder": "Required for file-based discovery."
|
||||||
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
"ext-openssl": "*",
|
||||||
|
"composer/semver": "^3.0",
|
||||||
|
"firebase/php-jwt": "^6.10 || ^7.0",
|
||||||
|
"laminas/laminas-httphandlerrunner": "^2.12",
|
||||||
|
"nyholm/psr7": "^1.8",
|
||||||
|
"nyholm/psr7-server": "^1.1",
|
||||||
|
"phar-io/composer-distributor": "^1.0.2",
|
||||||
"php-cs-fixer/shim": "^3.91",
|
"php-cs-fixer/shim": "^3.91",
|
||||||
|
"phpdocumentor/shim": "^3",
|
||||||
"phpstan/phpstan": "^2.1",
|
"phpstan/phpstan": "^2.1",
|
||||||
"phpunit/phpunit": "^10.5",
|
"phpunit/phpunit": "^10.5",
|
||||||
"psr/simple-cache": "^2.0 || ^3.0",
|
"psr/simple-cache": "^2.0 || ^3.0",
|
||||||
"symfony/cache": "^5.4 || ^6.4 || ^7.3 || ^8.0",
|
"symfony/cache": "^5.4 || ^6.4 || ^7.3 || ^8.0",
|
||||||
"symfony/console": "^5.4 || ^6.4 || ^7.3 || ^8.0",
|
"symfony/console": "^5.4 || ^6.4 || ^7.3 || ^8.0",
|
||||||
"symfony/process": "^5.4 || ^6.4 || ^7.3 || ^8.0",
|
"symfony/finder": "^5.4 || ^6.4 || ^7.3 || ^8.0",
|
||||||
"nyholm/psr7": "^1.8",
|
"symfony/http-client": "^5.4 || ^6.4 || ^7.3 || ^8.0",
|
||||||
"nyholm/psr7-server": "^1.1",
|
"symfony/process": "^5.4 || ^6.4 || ^7.3 || ^8.0"
|
||||||
"laminas/laminas-httphandlerrunner": "^2.12"
|
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
|
|
@ -53,23 +66,32 @@
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Mcp\\Example\\Server\\CachedDiscovery\\": "examples/server/cached-discovery/",
|
"Mcp\\Example\\Server\\CachedDiscovery\\": "examples/server/cached-discovery/",
|
||||||
"Mcp\\Example\\Server\\ClientCommunication\\": "examples/server/client-communication/",
|
"Mcp\\Example\\Server\\ClientCommunication\\": "examples/server/client-communication/",
|
||||||
|
"Mcp\\Example\\Server\\ClientLogging\\": "examples/server/client-logging/",
|
||||||
"Mcp\\Example\\Server\\CombinedRegistration\\": "examples/server/combined-registration/",
|
"Mcp\\Example\\Server\\CombinedRegistration\\": "examples/server/combined-registration/",
|
||||||
|
"Mcp\\Example\\Server\\Elicitation\\": "examples/server/elicitation/",
|
||||||
"Mcp\\Example\\Server\\ComplexToolSchema\\": "examples/server/complex-tool-schema/",
|
"Mcp\\Example\\Server\\ComplexToolSchema\\": "examples/server/complex-tool-schema/",
|
||||||
|
"Mcp\\Example\\Server\\Conformance\\": "examples/server/conformance/",
|
||||||
"Mcp\\Example\\Server\\CustomDependencies\\": "examples/server/custom-dependencies/",
|
"Mcp\\Example\\Server\\CustomDependencies\\": "examples/server/custom-dependencies/",
|
||||||
"Mcp\\Example\\Server\\CustomMethodHandlers\\": "examples/server/custom-method-handlers/",
|
"Mcp\\Example\\Server\\CustomMethodHandlers\\": "examples/server/custom-method-handlers/",
|
||||||
"Mcp\\Example\\Server\\DiscoveryCalculator\\": "examples/server/discovery-calculator/",
|
"Mcp\\Example\\Server\\DiscoveryCalculator\\": "examples/server/discovery-calculator/",
|
||||||
"Mcp\\Example\\Server\\DiscoveryUserProfile\\": "examples/server/discovery-userprofile/",
|
"Mcp\\Example\\Server\\DiscoveryUserProfile\\": "examples/server/discovery-userprofile/",
|
||||||
"Mcp\\Example\\Server\\EnvVariables\\": "examples/server/env-variables/",
|
"Mcp\\Example\\Server\\EnvVariables\\": "examples/server/env-variables/",
|
||||||
"Mcp\\Example\\Server\\ExplicitRegistration\\": "examples/server/explicit-registration/",
|
"Mcp\\Example\\Server\\ExplicitRegistration\\": "examples/server/explicit-registration/",
|
||||||
|
"Mcp\\Example\\Server\\McpApps\\": "examples/server/mcp-apps/",
|
||||||
|
"Mcp\\Example\\Server\\OAuthKeycloak\\": "examples/server/oauth-keycloak/",
|
||||||
|
"Mcp\\Example\\Server\\OAuthMicrosoft\\": "examples/server/oauth-microsoft/",
|
||||||
"Mcp\\Example\\Server\\SchemaShowcase\\": "examples/server/schema-showcase/",
|
"Mcp\\Example\\Server\\SchemaShowcase\\": "examples/server/schema-showcase/",
|
||||||
"Mcp\\Example\\Server\\ClientLogging\\": "examples/server/client-logging/",
|
|
||||||
"Mcp\\Tests\\": "tests/"
|
"Mcp\\Tests\\": "tests/"
|
||||||
}
|
},
|
||||||
|
"classmap": [
|
||||||
|
"tests/Unit/Capability/Discovery/Fixtures/AlternativeFileNameToolHandler.class.inc"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"sort-packages": true,
|
|
||||||
"allow-plugins": {
|
"allow-plugins": {
|
||||||
"php-http/discovery": false
|
"php-http/discovery": false,
|
||||||
}
|
"phpdocumentor/shim": true
|
||||||
|
},
|
||||||
|
"sort-packages": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue