Skip to content
Open
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
46 changes: 29 additions & 17 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
# Default single-stack mode. Possible: controlplane, dataplane.
CF_MCP_STACK_MODE=dataplane

# cf-controlplane checkout. Default: v1.0.6, matching the default image release.
# cf-controlplane checkout. Default: v1.0.7, matching the publisher contract.
# Possible: any branch, tag, or commit accepted by git checkout.
CF_CONTROLPLANE_REF=v1.0.6
CF_CONTROLPLANE_REF=v1.0.7

# cf-controlplane repository. Default: IBM upstream.
# Possible: any git clone URL.
Expand Down Expand Up @@ -52,22 +52,22 @@ CF_INTEGRATION_DIR=.integration
# Possible: main, user/luca/cp-parity-tool-names, any ref accepted by git checkout.
# CF_DATAPLANE_REF=user/luca/cp-parity-tool-names

# cf-dataplane repository. Default: contextforge-gateway-rs upstream.
# cf-dataplane repository. Default: contextforge-org upstream.
# Possible: any git clone URL.
CF_DATAPLANE_REPO=https://github.com/contextforge-gateway-rs/contextforge-gateway-rs.git
CF_DATAPLANE_REPO=https://github.com/contextforge-org/contextforge-data-plane.git

# cf-dataplane checkout directory. Default: .integration/contextforge-gateway-rs.
CF_DATAPLANE_DIR=.integration/contextforge-gateway-rs
# cf-dataplane checkout directory. Default: .integration/contextforge-data-plane.
CF_DATAPLANE_DIR=.integration/contextforge-data-plane

# Local cf-dataplane image tag used when CF_DATAPLANE_REF is set and CF_DATAPLANE_IMAGE is unset.
# Possible: any Docker image reference.
CF_DATAPLANE_LOCAL_IMAGE=contextforge-gateway-rs/contextforge-gateway-rs:local
CF_DATAPLANE_LOCAL_IMAGE=contextforge-org/contextforge-data-plane:local

# Full cf-dataplane image override. With no source ref or full override, the
# published default derives from CF_DATAPLANE_VERSION (currently 0.1.0). Leave
# published default derives from CF_DATAPLANE_VERSION (currently latest). Leave
# the full override unset in source mode to use CF_DATAPLANE_LOCAL_IMAGE.
# CF_DATAPLANE_IMAGE=ghcr.io/contextforge-gateway-rs/contextforge-gateway-rs:<tag>
CF_DATAPLANE_VERSION=0.1.0
# CF_DATAPLANE_IMAGE=ghcr.io/contextforge-org/contextforge-data-plane:<tag>
CF_DATAPLANE_VERSION=latest

# cf-dataplane image platform. Default: auto.
# auto resolves to linux/amd64 in published image mode, or the Docker server platform
Expand All @@ -83,28 +83,40 @@ CF_DATAPLANE_PUBLISHER_INTERVAL_SECONDS=2
# Possible: non-negative integer. Use 60 to restore the dataplane image default for load benchmarks.
CF_DATAPLANE_USER_CONFIG_CACHE_EXPIRY_SECONDS=0

# Optional comma-separated transport allowlist overrides. Defaults cover the
# loopback nginx origin using NGINX_PORT and the internal `nginx` service name
# used by containerized Locust runs.
# CF_DATAPLANE_MCP_ALLOWED_HOSTS=127.0.0.1:8080,localhost:8080,nginx
# CF_DATAPLANE_MCP_ALLOWED_ORIGINS=http://127.0.0.1:8080,http://localhost:8080

# Guarded Fast Time contract and default registered virtual server.
CF_FAST_TIME_EXPECTED_IMAGE=ghcr.io/ibm/cfex-mcp-fast-time-server:latest
CF_FAST_TIME_SERVER_ID=9779b6698cbd4b4995ee04a4fab38737

# Public nginx port. Default: 8080.
NGINX_PORT=8080

# Optional fixed loopback port for the direct conformance fixture. Zero asks
# Docker to allocate an ephemeral port.
# CF_CONFORMANCE_PORT=0
# Direct public-origin override; otherwise derived from NGINX_PORT.
# MCP_CLI_BASE_URL=http://127.0.0.1:8080

