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
8 changes: 4 additions & 4 deletions .github/workflows/installed.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ on:
inputs:
component_ref:
type: string
default: feature/jcb-mcp-runtime
default: main
plugin_ref:
type: string
default: feature/jcb-mcp-runtime
default: main

permissions:
contents: read
Expand Down Expand Up @@ -44,13 +44,13 @@ jobs:
- uses: actions/checkout@v7
with:
repository: joomengine/mcp_component
ref: ${{ inputs.component_ref || (github.ref_name == 'main' && 'main' || 'feature/jcb-mcp-runtime') }}
ref: ${{ inputs.component_ref || 'main' }}
path: component
persist-credentials: false
- uses: actions/checkout@v7
with:
repository: joomengine/mcp_plugin
ref: ${{ inputs.plugin_ref || (github.ref_name == 'main' && 'main' || 'feature/jcb-mcp-runtime') }}
ref: ${{ inputs.plugin_ref || 'main' }}
path: plugin
persist-credentials: false
- uses: shivammathur/setup-php@v2
Expand Down
83 changes: 83 additions & 0 deletions .github/workflows/packagist.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: Packagist consumer

on:
pull_request:
push:
branches: [main]
workflow_dispatch:
inputs:
version:
description: 'Packagist constraint: auto selects a stable release, or dev-main before the first release'
default: auto
required: false
type: string
expected_reference:
description: 'Optional full commit SHA that the installed public package must contain'
default: ''
required: false
type: string
wait_for_index:
description: 'Wait up to five minutes for the exact release version to appear in Packagist'
default: false
required: false
type: boolean
workflow_call:
inputs:
version:
description: 'Exact Packagist release version to install, for example 1.0.0'
required: true
type: string
expected_reference:
description: 'Optional full release commit SHA to verify against the installed package'
default: ''
required: false
type: string
wait_for_index:
description: 'Wait up to five minutes for the exact release version to appear in Packagist'
default: false
required: false
type: boolean

permissions:
contents: read

concurrency:
group: packagist-consumer-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
consumer:
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
php: ['8.3', '8.4']
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: curl, json, fileinfo, posix, pcntl
tools: composer:v2
coverage: none
- name: Install the public Packagist package and test it as an independent consumer
env:
MCP_PACKAGE_VERSION: ${{ inputs.version || 'auto' }}
PACKAGIST_EXPECTED_REFERENCE: ${{ inputs.expected_reference || '' }}
PACKAGIST_WAIT_FOR_INDEX: ${{ inputs.wait_for_index && 'true' || 'false' }}
PACKAGIST_REPORT_PATH: ${{ github.workspace }}/build/packagist/consumer.json
run: |
set -euo pipefail
mkdir -p build/packagist
bash tests/packagist.sh "$MCP_PACKAGE_VERSION" 2>&1 | tee build/packagist/consumer.log
- name: Preserve the resolved package and consumer evidence
if: always()
uses: actions/upload-artifact@v7
with:
name: packagist-consumer-php-${{ matrix.php }}
path: build/packagist/
if-no-files-found: error
retention-days: 14
97 changes: 64 additions & 33 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,78 +1,109 @@
name: Tested client release

on:
pull_request:
push:
branches: [main]
workflow_dispatch:
inputs:
version:
description: 'Version without v, for example 0.1.0 or 0.1.0-rc.1'
required: true
description: 'Optional version override, without v; otherwise use release.json'
required: false
type: string
component_ref:
description: Tested installed component tag or commit
required: true
default: main
description: Optional installed component tag or commit override
required: false
type: string
plugin_ref:
description: Tested console plugin tag or commit
required: true
default: main
description: Optional console plugin tag or commit override
required: false
type: string

permissions:
contents: read

concurrency:
group: client-release
group: client-release-${{ github.event_name == 'pull_request' && github.ref || 'publish' }}
cancel-in-progress: false

