cortexdb-mcp 0.3.0__tar.gz → 0.3.1__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.
@@ -69,3 +69,11 @@ dist/
69
69
  # Scratch/debug text files at root
70
70
  /*.txt
71
71
  /*.log
72
+
73
+ # Local debug / marketing / private content (not for repo)
74
+ harness/.reports/
75
+ harness_data_*/
76
+ blog/
77
+ sales/
78
+ videos/
79
+ local-instance/
@@ -7,6 +7,6 @@ COPY cortexdb_mcp/ cortexdb_mcp/
7
7
 
8
8
  RUN pip install --no-cache-dir .
9
9
 
10
- ENV CORTEXDB_URL=https://api.cortexdb.ai
10
+ ENV CORTEXDB_URL=https://api-v1.cortexdb.ai
11
11
 
12
12
  CMD ["cortexdb-mcp"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cortexdb-mcp
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: MCP Server for CortexDB — expose memory operations to AI agents
5
5
  License-Expression: MIT
6
6
  Requires-Python: >=3.10
@@ -56,6 +56,28 @@ Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) o
56
56
  }
57
57
  ```
58
58
 
59
+ > **Windows note (audit POL-3):** if Claude Desktop / Cursor / Windsurf
60
+ > can't find `cortexdb-mcp` on PATH, give it the full path to the
61
+ > installed executable:
62
+ >
63
+ > ```json
64
+ > {
65
+ > "mcpServers": {
66
+ > "cortexdb": {
67
+ > "command": "C:\\Python313\\Scripts\\cortexdb-mcp.exe"
68
+ > }
69
+ > }
70
+ > }
71
+ > ```
72
+ >
73
+ > Locate it with `where cortexdb-mcp` in `cmd.exe` or
74
+ > `(Get-Command cortexdb-mcp).Source` in PowerShell.
75
+ >
76
+ > Some Windows terminals don't have UTF-8 enabled by default, which can
77
+ > garble emoji in `--help` output (they show as `?`). The server itself
78
+ > isn't affected — only the CLI banner. Run
79
+ > `chcp 65001` once per terminal session to fix.
80
+
59
81
  ### Claude Code (CLI)
60
82
 
61
83
  Edit `~/.claude/mcp.json`:
@@ -200,7 +222,7 @@ Pre-built prompt templates:
200
222
 
201
223
  | Environment Variable | Default | Description |
202
224
  |---|---|---|
203
- | `CORTEXDB_URL` | `https://api.cortexdb.ai` | CortexDB server URL |
225
+ | `CORTEXDB_URL` | `https://api-v1.cortexdb.ai` | CortexDB server URL |
204
226
  | `CORTEXDB_API_KEY` | (none) | API key for authentication |
205
227
  | `CORTEXDB_TIMEOUT` | `30.0` | HTTP request timeout (seconds) |
206
228
 
@@ -43,6 +43,28 @@ Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) o
43
43
  }
44
44
  ```
45
45
 
46
+ > **Windows note (audit POL-3):** if Claude Desktop / Cursor / Windsurf
47
+ > can't find `cortexdb-mcp` on PATH, give it the full path to the
48
+ > installed executable:
49
+ >
50
+ > ```json
51
+ > {
52
+ > "mcpServers": {
53
+ > "cortexdb": {
54
+ > "command": "C:\\Python313\\Scripts\\cortexdb-mcp.exe"
55
+ > }
56
+ > }
57
+ > }
58
+ > ```
59
+ >
60
+ > Locate it with `where cortexdb-mcp` in `cmd.exe` or
61
+ > `(Get-Command cortexdb-mcp).Source` in PowerShell.
62
+ >
63
+ > Some Windows terminals don't have UTF-8 enabled by default, which can
64
+ > garble emoji in `--help` output (they show as `?`). The server itself
65
+ > isn't affected — only the CLI banner. Run
66
+ > `chcp 65001` once per terminal session to fix.
67
+
46
68
  ### Claude Code (CLI)
47
69
 
48
70
  Edit `~/.claude/mcp.json`:
@@ -187,7 +209,7 @@ Pre-built prompt templates:
187
209
 
188
210
  | Environment Variable | Default | Description |
189
211
  |---|---|---|
190
- | `CORTEXDB_URL` | `https://api.cortexdb.ai` | CortexDB server URL |
212
+ | `CORTEXDB_URL` | `https://api-v1.cortexdb.ai` | CortexDB server URL |
191
213
  | `CORTEXDB_API_KEY` | (none) | API key for authentication |
192
214
  | `CORTEXDB_TIMEOUT` | `30.0` | HTTP request timeout (seconds) |
193
215
 
@@ -1,3 +1,3 @@
1
1
  """CortexDB MCP Server -- expose CortexDB memory operations to AI agents via MCP."""
2
2
 
3
- __version__ = "0.2.1"
3
+ __version__ = "0.3.1"
@@ -6,7 +6,7 @@ display proactive insights. Results are cached with a configurable TTL (default
6
6
 
7
7
  Configuration is read from environment variables:
8
8
 
9
- - ``CORTEXDB_URL`` -- CortexDB base URL (default ``https://api.cortexdb.ai``)
9
+ - ``CORTEXDB_URL`` -- CortexDB base URL (default ``https://api-v1.cortexdb.ai``)
10
10
  - ``CORTEXDB_API_KEY`` -- Optional bearer token
11
11
  - ``CORTEXDB_TENANT_ID``-- Optional tenant scope
12
12
  - ``INSIGHTS_CACHE_TTL``-- Cache lifetime in seconds (default ``300``)
@@ -34,7 +34,7 @@ logger = logging.getLogger("cortexdb_mcp.api")
34
34
  # Configuration
35
35
  # ---------------------------------------------------------------------------
36
36
 
37
- CORTEXDB_URL = os.environ.get("CORTEXDB_URL", "https://api.cortexdb.ai")
37
+ CORTEXDB_URL = os.environ.get("CORTEXDB_URL", "https://api-v1.cortexdb.ai")
38
38
  CORTEXDB_API_KEY = os.environ.get("CORTEXDB_API_KEY")
39
39
  CORTEXDB_TENANT_ID = os.environ.get("CORTEXDB_TENANT_ID")
40
40
  INSIGHTS_CACHE_TTL = int(os.environ.get("INSIGHTS_CACHE_TTL", "300"))
@@ -1,145 +1,145 @@
1
- """Configuration management for the CortexDB MCP server.
2
-
3
- Reads settings from environment variables with sensible defaults.
4
-
5
- The MCP server targets the v1 API surface by default
6
- (``https://api-v1.cortexdb.ai``). Three auth paths are supported:
7
-
8
- 1. **Anonymous one-click** — leave ``CORTEXDB_API_KEY`` unset and the server
9
- will call ``POST /v1/auth/signup`` on first launch, caching the resulting
10
- PASETO token (+ actor + scope) under ``~/.config/cortexdb-mcp/state.json``.
11
- Re-launches reuse the cached token until it expires.
12
-
13
- 2. **Bring your own PASETO** — set ``CORTEXDB_API_KEY`` to a PASETO ``v4.public.*``
14
- token. The server still needs ``CORTEXDB_ACTOR`` and ``CORTEXDB_SCOPE`` so
15
- it can send the matching ``X-Cortex-Actor`` header.
16
-
17
- 3. **Legacy v0 key** — set ``CORTEXDB_API_KEY=cx_live_...`` and point
18
- ``CORTEXDB_URL`` at the legacy v0 surface (``https://api.cortexdb.ai``).
19
- Several MCP tools won't work in this mode because v0 lacks the
20
- layer-read endpoints.
21
- """
22
-
23
- from __future__ import annotations
24
-
25
- import json
26
- import os
27
- from dataclasses import dataclass, field
28
- from pathlib import Path
29
-
30
-
31
- def _state_path() -> Path:
32
- """Where the cached anonymous-signup token lives.
33
-
34
- Uses XDG_CONFIG_HOME on Linux/macOS, %APPDATA% on Windows, with a
35
- sensible fallback for both.
36
- """
37
- if os.name == "nt":
38
- base = Path(os.environ.get("APPDATA", str(Path.home() / "AppData" / "Roaming")))
39
- else:
40
- base = Path(os.environ.get("XDG_CONFIG_HOME", str(Path.home() / ".config")))
41
- return base / "cortexdb-mcp" / "state.json"
42
-
43
-
44
- @dataclass
45
- class CortexMCPConfig:
46
- """Configuration for connecting to a CortexDB instance.
47
-
48
- Attributes:
49
- url: Base URL of the CortexDB HTTP API.
50
- api_key: PASETO bearer token (or legacy cx_live_ key in v0 mode).
51
- actor: ActorId sent as the X-Cortex-Actor header. Required for v1.
52
- scope: Default scope path for write/recall calls. Required for v1.
53
- tenant_id: Legacy tenant id for v0 calls. Defaults to None.
54
- timeout: HTTP request timeout in seconds.
55
- """
56
-
57
- url: str = "https://api-v1.cortexdb.ai"
58
- api_key: str | None = None
59
- actor: str | None = None
60
- scope: str | None = None
61
- tenant_id: str | None = None
62
- timeout: float = 30.0
63
-
64
- # Set by save_state() / from_env() when an anonymous signup happens; used
65
- # by the server to indicate whether persistence is enabled. Kept off the
66
- # dataclass init signature so tests can construct configs directly.
67
- state_file: Path = field(default_factory=_state_path)
68
-
69
- @classmethod
70
- def from_env(cls) -> "CortexMCPConfig":
71
- """Build configuration from environment variables.
72
-
73
- Environment variables
74
- ---------------------
75
- CORTEXDB_URL -- API base URL (default ``https://api-v1.cortexdb.ai``).
76
- CORTEXDB_API_KEY -- PASETO token (preferred) or legacy v0 key.
77
- CORTEXDB_ACTOR -- ActorId for X-Cortex-Actor (e.g. ``user:alice``).
78
- CORTEXDB_SCOPE -- Default scope path for tool calls.
79
- CORTEXDB_TENANT_ID -- Legacy tenant id (v0 callers only).
80
- CORTEXDB_TIMEOUT -- Request timeout in seconds (default ``30.0``).
81
-
82
- When CORTEXDB_API_KEY is unset, the server will look for a cached
83
- state file written by a previous anonymous signup; failing that,
84
- the next outgoing request triggers a fresh ``/v1/auth/signup``.
85
- """
86
- url = os.environ.get("CORTEXDB_URL", cls.url)
87
- cfg = cls(
88
- url=url,
89
- api_key=os.environ.get("CORTEXDB_API_KEY"),
90
- actor=os.environ.get("CORTEXDB_ACTOR"),
91
- scope=os.environ.get("CORTEXDB_SCOPE"),
92
- tenant_id=os.environ.get("CORTEXDB_TENANT_ID"),
93
- timeout=float(os.environ.get("CORTEXDB_TIMEOUT", str(cls.timeout))),
94
- )
95
-
96
- # If env didn't provide credentials, hydrate from the on-disk cache
97
- # (anonymous signups from prior MCP-server launches). The cache only
98
- # applies when CORTEXDB_API_KEY is unset, so an explicit env key
99
- # always wins. We call `_state_path()` here (vs. reading
100
- # cfg.state_file) so tests can monkeypatch the module-level
101
- # function and influence resolution.
102
- cfg.state_file = _state_path()
103
- if cfg.api_key is None:
104
- cached = _load_state(cfg.state_file)
105
- if cached:
106
- cfg.api_key = cfg.api_key or cached.get("token")
107
- cfg.actor = cfg.actor or cached.get("actor")
108
- cfg.scope = cfg.scope or cached.get("scope")
109
- return cfg
110
-
111
- def save_state(self, token: str, actor: str, scope: str) -> None:
112
- """Persist an anonymous-signup outcome so subsequent launches reuse
113
- the same identity. Writes are best-effort — failures (perm denied,
114
- full disk, etc.) are silently swallowed because the server is still
115
- functional with an in-memory token."""
116
- try:
117
- self.state_file.parent.mkdir(parents=True, exist_ok=True)
118
- self.state_file.write_text(
119
- json.dumps(
120
- {"token": token, "actor": actor, "scope": scope},
121
- indent=2,
122
- ),
123
- encoding="utf-8",
124
- )
125
- # Best-effort tighten permissions on the secret-bearing file.
126
- try:
127
- os.chmod(self.state_file, 0o600)
128
- except OSError:
129
- pass
130
- except OSError:
131
- pass
132
-
133
-
134
- def _load_state(path: Path) -> dict[str, str] | None:
135
- """Read a cached signup state. Returns None on any error — the caller
136
- falls back to env-only config."""
137
- try:
138
- if not path.exists():
139
- return None
140
- data = json.loads(path.read_text(encoding="utf-8"))
141
- if isinstance(data, dict) and "token" in data:
142
- return {k: str(v) for k, v in data.items() if isinstance(v, str)}
143
- except (OSError, json.JSONDecodeError):
144
- pass
145
- return None
1
+ """Configuration management for the CortexDB MCP server.
2
+
3
+ Reads settings from environment variables with sensible defaults.
4
+
5
+ The MCP server targets the v1 API surface by default
6
+ (``https://api-v1.cortexdb.ai``). Three auth paths are supported:
7
+
8
+ 1. **Anonymous one-click** — leave ``CORTEXDB_API_KEY`` unset and the server
9
+ will call ``POST /v1/auth/signup`` on first launch, caching the resulting
10
+ PASETO token (+ actor + scope) under ``~/.config/cortexdb-mcp/state.json``.
11
+ Re-launches reuse the cached token until it expires.
12
+
13
+ 2. **Bring your own PASETO** — set ``CORTEXDB_API_KEY`` to a PASETO ``v4.public.*``
14
+ token. The server still needs ``CORTEXDB_ACTOR`` and ``CORTEXDB_SCOPE`` so
15
+ it can send the matching ``X-Cortex-Actor`` header.
16
+
17
+ 3. **Legacy v0 key** — set ``CORTEXDB_API_KEY=cx_live_...`` and point
18
+ ``CORTEXDB_URL`` at the legacy v0 surface (``https://api-v1.cortexdb.ai``).
19
+ Several MCP tools won't work in this mode because v0 lacks the
20
+ layer-read endpoints.
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import json
26
+ import os
27
+ from dataclasses import dataclass, field
28
+ from pathlib import Path
29
+
30
+
31
+ def _state_path() -> Path:
32
+ """Where the cached anonymous-signup token lives.
33
+
34
+ Uses XDG_CONFIG_HOME on Linux/macOS, %APPDATA% on Windows, with a
35
+ sensible fallback for both.
36
+ """
37
+ if os.name == "nt":
38
+ base = Path(os.environ.get("APPDATA", str(Path.home() / "AppData" / "Roaming")))
39
+ else:
40
+ base = Path(os.environ.get("XDG_CONFIG_HOME", str(Path.home() / ".config")))
41
+ return base / "cortexdb-mcp" / "state.json"
42
+
43
+
44
+ @dataclass
45
+ class CortexMCPConfig:
46
+ """Configuration for connecting to a CortexDB instance.
47
+
48
+ Attributes:
49
+ url: Base URL of the CortexDB HTTP API.
50
+ api_key: PASETO bearer token (or legacy cx_live_ key in v0 mode).
51
+ actor: ActorId sent as the X-Cortex-Actor header. Required for v1.
52
+ scope: Default scope path for write/recall calls. Required for v1.
53
+ tenant_id: Legacy tenant id for v0 calls. Defaults to None.
54
+ timeout: HTTP request timeout in seconds.
55
+ """
56
+
57
+ url: str = "https://api-v1.cortexdb.ai"
58
+ api_key: str | None = None
59
+ actor: str | None = None
60
+ scope: str | None = None
61
+ tenant_id: str | None = None
62
+ timeout: float = 30.0
63
+
64
+ # Set by save_state() / from_env() when an anonymous signup happens; used
65
+ # by the server to indicate whether persistence is enabled. Kept off the
66
+ # dataclass init signature so tests can construct configs directly.
67
+ state_file: Path = field(default_factory=_state_path)
68
+
69
+ @classmethod
70
+ def from_env(cls) -> "CortexMCPConfig":
71
+ """Build configuration from environment variables.
72
+
73
+ Environment variables
74
+ ---------------------
75
+ CORTEXDB_URL -- API base URL (default ``https://api-v1.cortexdb.ai``).
76
+ CORTEXDB_API_KEY -- PASETO token (preferred) or legacy v0 key.
77
+ CORTEXDB_ACTOR -- ActorId for X-Cortex-Actor (e.g. ``user:alice``).
78
+ CORTEXDB_SCOPE -- Default scope path for tool calls.
79
+ CORTEXDB_TENANT_ID -- Legacy tenant id (v0 callers only).
80
+ CORTEXDB_TIMEOUT -- Request timeout in seconds (default ``30.0``).
81
+
82
+ When CORTEXDB_API_KEY is unset, the server will look for a cached
83
+ state file written by a previous anonymous signup; failing that,
84
+ the next outgoing request triggers a fresh ``/v1/auth/signup``.
85
+ """
86
+ url = os.environ.get("CORTEXDB_URL", cls.url)
87
+ cfg = cls(
88
+ url=url,
89
+ api_key=os.environ.get("CORTEXDB_API_KEY"),
90
+ actor=os.environ.get("CORTEXDB_ACTOR"),
91
+ scope=os.environ.get("CORTEXDB_SCOPE"),
92
+ tenant_id=os.environ.get("CORTEXDB_TENANT_ID"),
93
+ timeout=float(os.environ.get("CORTEXDB_TIMEOUT", str(cls.timeout))),
94
+ )
95
+
96
+ # If env didn't provide credentials, hydrate from the on-disk cache
97
+ # (anonymous signups from prior MCP-server launches). The cache only
98
+ # applies when CORTEXDB_API_KEY is unset, so an explicit env key
99
+ # always wins. We call `_state_path()` here (vs. reading
100
+ # cfg.state_file) so tests can monkeypatch the module-level
101
+ # function and influence resolution.
102
+ cfg.state_file = _state_path()
103
+ if cfg.api_key is None:
104
+ cached = _load_state(cfg.state_file)
105
+ if cached:
106
+ cfg.api_key = cfg.api_key or cached.get("token")
107
+ cfg.actor = cfg.actor or cached.get("actor")
108
+ cfg.scope = cfg.scope or cached.get("scope")
109
+ return cfg
110
+
111
+ def save_state(self, token: str, actor: str, scope: str) -> None:
112
+ """Persist an anonymous-signup outcome so subsequent launches reuse
113
+ the same identity. Writes are best-effort — failures (perm denied,
114
+ full disk, etc.) are silently swallowed because the server is still
115
+ functional with an in-memory token."""
116
+ try:
117
+ self.state_file.parent.mkdir(parents=True, exist_ok=True)
118
+ self.state_file.write_text(
119
+ json.dumps(
120
+ {"token": token, "actor": actor, "scope": scope},
121
+ indent=2,
122
+ ),
123
+ encoding="utf-8",
124
+ )
125
+ # Best-effort tighten permissions on the secret-bearing file.
126
+ try:
127
+ os.chmod(self.state_file, 0o600)
128
+ except OSError:
129
+ pass
130
+ except OSError:
131
+ pass
132
+
133
+
134
+ def _load_state(path: Path) -> dict[str, str] | None:
135
+ """Read a cached signup state. Returns None on any error — the caller
136
+ falls back to env-only config."""
137
+ try:
138
+ if not path.exists():
139
+ return None
140
+ data = json.loads(path.read_text(encoding="utf-8"))
141
+ if isinstance(data, dict) and "token" in data:
142
+ return {k: str(v) for k, v in data.items() if isinstance(v, str)}
143
+ except (OSError, json.JSONDecodeError):
144
+ pass
145
+ return None
@@ -131,7 +131,7 @@ class InsightsEngine:
131
131
 
132
132
  def __init__(
133
133
  self,
134
- cortex_url: str = "https://api.cortexdb.ai",
134
+ cortex_url: str = "https://api-v1.cortexdb.ai",
135
135
  api_key: str | None = None,
136
136
  tenant_id: str | None = None,
137
137
  ) -> None: