Skip to content

Commit ad5cb80

Browse files
chr-hertelclaude
andcommitted
Streamline README header and add conformance score badges
- Add a shields.io badge row to the README header (version, CI, PHP version, license, conformance scores, protocol/spec) to match the other official MCP SDKs. - Expand "PHP Libraries Using the MCP SDK" to a curated, alphabetically sorted list of downstream projects. - Add tests/Conformance/bin/score.php: turns a conformance run's --output-dir into a shields.io endpoint badge JSON. - Add a reusable conformance workflow (conformance.yaml) and route pipeline.yaml, conformance-weekly.yaml and the new conformance-badges workflow through it, removing the duplicated server/client setup. - conformance-badges.yaml publishes client/server pass-rate JSON to an orphan `badges` branch consumed by the README endpoint badges. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent a820497 commit ad5cb80

6 files changed

Lines changed: 301 additions & 152 deletions

File tree

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: conformance-badges
2+
3+
# Publishes the client/server conformance pass-rate as shields.io endpoint
4+
# badges (consumed by the README). The shared conformance workflow runs the
5+
# suite WITHOUT the baseline filter so the badge reflects the true pass-rate;
6+
# the score JSON is pushed to the orphan `badges` branch (created on first run).
7+
on:
8+
push:
9+
branches: [main]
10+
schedule:
11+
- cron: '0 7 * * 1' # Mondays 07:00 UTC
12+
workflow_dispatch:
13+
14+
permissions:
15+
contents: write
16+
17+
jobs:
18+
conformance:
19+
uses: ./.github/workflows/conformance.yaml
20+
with:
21+
score: true
22+
23+
publish:
24+
name: publish badges
25+
runs-on: ubuntu-latest
26+
needs: [conformance]
27+
# Skip on forks: pushing the `badges` branch needs write access to this repo.
28+
if: github.repository == 'modelcontextprotocol/php-sdk'
29+
steps:
30+
- uses: actions/checkout@v6
31+
- uses: actions/download-artifact@v4
32+
with:
33+
name: server-badge
34+
path: badges-in
35+
- uses: actions/download-artifact@v4
36+
with:
37+
name: client-badge
38+
path: badges-in
39+
- name: Publish to badges branch
40+
run: |
41+
git config user.name "github-actions[bot]"
42+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
43+
44+
if git ls-remote --exit-code --heads origin badges >/dev/null 2>&1; then
45+
git fetch origin badges
46+
git worktree add badges-wt badges
47+
else
48+
git worktree add --detach badges-wt
49+
git -C badges-wt checkout --orphan badges
50+
git -C badges-wt rm -rf --quiet . >/dev/null 2>&1 || true
51+
fi
52+
53+
cp badges-in/server-conformance.json badges-in/client-conformance.json badges-wt/
54+
55+
cd badges-wt
56+
git add -A
57+
if git diff --cached --quiet; then
58+
echo "Conformance scores unchanged."
59+
else
60+
git commit -m "Update conformance score badges"
61+
git push origin badges
62+
fi

.github/workflows/conformance-weekly.yaml

Lines changed: 8 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
name: conformance-weekly
22

33
# Runs the MCP conformance suite weekly against the latest
4-
# @modelcontextprotocol/conformance release. The on:pull_request pipeline
5-
# pins to whatever version is available at PR time; this schedule catches
6-
# upstream releases that add scenarios between PRs.
4+
# @modelcontextprotocol/conformance release. Catches upstream releases that add
5+
# scenarios between PRs, and opens a tracking issue when the baseline no longer
6+
# holds.
77
on:
88
schedule:
99
- cron: '0 6 * * 1' # Mondays 06:00 UTC
@@ -14,66 +14,15 @@ permissions:
1414
issues: write
1515

1616
jobs:
17-
server:
18-
name: conformance / server (latest)
19-
runs-on: ubuntu-latest
20-
steps:
21-
- uses: actions/checkout@v6
22-
- uses: actions/setup-node@v6
23-
with:
24-
node-version: '22'
25-
- run: composer install --prefer-dist --no-progress --no-interaction
26-
- name: Start conformance server
27-
run: |
28-
mkdir -p tests/Conformance/sessions tests/Conformance/logs
29-
chmod -R 777 tests/Conformance/sessions tests/Conformance/logs
30-
docker compose -f tests/Conformance/Fixtures/docker-compose.yml up -d
31-
sleep 5
32-
- name: Run conformance tests
33-
working-directory: ./tests/Conformance
34-
run: npx --yes @modelcontextprotocol/conformance@latest server --url http://localhost:8000/ --expected-failures conformance-baseline.yml
35-
- name: Show docker logs on failure
36-
if: failure()
37-
run: docker compose -f tests/Conformance/Fixtures/docker-compose.yml logs
38-
- name: Upload conformance results
39-
if: failure()
40-
uses: actions/upload-artifact@v4
41-
with:
42-
name: conformance-server-results
43-
path: |
44-
tests/Conformance/logs
45-
tests/Conformance/results
46-
47-
client:
48-
name: conformance / client (latest)
49-
runs-on: ubuntu-latest
50-
steps:
51-
- uses: actions/checkout@v6
52-
- uses: shivammathur/setup-php@v2
53-
with:
54-
php-version: '8.4'
55-
coverage: none
56-
- uses: actions/setup-node@v6
57-
with:
58-
node-version: '22'
59-
- run: composer install --prefer-dist --no-progress --no-interaction
60-
- run: mkdir -p tests/Conformance/logs
61-
- name: Run conformance tests
62-
working-directory: ./tests/Conformance
63-
run: npx --yes @modelcontextprotocol/conformance@latest client --command "php ${{ github.workspace }}/tests/Conformance/client.php" --suite all --expected-failures conformance-baseline.yml
64-
- name: Upload conformance results
65-
if: failure()
66-
uses: actions/upload-artifact@v4
67-
with:
68-
name: conformance-client-results
69-
path: |
70-
tests/Conformance/logs
71-
tests/Conformance/results
17+
conformance:
18+
uses: ./.github/workflows/conformance.yaml
19+
with:
20+
expected-failures: true
7221

