bilinc 2.3.0__tar.gz → 2.3.3__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bilinc
3
- Version: 2.3.0
3
+ Version: 2.3.3
4
4
  Summary: Cloud-only memory SDK and MCP adapter for autonomous agents
5
5
  Author: Bilinc Team
6
6
  License-Expression: BUSL-1.1
@@ -56,7 +56,7 @@ Dynamic: license-file
56
56
 
57
57
  Retrieval answers *"what is similar to this?"*. Long-running agents also need to answer *"who wrote this state, was it verified, did it contradict what we already knew, and can we undo it?"* — that is the layer Bilinc provides.
58
58
 
59
- Bilinc 2.3.0 on PyPI is the public cloud-only package: a thin Python SDK, CLI, and MCP adapter for Bilinc Cloud. It does not ship the local StatePlane, storage backends, eval, observability, integrations, or server runtime internals.
59
+ Bilinc 2.3.3 on PyPI is the public cloud-only package: a thin Python SDK, CLI, and MCP adapter for Bilinc Cloud. It does not ship the local StatePlane, storage backends, eval, observability, integrations, or server runtime internals.
60
60
 
61
61
  > **Frozen regression receipt** — LongMemEval-s cleaned retrieval fixture, 500 questions: **Hit@5 98.0%**, **NDCG@5 0.913**, no LLM reranker, no paid API. This is an isolated retrieval guardrail, not a current hosted SLA, end-to-end agent score, or competitor ranking — see [Benchmark receipt](#benchmark-receipt) for the full scope and qualification.
62
62
 
@@ -93,13 +93,30 @@ The fastest path is `pip install -U bilinc`, `bilinc login`, then `bilinc quickt
93
93
  | --- | --- |
94
94
  | A hosted memory API for an agent or MCP client | The public cloud-only package from PyPI |
95
95
  | Local StatePlane, SQLite/PostgreSQL, benchmarks, or internals | This repository and the [architecture guide](docs/architecture.md) |
96
- | A hosted MCP connection | The [MCP setup guide](https://bilinc.space/docs/mcp) |
96
+ | A hosted MCP connection without a Python install | The [remote MCP setup guide](https://bilinc.space/docs/remote-mcp) |
97
97
 
98
98
  The public package is intentionally smaller than this repository. It does not bundle the internal StatePlane or local
99
99
  storage runtime.
100
100
 
101
101
  ## Start in 60 Seconds
102
102
 
103
+ ### Claude connector, without installing Python
104
+
105
+ Open **Customize → Connectors → Add custom connector** in Claude, name it Bilinc,
106
+ and paste `https://mcp.bilinc.space/mcp`. Sign in and choose read-only or read and
107
+ write access. The setup guide is [bilinc.space/mcp](https://bilinc.space/mcp).
108
+
109
+ For the remote connector in Claude Code:
110
+
111
+ ```bash
112
+ claude mcp add --transport http --scope user bilinc https://mcp.bilinc.space/mcp
113
+ ```
114
+
115
+ Then run `/mcp` and authenticate. The remote connector has ten tools; the Python
116
+ stdio adapter below has eight. Both connect to hosted Bilinc memory.
117
+
118
+ ### SDK and CLI
119
+
103
120
  ```bash
104
121
  pip install -U bilinc
105
122
  bilinc login
@@ -119,7 +136,7 @@ Cloud status check.
119
136
  To reproduce this release exactly:
120
137
 
121
138
  ```bash
122
- pip install -U bilinc==2.3.0
139
+ pip install -U bilinc==2.3.3
123
140
  ```
124
141
 
125
142
  If you prefer a browser guide, open https://bilinc.space/install and follow the
@@ -131,18 +148,28 @@ Bilinc exposes a standard Model Context Protocol server over **stdio**, so any
131
148
  MCP-compatible client can connect — Claude Code, Codex, Cursor, Hermes-Agent,
132
149
  opencode, and others.
133
150
 
151
+ After `bilinc login`, let the CLI write the setup for your client:
152
+
153
+ ```bash
154
+ bilinc mcp install --client claude-code # prints a ready `claude mcp add ...` command
155
+ bilinc mcp install --client claude-desktop # prints the config and where Claude Desktop keeps it
156
+ bilinc mcp install # plain mcpServers JSON for any other client
157
+ ```
158
+
134
159
  ```json
135
160
  {
136
161
  "mcpServers": {
137
162
  "bilinc": {
138
- "command": "python",
139
- "args": ["-m", "bilinc.cloud_mcp"],
140
- "env": { "BILINC_API_KEY": "bil_live_..." }
163
+ "command": "/path/to/python3",
164
+ "args": ["-m", "bilinc.cloud_mcp"]
141
165
  }
142
166
  }
143
167
  }
144
168
  ```
145
169
 
170
+ `command` is the interpreter that has Bilinc installed, and the adapter reads the key saved by `bilinc login`, so
171
+ the config carries no key. On a machine without a saved key, add `"env": { "BILINC_API_KEY": "bil_live_..." }`.
172
+
146
173
  Eight tools — the core memory lifecycle, and nothing else:
147
174
 
148
175
  | Tool | What it does |
@@ -14,7 +14,7 @@
14
14
 
15
15
  Retrieval answers *"what is similar to this?"*. Long-running agents also need to answer *"who wrote this state, was it verified, did it contradict what we already knew, and can we undo it?"* — that is the layer Bilinc provides.
16
16
 
17
- Bilinc 2.3.0 on PyPI is the public cloud-only package: a thin Python SDK, CLI, and MCP adapter for Bilinc Cloud. It does not ship the local StatePlane, storage backends, eval, observability, integrations, or server runtime internals.
17
+ Bilinc 2.3.3 on PyPI is the public cloud-only package: a thin Python SDK, CLI, and MCP adapter for Bilinc Cloud. It does not ship the local StatePlane, storage backends, eval, observability, integrations, or server runtime internals.
18
18
 
19
19
  > **Frozen regression receipt** — LongMemEval-s cleaned retrieval fixture, 500 questions: **Hit@5 98.0%**, **NDCG@5 0.913**, no LLM reranker, no paid API. This is an isolated retrieval guardrail, not a current hosted SLA, end-to-end agent score, or competitor ranking — see [Benchmark receipt](#benchmark-receipt) for the full scope and qualification.
20
20
 
@@ -51,13 +51,30 @@ The fastest path is `pip install -U bilinc`, `bilinc login`, then `bilinc quickt
51
51
  | --- | --- |
52
52
  | A hosted memory API for an agent or MCP client | The public cloud-only package from PyPI |
53
53
  | Local StatePlane, SQLite/PostgreSQL, benchmarks, or internals | This repository and the [architecture guide](docs/architecture.md) |
54
- | A hosted MCP connection | The [MCP setup guide](https://bilinc.space/docs/mcp) |
54
+ | A hosted MCP connection without a Python install | The [remote MCP setup guide](https://bilinc.space/docs/remote-mcp) |
55
55
 
56
56
  The public package is intentionally smaller than this repository. It does not bundle the internal StatePlane or local
57
57
  storage runtime.
58
58
 
59
59
  ## Start in 60 Seconds
60
60
 
61
+ ### Claude connector, without installing Python
62
+
63
+ Open **Customize → Connectors → Add custom connector** in Claude, name it Bilinc,
64
+ and paste `https://mcp.bilinc.space/mcp`. Sign in and choose read-only or read and
65
+ write access. The setup guide is [bilinc.space/mcp](https://bilinc.space/mcp).
66
+
67
+ For the remote connector in Claude Code:
68
+
69
+ ```bash
70
+ claude mcp add --transport http --scope user bilinc https://mcp.bilinc.space/mcp
71
+ ```
72
+
73
+ Then run `/mcp` and authenticate. The remote connector has ten tools; the Python
74
+ stdio adapter below has eight. Both connect to hosted Bilinc memory.
75
+
76
+ ### SDK and CLI
77
+
61
78
  ```bash
62
79
  pip install -U bilinc
63
80
  bilinc login
@@ -77,7 +94,7 @@ Cloud status check.
77
94
  To reproduce this release exactly:
78
95
 
79
96
  ```bash
80
- pip install -U bilinc==2.3.0
97
+ pip install -U bilinc==2.3.3
81
98
  ```
82
99
 
83
100
  If you prefer a browser guide, open https://bilinc.space/install and follow the
@@ -89,18 +106,28 @@ Bilinc exposes a standard Model Context Protocol server over **stdio**, so any
89
106
  MCP-compatible client can connect — Claude Code, Codex, Cursor, Hermes-Agent,
90
107
  opencode, and others.
91
108
 
109
+ After `bilinc login`, let the CLI write the setup for your client:
110
+
111
+ ```bash
112
+ bilinc mcp install --client claude-code # prints a ready `claude mcp add ...` command
113
+ bilinc mcp install --client claude-desktop # prints the config and where Claude Desktop keeps it
114
+ bilinc mcp install # plain mcpServers JSON for any other client
115
+ ```
116
+
92
117
  ```json
93
118
  {
94
119
  "mcpServers": {
95
120
  "bilinc": {
96
- "command": "python",
97
- "args": ["-m", "bilinc.cloud_mcp"],
98
- "env": { "BILINC_API_KEY": "bil_live_..." }
121
+ "command": "/path/to/python3",
122
+ "args": ["-m", "bilinc.cloud_mcp"]
99
123
  }
100
124
  }
101
125
  }
102
126
  ```
103
127
 
128
+ `command` is the interpreter that has Bilinc installed, and the adapter reads the key saved by `bilinc login`, so
129
+ the config carries no key. On a machine without a saved key, add `"env": { "BILINC_API_KEY": "bil_live_..." }`.
130
+
104
131
  Eight tools — the core memory lifecycle, and nothing else:
105
132
 
106
133
  | Tool | What it does |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "bilinc"
7
- version = "2.3.0"
7
+ version = "2.3.3"
8
8
  description = "Cloud-only memory SDK and MCP adapter for autonomous agents"
9
9
  readme = "README.md"
10
10
  license = "BUSL-1.1"
@@ -1,4 +1,4 @@
1
- """Bilinc 2.3.0: cloud-only SDK for agent memory."""
1
+ """Bilinc 2.3.3: cloud-only SDK for agent memory."""
2
2
 
3
3
  from bilinc.client import (
4
4
  ACTIVATION_SIGNUP_URL,
@@ -25,7 +25,7 @@ from bilinc.client import (
25
25
  CloudClient,
26
26
  )
27
27
 
28
- __version__ = "2.3.0"
28
+ __version__ = "2.3.3"
29
29
  version = __version__
30
30
 
31
31
  __all__ = [
@@ -6,6 +6,8 @@ from __future__ import annotations
6
6
  import argparse
7
7
  import json
8
8
  import os
9
+ import shlex
10
+ import subprocess
9
11
  import sys
10
12
  import time
11
13
  import webbrowser
@@ -64,16 +66,38 @@ def _print_start_guide(*, opened: bool = False) -> None:
64
66
  )
65
67
 
66
68
 
69
+ MCP_KEY_PLACEHOLDER = "bil_live_..."
70
+
71
+
72
+ def _mcp_server_entry() -> dict[str, Any]:
73
+ # The absolute interpreter works where a bare `python` does not: macOS ships
74
+ # only python3, and Claude Desktop launches servers without the shell PATH.
75
+ entry: dict[str, Any] = {"command": sys.executable, "args": ["-m", "bilinc.cloud_mcp"]}
76
+ if not load_config_api_key():
77
+ # With a key saved by `bilinc login` the adapter reads it itself, so the
78
+ # client config carries no key at all. Without one, the client must pass it.
79
+ entry["env"] = {"BILINC_API_KEY": MCP_KEY_PLACEHOLDER}
80
+ return entry
81
+
82
+
67
83
  def _mcp_config() -> dict[str, Any]:
68
- return {
69
- "mcpServers": {
70
- "bilinc": {
71
- "command": "python",
72
- "args": ["-m", "bilinc.cloud_mcp"],
73
- "env": {"BILINC_API_KEY": "${BILINC_API_KEY}"},
74
- }
75
- }
76
- }
84
+ return {"mcpServers": {"bilinc": _mcp_server_entry()}}
85
+
86
+
87
+ def _claude_code_command() -> str:
88
+ argv = ["claude", "mcp", "add", "--scope", "user", "bilinc"]
89
+ if not load_config_api_key():
90
+ argv += ["-e", f"BILINC_API_KEY={MCP_KEY_PLACEHOLDER}"]
91
+ argv += ["--", sys.executable, "-m", "bilinc.cloud_mcp"]
92
+ return subprocess.list2cmdline(argv) if os.name == "nt" else shlex.join(argv)
93
+
94
+
95
+ def _claude_desktop_config_path() -> str:
96
+ if sys.platform == "darwin":
97
+ return "~/Library/Application Support/Claude/claude_desktop_config.json"
98
+ if os.name == "nt":
99
+ return r"%APPDATA%\Claude\claude_desktop_config.json"
100
+ return "~/.config/Claude/claude_desktop_config.json"
77
101
 
78
102
 
79
103
  def build_parser() -> argparse.ArgumentParser:
@@ -174,7 +198,14 @@ def build_parser() -> argparse.ArgumentParser:
174
198
 
175
199
  mcp = sub.add_parser("mcp", help="Print hosted Cloud MCP adapter configuration")
176
200
  mcp_sub = mcp.add_subparsers(dest="mcp_command")
177
- mcp_sub.add_parser("install", help="Print MCP config for agent runtimes")
201
+ mcp_install = mcp_sub.add_parser("install", help="Print MCP config for agent runtimes")
202
+ mcp_install.add_argument(
203
+ "--client",
204
+ choices=["json", "claude-code", "claude-desktop"],
205
+ default="json",
206
+ help="json: mcpServers config (default); claude-code: a `claude mcp add` command; "
207
+ "claude-desktop: the config plus where Claude Desktop keeps it",
208
+ )
178
209
 
179
210
  sub.add_parser("signup", help="Print the signup URL (free, no card required)")
180
211
 
@@ -252,7 +283,22 @@ def main(argv: list[str] | None = None) -> int:
252
283
  )
253
284
  return 0
254
285
  if args.command == "mcp" and args.mcp_command == "install":
255
- _print(_mcp_config())
286
+ # stdout stays copy-paste clean; guidance goes to stderr.
287
+ if args.client == "claude-code":
288
+ print(_claude_code_command())
289
+ else:
290
+ _print(_mcp_config())
291
+ if args.client == "claude-desktop":
292
+ print(
293
+ f'Merge the "bilinc" entry into mcpServers in {_claude_desktop_config_path()}, '
294
+ "then restart Claude Desktop.",
295
+ file=sys.stderr,
296
+ )
297
+ if not load_config_api_key():
298
+ print(
299
+ f"No saved key yet: run `bilinc login` first, or replace {MCP_KEY_PLACEHOLDER} with your API key.",
300
+ file=sys.stderr,
301
+ )
256
302
  return 0
257
303
  if args.command is None:
258
304
  parser.print_help()
@@ -1,6 +1,6 @@
1
1
  """Bilinc Cloud client.
2
2
 
3
- Bilinc 2.3.0 is cloud-only: the PyPI package is a thin SDK and MCP adapter for
3
+ Bilinc 2.3.3 is cloud-only: the PyPI package is a thin SDK and MCP adapter for
4
4
  https://bilinc.space. Local self-hosted StatePlane internals are no longer
5
5
  shipped in the public package.
6
6
  """
@@ -18,10 +18,10 @@ from dataclasses import dataclass
18
18
  from pathlib import Path
19
19
  from typing import Any, Callable
20
20
 
21
- __version__ = "2.3.0"
21
+ __version__ = "2.3.3"
22
22
  DEFAULT_BASE_URL = "https://bilinc.space"
23
23
  SIGNUP_URL = "https://bilinc.space/signup"
24
- ACTIVATION_CAMPAIGN = "activation_2_3_0"
24
+ ACTIVATION_CAMPAIGN = "activation_2_3_3"
25
25
  ACTIVATION_SIGNUP_URL = (
26
26
  f"{SIGNUP_URL}?utm_source=pypi&utm_medium=cli&utm_campaign={ACTIVATION_CAMPAIGN}"
27
27
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bilinc
3
- Version: 2.3.0
3
+ Version: 2.3.3
4
4
  Summary: Cloud-only memory SDK and MCP adapter for autonomous agents
5
5
  Author: Bilinc Team
6
6
  License-Expression: BUSL-1.1
@@ -56,7 +56,7 @@ Dynamic: license-file
56
56
 
57
57
  Retrieval answers *"what is similar to this?"*. Long-running agents also need to answer *"who wrote this state, was it verified, did it contradict what we already knew, and can we undo it?"* — that is the layer Bilinc provides.
58
58
 
59
- Bilinc 2.3.0 on PyPI is the public cloud-only package: a thin Python SDK, CLI, and MCP adapter for Bilinc Cloud. It does not ship the local StatePlane, storage backends, eval, observability, integrations, or server runtime internals.
59
+ Bilinc 2.3.3 on PyPI is the public cloud-only package: a thin Python SDK, CLI, and MCP adapter for Bilinc Cloud. It does not ship the local StatePlane, storage backends, eval, observability, integrations, or server runtime internals.
60
60
 
61
61
  > **Frozen regression receipt** — LongMemEval-s cleaned retrieval fixture, 500 questions: **Hit@5 98.0%**, **NDCG@5 0.913**, no LLM reranker, no paid API. This is an isolated retrieval guardrail, not a current hosted SLA, end-to-end agent score, or competitor ranking — see [Benchmark receipt](#benchmark-receipt) for the full scope and qualification.
62
62
 
@@ -93,13 +93,30 @@ The fastest path is `pip install -U bilinc`, `bilinc login`, then `bilinc quickt
93
93
  | --- | --- |
94
94
  | A hosted memory API for an agent or MCP client | The public cloud-only package from PyPI |
95
95
  | Local StatePlane, SQLite/PostgreSQL, benchmarks, or internals | This repository and the [architecture guide](docs/architecture.md) |
96
- | A hosted MCP connection | The [MCP setup guide](https://bilinc.space/docs/mcp) |
96
+ | A hosted MCP connection without a Python install | The [remote MCP setup guide](https://bilinc.space/docs/remote-mcp) |
97
97
 
98
98
  The public package is intentionally smaller than this repository. It does not bundle the internal StatePlane or local
99
99
  storage runtime.
100
100
 
101
101
  ## Start in 60 Seconds
102
102
 
103
+ ### Claude connector, without installing Python
104
+
105
+ Open **Customize → Connectors → Add custom connector** in Claude, name it Bilinc,
106
+ and paste `https://mcp.bilinc.space/mcp`. Sign in and choose read-only or read and
107
+ write access. The setup guide is [bilinc.space/mcp](https://bilinc.space/mcp).
108
+
109
+ For the remote connector in Claude Code:
110
+
111
+ ```bash
112
+ claude mcp add --transport http --scope user bilinc https://mcp.bilinc.space/mcp
113
+ ```
114
+
115
+ Then run `/mcp` and authenticate. The remote connector has ten tools; the Python
116
+ stdio adapter below has eight. Both connect to hosted Bilinc memory.
117
+
118
+ ### SDK and CLI
119
+
103
120
  ```bash
104
121
  pip install -U bilinc
105
122
  bilinc login
@@ -119,7 +136,7 @@ Cloud status check.
119
136
  To reproduce this release exactly:
120
137
 
121
138
  ```bash
122
- pip install -U bilinc==2.3.0
139
+ pip install -U bilinc==2.3.3
123
140
  ```
124
141
 
125
142
  If you prefer a browser guide, open https://bilinc.space/install and follow the
@@ -131,18 +148,28 @@ Bilinc exposes a standard Model Context Protocol server over **stdio**, so any
131
148
  MCP-compatible client can connect — Claude Code, Codex, Cursor, Hermes-Agent,
132
149
  opencode, and others.
133
150
 
151
+ After `bilinc login`, let the CLI write the setup for your client:
152
+
153
+ ```bash
154
+ bilinc mcp install --client claude-code # prints a ready `claude mcp add ...` command
155
+ bilinc mcp install --client claude-desktop # prints the config and where Claude Desktop keeps it
156
+ bilinc mcp install # plain mcpServers JSON for any other client
157
+ ```
158
+
134
159
  ```json
135
160
  {
136
161
  "mcpServers": {
137
162
  "bilinc": {
138
- "command": "python",
139
- "args": ["-m", "bilinc.cloud_mcp"],
140
- "env": { "BILINC_API_KEY": "bil_live_..." }
163
+ "command": "/path/to/python3",
164
+ "args": ["-m", "bilinc.cloud_mcp"]
141
165
  }
142
166
  }
143
167
  }
144
168
  ```
145
169
 
170
+ `command` is the interpreter that has Bilinc installed, and the adapter reads the key saved by `bilinc login`, so
171
+ the config carries no key. On a machine without a saved key, add `"env": { "BILINC_API_KEY": "bil_live_..." }`.
172
+
146
173
  Eight tools — the core memory lifecycle, and nothing else:
147
174
 
148
175
  | Tool | What it does |
@@ -12,9 +12,9 @@ import pytest
12
12
  def test_public_api_is_cloud_only():
13
13
  import bilinc
14
14
 
15
- assert bilinc.__version__ == "2.3.0"
16
- assert bilinc.version == "2.3.0"
17
- assert "utm_campaign=activation_2_3_0" in bilinc.ACTIVATION_SIGNUP_URL
15
+ assert bilinc.__version__ == "2.3.3"
16
+ assert bilinc.version == "2.3.3"
17
+ assert "utm_campaign=activation_2_3_3" in bilinc.ACTIVATION_SIGNUP_URL
18
18
  assert hasattr(bilinc, "Bilinc")
19
19
  assert hasattr(bilinc, "CloudClient")
20
20
  assert not hasattr(bilinc, "StatePlane")
@@ -75,7 +75,7 @@ def test_cloud_client_normalizes_base_url():
75
75
  from bilinc import CloudClient
76
76
 
77
77
  transport = RecordingTransport({"status": "ok"})
78
- client = CloudClient(api_key="bil_live_test", base_url="https://bilinc.space///", transport=transport)
78
+ client = CloudClient(api_key="bil_live_example", base_url="https://bilinc.space///", transport=transport)
79
79
 
80
80
  client.status()
81
81
 
@@ -87,7 +87,7 @@ def test_cloud_client_base_url_can_come_from_env(monkeypatch):
87
87
 
88
88
  monkeypatch.setenv("BILINC_BASE_URL", "http://127.0.0.1:9999/")
89
89
  transport = RecordingTransport({"status": "ok"})
90
- client = CloudClient(api_key="bil_live_test", transport=transport)
90
+ client = CloudClient(api_key="bil_live_example", transport=transport)
91
91
 
92
92
  client.status()
93
93
 
@@ -98,7 +98,7 @@ def test_cloud_client_commit_posts_to_hosted_api():
98
98
  from bilinc import CloudClient
99
99
 
100
100
  transport = RecordingTransport({"success": True, "id": "mem_123"})
101
- client = CloudClient(api_key="bil_live_test", base_url="https://bilinc.space", transport=transport)
101
+ client = CloudClient(api_key="bil_live_example", base_url="https://bilinc.space", transport=transport)
102
102
 
103
103
  result = client.commit("project.status", {"phase": "trial"}, memory_type="semantic", importance=0.8)
104
104
 
@@ -108,11 +108,11 @@ def test_cloud_client_commit_posts_to_hosted_api():
108
108
  "method": "POST",
109
109
  "url": "https://bilinc.space/api/cloud/memory/commit",
110
110
  "headers": {
111
- "Authorization": "Bearer bil_live_test",
111
+ "Authorization": "Bearer bil_live_example",
112
112
  "Content-Type": "application/json",
113
- "User-Agent": "bilinc-python/2.3.0",
113
+ "User-Agent": "bilinc-python/2.3.3",
114
114
  "X-Bilinc-Client": "python-sdk",
115
- "X-Bilinc-Client-Version": "2.3.0",
115
+ "X-Bilinc-Client-Version": "2.3.3",
116
116
  },
117
117
  "body": json.dumps(
118
118
  {
@@ -133,7 +133,7 @@ def test_cloud_client_recall_posts_to_hosted_api():
133
133
  from bilinc import CloudClient
134
134
 
135
135
  transport = RecordingTransport({"results": [{"key": "project.status"}]})
136
- client = CloudClient(api_key="bil_live_test", transport=transport)
136
+ client = CloudClient(api_key="bil_live_example", transport=transport)
137
137
 
138
138
  result = client.recall("trial status", profile="balanced", limit=5)
139
139
 
@@ -148,7 +148,7 @@ def test_cloud_client_recall_forwards_point_in_time_timestamp():
148
148
  from bilinc import CloudClient
149
149
 
150
150
  transport = RecordingTransport({"results": []})
151
- client = CloudClient(api_key="bil_live_test", transport=transport)
151
+ client = CloudClient(api_key="bil_live_example", transport=transport)
152
152
 
153
153
  client.recall(
154
154
  "deployment target",
@@ -167,7 +167,7 @@ def test_cloud_client_recall_forwards_point_in_time_timestamp():
167
167
  def test_cloud_client_rejects_an_unbounded_point_in_time_timestamp():
168
168
  from bilinc import BilincValidationError, CloudClient
169
169
 
170
- client = CloudClient(api_key="bil_live_test", transport=RecordingTransport({}))
170
+ client = CloudClient(api_key="bil_live_example", transport=RecordingTransport({}))
171
171
 
172
172
  with pytest.raises(BilincValidationError, match="query_timestamp"):
173
173
  client.recall("q", query_timestamp="x" * 65)
@@ -177,7 +177,7 @@ def test_cloud_client_status_reads_the_authenticated_status_endpoint():
177
177
  from bilinc import CloudClient
178
178
 
179
179
  transport = RecordingTransport({"status": "ok", "plan": {"key": "pro"}})
180
- client = CloudClient(api_key="bil_live_test", transport=transport)
180
+ client = CloudClient(api_key="bil_live_example", transport=transport)
181
181
 
182
182
  result = client.status()
183
183
 
@@ -187,8 +187,8 @@ def test_cloud_client_status_reads_the_authenticated_status_endpoint():
187
187
  "method": "GET",
188
188
  "url": "https://bilinc.space/api/cloud/status",
189
189
  "headers": {
190
- "Authorization": "Bearer bil_live_test",
191
- "User-Agent": "bilinc-python/2.3.0",
190
+ "Authorization": "Bearer bil_live_example",
191
+ "User-Agent": "bilinc-python/2.3.3",
192
192
  },
193
193
  "body": None,
194
194
  "timeout": 30.0,
@@ -200,7 +200,7 @@ def test_cloud_client_health_stays_on_the_public_service_endpoint():
200
200
  from bilinc import CloudClient
201
201
 
202
202
  transport = RecordingTransport({"status": "ok", "mode": "live_cloud"})
203
- client = CloudClient(api_key="bil_live_test", transport=transport)
203
+ client = CloudClient(api_key="bil_live_example", transport=transport)
204
204
 
205
205
  result = client.health()
206
206
 
@@ -261,7 +261,7 @@ def test_cli_signup_and_missing_key_failure(monkeypatch, capsys):
261
261
  assert main(["signup"]) == 0
262
262
  signup_out = capsys.readouterr()
263
263
  assert "https://bilinc.space/signup" in signup_out.out
264
- assert "activation_2_3_0" in signup_out.out
264
+ assert "activation_2_3_3" in signup_out.out
265
265
  assert "quicktest" in signup_out.out
266
266
 
267
267
  assert main(["commit", "--key", "smoke_key", "--value", "hello"]) == 1
@@ -305,7 +305,7 @@ def test_cli_start_login_doctor_quicktest_and_mcp_config(monkeypatch, capsys, tm
305
305
  assert "bilinc login --api-key <key>" in start_out.out
306
306
  assert "bilinc quicktest" in start_out.out
307
307
  assert "install_guide" in start_out.out
308
- assert "activation_2_3_0" in start_out.out
308
+ assert "activation_2_3_3" in start_out.out
309
309
 
310
310
  assert cli_main.main(["login", "--api-key", "bil_live_cli_test"]) == 0
311
311
  login_out = capsys.readouterr()
@@ -327,6 +327,56 @@ def test_cli_start_login_doctor_quicktest_and_mcp_config(monkeypatch, capsys, tm
327
327
  assert "bil_live_cli_test" not in mcp_out.out
328
328
 
329
329
 
330
+ def test_mcp_install_uses_the_login_key_and_a_real_interpreter(monkeypatch, capsys, tmp_path):
331
+ import sys
332
+
333
+ from bilinc.cli import main as cli_main
334
+
335
+ monkeypatch.delenv("BILINC_API_KEY", raising=False)
336
+ monkeypatch.setenv("BILINC_CONFIG_DIR", str(tmp_path))
337
+ assert cli_main.main(["login", "--api-key", "bil_live_saved_key"]) == 0
338
+ capsys.readouterr()
339
+
340
+ # Default JSON: absolute interpreter, and no env block — the adapter reads the saved key.
341
+ assert cli_main.main(["mcp", "install"]) == 0
342
+ out = capsys.readouterr()
343
+ server = json.loads(out.out)["mcpServers"]["bilinc"]
344
+ assert server == {"command": sys.executable, "args": ["-m", "bilinc.cloud_mcp"]}
345
+ assert "bil_live_saved_key" not in out.out + out.err
346
+ assert "${BILINC_API_KEY}" not in out.out
347
+
348
+ # Claude Code: one runnable command, user scope, no key on the command line.
349
+ assert cli_main.main(["mcp", "install", "--client", "claude-code"]) == 0
350
+ out = capsys.readouterr()
351
+ assert out.out.startswith("claude mcp add --scope user bilinc -- ")
352
+ assert out.out.rstrip().endswith("-m bilinc.cloud_mcp")
353
+ assert "BILINC_API_KEY" not in out.out
354
+ assert "bil_live_saved_key" not in out.out + out.err
355
+
356
+ # Claude Desktop: clean JSON on stdout, the config location on stderr.
357
+ assert cli_main.main(["mcp", "install", "--client", "claude-desktop"]) == 0
358
+ out = capsys.readouterr()
359
+ assert json.loads(out.out)["mcpServers"]["bilinc"]["command"] == sys.executable
360
+ assert "claude_desktop_config.json" in out.err
361
+ assert "bil_live_saved_key" not in out.out + out.err
362
+
363
+
364
+ def test_mcp_install_without_a_saved_key_asks_for_one(monkeypatch, capsys, tmp_path):
365
+ from bilinc.cli import main as cli_main
366
+
367
+ monkeypatch.delenv("BILINC_API_KEY", raising=False)
368
+ monkeypatch.setenv("BILINC_CONFIG_DIR", str(tmp_path))
369
+
370
+ assert cli_main.main(["mcp", "install"]) == 0
371
+ out = capsys.readouterr()
372
+ assert json.loads(out.out)["mcpServers"]["bilinc"]["env"] == {"BILINC_API_KEY": "bil_live_..."}
373
+ assert "bilinc login" in out.err
374
+
375
+ assert cli_main.main(["mcp", "install", "--client", "claude-code"]) == 0
376
+ out = capsys.readouterr()
377
+ assert "-e BILINC_API_KEY=bil_live_..." in out.out
378
+
379
+
330
380
  def _forbidden_package_prefixes(root: str = "") -> tuple[str, ...]:
331
381
  return tuple(
332
382
  f"{root}{prefix}"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes