---
id: 10808
title: 'Operator-facing env var ergonomics: descriptive names + Chroma host/port overrides'
state: CLOSED
labels:
  - enhancement
  - ai
  - architecture
  - build
assignees:
  - neo-opus-ada
createdAt: '2026-05-06T08:49:52Z'
updatedAt: '2026-06-07T17:07:33Z'
githubUrl: 'https://github.com/neomjs/neo/issues/10808'
author: neo-opus-ada
commentsCount: 0
parentIssue: 9999
subIssues: []
subIssuesCompleted: 0
subIssuesTotal: 0
blockedBy: []
blocking: []
closedAt: '2026-05-06T11:06:53Z'
---
# Operator-facing env var ergonomics: descriptive names + Chroma host/port overrides

## Context

Surfaced during cross-family review of [PR #10806](https://github.com/neomjs/neo/pull/10806) ([#10800](https://github.com/neomjs/neo/issues/10800) — cloud deployment cookbook). The cookbook's Section 6 (Environment Variable Inventory) proposed operator-facing env-var names (`MCP_HTTP_PORT`, `NEO_CHROMA_HOST`, `NEO_CHROMA_PORT`) that are more descriptive for external operators than the current substrate names. @tobiu calibrated 2026-05-06: *"our env var names are not set in stone, except for the github and gemini ones."*

Per the calibration, the right path is **substrate-side rename + new-env-var wiring** to match the cookbook's forward-looking proposal. Cookbook footnotes this ticket in Section 6 so it reads as desired-state with a substrate-gap pointer.

## The Problem

Two operator-facing env-var deficiencies for the shared cloud deployment topology:

### (1) `SSE_PORT` is non-descriptive

Current substrate (`ai/mcp/server/memory-core/config.template.mjs:74`, `ai/mcp/server/knowledge-base/config.template.mjs:41`):
```javascript
ssePort: Number(process.env.SSE_PORT) || 3001,  // MC default
ssePort: Number(process.env.SSE_PORT) || 3000,  // KB default
```

`SSE_PORT` describes the *transport mechanism* (Server-Sent Events) rather than the *operator-relevant role* (the MCP server's HTTP listening port). For an external operator provisioning a container, `MCP_HTTP_PORT` (or similar) is more discoverable and intent-clear.

### (2) Chroma host/port are hardcoded, not env-overridable

Current substrate (`ai/mcp/server/knowledge-base/config.template.mjs:106-107` + equivalent paths in MC config):
```javascript
host: 'localhost',
port: 8000,
```

These are hardcoded literals, NOT `process.env`-driven. An operator deploying KB+MC against a shared cloud-hosted Chroma at, e.g., `team-chroma.example.com:8000` has no env-var path to configure this — they'd have to fork the config templates, which is a forking-friction the cookbook flow shouldn't require.

The cookbook's Section 5 prescribes `NEO_CHROMA_UNIFIED=true` (works — that env var IS wired) AND the host/port fields (which AREN'T wired). The asymmetry is the substrate gap.

## The Architectural Reality

- **Config templates touched:**
  - `ai/mcp/server/memory-core/config.template.mjs:74` (`ssePort`)
  - `ai/mcp/server/knowledge-base/config.template.mjs:41` (`ssePort`)
  - `ai/mcp/server/knowledge-base/config.template.mjs:106-107` (`host`, `port`)
  - Memory Core's chroma host/port equivalents (verify path during implementation)

- **Config consumers (downstream of the rename):**
  - `ai/mcp/server/memory-core/Server.mjs` (reads `aiConfig.ssePort` at SSE transport boot)
  - `ai/mcp/server/knowledge-base/Server.mjs` (same)
  - `ai/mcp/server/shared/services/TransportService.mjs` (likely reads from injected config)
  - Any other consumers `grep -rn 'ssePort\|aiConfig.ssePort'` reveals

- **Documentation surface:**
  - [`learn/agentos/SharedDeployment.md`](https://github.com/neomjs/neo/blob/dev/learn/agentos/SharedDeployment.md) currently documents `SSE_PORT` env var (verify and update during implementation)
  - [PR #10806](https://github.com/neomjs/neo/pull/10806) cookbook Section 6 references the new names
  - `MemoryCoreMcpAuth.md` example configs may reference `SSE_PORT`

- **Test surface:**
  - `test/playwright/unit/ai/mcp/server/memory-core/Server.spec.mjs` and similar may exercise these env vars; verify no regressions

## The Fix

Two-part substrate work:

### Part 1 — Rename `SSE_PORT` env var

Add a new descriptive env-var-driven config field (`mcpHttpPort` or similar, name decided during implementation). Decisions to make:
- **Hard rename** (drop `SSE_PORT` support) vs **soft rename with backwards-compat fallback** (`process.env.MCP_HTTP_PORT || process.env.SSE_PORT || <default>`). Soft fallback is cleaner for operators with existing `.env` files; hard rename is cleaner long-term.
- **Field name in config object:** `ssePort` is currently aliased to the runtime concept. Rename to `mcpHttpPort` everywhere or keep field name and just rename env var? Empirical preference: rename both for consistency.

Recommendation: soft rename with deprecation footnote in [`SharedDeployment.md`](https://github.com/neomjs/neo/blob/dev/learn/agentos/SharedDeployment.md) noting `SSE_PORT` is the legacy env var. This honors backwards-compat for any existing operator deployments while leading the documentation forward.

### Part 2 — Wire Chroma host/port as env-overridable

In `knowledge-base/config.template.mjs` (and equivalent in MC):
```javascript
// Current (hardcoded):
host: 'localhost',
port: 8000,

// Proposed:
host: process.env.NEO_CHROMA_HOST || 'localhost',
port: Number(process.env.NEO_CHROMA_PORT) || 8000,
```

Symmetrically apply to MC if its Chroma host/port lives in a parallel field.

### Part 3 — Documentation sync

Update all references to the old env-var names:
- [`SharedDeployment.md`](https://github.com/neomjs/neo/blob/dev/learn/agentos/SharedDeployment.md) (verify current refs)
- [`MemoryCoreMcpAuth.md`](https://github.com/neomjs/neo/blob/dev/learn/agentos/tooling/MemoryCoreMcpAuth.md) (verify current refs)
- Any `npx neo-app` template or harness config snippet
- The cookbook in [PR #10806](https://github.com/neomjs/neo/pull/10806) — once this ticket is filed, that PR can footnote this # in its Section 6

## Acceptance Criteria

- [ ] **AC1:** `MCP_HTTP_PORT` env var added (or whatever final name lands) to both KB and MC config templates; soft fallback to `SSE_PORT` if backwards-compat path chosen.
- [ ] **AC2:** `NEO_CHROMA_HOST` + `NEO_CHROMA_PORT` env vars wired in both KB and MC config templates (currently hardcoded `localhost`/`8000`).
- [ ] **AC3:** All documentation references updated: [`SharedDeployment.md`](https://github.com/neomjs/neo/blob/dev/learn/agentos/SharedDeployment.md), [`MemoryCoreMcpAuth.md`](https://github.com/neomjs/neo/blob/dev/learn/agentos/tooling/MemoryCoreMcpAuth.md), `learn/agentos/DeploymentCookbook.md` (lands in [PR #10806](https://github.com/neomjs/neo/pull/10806)) — all consistent on the new names.
- [ ] **AC4:** Healthcheck `database.topology.coordinates.{host, port}` reflects the env-var-driven values when set (per [#10127](https://github.com/neomjs/neo/issues/10127) topology contract).
- [ ] **AC5:** No regression in existing unit tests; if any tests reference `SSE_PORT`, update them or add fallback compatibility.

## Contract Ledger (T3)

Per canonical specification in [`learn/agentos/contract-ledger.md`](https://github.com/neomjs/neo/blob/dev/learn/agentos/contract-ledger.md). Authored 2026-05-06 — added retroactively by ticket author (@neo-opus-ada) at intake-time per the systemic batch-velocity gap surfaced during the cookbook follow-ups (#10801-#10805). Closing the gap on my own ticket while picking up Lane A in the next-sprint coordination.

| Target Surface | Source of Authority | Proposed Behavior | Fallback / Edge Case | Docs | Evidence |
|---|---|---|---|---|---|
| `aiConfig.mcpHttpPort` (new field, replacing `ssePort` in spirit) + `NEO_CHROMA_HOST` / `NEO_CHROMA_PORT` env-overridability — both KB (`ai/mcp/server/knowledge-base/config.template.mjs`) + MC (`ai/mcp/server/memory-core/config.template.mjs`). | #10808, parent [#9999](https://github.com/neomjs/neo/issues/9999), surfacing [PR #10806](https://github.com/neomjs/neo/pull/10806) cookbook Section 6 footnote (forward-looking env-var names referenced ahead of substrate wiring), @tobiu calibration 2026-05-06 (*"our env var names are not set in stone, except for the github and gemini ones"*). | Soft rename: KB/MC servers read `process.env.MCP_HTTP_PORT \|\| process.env.SSE_PORT \|\| <default-port>` for the listening port (KB defaults `3000`, MC defaults `3001`). Field rename `ssePort` → `mcpHttpPort` in config object across both `config.template.mjs` files; consumers (`Server.mjs`, `TransportService.mjs`) updated to read `aiConfig.mcpHttpPort`. Chroma host/port wiring: `host: process.env.NEO_CHROMA_HOST \|\| 'localhost'`, `port: Number(process.env.NEO_CHROMA_PORT) \|\| 8000` (KB config; symmetric in MC if a parallel field exists). | Backwards-compat for operators with existing `.env` files using `SSE_PORT`: legacy env var still recognized. Deprecation warning emitted when both `MCP_HTTP_PORT` and `SSE_PORT` set with different values (resolver decides based on documented precedence — prefer `MCP_HTTP_PORT`). If neither set, default port unchanged. Chroma host/port: when env var unset, fallback to current hardcoded `localhost:8000`; new value takes effect on next server boot. | Update [`learn/agentos/SharedDeployment.md`](https://github.com/neomjs/neo/blob/dev/learn/agentos/SharedDeployment.md) (env var inventory + env-overridable Chroma section), [`learn/agentos/tooling/MemoryCoreMcpAuth.md`](https://github.com/neomjs/neo/blob/dev/learn/agentos/tooling/MemoryCoreMcpAuth.md) (operator config examples that reference `SSE_PORT`), `learn/agentos/DeploymentCookbook.md` Section 6 (replace existing #10808 footnote with ratification: "these env vars are now wired"). Cookbook Section 5 (Chroma topology) — verify `NEO_CHROMA_HOST/PORT` examples land cleanly. | L2 unit-test: extend `test/playwright/unit/ai/mcp/server/memory-core/config.spec.mjs` (or sibling) — fallback test (only `SSE_PORT` set → consumed); precedence test (both set with different values → `MCP_HTTP_PORT` wins + warning emitted); default test (neither set → default port). For Chroma host/port: similar 3-case matrix. Plus `node --check` on both updated `config.template.mjs` files. Manual verification: `npm run server-start` with new env vars + curl `/healthcheck` to confirm port + Chroma connectivity. |

## Out of Scope

- **`NEO_AUTH_*` env-var renames** — they're already operator-facing and descriptive.
- **`NEO_CHROMA_EMBEDDING_PROVIDER` / `NEO_EMBEDDING_PROVIDER` consolidation** — covered by [#10804](https://github.com/neomjs/neo/issues/10804). This ticket is purely transport + Chroma host/port.
- **`GITHUB_*` and `GEMINI_API_KEY`** — explicitly named by @tobiu as set-in-stone (per the calibration that birthed this ticket).
- **Docker artifacts that consume these env vars** — covered by [#10801](https://github.com/neomjs/neo/issues/10801).
- **`MCP_HTTPS_PORT` / TLS-on-server-side** — separate concern; the deployment topology assumes TLS termination at reverse proxy.

## Avoided Traps

- **Rejected: hard-rename without backwards-compat.** Some operators may have `.env` files using `SSE_PORT`; soft fallback is operator-friendlier even if the long-term goal is single-name.
- **Rejected: bundle Chroma host/port with Memory Core SQLite path env vars.** SQLite path is also currently hardcoded (`path.resolve(neoRootDir, '.neo-ai-data/...')`) but operators in shared mode don't need to override it (each container has its own filesystem). Different concern; defer.
- **Rejected: do this work as part of [PR #10806](https://github.com/neomjs/neo/pull/10806) cookbook polish.** Substrate code change vs documentation are different review surfaces; bundling would scope-creep the cookbook PR. Sibling ticket is cleaner.

## Related

- **Parent epic:** [#9999](https://github.com/neomjs/neo/issues/9999) — Cloud-Native Knowledge & Multi-Tenant Memory Core.
- **Surfacing PR:** [PR #10806](https://github.com/neomjs/neo/pull/10806) — cookbook authorship that proposed the new env-var names.
- **Sibling substrate tickets:** [#10801](https://github.com/neomjs/neo/issues/10801) (Docker artifacts — will consume these env vars), [#10802](https://github.com/neomjs/neo/issues/10802) (public canonical URL — may benefit from `MCP_HTTP_PORT` for URL composition), [#10803](https://github.com/neomjs/neo/issues/10803) (reverse proxy reference — references these ports), [#10804](https://github.com/neomjs/neo/issues/10804) (provider consolidation — separate concern), [#10805](https://github.com/neomjs/neo/issues/10805) (integration test harness — will use these env vars in fixture).
- **Operator calibration that birthed this ticket:** @tobiu 2026-05-06 in this session — *"our env var names are not set in stone, except for the github and gemini ones."*

Origin Session ID: `34c8f800-1855-43ff-aea6-d5e6b9410978`

Retrieval Hint: `query_raw_memories(query="env var rename SSE_PORT MCP_HTTP_PORT NEO_CHROMA_HOST NEO_CHROMA_PORT operator descriptive cloud deployment cookbook substrate")`

## Timeline

- 2026-05-06T08:49:53Z @neo-opus-ada added the `enhancement` label
- 2026-05-06T08:49:53Z @neo-opus-ada added the `ai` label
- 2026-05-06T08:49:53Z @neo-opus-ada added the `architecture` label
- 2026-05-06T08:49:53Z @neo-opus-ada added the `build` label
- 2026-05-06T08:50:01Z @neo-opus-ada added parent issue #9999
- 2026-05-06T08:57:55Z @neo-opus-ada cross-referenced by PR #10806
- 2026-05-06T09:00:42Z @neo-opus-ada cross-referenced by #10804
- 2026-05-06T09:14:01Z @neo-opus-ada cross-referenced by #10801
- 2026-05-06T09:14:02Z @neo-opus-ada cross-referenced by #10802
- 2026-05-06T09:43:32Z @neo-opus-ada cross-referenced by PR #10811
- 2026-05-06T09:48:48Z @tobiu referenced in commit `5e320f6` - "docs(agentos): polish cookbook Section 7 healthcheck JSON sample (#10800) (#10811)

Three-fold Section 7 polish following the merged PR #10806 cookbook +
PR #10810 provider consolidation:

1. Added providers.auth block to the JSON sample with the canonical
   {configured, oidc.{...}, proxyHeader.{...}} shape from PR #10798
   (#10770 healthcheck observability substrate). Documents the
   trustProxyIdentity-mode operator verification path explicitly.
2. Added providers.summary block (#10724 shape) so operators see all
   three providers.* siblings (embedding, summary, auth) in one place.
3. Updated providers.embedding from the prior dual-shape
   {aligned, chroma, neo} (Option B from PR #10799) to the single-block
   {active, host, model, dimensions} shape per PR #10810 consolidation.
   The dual-shape sample shipped in cookbook PR #10806 before
   PR #10810's merge; this aligns the reference doc with the now-shipped
   substrate.

Plus added #10808 cross-link to Section 9 Known Gaps (filed after
cookbook PR opened; fresh follow-up tracking env-var ergonomics).

Doc-only change; no runtime impact. Per pull-request-workflow §6.1
micro-change exemption ("pure documentation with no runtime impact"),
cross-family approval not strictly required, but routing primary review
to @neo-gemini-3-1-pro per round-robin + subsystem-familiarity (she
authored the cookbook).

Co-authored-by: tobiu <tobiasuhlig78@gmail.com>"
- 2026-05-06T09:54:01Z @neo-opus-ada assigned to @neo-opus-ada
- 2026-05-06T10:02:49Z @neo-opus-ada cross-referenced by PR #10812
- 2026-05-06T10:27:46Z @neo-opus-ada referenced in commit `78c5e8c` - "feat(ai): operator-facing env var ergonomics for MCP HTTP port + Chroma host/port (#10808)

Renames `SSE_PORT` env var to `MCP_HTTP_PORT` (transport-mechanism-agnostic +
intent-clear for operators provisioning containers) and wires `NEO_CHROMA_HOST` /
`NEO_CHROMA_PORT` as env-overridable across both KB and MC config templates.
Soft-rename pattern preserves backwards-compat: legacy env vars remain readable
during the deprecation window with warnings on conflict.

Surfaced from PR #10806 cookbook Section 6 forward-looking env-var names + @tobiu
calibration 2026-05-06: "our env var names are not set in stone, except for the
github and gemini ones". Cookbook documented these names ahead of substrate
wiring; this PR closes the gap.

## Implementation

- `ai/mcp/server/shared/helpers/DeploymentConfig.mjs` (new): pure
  `resolveMcpHttpPort({env, warn, defaultPort})` helper. Pattern mirrors
  `EmbeddingProviderConfig.mjs` from #10810 (testable without singleton; mocked
  env + warn sinks in unit tests).
- `ai/mcp/server/knowledge-base/config.template.mjs`: `ssePort` field renamed
  to `mcpHttpPort` consuming the resolver; KB Chroma `host` / `port` now read
  `NEO_CHROMA_HOST` / `NEO_CHROMA_PORT` (currently hardcoded `localhost:8000`).
- `ai/mcp/server/memory-core/config.template.mjs`: `ssePort` → `mcpHttpPort`;
  `engines.kb.chroma.{host, port}` fallback chain prefers
  `NEO_CHROMA_HOST/PORT` → `NEO_KB_CHROMA_HOST/PORT` legacy → `localhost:8000`.
- Consumers updated: `TransportService.mjs` (SSE setup + URL composition),
  `Server.mjs` JSDoc references in both KB + MC.

## Test Evidence

- 6 new unit tests in
  `test/playwright/unit/ai/mcp/server/shared/helpers/DeploymentConfig.spec.mjs`
  covering: default fallback, MCP_HTTP_PORT-only, SSE_PORT-only legacy fallback
  with deprecation warning, both-set-conflict (MCP_HTTP_PORT wins + conflict
  warning), both-set-same-value (deprecation but no conflict), empty-string
  treated as unset.
- All 6 passing in 1.0s (`npx playwright test ...DeploymentConfig.spec.mjs`).
- `TransportService.spec.mjs` mock fixture updated `ssePort` → `mcpHttpPort`.
- `Authorization.spec.mjs` left unchanged as the legacy-env-var fallback
  validation (its `SSE_PORT=...` env block still works via the resolver
  fallback — empirical backwards-compat proof).
- `node --check` passes on all 6 modified `.mjs` files + the new helper.

## Doc Updates

- `learn/agentos/DeploymentCookbook.md` Section 6 footnote ratified
  ("Env-var ergonomics shipped in #10808 — `MCP_HTTP_PORT` is the canonical
  operator-facing env var; `NEO_CHROMA_HOST/PORT` are now env-overridable").
- `learn/agentos/MemoryCore.md` config example + cloud-deployment narrative
  updated.
- `learn/agentos/KnowledgeBase.md` `mcpHttpPort` field reference.
- `learn/agentos/tooling/{Authorization,GoogleAuthDemo,MemoryCoreMcpAuth}.md`
  operator config examples updated with `MCP_HTTP_PORT` + legacy-alias notes.

## Slot Rationale

`learn/agentos/**` paths touched (5 doc files) — substrate-mutation per
`pull-request-workflow §1.1`.

- All 5 doc updates: disposition `keep` → `keep`. 3-axis: trigger-frequency
  medium (operator deployment / IdP setup / cookbook walkthrough),
  failure-severity high (operators following stale env-var docs would hit
  silent default-binding misconfigurations), enforceability medium (verifiable
  by inspection against the new helper + config templates).
- Decay mitigation: deprecation language consistently names `SSE_PORT` /
  `NEO_KB_CHROMA_HOST/PORT` as legacy aliases tied to one #10808 deprecation
  window. Each doc references the ticket for traceability when the alias
  removal lands later.

## Backwards-Compat Migration

Operators with existing gitignored `config.mjs` files using `ssePort` field
should migrate to `mcpHttpPort` in their next config refresh, OR rely on the
resolver's fallback to legacy `SSE_PORT` env var (deprecation warning will
surface in logs). Same friction shape as #10810 post-merge migration —
worktree gitignored configs need updating to match new template shape.

`bootstrapWorktree.mjs --migrate-config` substrate guardrail (proposed in MX
ticket coming) would catch this automatically; tracked separately.

Co-authored-by: Claude Opus 4.7 <neo-opus-4-7@neomjs.com>"
- 2026-05-06T10:27:46Z @neo-opus-ada referenced in commit `917d2c9` - "feat(ai): polish #10808 — NaN handling, Chroma resolver helpers, doc fix (#10808)

Cycle-2 polish addressing PR #10812 review by @neo-gpt:

1. Hardened resolveMcpHttpPort against non-numeric / out-of-range / non-integer
   port values. Previously `MCP_HTTP_PORT=abc` silently leaked NaN through
   `app.listen(port)`. Now invalid values fall back to default with explicit
   warning naming the offending env var. Bounded to TCP port range 1..65535.

2. Extracted resolveChromaHost / resolveChromaPort sibling helpers in
   ai/mcp/server/shared/helpers/DeploymentConfig.mjs. Pattern matches
   resolveMcpHttpPort: pure functions with mockable env + warn sinks. Both KB
   config.template.mjs (own Chroma) and MC config.template.mjs
   (engines.kb.chroma fallback chain) now consume them. Replaces the inline
   `process.env.X || ...` patterns from cycle 1, which were not unit-testable
   without dynamic config-import (which triggers Namespace collision in
   unitTestMode).

3. Added 14 new unit tests covering:
   - resolveMcpHttpPort: NaN handling, out-of-range, non-integer, invalid
     MCP_HTTP_PORT-with-valid-SSE_PORT fallthrough (4 new tests)
   - resolveChromaHost: defaults, NEO_CHROMA_HOST consumed, legacy fallback,
     precedence, KB-callsite-no-legacy isolation, empty-string handling,
     custom defaultHost (7 tests)
   - resolveChromaPort: defaults, valid integer, legacy fallback, precedence,
     NaN handling, out-of-range rejection, invalid-with-legacy-fallthrough,
     custom defaultPort (8 tests, but some overlap as table-driven)
   - 26/26 tests passing; npx playwright test ... DeploymentConfig.spec.mjs
     reports 1.1s.

4. Fixed MemoryCoreMcpAuth.md prefixed-env-var doc drift. The
   NEO_MEMORY_CORE_* prefixed example was conceptual but the substrate
   consumes the unprefixed forms (TRANSPORT, MCP_HTTP_PORT, AUTH_ISSUER_URL,
   OAUTH_CLIENT_*). Replaced the prefixed example with the actual env vars
   the code reads + added explicit operator note about per-server env-var
   namespacing patterns (launcher-layer .env / docker-compose / systemd) for
   multi-MCP-server deployments. Prefixed substrate translation tracked as
   future work if the pattern proves load-bearing.

Co-authored-by: Claude Opus 4.7 <neo-opus-4-7@neomjs.com>"
- 2026-05-06T11:06:53Z @tobiu referenced in commit `9e349cb` - "feat(ai): operator-facing env var ergonomics for MCP HTTP port + Chroma host/port (#10808) (#10812)

* feat(ai): operator-facing env var ergonomics for MCP HTTP port + Chroma host/port (#10808)

Renames `SSE_PORT` env var to `MCP_HTTP_PORT` (transport-mechanism-agnostic +
intent-clear for operators provisioning containers) and wires `NEO_CHROMA_HOST` /
`NEO_CHROMA_PORT` as env-overridable across both KB and MC config templates.
Soft-rename pattern preserves backwards-compat: legacy env vars remain readable
during the deprecation window with warnings on conflict.

Surfaced from PR #10806 cookbook Section 6 forward-looking env-var names + @tobiu
calibration 2026-05-06: "our env var names are not set in stone, except for the
github and gemini ones". Cookbook documented these names ahead of substrate
wiring; this PR closes the gap.

## Implementation

- `ai/mcp/server/shared/helpers/DeploymentConfig.mjs` (new): pure
  `resolveMcpHttpPort({env, warn, defaultPort})` helper. Pattern mirrors
  `EmbeddingProviderConfig.mjs` from #10810 (testable without singleton; mocked
  env + warn sinks in unit tests).
- `ai/mcp/server/knowledge-base/config.template.mjs`: `ssePort` field renamed
  to `mcpHttpPort` consuming the resolver; KB Chroma `host` / `port` now read
  `NEO_CHROMA_HOST` / `NEO_CHROMA_PORT` (currently hardcoded `localhost:8000`).
- `ai/mcp/server/memory-core/config.template.mjs`: `ssePort` → `mcpHttpPort`;
  `engines.kb.chroma.{host, port}` fallback chain prefers
  `NEO_CHROMA_HOST/PORT` → `NEO_KB_CHROMA_HOST/PORT` legacy → `localhost:8000`.
- Consumers updated: `TransportService.mjs` (SSE setup + URL composition),
  `Server.mjs` JSDoc references in both KB + MC.

## Test Evidence

- 6 new unit tests in
  `test/playwright/unit/ai/mcp/server/shared/helpers/DeploymentConfig.spec.mjs`
  covering: default fallback, MCP_HTTP_PORT-only, SSE_PORT-only legacy fallback
  with deprecation warning, both-set-conflict (MCP_HTTP_PORT wins + conflict
  warning), both-set-same-value (deprecation but no conflict), empty-string
  treated as unset.
- All 6 passing in 1.0s (`npx playwright test ...DeploymentConfig.spec.mjs`).
- `TransportService.spec.mjs` mock fixture updated `ssePort` → `mcpHttpPort`.
- `Authorization.spec.mjs` left unchanged as the legacy-env-var fallback
  validation (its `SSE_PORT=...` env block still works via the resolver
  fallback — empirical backwards-compat proof).
- `node --check` passes on all 6 modified `.mjs` files + the new helper.

## Doc Updates

- `learn/agentos/DeploymentCookbook.md` Section 6 footnote ratified
  ("Env-var ergonomics shipped in #10808 — `MCP_HTTP_PORT` is the canonical
  operator-facing env var; `NEO_CHROMA_HOST/PORT` are now env-overridable").
- `learn/agentos/MemoryCore.md` config example + cloud-deployment narrative
  updated.
- `learn/agentos/KnowledgeBase.md` `mcpHttpPort` field reference.
- `learn/agentos/tooling/{Authorization,GoogleAuthDemo,MemoryCoreMcpAuth}.md`
  operator config examples updated with `MCP_HTTP_PORT` + legacy-alias notes.

## Slot Rationale

`learn/agentos/**` paths touched (5 doc files) — substrate-mutation per
`pull-request-workflow §1.1`.

- All 5 doc updates: disposition `keep` → `keep`. 3-axis: trigger-frequency
  medium (operator deployment / IdP setup / cookbook walkthrough),
  failure-severity high (operators following stale env-var docs would hit
  silent default-binding misconfigurations), enforceability medium (verifiable
  by inspection against the new helper + config templates).
- Decay mitigation: deprecation language consistently names `SSE_PORT` /
  `NEO_KB_CHROMA_HOST/PORT` as legacy aliases tied to one #10808 deprecation
  window. Each doc references the ticket for traceability when the alias
  removal lands later.

## Backwards-Compat Migration

Operators with existing gitignored `config.mjs` files using `ssePort` field
should migrate to `mcpHttpPort` in their next config refresh, OR rely on the
resolver's fallback to legacy `SSE_PORT` env var (deprecation warning will
surface in logs). Same friction shape as #10810 post-merge migration —
worktree gitignored configs need updating to match new template shape.

`bootstrapWorktree.mjs --migrate-config` substrate guardrail (proposed in MX
ticket coming) would catch this automatically; tracked separately.

Co-authored-by: Claude Opus 4.7 <neo-opus-4-7@neomjs.com>

* feat(ai): polish #10808 — NaN handling, Chroma resolver helpers, doc fix (#10808)

Cycle-2 polish addressing PR #10812 review by @neo-gpt:

1. Hardened resolveMcpHttpPort against non-numeric / out-of-range / non-integer
   port values. Previously `MCP_HTTP_PORT=abc` silently leaked NaN through
   `app.listen(port)`. Now invalid values fall back to default with explicit
   warning naming the offending env var. Bounded to TCP port range 1..65535.

2. Extracted resolveChromaHost / resolveChromaPort sibling helpers in
   ai/mcp/server/shared/helpers/DeploymentConfig.mjs. Pattern matches
   resolveMcpHttpPort: pure functions with mockable env + warn sinks. Both KB
   config.template.mjs (own Chroma) and MC config.template.mjs
   (engines.kb.chroma fallback chain) now consume them. Replaces the inline
   `process.env.X || ...` patterns from cycle 1, which were not unit-testable
   without dynamic config-import (which triggers Namespace collision in
   unitTestMode).

3. Added 14 new unit tests covering:
   - resolveMcpHttpPort: NaN handling, out-of-range, non-integer, invalid
     MCP_HTTP_PORT-with-valid-SSE_PORT fallthrough (4 new tests)
   - resolveChromaHost: defaults, NEO_CHROMA_HOST consumed, legacy fallback,
     precedence, KB-callsite-no-legacy isolation, empty-string handling,
     custom defaultHost (7 tests)
   - resolveChromaPort: defaults, valid integer, legacy fallback, precedence,
     NaN handling, out-of-range rejection, invalid-with-legacy-fallthrough,
     custom defaultPort (8 tests, but some overlap as table-driven)
   - 26/26 tests passing; npx playwright test ... DeploymentConfig.spec.mjs
     reports 1.1s.

4. Fixed MemoryCoreMcpAuth.md prefixed-env-var doc drift. The
   NEO_MEMORY_CORE_* prefixed example was conceptual but the substrate
   consumes the unprefixed forms (TRANSPORT, MCP_HTTP_PORT, AUTH_ISSUER_URL,
   OAUTH_CLIENT_*). Replaced the prefixed example with the actual env vars
   the code reads + added explicit operator note about per-server env-var
   namespacing patterns (launcher-layer .env / docker-compose / systemd) for
   multi-MCP-server deployments. Prefixed substrate translation tracked as
   future work if the pattern proves load-bearing.

Co-authored-by: Claude Opus 4.7 <neo-opus-4-7@neomjs.com>

---------

Co-authored-by: tobiu <tobiasuhlig78@gmail.com>"
- 2026-05-06T11:06:53Z @tobiu closed this issue
- 2026-05-06T11:40:41Z @neo-opus-ada cross-referenced by #10815
- 2026-05-06T11:56:20Z @neo-opus-ada cross-referenced by PR #10817
- 2026-05-06T16:00:40Z @neo-opus-ada cross-referenced by #10822
- 2026-05-06T16:14:34Z @neo-opus-ada cross-referenced by #10826
- 2026-05-06T16:21:37Z @neo-opus-ada cross-referenced by PR #10828
- 2026-05-06T17:19:44Z @tobiu referenced in commit `7af53f8` - "feat(skills): codify clean-slate sunset rule for env-var renames (#10826) (#10828)

* feat(skills): codify clean-slate sunset rule for env-var renames (#10826)

Adds pull-request-workflow.md §1.1.1 with the clean-slate hard-cut rule:
env-var renames must rename code + .env + tests in ONE PR, no legacyEnvVar
parameters, no console.warn deprecation chains, no multi-layer fallback.
Released-version compat exception requires explicit sunset trigger
documentation (commit SHA / version / N-cycles-from-merge).

Cross-links the rule from pr-review-guide.md §7.6 anti-patterns table so
reviewers can immediately reject deprecation chains at first cycle.

Empirical anchor: PRs #10808 / #10810 / #10814 each added env-var
deprecation chains protecting users-who-don't-exist (legacy vars never
shipped in a released npm version). AGENTS.md §13 substrate-accretion-
defense was empirically not enforced at PR-review time; this rule
codifies the gate at the workflow surface where authors author and
reviewers review.

Resolves AC3 of Epic #10822 Config substrate cleanup.
Calibration absorbed: env-var precedence remains intentional (Playwright,
sub-agents, operator overrides all rely on env-first); the rule eliminates
deprecated-name fallback chains UNDERNEATH env-var resolution, not the
env-var-first precedence itself.

Co-authored-by: Claude Opus 4.7 <neo-opus-4-7@neomjs.com>

* fix(skills): revert pr-review-guide §7.6 anti-patterns row addition (#10826)

Operator-relayed calibration: `pr-review-guide.md` is the always-loaded
payload of a high-traffic skill (45,210 bytes / 436 lines per
cognitive-load-baseline-2026-05.md). Even a single substantive row
addition compounds the per-PR-review byte cost across the swarm.

Per Progressive Disclosure pattern (skill-authoring-guide §"Slot-Rule
Discriminator", origin in #9698 / #9701, summary_d87e357e 2026-04-04):
the §7.6 row should have rated `compress-to-trigger` not `keep` —
trigger-frequency every-PR-review, failure-severity moderate, env-var-
rename PRs are low-frequency relative to total review surface.

Substrate (rule body in pull-request-workflow.md §1.1.1) fires at PR-
authoring time which is the right moment. Reviewers covering env-var-
rename PRs already trigger §1.1 Substrate-Mutation Pre-Flight Gate
(touches .agents/skills/** etc.) so the §7.6 row was over-engineering
the always-loaded reviewer surface.

Aligns with Epic #10733 (cognitive-load audit) byte-reduction direction.

Co-authored-by: Claude Opus 4.7 <neo-opus-4-7@neomjs.com>

* refactor(skills): restructure env-var-rename rule per Progressive Disclosure (#10826)

Operator-relayed Cycle 3 calibration: 21-line rule body in
pull-request-workflow.md §1.1 + 1-row reference in pr-review-guide.md
§7.6 was inverted. Rule body should live in a NEW dedicated reference
file behind triggers; both high-traffic substrate files should carry
ONLY minimal triggers per Progressive Disclosure pattern.

Per skill-authoring-guide §"Slot-Rule Discriminator" + cognitive-load-
baseline-2026-05.md byte-budget targeting:
- pull-request-workflow.md (22,638 bytes baseline) loses 19 lines (was
  21-line rule body, now 1-line trigger)
- pr-review-guide.md (45,210 bytes baseline) gains 1-line trigger row
  (Cycle 2 reverted the original; Cycle 3 restores as proper trigger)
- New ai/skills/pull-request/references/env-var-rename-rule.md (30
  lines) holds the rule body — loaded conditionally only when trigger
  fires (PR touches env-var resolvers)

File renamed from "sunset-rule" to "env-var-rename-rule" per operator:
"sunset" namespace collides with /session-sunset skill. Topic-preserving
name: env vars in general; action: hard cut / one shot.

Net delta: -18 lines on always-loaded substrate; +30 lines in
conditional payload. Aligns with Epic #10733 cognitive-load audit.

Co-authored-by: Claude Opus 4.7 <neo-opus-4-7@neomjs.com>

* fix(skills): remove harness-private memory citation + correct Neo identity framing (#10826)

Operator-relayed calibration on env-var-rename-rule.md "Why no
deprecation chains" rationale:

- Removed `feedback_neo_is_engine_not_framework` citation per
  feedback_no_harness_private_load_bearing_citations rule (Claude
  Code memory file; not a public-anchor citation).
- Replaced "Engine-class deployments" framing with accurate Neo
  identity framing per README.md: Neo is a self-evolving digital
  organism (Brain + Institution + Body + Evolution), not a
  framework/engine binary. The framing now describes the operator
  population directly (swarm + selected partners deploying the
  Agent OS) without invoking misleading framework-vs-engine
  category metaphors.

Substantively the conclusion is unchanged: framework-class
deprecation chains assume an external user base across release
windows; that assumption doesn't apply to Neo's substrate
deployment topology, so KISS hard-cut renames are correct.

Co-authored-by: Claude Opus 4.7 <neo-opus-4-7@neomjs.com>

---------

Co-authored-by: tobiu <tobiasuhlig78@gmail.com>"
- 2026-05-06T17:20:57Z @neo-opus-ada cross-referenced by PR #10836
- 2026-05-06T23:49:42Z @neo-opus-ada cross-referenced by #10862
- 2026-05-07T00:35:05Z @neo-opus-ada cross-referenced by PR #10863
- 2026-05-07T07:13:43Z @neo-opus-ada cross-referenced by PR #10877
- 2026-05-07T07:31:51Z @neo-opus-ada cross-referenced by #10878
- 2026-05-07T07:41:13Z @neo-opus-ada cross-referenced by PR #10879
- 2026-05-07T07:48:03Z @neo-opus-ada cross-referenced by PR #10880
- 2026-05-07T09:14:38Z @tobiu referenced in commit `95f451d` - "test(mcp/Authorization): align fixture env-vars with #10877 NEO_ rename (#10878) (#10879)

Post-#10877 NEO_ prefix institutionalization, the Authorization.spec.mjs env
fixtures still passed bare-name vars (TRANSPORT, AUTO_SYNC, AUTO_SUMMARIZE)
to the spawned MCP server processes. The server's envBindings tables now
read NEO_TRANSPORT, NEO_AUTO_SYNC, NEO_AUTO_SUMMARIZE — bare-name fixtures
were silently ignored, server fell back to defaults, and the test contract
no longer reflected its own intent.

This commit renames all 6 occurrences across the two `spawn(...)` env blocks
(test.beforeAll at line 73-86, discoveryProcess at line 185-196) to use the
canonical NEO_ prefix where applicable:
- TRANSPORT      -> NEO_TRANSPORT
- AUTO_SYNC      -> NEO_AUTO_SYNC
- AUTO_SUMMARIZE -> NEO_AUTO_SUMMARIZE

Block-formatting alignment recalculated to the new longest property
(NEO_AUTO_SUMMARIZE, 18 chars) per coding-guideline §8.32.

Out of scope:
- SSE_PORT (legacy alias under #10808 deprecation window — separate concern).
- AUTH_*, OAUTH_*, HOST: third-party canonical / host-config; correctly NOT
  prefixed per #10862's out-of-scope policy.
- Pre-existing stdout/stderr listener bug: the test's resolve callback only
  watches stdout for "Server started on SSE transport", but the server logs
  this via logger.info to stderr — so the wait times out regardless of env
  state. Filed as follow-up issue (separate from the env-var contract fix
  this commit ships).

Verification:
- `node --check test/playwright/unit/ai/mcp/Authorization.spec.mjs` — passes.
- `grep -rE "(?<![A-Z_])(AUTO_SUMMARIZE|AUTO_DREAM|AUTO_GOLDEN_PATH|REAL_TIME_MEMORY_PARSING|AUTO_INGEST_FS|AUTO_SYNC|MEMORY_COLLECTION_NAME|SESSION_COLLECTION_NAME|GRAPH_COLLECTION_NAME|GRAPH_DECAY_FACTOR)\\b" test/` returns zero results.

Origin Session ID: 78a3272e-847b-4799-ad6c-ce334464844c

Co-authored-by: tobiu <tobiasuhlig78@gmail.com>"
- 2026-05-07T09:26:17Z @neo-gpt cross-referenced by #10884
- 2026-05-07T09:57:28Z @neo-opus-ada cross-referenced by PR #10885
- 2026-05-07T11:41:58Z @neo-opus-ada cross-referenced by PR #10893
- 2026-05-21T15:58:56Z @neo-gpt cross-referenced by #11719
- 2026-05-25T00:52:38Z @neo-opus-ada cross-referenced by PR #11943
- 2026-05-25T02:11:32Z @neo-opus-ada cross-referenced by PR #11949
- 2026-05-25T10:28:47Z @neo-opus-ada cross-referenced by PR #11967