7322
notify:
7423
name: Open issue on failure
7524
runs-on: ubuntu-latest
76-
needs: [server, client]
25+
needs: [conformance]
7726
if: failure() && github.event_name == 'schedule'
7827
env:
7928
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/conformance.yaml

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
name: conformance (reusable)
2+
3+
# Reusable conformance run shared by pipeline.yaml, conformance-weekly.yaml and
4+
# conformance-badges.yaml. Callers pick a mode:
5+
# expected-failures: true -> gate the run against tests/Conformance/conformance-baseline.yml
6+
# score: true -> run unfiltered and emit shields.io badge JSON as artifacts
7+
on:
8+
workflow_call:
9+
inputs:
10+
expected-failures:
11+
description: 'Gate the run against tests/Conformance/conformance-baseline.yml'
12+
type: boolean
13+
default: false
14+
score:
15+
description: 'Run unfiltered and upload conformance score badge JSON (server-badge / client-badge)'
16+
type: boolean
17+
default: false
18+
19+
permissions:
20+
contents: read
21+
22+
jobs:
23+
server:
24+
name: server
25+
runs-on: ubuntu-latest
26+
steps:
27+
- uses: actions/checkout@v6
28+
- uses: actions/setup-node@v6
29+
with:
30+
node-version: '22'
31+
- uses: "ramsey/composer-install@v4"
32+
- name: Start conformance server
33+
run: |
34+
mkdir -p tests/Conformance/sessions tests/Conformance/logs
35+
chmod -R 777 tests/Conformance/sessions tests/Conformance/logs
36+
docker compose -f tests/Conformance/Fixtures/docker-compose.yml up -d
37+
sleep 5
38+
- name: Run conformance tests
39+
working-directory: ./tests/Conformance
40+
run: |
41+
if [ "${{ inputs.score }}" = "true" ]; then
42+
npx --yes @modelcontextprotocol/conformance@latest server --url http://localhost:8000/ --output-dir results || true
43+
php bin/score.php results "server conformance" "$GITHUB_WORKSPACE/server-conformance.json"
44+
elif [ "${{ inputs.expected-failures }}" = "true" ]; then
45+
npx --yes @modelcontextprotocol/conformance@latest server --url http://localhost:8000/ --expected-failures conformance-baseline.yml
46+
else
47+
npx --yes @modelcontextprotocol/conformance@latest server --url http://localhost:8000/
48+
fi
49+
- name: Show logs on failure
50+
if: failure()
51+
run: |
52+
echo "=== Docker Compose Logs ==="
53+
docker compose -f tests/Conformance/Fixtures/docker-compose.yml logs
54+
echo "=== Conformance Log ==="
55+
cat tests/Conformance/logs/conformance.log 2>/dev/null || echo "No conformance log found"
56+
- name: Upload conformance results
57+
if: failure()
58+
uses: actions/upload-artifact@v4
59+
with:
60+
name: conformance-server-results
61+
path: |
62+
tests/Conformance/logs
63+
tests/Conformance/results
64+
if-no-files-found: ignore
65+
- name: Upload score badge
66+
if: inputs.score
67+
uses: actions/upload-artifact@v4
68+
with:
69+
name: server-badge
70+
path: server-conformance.json
71+
- name: Cleanup
72+
if: always()
73+
run: docker compose -f tests/Conformance/Fixtures/docker-compose.yml down
74+
75+
client:
76+
name: client
77+
runs-on: ubuntu-latest
78+
steps:
79+
- uses: actions/checkout@v6
80+
- uses: shivammathur/setup-php@v2
81+
with:
82+
php-version: '8.4'
83+
coverage: none
84+
- uses: actions/setup-node@v6
85+
with:
86+
node-version: '22'
87+
- uses: "ramsey/composer-install@v4"
88+
- name: Run conformance tests
89+
working-directory: ./tests/Conformance
90+
run: |
91+
mkdir -p logs
92+
CMD="php ${{ github.workspace }}/tests/Conformance/client.php"
93+
if [ "${{ inputs.score }}" = "true" ]; then
94+
npx --yes @modelcontextprotocol/conformance@latest client --command "$CMD" --suite all --output-dir results || true
95+
php bin/score.php results "client conformance" "$GITHUB_WORKSPACE/client-conformance.json"
96+
elif [ "${{ inputs.expected-failures }}" = "true" ]; then
97+
npx --yes @modelcontextprotocol/conformance@latest client --command "$CMD" --suite all --expected-failures conformance-baseline.yml
98+
else
99+
npx --yes @modelcontextprotocol/conformance@latest client --command "$CMD" --suite all
100+
fi
101+
- name: Show logs on failure
102+
if: failure()
103+
run: cat tests/Conformance/logs/client-conformance.log 2>/dev/null || echo "No client conformance log found"
104+
- name: Upload conformance results
105+
if: failure()
106+
uses: actions/upload-artifact@v4
107+
with:
108+
name: conformance-client-results
109+
path: |
110+
tests/Conformance/logs
111+
tests/Conformance/results
112+
if-no-files-found: ignore
113+
- name: Upload score badge
114+
if: inputs.score
115+
uses: actions/upload-artifact@v4
116+
with:
117+
name: client-badge
118+
path: client-conformance.json

