Repository navigation
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
8 new issues
|
|
Coverage Impact Unable to calculate total coverage change because base branch coverage was not found. Modified Files with Diff Coverage (15)
🤖 Increase coverage with AI coding...🚦 See full report on Qlty Cloud » 🛟 Help
|
Manual test against a real agent
MCP (Claude Code,
|
P='' |
P=/ai |
|
|---|---|---|
Browser OAuth on P/oauth/* |
✅ | ✅ |
describeCollection + list on customers |
✅ 10 records | ✅ |
API with a service-account key (X-Forest-Bff-Key: fgw_…, X-Forest-Timezone)
| Call | P='' |
P=/ai |
|---|---|---|
GET P/api/agent/v1/context |
200 | 200 |
POST P/api/agent/v1/companies/count |
200 | 200 |
POST P/api/agent/v1/companies/list |
200 | 200 |
GET P/api/agent/openapi.json → servers[0].url |
/api |
/ai/api |
P/api/docs → document and bundle paths |
/api/… |
/ai/api/… |
GET P/api/health |
200 | 200 |
API OAuth (P=/ai, DCR client on the Forest server, PKCE)
| Step | Result |
|---|---|
GET /ai/oauth/authorize?service=api → Forest login → code |
✅ |
POST /ai/oauth/token?service=api (authorization_code) |
200, served by the BFF, access + refresh |
POST /ai/api/agent/v1/companies/count with the bearer |
200 |
Refresh on /ai/oauth/token?service=api |
200 |
| API call with the refreshed bearer | 200 |
API bearer on /ai/mcp |
401 invalid_token: each service keeps its own tokens |
Routing
| Call | P='' |
P=/ai |
|---|---|---|
POST P/oauth/token without service |
MCP (Express, 400) | MCP |
POST P/oauth/token?service=api |
BFF (400) | BFF |
OPTIONS P/oauth/token?service=api from an allowed origin |
204 + BFF CORS | 204 + BFF CORS |
| MCP discovery | /.well-known/oauth-authorization-server |
/.well-known/oauth-authorization-server/ai, issuer http://localhost:3931/ai |
POST P/api/oauth/token |
404 | 404 |
/agent/v1/* at the root |
404 | 404 |
/api/*, /oauth/token?service=api without /ai |
n/a | 404 |
Root /health |
200, mcp: ok, api: ok |
200 |
X-Forest-Gateway-Version |
every response | every response |
Still not covered
- e2e against the non-Node benches.
…d-1337-create-the-forestadmingateway-package-that-serves-both
There was a problem hiding this comment.
Spec (PRD-1337): conforms to the acceptance criteria, but the gateway's tests never run in CI.
Applies to: .github/workflows/build.yml:62-79 (not in this diff)
Should fix: no CI job runs the gateway's lint or its 106 tests, so a regression merges green.
Code
The test matrix in .github/workflows/build.yml:62-79 lists each package by name, and gateway is not in it.
A way out: add gateway to that matrix.
Reproduction
- Break a gateway test on this branch.
- Push.
- The Linting & Testing jobs all pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Exact pins fall behind the workspace on each release, since a private package is skipped by multi-semantic-release, and Yarn then pulls the published versions instead of linking the local ones. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rver own A rename on their side now fails the gateway's typecheck, or carries over through the exported name, instead of silently moving the API's public url and port. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Verified closed: the gateway package now runs lint and tests in CI on every push. How it was checkedA regression in the gateway package merged green because no CI job ran its lint or tests. |
Tonours
left a comment
There was a problem hiding this comment.
Approved after /verify-fixes run 1978-20261007133450-Tonours-verify: 5 findings of 1978-20261007121227-Tonours closed or accepted, none reopened.
| if (process.env.GITHUB_STEP_SUMMARY) fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, `${summary}\n`); | ||
|
|
||
| // Block only on Docker-only OTel packages — everything else is report-only. | ||
| const blocking = findings.filter(f => f.pkg.startsWith('@opentelemetry/')); |
There was a problem hiding this comment.
🟡 Medium docker/scan-gate.js:48
A fixable vulnerability in a Docker-only transitive dependency such as import-in-the-middle or require-in-the-middle is excluded from blocking, so this script exits successfully and permits publishing the vulnerable image. The @opentelemetry/ name-prefix check misses those packages; classify findings against the Docker-only dependency closure instead.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @packages/gateway/docker/scan-gate.js around line 48:
A fixable vulnerability in a Docker-only transitive dependency such as `import-in-the-middle` or `require-in-the-middle` is excluded from `blocking`, so this script exits successfully and permits publishing the vulnerable image. The `@opentelemetry/` name-prefix check misses those packages; classify findings against the Docker-only dependency closure instead.
| echo "is_latest=$IS_LATEST" >> "$GITHUB_OUTPUT" | ||
|
|
||
| TAGS="-t $IMAGE:$FULL" | ||
| if [ "$IS_LATEST" = "true" ]; then |
There was a problem hiding this comment.
🟠 High workflows/docker-publish-gateway.yml:423
An older release can overwrite :latest, the major/minor tags, and the server deployment with its version after a newer release has already published. IS_LATEST is decided before imagetools create, while different versions run concurrently, so the newer tag can appear and publish between the check and write; serialize the mutable-tag decision and write, and ensure notifications use the same protected decision.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @.github/workflows/docker-publish-gateway.yml around line 423:
An older release can overwrite `:latest`, the major/minor tags, and the server deployment with its version after a newer release has already published. `IS_LATEST` is decided before `imagetools create`, while different versions run concurrently, so the newer tag can appear and publish between the check and write; serialize the mutable-tag decision and write, and ensure notifications use the same protected decision.
| code="" | ||
| attempt=0 | ||
| while [ "$attempt" -lt 90 ]; do | ||
| code=$(curl -s -o "$out" -w '%{http_code}' "$url" || true) |
There was a problem hiding this comment.
🟡 Medium docker/smoke-test.sh:77
The smoke test can hang indefinitely if an HTTP endpoint accepts a connection but never completes its response, so it never reaches the retry limit, failure diagnostics, or cleanup. curl has no transfer timeout by default, and the direct requests at lines 137 and 144 have the same issue; add --max-time to each request.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @packages/gateway/docker/smoke-test.sh around line 77:
The smoke test can hang indefinitely if an HTTP endpoint accepts a connection but never completes its response, so it never reaches the retry limit, failure diagnostics, or cleanup. `curl` has no transfer timeout by default, and the direct requests at lines 137 and 144 have the same issue; add `--max-time` to each request.

Summary
New
@forestadmin/gatewaypackage ("private": true, binforest-gateway): one process, one port, serving the MCP, the API or both, for agents that can't embed them.forest-mcp-serverandforest-bffare untouched.Decisions (also on the ticket):
PORT(default 3931 withmcp, 3450 withapionly) andFOREST_GATEWAY_URLonly;MCP_SERVER_PORT,HTTP_PORT,FOREST_MCP_SERVER_URL,BFF_PUBLIC_URLare ignored with a warning. MCP variables keep their legacy names (parseMcpEnv).services.mcp/services.apireport'disabled'when not enabled.ignorePrivatedefaults to true), so no tag, changelog or publish until the Docker-image ticket flips it. npm name reservation and trusted publishing happen before that flip, outside this PR.Evidence
/oauth/*at the root.After:
yarn workspace @forestadmin/gateway test— 4 suites, 106 tests pass:run-gateway.test.ts: a real Gateway (real MCP + BFF, fake Forest server over HTTP) onP=''andP='/ai': MCP token endpoint withoutserviceserved by the MCP's real callback,?service=apiby the BFF, API CORS preflight, discovery at/.well-known/oauth-authorization-server/ai, 404 on/api/oauth/token, root/agent/v1/*,/mcpand/api/*outside/ai; 503service_unavailablewithoutFOREST_AUTH_SECRET/FOREST_ENV_SECRET;/healthand/api/healthagree on a degraded API; Forest unreachable →/health200; version header on every kind of response; listener logs say "Forest Gateway".shutdown.test.ts(spawned process): SIGTERM / SIGINT exit 0 before the deadline, SIGTERM+SIGTERM and SIGTERM+SIGINT stop once, an in-flight API request finishes, one still open past the 10 s deadline is closed.cli-dispatch.test.ts:forest-gateway openapiannounces/api,/ai/api, orFOREST_GATEWAY_URL+P/api.gateway-env.test.ts: services parsing (empty /mcp,foo→ error listingmcp,api), port defaults,PORT=0without URL fails naming both.npm pack+npm installin an empty external project resolvesagent-bff@1.39.0andmcp-server@1.27.1from the registry with no peer conflict;forest-gateway --versionandopenapirun.Not done here:
/api(scripts live in the non-Node matrix replay folder, not in this repo).Merge Danger
Door: two-way
New private package; reverting the PR removes it.
Blast Radius: none
Nothing imports it, it isn't published, and no existing package or Docker image changes.
Fixes PRD-1337
🤖 Generated with Claude Code
Note
Add
@forestadmin/gatewaypackage serving MCP and API on one portforest-gatewayCLI starts a singleBFFHttpServerthat routes MCP and API handlers behind one shared listener, with a root/healthendpoint and JSON 404 fallback (run-gateway.ts, standalone-handler.ts).FOREST_GATEWAY_SERVICES,FOREST_GATEWAY_BASE_PATH,FOREST_GATEWAY_URL, andPORT; defaults are port 3931 with MCP enabled and 3450 for API-only (gateway-env.ts).openapiCLI command renders the API document under the gateway base path plus/api, with optional--outputfile writing (cli-dispatch.ts)./apipublic path, and MCP listener/URL variables are removed before parsing (gateway-env.ts).FOREST_ENV_SECRET,FOREST_AUTH_SECRET) no longer fail startup; MCP routes return HTTP 503service_unavailableand gateway health reports degraded.BFF_PUBLIC_URL,HTTP_PORT, andMCP_SERVER_PORT/FOREST_MCP_SERVER_URLvariables are unused by the gateway and trigger startup warnings instead of configuring it.Changes since #1978 opened
MCP_LISTENER_ENVconstant in@forestadmin/mcp-serverandBFF_LISTENER_ENVmapping ingateway, then updatedLEGACY_VARS,toBffEnv, andtoMcpEnvto reference these constants instead of hardcoded environment variable names [b3b7e52]@forestadmin/gatewayto@forestadmin/agent-bffpackage, including constants, parsing logic, and file writing functionality [b3b7e52]gatewaypackage to the CI build workflow matrix and bumped@forestadmin/agent-bffdependency from 1.39.0 to ^1.40.0 and@forestadmin/mcp-serverfrom 1.27.1 to ^1.28.0 ingatewaypackage [b3b7e52]EnvAliasesclass to resolve canonical environment variable names against legacy aliases, with support for precedence rules, conflict detection, deprecation warnings, and optional blank-value handling [605c738]parseGatewayEnvto use structured per-service configuration with alias resolution, validation, and aggregated warnings [605c738]parseAllowedOAuthClientsparser and integrated OAuth client allowlist support into API service configuration [605c738]buildApiServiceandbuildMcpServiceto accept structured settings with pre-resolved environment variables and labels [605c738]runGatewayto useparseGatewayEnvoutput, log aggregated warnings, and construct services from structured settings [605c738]probeHealthutility and CLI for programmatic health checks with alias-aware port resolution [605c738]renderGatewayOpenApiandrenderOpenApito use aliased configuration and emit warnings with correct key labels [605c738]parseOpenApiEnvexported function to resolve API environment configuration with base path and warnings for OpenAPI generation [605c738]parsePort,parseGatewayUrl, andassertAgentUrlvalidators that reference specific resolved keys in error messages [605c738]@forestadmin/gatewaytags and trigger Docker image publishing [605c738]scan-gate.jsscript to parse Trivy results and gate builds on fixable vulnerabilities in OpenTelemetry packages [605c738]private: trueflag from@forestadmin/gatewaypackage and added@forestadmin/forestadmin-clientruntime dependency [605c738]CONFIG_KEYSandparsePublicUrlfrom@forestadmin/agent-bffpackage entrypoint [605c738]zoddependency in@forestadmin/mcp-serverto exact version4.6.5[605c738]FOREST_*canonical names and deterministic encryption key [605c738]@forestadmin/agent-bffand@forestadmin/mcp-serverCLI entrypoints directing users to@forestadmin/gateway[f65b49f]@forestadmin/agent-bffand@forestadmin/mcp-serverto document deprecation [f65b49f]📊 Macroscope summarized f65b49f. 20 files reviewed, 4 issues evaluated, 1 issue filtered, 3 comments posted
🗂️ Filtered Issues
packages/gateway/docker/smoke-test.sh — 1 comment posted, 2 evaluated, 1 filtered
mktemp -d, before the initial checks. Withset -e, a faileddocker run, help grep, bundle check, or OpenAPI check at lines 10–23 exits before this trap is registered, leaving$TMP_DIRand its output files behind. The failures this smoke test is meant to detect therefore bypass temporary-file cleanup. [ Out of scope (post-validation triage) ]