# Default MCP protocol version for probe, load, live, and Inspector workflows.
# Conformance run keeps its pinned 2026-07-28 default unless overridden.
MCP_PROTOCOL_VERSION=2025-11-25
# Optional global MCP protocol override. Current probe/load/Inspector/live
# workflows default to the latest dataplane-compatible session protocol,
# 2025-11-25. Conformance keeps its pinned 2026-07-28 readiness default.
# MCP_PROTOCOL_VERSION=2026-07-28

# Admin/test token identity. Stable random local secrets are created automatically
# under CF_INTEGRATION_DIR when these overrides are unset.
MCP_JWT_SUBJECT=admin@example.com
# Local integration administrator. Stable random signing/encryption secrets are
# created automatically under CF_INTEGRATION_DIR when their overrides are unset.
PLATFORM_ADMIN_EMAIL=admin@example.com
PLATFORM_ADMIN_PASSWORD=changeme
# JWT_SECRET_KEY=<integration-secret>
# AUTH_ENCRYPTION_SECRET=<integration-encryption-secret>

# Optional existing fixture and token overrides. Empty values use the harness
# Fast Time fixture and a Rust-generated mode-appropriate JWT.
# Fast Time fixture; managed dataplane workflows request and revoke a
# control-plane API token whose UUID subject matches the published snapshot.
# MCP_SERVER_ID=<virtual-server-id>
# MCPGATEWAY_BEARER_TOKEN=<token>

Expand Down
111 changes: 81 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ The public routing contract is fixed:
`/contextforge-rs/servers/{virtual_host_id}/mcp`.
- Raw `/mcp`, UI traffic, and API traffic stay on `cf-controlplane`.

The `/servers/{id}/mcp` route does not fall back to the Python control plane on
dataplane errors. This makes routing failures visible and keeps the harness
aligned with the planned split between legacy slow-path traffic and modern
Rust dataplane traffic.

The harness owns Docker Compose overlays, nginx routing, reproducible stack
lifecycle, public-route probes, Locust and Goose load tests, and official MCP
conformance orchestration. Generated checkout, build, and runtime state stays
Expand Down Expand Up @@ -70,10 +75,13 @@ Single-lane commands resolve their lane in this order:
3. `dataplane`.

They resolve the protocol version from explicit `--protocol-version`, then
`MCP_PROTOCOL_VERSION`, then `2025-11-25`. Live protocol tests and conformance
also accept `fixture-direct`; other workflows reject it because they have no
direct-fixture execution path. Conformance defaults to all three lanes and its
pinned `2026-07-28` protocol version.
`MCP_PROTOCOL_VERSION`, then `2025-11-25`. That session-oriented default is
the working contract of the current `latest` dataplane image. Pass
`--protocol-version 2026-07-28` explicitly to exercise the implemented
stateless readiness path as the future architecture lands. Live protocol tests
and conformance also accept `fixture-direct`; other workflows reject it
because they have no direct-fixture execution path. Conformance defaults to
all three lanes and its pinned `2026-07-28` protocol version.

`--topology` remains a compatibility alias for `--lane` on workflows.
Conformance also retains `--client-version` and `--spec-version` as aliases for
Expand All @@ -85,7 +93,7 @@ because they operate on physical stacks, not test lanes.
Probe the dataplane public MCP route:

```bash
cf-integration probe --lane dataplane --protocol-version 2025-11-25
cf-integration probe --lane dataplane
```

`stack up` synchronizes the required source checkouts, validates the Compose
Expand Down Expand Up @@ -168,22 +176,24 @@ fresh start.
### Probe

```bash
cf-integration probe --lane dataplane --protocol-version 2025-11-25
cf-integration probe --lane dataplane
```

The probe checks unauthenticated rejection, initialization,
`notifications/initialized`, session reuse, `tools/list`, and one known-safe
`tools/call`. It targets `/mcp` in controlplane topology and
The modern dataplane probe checks unauthenticated rejection,
`server/discover`, required per-request metadata and routing headers,
`tools/list`, and one known-safe `tools/call` without creating a session. The
legacy control-plane probe retains initialize, `notifications/initialized`,
and session reuse. It targets `/mcp` in controlplane topology and
`/servers/{id}/mcp` in dataplane topology.

### Locust and Goose

Both load engines exercise the same MCP lifecycle and remain first-class:

