LearnNewsExamplesServices
Frontmatter
id11878
titleShared MCP stderr logger primitive
stateClosed
labels
enhancementairefactoringarchitecture
assigneesneo-gpt
createdAtMay 24, 2026, 4:53 AM
updatedAtMay 24, 2026, 12:54 PM
githubUrlhttps://github.com/neomjs/neo/issues/11878
authorneo-gpt
commentsCount0
parentIssue11871
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMay 24, 2026, 12:54 PM

Shared MCP stderr logger primitive

Closed v13.0.0/archive-v13-0-0-chunk-13 enhancementairefactoringarchitecture
neo-gpt
neo-gpt commented on May 24, 2026, 4:53 AM

Authored by: GPT-5 (Codex Desktop)

Parent Epic: #11871 — Sub 2 of the env-primitive deduplication + shared MCP stderr logger Epic graduated from Discussion #11869 cycle-2.2.

Decision Record impact: none directly. This ticket implements the logger half of the already-graduated Epic #11871 shape and preserves ADR 0014 deployment-subset composability by keeping per-server logger policy in each server config instead of adding a central catalog.

Context

Epic #11871 has an explicit Sub 2 for a shared MCP stderr logger primitive, but the work was still only prose after Sub 1 (#11873 / PR #11876) and the adjacent SwarmHeartbeatService cleanup (#11874 / PR #11877) moved into review. During the nightshift re-review sweep on 2026-05-24, the open issue list showed #11871 and #11873 but no dedicated Sub 2 ticket.

Duplicate sweep:

  • Live GitHub search for shared MCP stderr logger returned only #11871 and #11873.
  • Live GitHub search for MCP stderr logger Logger.mjs returned #11871 plus older closed logger/observability tickets (#7534, #7570, #10576, #10582) with different historical scope.
  • Local resources/content/issues / resources/content/discussions grep for shared MCP stderr logger, Centralized stderr Logger, Centralized Logger for MCP Servers, and Logger.mjs returned no dedicated active Sub 2 match.
  • Knowledge Base semantic duplicate query was attempted but returned Internal error: Error finding id while KB healthcheck was otherwise healthy; this ticket relies on the live GitHub + local content sweep above rather than treating the failed semantic sweep as evidence.

The Problem

Five MCP servers currently carry per-server logger primitives with overlapping behavior but divergent implementation:

Server Current logger shape
github-workflow priority-filtered stderr, default warn, debug promotion
gitlab-workflow same family as github-workflow
knowledge-base daily-rotated file sink plus debug-gated stderr
memory-core daily-rotated file sink plus debug-gated stderr plus flush() for short-lived scripts
neural-link daily-rotated file sink plus tier-gated stderr where info/warn/error write without debug

The drift is not that these servers need identical policy. The drift is that each server owns a separate primitive. Epic #11871 resolved the intended shape: one shared MCP-side stderr-safe primitive, with each server's differences preserved as config.

The Architectural Reality

Pre-Flight (structural fast-path): authoring ai/mcp/server/shared/Logger.mjs matches sibling pattern of ai/mcp/server/shared/BaseConfig.mjs in ai/mcp/server/shared/; both are shared MCP-server primitives consumed by per-server config.template.mjs / server implementations. Sibling-file-lift applies; no novel directory choice. Map-maintenance: not needed because the existing ai/mcp/server/shared/ role already covers shared MCP server substrate.

This must NOT reuse src/util/Logger.mjs / Neo.util.Logger: app/browser logging has incompatible semantics for MCP server transport. MCP server logging must be protocol-safe: stderr/file only, never stdout; error() logs and must not throw.

The deployment-subset model remains load-bearing: every server imports the shared primitive and passes its own config. There is no central logger-config catalog.

The Fix

  1. Create ai/mcp/server/shared/Logger.mjs as the single shared MCP logger primitive.
  2. Migrate the five server logger implementations to consume the shared primitive:
    • ai/mcp/server/github-workflow/logger.mjs
    • ai/mcp/server/gitlab-workflow/logger.mjs
    • ai/mcp/server/knowledge-base/logger.mjs
    • ai/mcp/server/memory-core/logger.mjs
    • ai/mcp/server/neural-link/logger.mjs
  3. Preserve per-server behavior through each server's config/template surface: file prefix, log path, log level, debug gating, tier-gated stderr, error serialization, and flush() support where applicable.
  4. Add or update unit tests for the shared primitive and per-server config differences.
  5. Keep stdout clean for MCP protocol safety.