.github/workflows/pipeline.yaml

Lines changed: 4 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -72,97 +72,10 @@ jobs:
7272
- name: Tests
7373
run: vendor/bin/phpunit --testsuite=inspector
7474

75-
conformance-server:
76-
name: conformance / server
77-
runs-on: ubuntu-latest
78-
steps:
79-
- name: Checkout
80-
uses: actions/checkout@v6
81-
82-
- name: Setup Node
83-
uses: actions/setup-node@v6
84-
with:
85-
node-version: '22'
86-
87-
- name: Install Composer
88-
uses: "ramsey/composer-install@v4"
89-
90-
- name: Start conformance server
91-
run: |
92-
mkdir -p tests/Conformance/sessions tests/Conformance/logs
93-
chmod -R 777 tests/Conformance/sessions tests/Conformance/logs
94-
docker compose -f tests/Conformance/Fixtures/docker-compose.yml up -d
95-
sleep 5
96-
97-
- name: Run conformance tests
98-
working-directory: ./tests/Conformance
99-
run: npx @modelcontextprotocol/conformance server --url http://localhost:8000/ --expected-failures conformance-baseline.yml
100-
101-
- name: Show logs on failure
102-
if: failure()
103-
run: |
104-
echo "=== Docker Compose Logs ==="
105-
docker compose -f tests/Conformance/Fixtures/docker-compose.yml logs
106-
echo ""
107-
echo "=== Conformance Log ==="
108-
cat tests/Conformance/logs/conformance.log 2>/dev/null || echo "No conformance log found"
109-
echo ""
110-
echo "=== Test Results (first failed test) ==="
111-
find tests/Conformance/results -name "checks.json" 2>/dev/null | head -3 | while read f; do
112-
echo "--- $f ---"
113-
cat "$f"
114-
echo ""
115-
done || echo "No results found"
116-
echo ""
117-
echo "=== Directory permissions ==="
118-
ls -la tests/Conformance/
119-
ls -la tests/Conformance/logs/ 2>/dev/null || echo "logs dir issue"
120-
ls -la tests/Conformance/sessions/ 2>/dev/null || echo "sessions dir issue"
121-
122-
- name: Cleanup
123-
if: always()
124-
run: docker compose -f tests/Conformance/Fixtures/docker-compose.yml down
125-
126-
conformance-client:
127-
name: conformance / client
128-
runs-on: ubuntu-latest
129-
steps:
130-
- name: Checkout
131-
uses: actions/checkout@v6
132-
133-
- name: Setup PHP
134-
uses: shivammathur/setup-php@v2
135-
with:
136-
php-version: '8.4'
137-
coverage: "none"
138-
139-
- name: Setup Node
140-
uses: actions/setup-node@v6
141-
with:
142-
node-version: '22'
143-
144-
- name: Install Composer
145-
uses: "ramsey/composer-install@v4"
146-
147-
- name: Create log directory
148-
run: mkdir -p tests/Conformance/logs
149-
150-
- name: Run client conformance tests
151-
working-directory: ./tests/Conformance
152-
run: npx @modelcontextprotocol/conformance client --command "php ${{ github.workspace }}/tests/Conformance/client.php" --suite all --expected-failures conformance-baseline.yml
153-
154-
- name: Show logs on failure
155-
if: failure()
156-
run: |
157-
echo "=== Client Conformance Log ==="
158-
cat tests/Conformance/logs/client-conformance.log 2>/dev/null || echo "No client conformance log found"
159-
echo ""
160-
echo "=== Test Results ==="
161-
find tests/Conformance/results -name "checks.json" 2>/dev/null | head -3 | while read f; do
162-
echo "--- $f ---"
163-
cat "$f"
164-
echo ""
165-
done || echo "No results found"
75+
conformance:
76+
uses: ./.github/workflows/conformance.yaml
77+
with:
78+
expected-failures: true
16679

16780
qa:
16881
runs-on: ubuntu-latest

0 commit comments

Comments
 (0)