```bash
cf-integration load --lane dataplane --protocol-version 2025-11-25 \
cf-integration load --lane dataplane \
--engine locust --smoke
cf-integration load --lane dataplane --protocol-version 2025-11-25 \
cf-integration load --lane dataplane \
--engine goose --smoke

cf-integration load --lane dataplane --engine locust \
Expand All @@ -198,10 +208,12 @@ CLI settings override `.env`; explicitly exported `LOCUST_USERS`,
engines. Smoke defaults are one user, one user/second, and ten seconds.

Locust uses the framework-required Python adapter. Goose is the native Rust
runner. Both initialize real MCP sessions, send
`notifications/initialized`, discover tools, call only a finite allowlist of
safe fixture tools, exercise ping, and audit generated artifacts for credential
leakage.
runner. On the modern dataplane lane both use `server/discover`, attach the
mandatory client `_meta` plus `Mcp-Method`/`Mcp-Name` headers to every request,
and avoid sessions and the removed `ping` method. The legacy control-plane
lane retains initialize, `notifications/initialized`, session cleanup, and
ping. Both engines call only a finite allowlist of safe fixture tools and audit
generated artifacts for credential leakage.

### Upstream live tests

Expand All @@ -220,6 +232,10 @@ cf-integration live \
--protocol-version 2025-06-18
```

`--group all` is the exact union of the `mcp`, `rbac`, and `protocol` groups.
Upstream plugin and SSO suites are excluded because this harness does not
start their additional services.

The `mcp` and `all` groups start the upstream profile-gated `fast_test_server`,
run its one-shot registration job, and, for the dataplane topology, wait until
the publisher snapshot contains its fixed virtual server before launching the
Expand All @@ -234,9 +250,9 @@ cannot emit it.
## Official MCP conformance

The official runner is pinned to
`@modelcontextprotocol/conformance@0.2.0-alpha.9`. The official TypeScript
`@modelcontextprotocol/conformance@0.2.0-alpha.11`. The official TypeScript
fixture is built from matching source revision
`794dcab99ed1ef2b89607be9999574140ea5c96e`.
`c321dd32035556e6769d3724a8ee97d87c3faaac`.

The default command is intentionally complete and reproducible:

Expand Down Expand Up @@ -337,7 +353,6 @@ Debug commands are useful for manual diagnosis but are not compliance gates.
```bash
cf-integration debug inspect \
--lane dataplane \
--protocol-version 2025-11-25 \
--method tools/list

cf-integration debug token \
Expand All @@ -347,9 +362,14 @@ cf-integration debug token \
cf-integration debug token --kind admin
```

Inspector is pinned to `@modelcontextprotocol/inspector@0.22.0` and uses the
same loopback authentication proxy as conformance. The proxy applies the
selected protocol version to Inspector's initialize request.
Token generation now authenticates against a running control plane using
`PLATFORM_ADMIN_EMAIL` and `PLATFORM_ADMIN_PASSWORD`. Scoped debug tokens
are catalog-backed, restricted to the selected virtual server, expire after
one day, and are intentionally left active for manual use.

Inspector is pinned to `@modelcontextprotocol/inspector@2.2.0` and uses the
same loopback authentication proxy as conformance. Select `2026-07-28` to use
its modern MCP SDK path for stateless dataplane requests.

## Configuration

Expand All @@ -363,26 +383,28 @@ CF_MCP_STACK_MODE=dataplane
CF_INTEGRATION_DIR=.integration

CF_CONTROLPLANE_REPO=https://github.com/IBM/mcp-context-forge.git
CF_CONTROLPLANE_REF=v1.0.6
CF_CONTROLPLANE_REF=v1.0.7
CF_CONTROLPLANE_IMAGE=ghcr.io/ibm/mcp-context-forge:latest
CF_CONTROLPLANE_VERSION=latest

CF_DATAPLANE_REPO=https://github.com/contextforge-gateway-rs/contextforge-gateway-rs.git
CF_DATAPLANE_REPO=https://github.com/contextforge-org/contextforge-data-plane.git
CF_DATAPLANE_REF=
CF_DATAPLANE_IMAGE=ghcr.io/contextforge-gateway-rs/contextforge-gateway-rs:0.1.0
CF_DATAPLANE_IMAGE=ghcr.io/contextforge-org/contextforge-data-plane:latest
CF_DATAPLANE_PLATFORM=auto

CF_COMPOSE_BUILD=auto
CF_FAST_TIME_EXPECTED_IMAGE=ghcr.io/ibm/cfex-mcp-fast-time-server:latest
CF_FAST_TIME_SERVER_ID=9779b6698cbd4b4995ee04a4fab38737