jobs:
validate:
plan:
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
publish: ${{ steps.plan.outputs.publish }}
verify: ${{ steps.plan.outputs.verify }}
version: ${{ steps.plan.outputs.version }}
component_ref: ${{ steps.plan.outputs.component_ref }}
plugin_ref: ${{ steps.plan.outputs.plugin_ref }}
client_ref: ${{ steps.plan.outputs.client_ref }}
steps:
- name: Require a main-branch semantic version
- uses: actions/checkout@v7
with:
ref: ${{ github.sha }}
persist-credentials: false
- name: Validate the declaration and release-state behavior without publishing
run: |
bash tools/release-plan.sh validate
bash tests/release.sh
- name: Plan the immutable release from the reviewed main commit
id: plan
if: github.event_name != 'pull_request'
env:
VERSION: ${{ inputs.version }}
GH_TOKEN: ${{ github.token }}
RELEASE_VERSION: ${{ inputs.version }}
RELEASE_COMPONENT_REF: ${{ inputs.component_ref }}
RELEASE_PLUGIN_REF: ${{ inputs.plugin_ref }}
run: |
set -euo pipefail
test "$GITHUB_REF" = refs/heads/main
[[ "$VERSION" =~ ^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-(alpha|beta|rc)\.(0|[1-9][0-9]*))?$ ]]
bash tools/release-plan.sh plan | tee "$GITHUB_OUTPUT"
tests:
needs: validate
needs: plan
if: needs.plan.outputs.publish == 'true'
uses: ./.github/workflows/ci.yml
permissions:
contents: read
installed:
needs: validate
needs: plan
if: needs.plan.outputs.publish == 'true'
uses: ./.github/workflows/installed.yml
with:
component_ref: ${{ inputs.component_ref }}
plugin_ref: ${{ inputs.plugin_ref }}
component_ref: ${{ needs.plan.outputs.component_ref }}
plugin_ref: ${{ needs.plan.outputs.plugin_ref }}
permissions:
contents: read
release:
needs: [validate, tests, installed]
needs: [plan, tests, installed]
if: github.ref == 'refs/heads/main' && needs.plan.outputs.publish == 'true'
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: write
outputs:
version: ${{ steps.publish.outputs.version }}
client_ref: ${{ steps.publish.outputs.client_ref }}
steps:
- uses: actions/checkout@v7
with:
ref: ${{ github.sha }}
ref: ${{ needs.plan.outputs.client_ref }}
persist-credentials: true
- name: Push a new tested version tag and create its GitHub release
- name: Publish or recover the tested version without moving an existing tag
id: publish
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ inputs.version }}
RELEASE_VERSION: ${{ needs.plan.outputs.version }}
RELEASE_COMPONENT_REF: ${{ needs.plan.outputs.component_ref }}
RELEASE_PLUGIN_REF: ${{ needs.plan.outputs.plugin_ref }}
run: |
set -euo pipefail
TAG="v$VERSION"
if gh api "repos/$GITHUB_REPOSITORY/git/ref/tags/$TAG" >/dev/null 2>&1; then
echo 'This version tag already exists; it will not be overwritten.' >&2
exit 1
fi
git tag "$TAG" "$GITHUB_SHA"
git push origin "refs/tags/$TAG"
release_flags=()
if [[ "$VERSION" == *-* ]]; then release_flags+=(--prerelease); fi
gh release create "$TAG" --repo "$GITHUB_REPOSITORY" --verify-tag \
"${release_flags[@]}" --generate-notes --title "JoomEngine MCP Client $VERSION"
bash tools/release-plan.sh publish | tee "$GITHUB_OUTPUT"
verify:
needs: [plan, release]
if: always() && needs.plan.result == 'success' && needs.plan.outputs.verify == 'true' && (needs.plan.outputs.publish == 'false' || needs.release.result == 'success')
uses: ./.github/workflows/packagist.yml
with:
version: ${{ needs.plan.outputs.version }}
expected_reference: ${{ needs.plan.outputs.client_ref }}
wait_for_index: true
permissions:
contents: read
39 changes: 24 additions & 15 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
# Changelog

## Unreleased
## 1.0.0