Contract Ledger

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
ai/mcp/server/shared/Logger.mjs Epic #11871 Sub 2 AC 1-2 Single shared MCP logger primitive; stderr/file only; error() logs and does not throw Existing per-server logger files remain until migrated in same PR JSDoc @summary on shared primitive Unit test: no stdout write; error/log methods do not throw
Per-server logger config slots Epic #11871 Sub 2 AC 4-6 Each server declares its own logger config: sink/gating/file-prefix/log-path/flush availability Defaults reproduce current behavior when config is absent config.template.mjs config comments Unit tests per server config shape
github/gitlab logger behavior Current logger.mjs siblings Priority-filtered stderr, default warn, debug promotes verbosity Default warn if invalid/absent log level Config template Unit test: priority threshold matrix
knowledge-base logger behavior Existing KB logger Daily-rotated file sink plus debug-gated stderr File path default under .neo-ai-data/logs Config template + logger JSDoc Unit test: writes file line and only writes stderr when debug enabled
memory-core logger behavior Existing MC logger Same file sink/debug-gated stderr plus flush() for short-lived scripts flush() resolves no-op when no stream exists Logger JSDoc Unit test: flush() resolves and does not throw
neural-link logger behavior Existing NL logger Daily-rotated file sink plus tier-gated stderr; info/warn/error write without debug, debug gated File path default under .neo-ai-data/logs Config template + logger JSDoc Unit test: info/warn/error stderr behavior independent of debug
stdout safety MCP protocol invariant Logger writes nothing to stdout under all levels N/A Logger JSDoc warning Test spies process.stdout.write / console.log and verifies zero calls
Neo.util.Logger negative boundary Epic #11871 avoided trap No Neo.util.Logger import/use inside ai/ MCP logger path N/A PR body grep evidence git grep -n "Neo.util.Logger|src/util/Logger" -- ai/mcp/server clean

Acceptance Criteria

  1. ai/mcp/server/shared/Logger.mjs exists as the only shared primitive for MCP server logging.
  2. The logger never writes to stdout; all console output goes to stderr or configured file sinks.
  3. error() logs and does not throw.
  4. The five per-server logger reimplementations are deleted or reduced to thin config-bound wrappers around the shared primitive.
  5. Existing per-server behavior is preserved as config, not separate primitive code:
    • github/gitlab: priority-filtered stderr, default warn, debug promotion.
    • knowledge-base: daily-rotated file sink plus debug-gated stderr.
    • memory-core: daily-rotated file sink plus debug-gated stderr plus flush().
    • neural-link: daily-rotated file sink plus tier-gated stderr.
  6. No central logger-config catalog is introduced; each server owns its logger config slot in its own config.template.mjs.
  7. No Neo.util.Logger usage is introduced under ai/mcp/server.
  8. Tests cover stdout safety, priority/tier gating, daily-file prefix/path behavior, Error serialization, and flush() semantics.
  9. All related unit tests and static checks pass.

Out of Scope

  • Changing deployment topology, compose profiles, or TARGET_SERVER behavior.
  • Centralizing every log config into a global catalog.
  • Reworking non-MCP daemon logging outside ai/mcp/server/**.
  • Replacing application/browser Neo.util.Logger.
  • Adding compatibility shims for released users; v13 is unreleased.

Avoided Traps

  • Do not adapt Neo.util.Logger for MCP servers; its browser/app semantics are wrong here.
  • Do not normalize away real per-server policy differences; preserve them as config.
  • Do not write to stdout from an MCP server logger.
  • Do not create another per-server helper layer that reintroduces N implementations.
  • Do not broaden into env-primitive work; Sub 1 is #11873.

Related

Handoff Retrieval Hints

  • Search terms: shared MCP stderr logger, per-server logger reimplementations, Logger.mjs, github-workflow logger, memory-core logger flush, neural-link tier-gated stderr.
  • Useful source files: ai/mcp/server/shared/BaseConfig.mjs, five ai/mcp/server/*/logger.mjs siblings, and each server's config.template.mjs.
  • Retrieval hint: start from Epic #11871 Sub 2 section and this ticket's Contract Ledger before touching code.
tobiu closed this issue on May 24, 2026, 12:54 PM
tobiu referenced in commit 8e97923 - "feat(mcp): add shared logger primitive (#11878) (#11879) on May 24, 2026, 12:54 PM