MCP_CLI_BASE_URL=http://127.0.0.1:8080
MCP_PROTOCOL_VERSION=2025-11-25
# Optional global override; leave unset for the current 2025-11-25 default.
# MCP_PROTOCOL_VERSION=2026-07-28
NGINX_PORT=8080
```

Published control-plane and dataplane images are the defaults. The control-plane
checkout defaults to the release matching the current `latest` image. Set
Published control-plane and dataplane images are the defaults; the dataplane
uses its `latest` tag. The control-plane checkout defaults to v1.0.7, whose
publisher uses UUID token subjects and the current backend snapshot schema. Set
`CF_DATAPLANE_REF` to build an explicit local dataplane ref.
`CF_COMPOSE_BUILD=auto` pulls or reuses prebuilt images and rebuilds a missing
or revision-stale source dataplane; `true` always builds and `false` never
Expand All @@ -391,19 +413,48 @@ builds.
Token and endpoint overrides used by probe, load, and debug commands:

```bash
# Optional overrides. Without them, stable random local values are generated
# once under CF_INTEGRATION_DIR.
# Optional overrides. Without them, stable random local signing values are
# generated once under CF_INTEGRATION_DIR.
JWT_SECRET_KEY=<integration-secret>
AUTH_ENCRYPTION_SECRET=<integration-encryption-secret>
MCP_JWT_SUBJECT=admin@example.com
PLATFORM_ADMIN_EMAIL=admin@example.com
PLATFORM_ADMIN_PASSWORD=<local-integration-password>
MCPGATEWAY_BEARER_TOKEN=<pre-minted-token>
MCP_SERVER_ID=<virtual-server-id>
MCP_TOOL_NAMES=<comma-separated-safe-tool-names>
```

Managed workflows authenticate through the control-plane email-login endpoint.
Dataplane probe, load, Inspector, and conformance runs then request a one-day,
server-scoped API token from the token catalog and revoke it before stack
teardown. This ensures the token's UUID subject selects the same `UserConfig`
snapshot the publisher wrote. `MCPGATEWAY_BEARER_TOKEN` bypasses that
lifecycle and is never revoked by the harness.

Conformance ignores caller-managed fixture IDs and tokens so every lane uses
the same official fixture. Never commit `.env` or generated tokens.

## Future architecture alignment

The dataplane repository's tentative ContextForge 2.0 wiki describes a
management plane, a legacy Python MCP slow path, and a modern `2026-07-28`
Rust fast path consuming revisioned effective configuration from a shared
store. This harness prepares for that split by keeping management and raw
`/mcp` traffic on control-plane, routing `/servers/{id}/mcp` strictly to the
dataplane, providing explicit stateless modern probe/load/Inspector paths, and
obtaining dataplane credentials from the management plane. The ordinary
workflow default remains `2025-11-25` until the current upstream expected
failure baseline for stateless aggregate and targeted operations is retired.

The remaining boundary belongs upstream rather than in this harness:
control-plane must publish atomic compiled configuration and perform discovery,
catalog normalization, pagination, and liveness; dataplane must serve aggregate
catalog methods from that configuration and route targeted operations to one
backend without live fan-out. When those phases land, the harness should add
revision-isolation and tenant/principal partition tests instead of compatibility
fallbacks. See the
[`_context/wiki` architecture notes](https://github.com/contextforge-org/contextforge-data-plane/tree/main/_context/wiki).

## Repository layout

```text
Expand Down
5 changes: 3 additions & 2 deletions crates/compliance/src/conformance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,12 @@ pub fn is_trusted_official_fixture(fixture: Option<&ConformanceFixtureMetadata>)
})
}

