@kinqs/brainrouter-mcp-server 0.3.4 → 0.3.5

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/README.md CHANGED
@@ -1,6 +1,14 @@
1
- # @kinqs/brainrouter-mcp-server
1
+ # `@kinqs/brainrouter-mcp-server`
2
2
 
3
- The cognitive memory engine behind [BrainRouter](https://github.com/kinqsradiollc/BrainRouter) — exposed as a [Model Context Protocol](https://modelcontextprotocol.io/) server so any MCP-speaking agent (Claude Desktop, Cursor, [`@kinqs/brainrouter-cli`](https://www.npmjs.com/package/@kinqs/brainrouter-cli), custom clients) can recall, capture, and reason over long-term memory.
3
+ The cognitive memory engine behind [BrainRouter](https://github.com/kinqsradiollc/BrainRouter)
4
+ — exposed as a [Model Context Protocol](https://modelcontextprotocol.io/)
5
+ server so any MCP-speaking agent (Claude Desktop, Cursor,
6
+ [`@kinqs/brainrouter-cli`](https://www.npmjs.com/package/@kinqs/brainrouter-cli),
7
+ custom clients) can recall, capture, and reason over long-term memory.
8
+
9
+ Ships the `brainrouter-mcp` binary.
10
+
11
+ ---
4
12
 
5
13
  ## What it gives you
6
14
 
@@ -10,46 +18,111 @@ The cognitive memory engine behind [BrainRouter](https://github.com/kinqsradioll
10
18
  - **Skill catalogue** — `list_skills`, `get_skill`, `search_skills`, `get_persona` — ships with 70+ canonical skills bundled at publish time.
11
19
  - **HTTP and stdio transports** — run as a hosted service (HTTP/SSE) or spawn as a stdio child from any MCP client.
12
20
 
21
+ ---
22
+
13
23
  ## Install
14
24
 
15
25
  ```bash
16
- npm install @kinqs/brainrouter-mcp-server
26
+ npm install -g @kinqs/brainrouter-mcp-server
17
27
  ```
18
28
 
19
- ## Run
29
+ The `-g` flag is required so `brainrouter-mcp` lands on your `$PATH`.
30
+ See [`@kinqs/brainrouter-cli`'s README](https://www.npmjs.com/package/@kinqs/brainrouter-cli)
31
+ for the sudo / nvm caveats — the same rules apply.
20
32
 
21
- ```bash
22
- # HTTP transport on :3747
23
- npx brainrouter-mcp --http --port 3747
33
+ Verify:
24
34
 
25
- # stdio (default — for clients that spawn the server themselves)
26
- npx brainrouter-mcp
35
+ ```bash
36
+ which brainrouter-mcp
37
+ brainrouter-mcp --version # prints 0.3.5
27
38
  ```
28
39
 
40
+ ---
41
+
29
42
  ## Configure
30
43
 
31
- Copy `.env.example` to `.env` and set at minimum:
44
+ The server reads its config from a `.env` file. The challenge for a
45
+ globally-installed package is that you don't know where the package
46
+ lives, and even if you did, it's typically in a path you can't easily
47
+ edit (`/usr/local/lib/node_modules/...` or similar). To fix that, the
48
+ server looks for `.env` in three places, in order:
49
+
50
+ 1. `$BRAINROUTER_ENV_FILE` — explicit override (set this when you want a
51
+ per-project or per-deployment config).
52
+ 2. `~/.config/brainrouter/server.env` — the canonical user location.
53
+ 3. `./.env` — current working directory (matches the classic dotenv
54
+ behavior; useful for monorepo dev).
55
+
56
+ At startup the server prints which path it loaded from, so there's never
57
+ any ambiguity:
58
+
59
+ ```
60
+ env: loaded 17 vars from /Users/you/.config/brainrouter/server.env
61
+ ```
62
+
63
+ ### One-time setup
32
64
 
33
65
  ```bash
66
+ brainrouter-mcp init # scaffolds ~/.config/brainrouter/server.env
67
+ $EDITOR ~/.config/brainrouter/server.env
68
+ ```
69
+
70
+ `init` copies the package's bundled `.env.example` to
71
+ `~/.config/brainrouter/server.env` and chmods it to `0600`. It won't
72
+ overwrite an existing file.
73
+
74
+ ### Minimum fields to set
75
+
76
+ ```bash
77
+ # Cognitive extraction LLM (any OpenAI-compatible endpoint:
78
+ # OpenAI, OpenRouter, LM Studio, Ollama, vLLM…)
34
79
  BRAINROUTER_LLM_API_KEY=sk-...
35
80
  BRAINROUTER_LLM_ENDPOINT=https://api.openai.com/v1/chat/completions
36
81
  BRAINROUTER_LLM_MODEL=gpt-4o-mini
37
82
 
83
+ # Embeddings — required for vector recall. Key falls back to BRAINROUTER_LLM_API_KEY.
38
84
  BRAINROUTER_EMBEDDING_ENDPOINT=https://api.openai.com/v1/embeddings
39
85
  BRAINROUTER_EMBEDDING_MODEL=text-embedding-3-small
40
86
  BRAINROUTER_EMBEDDING_DIMENSIONS=1536
41
87
 
88
+ # Server auth — change before exposing the server
42
89
  BRAINROUTER_ADMIN_PASSWORD=change_me_before_use
43
- BRAINROUTER_JWT_SECRET=replace_with_a_long_random_secret
90
+ BRAINROUTER_JWT_SECRET=replace_with_a_long_random_secret # `node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"`
91
+ ```
92
+
93
+ Full knob list (reranker, prewarming, focus-scene triggers, sweep
94
+ intervals, JWT, CORS) lives in the bundled `.env.example` — view it
95
+ after `init` ran, or directly with:
96
+
97
+ ```bash
98
+ cat "$(npm root -g)/@kinqs/brainrouter-mcp-server/.env.example"
44
99
  ```
45
100
 
46
- Full knob list (reranker, prewarming, focus-scene triggers, sweep intervals, JWT, CORS) lives in `.env.example` next to this README.
101
+ ---
102
+
103
+ ## Run
104
+
105
+ ```bash
106
+ # HTTP transport on :3747 — what the CLI connects to via login
107
+ brainrouter-mcp --http --port 3747
108
+
109
+ # stdio transport — for clients that spawn the server themselves
110
+ brainrouter-mcp
111
+ ```
112
+
113
+ The server writes logs to stderr. To leave it running detached, use a
114
+ process manager (launchd / systemd / tmux / `nohup`) of your choice.
115
+
116
+ ---
47
117
 
48
118
  ## Docs
49
119
 
50
- - [BrainRouter overview](https://github.com/kinqsradiollc/BrainRouter)
51
- - [What the memory engine does](https://github.com/kinqsradiollc/BrainRouter/blob/main/BRAINROUTER.md)
52
- - [Deep dives](https://github.com/kinqsradiollc/BrainRouter/tree/main/brainrouter-docs)
120
+ - **Repo**: <https://github.com/kinqsradiollc/BrainRouter>
121
+ - **Memory engine deep-dive**: [BRAINROUTER.md](https://github.com/kinqsradiollc/BrainRouter/blob/main/BRAINROUTER.md)
122
+ - **Maintainer runbook**: [SETUP.md](https://github.com/kinqsradiollc/BrainRouter/blob/main/SETUP.md)
123
+ - **Bugs / requests**: <https://github.com/kinqsradiollc/BrainRouter/issues>
124
+
125
+ ---
53
126
 
54
127
  ## License
55
128
 
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,47 @@
1
+ // Side-effect module: imported FIRST in src/index.ts so it sets process.env
2
+ // from the right .env file BEFORE any other module evaluates and tries to
3
+ // read those vars.
4
+ //
5
+ // Priority order (first hit wins):
6
+ // 1. $BRAINROUTER_ENV_FILE (explicit user override)
7
+ // 2. ~/.config/brainrouter/server.env (canonical user location — the
8
+ // one a globally-installed
9
+ // `npm i -g @kinqs/brainrouter-mcp-server`
10
+ // user should write to)
11
+ // 3. ./.env (cwd — matches dotenv default,
12
+ // keeps monorepo dev working)
13
+ //
14
+ // The third entry matches dotenv's classic behavior, so existing
15
+ // `cd brainrouter/ && npm run start:http` workflows keep loading
16
+ // `brainrouter/.env` exactly as before. The first two are the additions
17
+ // that fix the global-install UX (users no longer need to cd anywhere
18
+ // special).
19
+ import dotenv from 'dotenv';
20
+ import fs from 'node:fs';
21
+ import path from 'node:path';
22
+ import os from 'node:os';
23
+ function resolveEnvFile() {
24
+ const candidates = [
25
+ process.env.BRAINROUTER_ENV_FILE,
26
+ path.join(os.homedir(), '.config', 'brainrouter', 'server.env'),
27
+ path.join(process.cwd(), '.env'),
28
+ ].filter(Boolean);
29
+ for (const file of candidates) {
30
+ if (fs.existsSync(file))
31
+ return file;
32
+ }
33
+ return null;
34
+ }
35
+ const envFile = resolveEnvFile();
36
+ if (envFile) {
37
+ const result = dotenv.config({ path: envFile });
38
+ const count = Object.keys(result.parsed ?? {}).length;
39
+ process.stderr.write(`env: loaded ${count} var${count === 1 ? '' : 's'} from ${envFile}\n`);
40
+ }
41
+ else {
42
+ process.stderr.write(`env: no .env file found — looked at:\n` +
43
+ ` $BRAINROUTER_ENV_FILE (${process.env.BRAINROUTER_ENV_FILE ? 'set, but missing' : 'unset'})\n` +
44
+ ` ~/.config/brainrouter/server.env\n` +
45
+ ` ${path.join(process.cwd(), '.env')}\n` +
46
+ `Run 'brainrouter-mcp init' to scaffold one (or set BRAINROUTER_LLM_API_KEY and friends in your shell).\n`);
47
+ }
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  #!/usr/bin/env node
2
- export {};
2
+ import './init.js';
3
+ import './env-loader.js';
package/dist/index.js CHANGED
@@ -14,6 +14,17 @@
14
14
  // Runs an Express HTTP server. Connect via serverUrl in tool config.
15
15
  // Usage: node dist/index.js --root /path/to/project --http --port 3747
16
16
  //
17
+ // init subcommand
18
+ // Scaffold ~/.config/brainrouter/server.env from the bundled
19
+ // .env.example and exit. Run this once after a global install.
20
+ // Usage: brainrouter-mcp init
21
+ //
22
+ // CRITICAL: import order matters. `init` may exit the process before
23
+ // anything else loads (for `brainrouter-mcp init`). `env-loader` runs next
24
+ // and sets process.env from the right .env file before any module body
25
+ // reads env vars (sqlite/embedding/extractor all do at load time).
26
+ import './init.js';
27
+ import './env-loader.js';
17
28
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
18
29
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
19
30
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
@@ -91,7 +102,7 @@ const PORT = parseInt(parseFlag('--port') ?? '3747', 10);
91
102
  function buildMcpServer(registry, options) {
92
103
  const defaultUserId = options?.defaultUserId ?? STDIO_DEFAULT_USER_ID;
93
104
  const isAdmin = options?.isAdmin ?? false;
94
- const server = new Server({ name: 'brainrouter-mcp-server', version: '0.3.4' }, { capabilities: { tools: {} } });
105
+ const server = new Server({ name: 'brainrouter-mcp-server', version: '0.3.5' }, { capabilities: { tools: {} } });
95
106
  // ── Tool list ──────────────────────────────────────────────────────────────
96
107
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
97
108
  tools: [
package/dist/init.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/init.js ADDED
@@ -0,0 +1,64 @@
1
+ // Side-effect module: imported FIRST in src/index.ts (before env-loader).
2
+ //
3
+ // Handles the `brainrouter-mcp init` subcommand by scaffolding a user-editable
4
+ // .env file at ~/.config/brainrouter/server.env from the package's bundled
5
+ // .env.example, then exiting. Never returns control when invoked.
6
+ //
7
+ // This solves the global-install UX gap: a user who runs
8
+ // `npm install -g @kinqs/brainrouter-mcp-server` has no obvious place to put
9
+ // their LLM credentials. `brainrouter-mcp init` creates the file in a known
10
+ // user-writable location that env-loader.ts then auto-finds.
11
+ //
12
+ // If the file already exists, init prints the path so the user knows where
13
+ // to edit it — but does NOT overwrite (don't clobber a user's real config).
14
+ import fs from 'node:fs';
15
+ import path from 'node:path';
16
+ import os from 'node:os';
17
+ import url from 'node:url';
18
+ function runInit() {
19
+ const userConfigDir = path.join(os.homedir(), '.config', 'brainrouter');
20
+ const userEnvFile = path.join(userConfigDir, 'server.env');
21
+ // .env.example sits at the package root (one level above src/ in source,
22
+ // one level above dist/ after build, both layouts work in the installed
23
+ // tarball because the `files` allowlist in package.json includes it).
24
+ const here = path.dirname(url.fileURLToPath(import.meta.url));
25
+ const exampleCandidates = [
26
+ path.resolve(here, '..', '.env.example'), // dist/init.js → ../.env.example
27
+ path.resolve(here, '..', '..', '.env.example'), // src/init.ts (dev) → ../../.env.example
28
+ ];
29
+ const examplePath = exampleCandidates.find((p) => fs.existsSync(p));
30
+ if (!examplePath) {
31
+ process.stderr.write(`init: couldn't find .env.example bundled with the package.\n` +
32
+ `Checked:\n${exampleCandidates.map((p) => ` ${p}`).join('\n')}\n` +
33
+ `This is a packaging bug — please file an issue at ` +
34
+ `https://github.com/kinqsradiollc/BrainRouter/issues\n`);
35
+ process.exit(1);
36
+ }
37
+ if (fs.existsSync(userEnvFile)) {
38
+ process.stdout.write(`init: ${userEnvFile} already exists — not overwriting.\n` +
39
+ `Edit it with: $EDITOR ${userEnvFile}\n` +
40
+ `(Or compare against the latest template at ${examplePath})\n`);
41
+ process.exit(0);
42
+ }
43
+ fs.mkdirSync(userConfigDir, { recursive: true });
44
+ fs.copyFileSync(examplePath, userEnvFile);
45
+ // Tighten perms — this file will hold API keys + a JWT secret.
46
+ try {
47
+ fs.chmodSync(userEnvFile, 0o600);
48
+ }
49
+ catch { /* best effort */ }
50
+ process.stdout.write(`init: created ${userEnvFile}\n` +
51
+ `\n` +
52
+ `Next steps:\n` +
53
+ ` 1. Edit it: $EDITOR ${userEnvFile}\n` +
54
+ ` 2. Set BRAINROUTER_LLM_API_KEY (required for cognitive extraction)\n` +
55
+ ` 3. Change BRAINROUTER_ADMIN_PASSWORD and BRAINROUTER_JWT_SECRET\n` +
56
+ ` 4. Start the server: brainrouter-mcp --http --port 3747\n` +
57
+ `\n` +
58
+ `The server auto-finds this file via ~/.config/brainrouter/server.env\n` +
59
+ `(or set BRAINROUTER_ENV_FILE=/some/other/path to override).\n`);
60
+ process.exit(0);
61
+ }
62
+ if (process.argv.includes('init')) {
63
+ runInit();
64
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kinqs/brainrouter-mcp-server",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "description": "BrainRouter MCP server — the cognitive memory engine. Exposes recall, capture, focus scenes, persona, contradictions, skills, and graph queries as MCP tools for any MCP-speaking agent.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -45,7 +45,7 @@
45
45
  "gray-matter": "^4.0.3",
46
46
  "sqlite-vec": "^0.1.9",
47
47
  "zod": "^3.22.4",
48
- "@kinqs/brainrouter-types": "^0.3.4"
48
+ "@kinqs/brainrouter-types": "^0.3.5"
49
49
  },
50
50
  "engines": {
51
51
  "node": ">=22.0.0"