Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 48 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,43 @@ jobs:
if: github.event_name != 'pull_request'
run: vendor/bin/infection --threads=max --logger-github

mcp-sdk-0-9:
name: mcp/sdk 0.9.0 (PHP 8.5)
discover:
name: Discover the MCP library releases (Packagist)
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
releases: ${{ steps.releases.outputs.releases }}
mcp-sdk: ${{ steps.releases.outputs.mcp-sdk }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962 # 2.40.0
with:
php-version: '8.5'
coverage: none
- uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0
with:
dependency-versions: highest
# The newest patch of each minor release from the require-dev floor, so a new release joins the matrix by itself.
- name: Read the releases from Packagist
id: releases
run: |
releases="$(php -d display_errors=stderr tests/bin/released-versions.php)"
jq -e 'type == "array" and length > 0' <<<"$releases" >/dev/null
echo "releases=${releases}" >>"$GITHUB_OUTPUT"
echo "mcp-sdk=$(jq -c 'map(select(.library == "mcp/sdk"))' <<<"$releases")" >>"$GITHUB_OUTPUT"
jq -r '.[] | "- \(.library) \(.version) (lowest PHP \(.php))"' <<<"$releases" >>"$GITHUB_STEP_SUMMARY"
released:
name: ${{ matrix.library }} ${{ matrix.version }} (PHP 8.5)
needs: discover
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
include: ${{ fromJSON(needs.discover.outputs.releases) }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand All @@ -174,11 +207,12 @@ jobs:
- uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0
with:
dependency-versions: highest
- name: Remove symfony/mcp-bundle, which requires mcp/sdk ^0.8.1
run: composer remove --dev --no-update symfony/mcp-bundle
- name: Install the mcp/sdk 0.9.0 tag
run: composer require --dev --no-interaction --update-with-all-dependencies "mcp/sdk:0.9.0"
- run: vendor/bin/phpstan analyse --no-progress --error-format=github --configuration=phpstan.no-bundle.neon
- name: Install ${{ matrix.library }} ${{ matrix.version }}
env:
LIBRARY: ${{ matrix.library }}
VERSION: ${{ matrix.version }}
run: tests/bin/require-library.sh "$LIBRARY" "$VERSION"
- run: vendor/bin/phpstan analyse --no-progress --error-format=github --memory-limit=1G --configuration=${{ matrix.library == 'mcp/sdk' && 'phpstan.no-bundle.neon' || 'phpstan.neon.dist' }}
- run: vendor/bin/phpunit --testsuite Contract,Integration,E2E

runtime:
Expand Down Expand Up @@ -206,25 +240,27 @@ jobs:
AGENTCAT_RUNTIME_TESTS: '1'

package:
name: Package probe (PHP 8.3, mcp/sdk ${{ matrix.mcp-sdk }})
name: Package probe (PHP ${{ matrix.php }}, mcp/sdk ${{ matrix.version }})
needs: discover
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
mcp-sdk: ['0.8.1', '0.9.0']
# Each mcp/sdk release, on the lowest PHP that CI tests and the release allows (8.3 today).
include: ${{ fromJSON(needs.discover.outputs.mcp-sdk) }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962 # 2.40.0
with:
php-version: '8.3'
php-version: ${{ matrix.php }}
coverage: none
- name: Install the dist in a fresh project and track an mcp/sdk server
run: tests/bin/package-probe.sh "$MCP_SDK"
env:
MCP_SDK: ${{ matrix.mcp-sdk }}
MCP_SDK: ${{ matrix.version }}

bc:
name: Backward compatibility
Expand All @@ -250,7 +286,7 @@ jobs:
all-green:
name: All green
if: always()
needs: [static, static-lowest, tests, coverage, mutation, mcp-sdk-0-9, runtime, package, bc]
needs: [static, static-lowest, tests, coverage, mutation, discover, released, runtime, package, bc]
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
Expand Down
137 changes: 137 additions & 0 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,143 @@ jobs:
if [ -s "$OUTPUT" ]; then { printf '~~~text\n'; cat "$OUTPUT"; printf '~~~\n'; } >"$REPORT"; fi
.github/nightly-issue.sh "${{ github.job }}" "$REPORT"

discover:
name: Discover the MCP library releases (Packagist)
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
releases: ${{ steps.releases.outputs.releases }}
mcp-sdk: ${{ steps.releases.outputs.mcp-sdk }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962 # 2.40.0
with:
php-version: '8.5'
coverage: none
- uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0
with:
dependency-versions: highest
# The same releases as the released jobs of ci.yml: a release published since the last pull request runs here.
- name: Read the releases from Packagist
id: releases
timeout-minutes: 2
env:
ERRORS: ${{ runner.temp }}/discover.txt
run: |
releases="$(php -d display_errors=stderr tests/bin/released-versions.php 2> >(tee "$ERRORS" >&2))"
jq -e 'type == "array" and length > 0' <<<"$releases" >/dev/null
echo "releases=${releases}" >>"$GITHUB_OUTPUT"
echo "mcp-sdk=$(jq -c 'map(select(.library == "mcp/sdk"))' <<<"$releases")" >>"$GITHUB_OUTPUT"
jq -r '.[] | "- \(.library) \(.version) (lowest PHP \(.php))"' <<<"$releases" >>"$GITHUB_STEP_SUMMARY"
- name: Fail on purpose (workflow_dispatch force_failure)
if: github.event_name == 'workflow_dispatch' && inputs.force_failure == github.job
run: exit 1
- name: Open or update the nightly issue
if: failure() && github.event_name != 'pull_request'
env:
GH_TOKEN: ${{ github.token }}
ERRORS: ${{ runner.temp }}/discover.txt
REPORT: ${{ runner.temp }}/discover.md
run: |
# The message of released-versions.php, when it wrote one: the issue says why the releases could not be read.
if [ -s "$ERRORS" ]; then { printf '~~~text\n'; cat "$ERRORS"; printf '~~~\n'; } >"$REPORT"; fi
.github/nightly-issue.sh "${{ github.job }}" "$REPORT"

released:
name: ${{ matrix.library }} ${{ matrix.version }} (PHP 8.5)
needs: discover
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include: ${{ fromJSON(needs.discover.outputs.releases) }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962 # 2.40.0
with:
php-version: '8.5'
coverage: none
- uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0
with:
dependency-versions: highest
# Each step adds its output to $LOG, so the issue shows why the install, PHPStan or the tests failed.
- name: Install ${{ matrix.library }} ${{ matrix.version }}
timeout-minutes: 8
env:
LIBRARY: ${{ matrix.library }}
VERSION: ${{ matrix.version }}
LOG: ${{ runner.temp }}/released.txt
run: |
set -o pipefail
tests/bin/require-library.sh "$LIBRARY" "$VERSION" 2>&1 | tee -a "$LOG"
- name: PHPStan
timeout-minutes: 6
env:
LOG: ${{ runner.temp }}/released.txt
run: |
set -o pipefail
vendor/bin/phpstan analyse --no-progress --error-format=github --memory-limit=1G --configuration=${{ matrix.library == 'mcp/sdk' && 'phpstan.no-bundle.neon' || 'phpstan.neon.dist' }} 2>&1 | tee -a "$LOG"
- name: Run the tests
timeout-minutes: 12
env:
LOG: ${{ runner.temp }}/released.txt
run: |
set -o pipefail
vendor/bin/phpunit --testsuite Contract,Integration,E2E --no-progress --colors=never 2>&1 | tee -a "$LOG"
- name: Fail on purpose (workflow_dispatch force_failure)
if: github.event_name == 'workflow_dispatch' && inputs.force_failure == github.job
run: exit 1
- name: Open or update the nightly issue
if: failure() && github.event_name != 'pull_request'
env:
GH_TOKEN: ${{ github.token }}
LIBRARY: ${{ matrix.library }}
VERSION: ${{ matrix.version }}
LOG: ${{ runner.temp }}/released.txt
REPORT: ${{ runner.temp }}/released.md
run: |
# The end of the log: the failing step writes last, and an issue body holds at most 65536 characters.
if [ -s "$LOG" ]; then { printf '~~~text\n'; tail -c 50000 "$LOG"; printf '\n~~~\n'; } >"$REPORT"; fi
# One issue per minor release: a failure that lasts across its patches stays in one issue.
.github/nightly-issue.sh "released (${LIBRARY} $(cut -d. -f1-2 <<<"$VERSION"))" "$REPORT"

package:
name: Package probe (PHP ${{ matrix.php }}, mcp/sdk ${{ matrix.version }})
needs: discover
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
include: ${{ fromJSON(needs.discover.outputs.mcp-sdk) }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962 # 2.40.0
with:
php-version: ${{ matrix.php }}
coverage: none
- name: Install the dist in a fresh project and track an mcp/sdk server
timeout-minutes: 8
env:
MCP_SDK: ${{ matrix.version }}
run: tests/bin/package-probe.sh "$MCP_SDK"
- name: Fail on purpose (workflow_dispatch force_failure)
if: github.event_name == 'workflow_dispatch' && inputs.force_failure == github.job
run: exit 1
- name: Open or update the nightly issue
if: failure() && github.event_name != 'pull_request'
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ matrix.version }}
run: .github/nightly-issue.sh "package (mcp/sdk $(cut -d. -f1-2 <<<"$VERSION"))"

php-nightly:
name: Tests (PHP nightly)
runs-on: ubuntu-latest
Expand Down
5 changes: 4 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ git commit -m "docs: update API documentation"

### The Required Check

The one required check is `All green`. It passes when every CI job passes: static analysis on the lowest and the highest dependencies, the tests on PHP 8.3, 8.4 and 8.5, coverage, mutation testing, the `mcp/sdk` 0.9.0 leg, the runtime suite, the package probe and the backward-compatibility check.
The one required check is `All green`. It passes when every CI job passes: static analysis on the lowest and the highest dependencies, the tests on PHP 8.3, 8.4 and 8.5, coverage, mutation testing, the released-version legs, the runtime suite, the package probe and the backward-compatibility check. The released-version legs take their versions from Packagist on each run: the newest patch of each minor release of `mcp/sdk`, `laravel/mcp` and `symfony/mcp-bundle`, from the lowest version that `require-dev` allows. The package probe runs on each of those `mcp/sdk` releases. The nightly runs the legs and the probe again, so a release published between pull requests that breaks them opens an issue. When the install step of a leg fails, `require-dev` does not allow that release yet: widen its range, or the range of the framework that it needs.

### No Issue Required

Expand All @@ -117,6 +117,9 @@ vendor/bin/infection --threads=max --filter=src/Internal/Engine/ToolListing.php
# PHPStan and the adapter suites against mcp/sdk 0.9.0, in a throwaway copy under .cache/
tests/bin/mcp-sdk-leg.sh

# The releases that the released-version legs test, read from Packagist
php tests/bin/released-versions.php

# The runtime suite: PHP-FPM, FrankenPHP, Apache, NGINX Unit, the CLI and Swoole, in Docker
AGENTCAT_RUNTIME_TESTS=1 vendor/bin/phpunit --testsuite Runtime --group runtime

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ Diagnostics are on by default and can be turned off completely with either:
- **Containers**: start a stdio server with `docker run --init`, so that a stop signal ends PHP and AgentCat (with ext-pcntl) first delivers the queued events.
- **Swoole, RoadRunner and Workerman**: with Laravel or Symfony, AgentCat delivers in the framework's terminate hooks (Laravel's `terminating()` callbacks, also under Octane, and Symfony's `kernel.terminate`), waiting at most 1 s after each request; a plain `mcp/sdk` worker calls `AgentCat::flush(1.0)` after each request.

AgentCat supports PHP 8.3, 8.4 and 8.5; `mcp/sdk` 0.8.1 or later (tested 0.8.1 and 0.9.0); `laravel/mcp` 1.x (tested 1.0.0 and 1.1.0) with Laravel 12.41.1 or later and 13; and `symfony/mcp-bundle` 0.13 and 0.14 with Symfony 7.3 and 8.x. The [runtimes guide](https://docs.agentcat.com/sdk/php/runtimes) covers each runtime, containers and hardened hosts, and the [limits page](https://docs.agentcat.com/sdk/php/limits) lists the differences from the TypeScript SDK and the known limits.
AgentCat supports PHP 8.3, 8.4 and 8.5; `mcp/sdk` 0.8.1 or later; `laravel/mcp` 1.x with Laravel 12.41.1 or later and 13; and `symfony/mcp-bundle` 0.13 and 0.14 with Symfony 7.3 and 8.x. On every pull request and every night, CI reads Packagist and tests the newest patch of each minor release of these three libraries, from the lowest supported version up. The [runtimes guide](https://docs.agentcat.com/sdk/php/runtimes) covers each runtime, containers and hardened hosts, and the [limits page](https://docs.agentcat.com/sdk/php/limits) lists the differences from the TypeScript SDK and the known limits.

## Free for open source

Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
},
"require-dev": {
"brianium/paratest": "^7.8.6",
"composer/semver": "^3.4",
"ergebnis/composer-normalize": "^2.54",
"ergebnis/phpunit-slow-test-detector": "^2.26",
"infection/infection": "^0.35.8",
Expand Down
2 changes: 1 addition & 1 deletion phpstan.no-bundle.neon
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# PHPStan for the mcp/sdk main leg, which removes symfony/mcp-bundle (it requires mcp/sdk ^0.8.1).
# PHPStan for the mcp/sdk legs (released versions and main), which remove symfony/mcp-bundle (it requires mcp/sdk ^0.8.1).
includes:
- phpstan.neon.dist
parameters:
Expand Down
Loading
Loading