// Exact server catalogs emitted by @modelcontextprotocol/conformance@0.2.0-alpha.9.
// Exact server catalogs emitted by @modelcontextprotocol/conformance@0.2.0-alpha.11.
// Keep these coupled to OFFICIAL_CONFORMANCE_PACKAGE and verify the pin with
// the ignored package-backed test before updating either.
const SERVER_SCENARIOS_2025_06_18: [&str; 26] = [
const SERVER_SCENARIOS_2025_06_18: [&str; 27] = [
"server-initialize",
"server-session-lifecycle",
"logging-set-level",
"ping",
"completion-complete",
Expand Down
8 changes: 6 additions & 2 deletions crates/compliance/src/conformance_fixture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ use url::Url;
pub use crate::profile::{OFFICIAL_CONFORMANCE_REPOSITORY, OFFICIAL_CONFORMANCE_REVISION};
/// Docker Compose service name for the official conformance server.
pub const OFFICIAL_CONFORMANCE_SERVICE: &str = "mcp_conformance_server";
/// Backend URL reachable from the control-plane container.
pub const OFFICIAL_CONFORMANCE_BACKEND_URL: &str = "http://mcp_conformance_server:3000/mcp";
/// Docker Compose service name for the fixture's backend-only Host proxy.
pub const OFFICIAL_CONFORMANCE_PROXY_SERVICE: &str = "mcp_conformance_proxy";
/// Backend URL reachable from the control-plane and dataplane containers.
pub const OFFICIAL_CONFORMANCE_BACKEND_URL: &str = "http://mcp_conformance_proxy/mcp";
/// Reserved gateway name used by the fixture.
///
/// `_` intentionally produces an empty gateway slug when paired with the
Expand Down Expand Up @@ -221,6 +223,8 @@ impl ConformanceFixtureClient {
"name": OFFICIAL_CONFORMANCE_GATEWAY_NAME,
"url": backend_url,
"transport": GATEWAY_TRANSPORT,
"authType": "authheaders",
"authHeaders": [{"key": "Host", "value": "localhost:3000"}],
"description": GATEWAY_DESCRIPTION,
}),
)
Expand Down
4 changes: 2 additions & 2 deletions crates/compliance/src/profile.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
//! Coherent official conformance runner, fixture, and protocol pins.

/// Published official CLI package used as the conformance client.
pub const OFFICIAL_CONFORMANCE_PACKAGE: &str = "@modelcontextprotocol/conformance@0.2.0-alpha.9";
pub const OFFICIAL_CONFORMANCE_PACKAGE: &str = "@modelcontextprotocol/conformance@0.2.0-alpha.11";
/// Official repository containing the matching TypeScript fixture server.
pub const OFFICIAL_CONFORMANCE_REPOSITORY: &str =
"https://github.com/modelcontextprotocol/conformance";
/// Exact source revision behind the published CLI and TypeScript fixture.
pub const OFFICIAL_CONFORMANCE_REVISION: &str = "794dcab99ed1ef2b89607be9999574140ea5c96e";
pub const OFFICIAL_CONFORMANCE_REVISION: &str = "c321dd32035556e6769d3724a8ee97d87c3faaac";
/// Default draft protocol revision exercised by official conformance commands.
pub const DEFAULT_MCP_SPEC_VERSION: &str = "2026-07-28";
/// Previous stable revision supported by the pinned official conformance package.
Expand Down
8 changes: 4 additions & 4 deletions crates/compliance/tests/conformance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,9 @@ fn pinned_server_scenario_catalog_has_exact_suite_differences() {
let draft_all = expected_server_scenarios("all", "2026-07-28")
.expect("draft all scenario catalog should be pinned");

assert_eq!(stable_active.len(), 30);
assert_eq!(stable_all.len(), 32);
assert_eq!(previous.len(), 26);
assert_eq!(stable_active.len(), 31);
assert_eq!(stable_all.len(), 33);
assert_eq!(previous.len(), 27);
assert_eq!(draft_active.len(), 20);
assert_eq!(draft_all.len(), 40);
assert_eq!(
Expand Down Expand Up @@ -185,7 +185,7 @@ fn official_command_is_pinned_complete_and_ordered() {

assert_eq!(
OFFICIAL_CONFORMANCE_PACKAGE,
"@modelcontextprotocol/conformance@0.2.0-alpha.9"
"@modelcontextprotocol/conformance@0.2.0-alpha.11"
);
assert_eq!(DEFAULT_MCP_SPEC_VERSION, "2026-07-28");
assert!(!spec.inherits_environment());
Expand Down
2 changes: 2 additions & 0 deletions crates/compliance/tests/conformance_fixture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,8 @@ async fn provision_uses_authenticated_admin_api_in_exact_order() {
"name":OFFICIAL_CONFORMANCE_GATEWAY_NAME,
"url":OFFICIAL_CONFORMANCE_BACKEND_URL,
"transport":"STREAMABLEHTTP",
"authType":"authheaders",
"authHeaders":[{"key":"Host", "value":"localhost:3000"}],
"description":"Official MCP conformance fixture"
})
);
Expand Down
Loading