@theronap/cortex-mcp 0.9.113 → 0.9.114

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/diagnose.mjs CHANGED
@@ -238,11 +238,29 @@ export function classify(status, contentType, bodyText, requestId) {
238
238
  let SESSION_KEY = null
239
239
  export function setSessionKey(key) { SESSION_KEY = key || null }
240
240
 
241
+ // WHO IS CALLING — an identity INDEPENDENT of the session key, and it must stay independent.
242
+ //
243
+ // The server labels every page revision's `actor_source` from this header. It used to default to
244
+ // 'mcp' server-side with nothing setting it, so 'mcp' silently meant "unlabelled" and an ADR-0030
245
+ // scrub run against the HTTP API on 2026-08-26 wrote 340 revisions that read as MCP sessions with
246
+ // dropped attribution — which reopened gate 2 on 2026-08-27 against a cause that was never MCP.
247
+ //
248
+ // ⚠ Deliberately NOT derived from SESSION_KEY, and deliberately sent even when SESSION_KEY is null.
249
+ // The alarm's whole subject is `actor_source='mcp' AND session_key IS NULL`; if this header were
250
+ // conditional on the session, that pair could never occur and the detector would be structurally
251
+ // blind. A one-shot hook process legitimately has no session and IS still cortex-mcp — it should
252
+ // say so, and be visible as one, not disguise itself as a script.
253
+ let CLIENT_ID = 'cortex-mcp/unknown'
254
+ export function setClientVersion(version) {
255
+ CLIENT_ID = `cortex-mcp/${version || 'unknown'}`
256
+ }
257
+
241
258
  export async function fetchCortex(url, opts = {}, { retries = 2, baseDelayMs = 400 } = {}) {
242
259
  const { timeoutMs: optTimeout, signal: callerSignal, ...rest } = opts
260
+ const baseHeaders = { ...(rest.headers ?? {}), 'x-cortex-client': CLIENT_ID }
243
261
  const fetchOpts = SESSION_KEY
244
- ? { ...rest, headers: { ...(rest.headers ?? {}), 'x-cortex-session-key': SESSION_KEY } }
245
- : rest
262
+ ? { ...rest, headers: { ...baseHeaders, 'x-cortex-session-key': SESSION_KEY } }
263
+ : { ...rest, headers: baseHeaders }
246
264
  const timeoutMs = optTimeout ?? (Number(process.env.CORTEX_HTTP_TIMEOUT_MS) || 15_000)
247
265
  let lastErr
248
266
  for (let attempt = 0; attempt <= retries; attempt++) {
package/lib/server.mjs CHANGED
@@ -5,7 +5,7 @@ import { writeFileSync, mkdirSync } from 'fs'
5
5
  import { homedir } from 'os'
6
6
  import { join } from 'path'
7
7
  import { createHash, randomUUID } from 'crypto'
8
- import { fetchCortex, classify, resolveBase, resolveEnvToken, setSessionKey } from './diagnose.mjs'
8
+ import { fetchCortex, classify, resolveBase, resolveEnvToken, setSessionKey, setClientVersion } from './diagnose.mjs'
9
9
  import { folderClaimLine } from './folder_claims.mjs'
10
10
  import { resolveSessionKey, resolveLogSessionId } from './session_key.mjs'
11
11
  import { runSendImessage } from './imessage_send.mjs'
@@ -104,6 +104,10 @@ export async function runServer(version) {
104
104
  // resolve THIS session's brain pointer. Must be set BEFORE the first fetchCortex call below —
105
105
  // otherwise the opening requests of a session would silently resolve by the account pointer.
106
106
  setSessionKey(SESSION_KEY)
107
+ // Paired with setSessionKey and set at the same moment, but a SEPARATE signal on purpose: the
108
+ // server labels actor_source from this and watches for `mcp` + no session. Tying the two together
109
+ // would make that pair impossible and blind the detector. See actorSourceFrom in auth_cloud.ts.
110
+ setClientVersion(version)
107
111
  async function pingSession() {
108
112
  try {
109
113
  await fetchCortex(`${BASE}/api/session-ping`, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theronap/cortex-mcp",
3
- "version": "0.9.113",
3
+ "version": "0.9.114",
4
4
  "description": "Connect your AI assistant to Cortex — your org's projects, activity, gaps, and directives, scoped to you.",
5
5
  "type": "module",
6
6
  "bin": {