- Establish independent ownership of the external PHP MCP client and remote stdio bridge.
- Add HTTPS installation URL/token configuration, endpoint-bound official SDK connection and bounded transport derived from the component infrastructure.
- Add dynamic tool/resource/prompt discovery and call contract tests, developer discovery example, PHP 8.3/8.4 CI and tested prerelease automation.
- Preserve the server-first acceptance dependency, full JCB discovery objective and transport provenance. No stable release, available stdio executable or live Joomla/JCB certification is claimed.
Changes for the first stable client release. Publication is recorded by the [`v1.0.0` GitHub release](https://github.com/joomengine/mcp_client/releases/tag/v1.0.0) and [Packagist version history](https://packagist.org/packages/joomengine/mcp-client); this entry alone does not establish availability. Client versions are independent of component/plugin versions; the selected interoperability targets are component 0.1.1 and console plugin 0.1.0.

## Unreleased — remote connection completion
### Added

- Add the generic remote `joomengine-mcp` stdio executable and private named-site configuration.
- Preserve HTTP identity, protocol/session metadata, pagination, finite SSE events, cancellation notifications and structured job results.
- Add concurrent bounded HTTPS transport with TLS failure and process-level acceptance coverage.
- Gate stable/prerelease automation on client contracts and packaged Joomla interoperability.
- Standalone external PHP MCP client and remote stdio bridge, with generic tool/resource/template/prompt discovery through the official SDK.
- HTTPS installation URL/token configuration, subdirectory support, bounded transport and endpoint-bound credentials.
- `joomengine-mcp` executable with ephemeral connections and private named-site configuration.
- Concurrent remote forwarding of HTTP identity, JSON-RPC/session metadata, pagination, finite SSE events, cancellation notifications and structured job results.
- Complete non-root Docker image and read-only Compose service, using a site URL and API token through environment variables.
- PHP 8.3/8.4 source contracts, TLS and process tests, real Compose checks and packaged Joomla interoperability acceptance.
- Reviewed `release.json` manifest with an independent client version and immutable component/plugin revisions; pull requests validate the release plan without publishing.
- Automatic publication after merge to `main`, gated on client, Docker and installed Joomla checks; existing versions are never overwritten and manual dispatch remains available.
- Packagist metadata, stable-version/download badges, release links, Composer-first local/global installation, AI launcher setup and a complete PHP discovery example.
- Stable `^1.0` installation instructions with an explicit `dev-main` fallback before publication and for development testing.
- PHP 8.3/8.4 consumer checks that download the actual Packagist distribution into a clean project and exercise its generated executable and SDK against trusted local HTTPS.

## Unreleased — Docker Compose client
### Fixed

- Add a complete non-root Docker image and read-only Compose service for connecting an AI application's stdio MCP transport using a Joomla HTTPS URL and API token.
- Allow `connect` to obtain its URL from `JOOMENGINE_MCP_URL` while preserving explicit URL arguments and credential isolation.
- Gate client releases on real Compose/TLS packaging checks for PHP 8.3 and 8.4, alongside existing PHP and installed Joomla acceptance.
- Document AI launcher setup, private CA bundles, Docker operation and the distinction between remote HTTP authority and trusted local-console serving.
- Use component/plugin `main` for installed interoperability after the original feature branches were merged and deleted.
- Send the SDK's negotiated `MCP-Protocol-Version` on subsequent HTTP requests, notifications and session deletion, including when the server selects a different supported revision.

### Distribution verification

- Consumer evidence identifies the downloaded version and commit and records SDK requests missing their negotiated protocol header. This compatibility diagnostic supports the older development package; stable versions from 1.0.0 must report zero missing SDK protocol headers.
- The stdio bridge must send the negotiated protocol header, and the fixture rejects incorrect supplied revisions for both SDK and stdio clients.
- Packagist synchronization and exact-version consumer verification are documented separately from source tests and installed Joomla acceptance.
- Release automation waits a bounded time for Packagist to index the new version, then verifies that exact version and source commit with the consumer suite on PHP 8.3 and 8.4. Installation and protocol failures fail the workflow without retries.
Loading
Loading