@littlebigbrain/mcp 0.2.3 → 0.2.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,12 +1,10 @@
1
1
  # @littlebigbrain/mcp
2
2
 
3
- Eleven task-shaped MCP tools for searching, querying, and writing a little big
4
- brain graph from Claude, Cursor, Codex, or another MCP client.
3
+ Eleven task-shaped [MCP](https://modelcontextprotocol.io) tools that let Claude, Cursor, Codex, or any MCP client search, query, and write a [Little Big Brain](https://littlebigbrain.com) graph. Ships two ways: a hosted endpoint with OAuth sign-in, and a local stdio server.
5
4
 
6
- ## Hosted OAuth
5
+ ## Hosted (OAuth) — recommended
7
6
 
8
- Use the hosted endpoint; the client opens WorkOS sign-in and your machine never
9
- stores a little big brain stack key:
7
+ The client opens WorkOS sign-in; your machine never stores a Little Big Brain key. Point it at your stack:
10
8
 
11
9
  ```json
12
10
  {
@@ -18,7 +16,7 @@ stores a little big brain stack key:
18
16
  }
19
17
  ```
20
18
 
21
- Codex Desktop should use the origin plus a stack header:
19
+ Codex sends the URL as an OAuth `resource`, so use the origin plus a stack header instead:
22
20
 
23
21
  ```json
24
22
  {
@@ -32,7 +30,9 @@ Codex Desktop should use the origin plus a stack header:
32
30
  }
33
31
  ```
34
32
 
35
- ## Local stdio
33
+ ## Local (stdio)
34
+
35
+ Run against any data-plane endpoint with a stack API key:
36
36
 
37
37
  ```json
38
38
  {
@@ -41,7 +41,7 @@ Codex Desktop should use the origin plus a stack header:
41
41
  "command": "npx",
42
42
  "args": ["-y", "@littlebigbrain/mcp"],
43
43
  "env": {
44
- "LBB_BASE_URL": "https://db.eu.littlebigbrain.com",
44
+ "LBB_BASE_URL": "https://0abc1def--production.db.eu.littlebigbrain.com",
45
45
  "LBB_API_KEY": "lbb_sk_live_..."
46
46
  }
47
47
  }
@@ -49,7 +49,10 @@ Codex Desktop should use the origin plus a stack header:
49
49
  }
50
50
  ```
51
51
 
52
- Set `LBB_GRAPH` or `LBB_BRANCH` when the scope is not `main`.
52
+ Set `LBB_GRAPH` or `LBB_BRANCH` to target a scope other than `main`.
53
+ `LBB_BASE_URL` has no hosted default: copy `endpoint_url` from the stack's
54
+ Connect page. The MCP process exits with a configuration error when it is
55
+ missing.
53
56
 
54
57
  ## Tools
55
58
 
@@ -67,24 +70,22 @@ Set `LBB_GRAPH` or `LBB_BRANCH` when the scope is not `main`.
67
70
  | `lbb_configure` | ontology, schema, and inference rules |
68
71
  | `lbb_index` | BM25, vector, and adjacency refresh |
69
72
 
70
- Read tools return compact structured envelopes by default. Use `detail`,
71
- `row_limit`, and returned cursors to page without silently truncating results.
72
- Write tools derive idempotency keys unless you provide one.
73
+ Read tools return compact structured envelopes by default use `detail`, `row_limit`, and returned cursors to page without silently truncating. Write tools derive an idempotency key unless you provide one.
73
74
 
74
75
  ## Embed the server
75
76
 
77
+ For self-hosting behind your own auth, the package also serves the tools over HTTP:
78
+
76
79
  ```ts
77
80
  import { createMcpHttpServer } from "@littlebigbrain/mcp";
78
81
 
79
82
  createMcpHttpServer({
80
- baseUrl: "https://db.eu.littlebigbrain.com",
83
+ baseUrl: "https://0abc1def--production.db.eu.littlebigbrain.com",
81
84
  mcpPath: "/mcp",
82
- allowedHosts: ["127.0.0.1", "localhost", "::1", "[::1]"],
85
+ allowedHosts: ["127.0.0.1", "localhost", "::1"],
83
86
  }).listen(8080, "127.0.0.1");
84
87
  ```
85
88
 
86
- The embedded HTTP server passes a key bearer to the database and is for
87
- self-hosting behind your own auth. The hosted endpoint's OAuth/ownership layer
88
- is served by the little big brain API.
89
+ The embedded server passes a key bearer to the data plane; the hosted endpoint's OAuth and ownership layer is served separately by the Little Big Brain API.
89
90
 
90
- Full tool schemas and examples: [MCP documentation](https://docs.littlebigbrain.com/sdks/mcp/).
91
+ Full tool schemas and examples: [docs.littlebigbrain.com/sdks/mcp](https://docs.littlebigbrain.com/sdks/mcp/).
@@ -1,7 +1,8 @@
1
1
  import { type IncomingMessage, type Server } from "node:http";
2
2
  import { LbbClient } from "@littlebigbrain/client";
3
3
  export interface McpHttpServerOptions {
4
- baseUrl?: string;
4
+ /** Required Little Big Brain stack endpoint (`endpoint_url` from Connect). */
5
+ baseUrl: string;
5
6
  mcpPath?: string;
6
7
  maxBodyBytes?: number;
7
8
  requestTimeoutMs?: number;
@@ -30,4 +31,4 @@ export declare function readJsonBody(req: IncomingMessage, maxBodyBytes?: number
30
31
  * Build the key-bearer streamable-HTTP MCP edge without starting a listener.
31
32
  * The hosted multi-tenant OAuth endpoint remains in the SaaS API.
32
33
  */
33
- export declare function createMcpHttpServer(options?: McpHttpServerOptions): Server;
34
+ export declare function createMcpHttpServer(options: McpHttpServerOptions): Server;
@@ -96,13 +96,16 @@ export function readJsonBody(req, maxBodyBytes = DEFAULT_MAX_BODY_BYTES) {
96
96
  * Build the key-bearer streamable-HTTP MCP edge without starting a listener.
97
97
  * The hosted multi-tenant OAuth endpoint remains in the SaaS API.
98
98
  */
99
- export function createMcpHttpServer(options = {}) {
100
- const baseUrl = options.baseUrl ?? "https://db.eu.littlebigbrain.com";
99
+ export function createMcpHttpServer(options) {
100
+ const baseUrl = options.baseUrl.trim();
101
101
  const mcpPath = options.mcpPath ?? "/mcp";
102
102
  const maxBodyBytes = options.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES;
103
103
  const requestTimeoutMs = options.requestTimeoutMs ?? 30_000;
104
104
  const allowedHosts = options.allowedHosts;
105
105
  const allowedOrigins = options.allowedOrigins ?? [];
106
+ if (!baseUrl) {
107
+ throw new Error("baseUrl is required; copy endpoint_url from the stack's Connect page");
108
+ }
106
109
  if (!mcpPath.startsWith("/"))
107
110
  throw new Error("mcpPath must start with /");
108
111
  if (!Number.isInteger(maxBodyBytes) || maxBodyBytes <= 0) {
package/dist/http.js CHANGED
@@ -20,8 +20,12 @@ function csv(value) {
20
20
  return items && items.length > 0 ? items : undefined;
21
21
  }
22
22
  const mcpPath = process.env.LBB_MCP_PATH ?? "/mcp";
23
+ const baseUrl = process.env.LBB_BASE_URL?.trim();
24
+ if (!baseUrl) {
25
+ throw new Error("LBB_BASE_URL is required. Copy endpoint_url from your stack's Connect page, for example https://<tenant-short-id>--<stack-slug>.db.eu.littlebigbrain.com");
26
+ }
23
27
  const httpServer = createMcpHttpServer({
24
- baseUrl: process.env.LBB_BASE_URL ?? "https://db.eu.littlebigbrain.com",
28
+ baseUrl,
25
29
  mcpPath,
26
30
  allowedHosts: csv(process.env.LBB_MCP_ALLOWED_HOSTS) ??
27
31
  (isLoopback ? LOOPBACK_HOSTS : undefined),
package/dist/stdio.js CHANGED
@@ -1,27 +1,75 @@
1
1
  #!/usr/bin/env node
2
+ import { realpathSync } from "node:fs";
3
+ import { pathToFileURL } from "node:url";
2
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
5
  import { LbbClient } from "@littlebigbrain/client";
4
6
  import { buildLbbServer } from "./server.js";
7
+ const BASE_URL_HELP = "LBB_BASE_URL is required. Copy endpoint_url from your Little Big Brain stack's Connect page.";
5
8
  /**
6
9
  * Local stdio entrypoint (`npx @littlebigbrain/mcp`). Reads the connection from the
7
10
  * environment and serves the little big brain tools over stdio, the transport every
8
11
  * editor (Claude Code, Cursor, Codex) supports.
9
12
  *
10
- * LBB_BASE_URL (default http://127.0.0.1:7400)
13
+ * LBB_BASE_URL (required for tool calls; copy endpoint_url from the stack Connect page)
11
14
  * LBB_API_KEY stack API key (lbb_sk_live_…) or single-mode token
12
15
  * LBB_GRAPH / LBB_BRANCH (optional; server defaults to main/main)
16
+ *
17
+ * The base URL is required lazily, at the first tool invocation, not at
18
+ * process start: MCP hosts launch servers to complete the initialize
19
+ * handshake and list tools before the user has finished configuring them,
20
+ * and a startup crash surfaces as an opaque "server failed to start" in
21
+ * every editor. An unconfigured server therefore boots, handshakes, and
22
+ * lists tools normally — and every tool call fails with the actionable
23
+ * message above until LBB_BASE_URL is set. (There is deliberately no
24
+ * localhost default: pointing production tool calls at 127.0.0.1 silently
25
+ * was worse than failing loudly.)
13
26
  */
14
- async function main() {
15
- const client = new LbbClient({
16
- baseUrl: process.env.LBB_BASE_URL ?? "http://127.0.0.1:7400",
17
- apiKey: process.env.LBB_API_KEY,
18
- graph: process.env.LBB_GRAPH,
19
- branch: process.env.LBB_BRANCH,
27
+ export function unconfiguredClient() {
28
+ return new Proxy({}, {
29
+ get(_target, property) {
30
+ // Allow the runtime's duck-typing probes without exploding.
31
+ if (property === "then" || typeof property === "symbol")
32
+ return undefined;
33
+ throw new Error(BASE_URL_HELP);
34
+ },
20
35
  });
36
+ }
37
+ async function main() {
38
+ const baseUrl = process.env.LBB_BASE_URL?.trim();
39
+ if (!baseUrl) {
40
+ console.error(`warning: ${BASE_URL_HELP} Tool calls will fail until it is set.`);
41
+ }
42
+ const client = baseUrl
43
+ ? new LbbClient({
44
+ baseUrl,
45
+ apiKey: process.env.LBB_API_KEY,
46
+ graph: process.env.LBB_GRAPH,
47
+ branch: process.env.LBB_BRANCH,
48
+ })
49
+ : unconfiguredClient();
21
50
  const server = buildLbbServer(client);
22
51
  await server.connect(new StdioServerTransport());
23
52
  }
24
- main().catch((error) => {
25
- console.error(error);
26
- process.exit(1);
27
- });
53
+ // Only start the server when executed as the bin entrypoint — importing this
54
+ // module (e.g. the unconfiguredClient unit tests) must not attach a stdio
55
+ // transport to the host process, which would hold its stdin open forever.
56
+ // npm installs bins as symlinks, so argv[1] must be realpath'd before
57
+ // comparing against import.meta.url (which is always the resolved file).
58
+ function entrypointHref() {
59
+ const argv1 = process.argv[1];
60
+ if (argv1 === undefined)
61
+ return undefined;
62
+ try {
63
+ return pathToFileURL(realpathSync(argv1)).href;
64
+ }
65
+ catch {
66
+ return pathToFileURL(argv1).href;
67
+ }
68
+ }
69
+ const invokedAsBin = import.meta.url === entrypointHref();
70
+ if (invokedAsBin) {
71
+ main().catch((error) => {
72
+ console.error(error);
73
+ process.exit(1);
74
+ });
75
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@littlebigbrain/mcp",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "MCP server for little big brain — graph and hybrid search tools for agents",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -52,7 +52,7 @@
52
52
  "test:coverage": "npm run clean:test && tsc -p tsconfig.test.json && node --test --experimental-test-coverage --test-coverage-include=test-dist/*.js --test-coverage-exclude=test-dist/*.test.js --test-coverage-exclude=test-dist/stdio.js --test-coverage-exclude=test-dist/test-support.js --test-coverage-lines=90 --test-coverage-branches=70 --test-coverage-functions=85 \"test-dist/**/*.test.js\""
53
53
  },
54
54
  "dependencies": {
55
- "@littlebigbrain/client": "^0.4.0",
55
+ "@littlebigbrain/client": "^0.6.0",
56
56
  "@modelcontextprotocol/sdk": "^1",
57
57
  "zod": "^3"
58
58
  },