ctxstore-mcp 1.0.0__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ctxstore.ai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,27 @@
1
+ Metadata-Version: 2.4
2
+ Name: ctxstore-mcp
3
+ Version: 1.0.0
4
+ Summary: Persistent AI memory via MCP. Give your AI agent long-term memory in 30 seconds.
5
+ Author-email: "ctxstore.ai" <hello@ctxstore.ai>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://ctxstore.ai
8
+ Project-URL: Documentation, https://ctxstore.ai/docs
9
+ Project-URL: Repository, https://github.com/ctxstore-ai/ctxstore-mcp
10
+ Project-URL: Bug Tracker, https://github.com/ctxstore-ai/ctxstore-mcp/issues
11
+ Keywords: mcp,ai,memory,context,vector,llm,claude,cursor
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: mcp>=1.0.0
24
+ Requires-Dist: httpx>=0.25.0
25
+ Dynamic: license-file
26
+
27
+ # ctxstore-mcp
@@ -0,0 +1 @@
1
+ # ctxstore-mcp
@@ -0,0 +1,15 @@
1
+ """
2
+ ctxstore-mcp — Persistent AI memory via MCP.
3
+
4
+ Thin MCP server that proxies to the ctxstore.ai API.
5
+ All embedding and storage happens server-side.
6
+ """
7
+
8
+ import asyncio
9
+
10
+ from .server import main as _main
11
+
12
+
13
+ def main():
14
+ """CLI entry point."""
15
+ asyncio.run(_main())
@@ -0,0 +1,4 @@
1
+ """Allow running as: python -m ctxstore_mcp"""
2
+ from . import main
3
+
4
+ main()
@@ -0,0 +1,120 @@
1
+ """
2
+ ctxstore credential management — auto-provisioning and credential caching.
3
+
4
+ Flow:
5
+ 1. Check TENANT_API_KEY env var (always wins)
6
+ 2. Check ~/.ctxstore/credentials.json
7
+ 3. POST /api/v1/provision to get a free key, save it
8
+ """
9
+
10
+ import json
11
+ import logging
12
+ import os
13
+ import sys
14
+ from datetime import datetime, timezone
15
+ from pathlib import Path
16
+ from typing import Optional
17
+
18
+ import httpx
19
+
20
+ logger = logging.getLogger("ctxstore.auth")
21
+
22
+ CREDENTIALS_FILE = Path.home() / ".ctxstore" / "credentials.json"
23
+ CTXSTORE_URL = os.getenv("CTXSTORE_URL", "https://ctxstore.ai").rstrip("/")
24
+ PROVISION_URL = f"{CTXSTORE_URL}/api/v1/provision"
25
+
26
+
27
+ def load_credentials() -> Optional[dict]:
28
+ """Load saved credentials from disk. Returns None if not found or invalid."""
29
+ if not CREDENTIALS_FILE.exists():
30
+ return None
31
+ try:
32
+ with open(CREDENTIALS_FILE) as f:
33
+ data = json.load(f)
34
+ if data.get("api_key"):
35
+ return data
36
+ except (json.JSONDecodeError, OSError):
37
+ pass
38
+ return None
39
+
40
+
41
+ def save_credentials(api_key: str, tenant_id: str = "") -> None:
42
+ """Save credentials to ~/.ctxstore/credentials.json with 600 permissions."""
43
+ CREDENTIALS_FILE.parent.mkdir(parents=True, exist_ok=True)
44
+ data = {
45
+ "api_key": api_key,
46
+ "tenant_id": tenant_id,
47
+ "provisioned_at": datetime.now(timezone.utc).isoformat(),
48
+ }
49
+ with open(CREDENTIALS_FILE, "w") as f:
50
+ json.dump(data, f, indent=2)
51
+ f.write("\n")
52
+ CREDENTIALS_FILE.chmod(0o600)
53
+
54
+
55
+ def provision() -> str:
56
+ """
57
+ Call the ctxstore.ai provision endpoint to get a free API key.
58
+ Saves credentials to disk and prints a friendly first-run message.
59
+ Returns the api_key string.
60
+ """
61
+ print(
62
+ "\n✦ ctxstore.ai: provisioning your free memory account...",
63
+ file=sys.stderr,
64
+ )
65
+ try:
66
+ resp = httpx.post(
67
+ PROVISION_URL,
68
+ json={"source": "ctxstore-mcp"},
69
+ timeout=15.0,
70
+ )
71
+ resp.raise_for_status()
72
+ data = resp.json()
73
+ except httpx.HTTPError as e:
74
+ raise RuntimeError(
75
+ f"Could not provision ctxstore.ai account: {e}\n"
76
+ f"Set TENANT_API_KEY manually or visit {CTXSTORE_URL}"
77
+ ) from e
78
+
79
+ api_key = data.get("api_key", "")
80
+ tenant_id = data.get("tenant_id", "")
81
+
82
+ if not api_key:
83
+ raise RuntimeError(
84
+ f"Provisioning response missing api_key. "
85
+ f"Visit {CTXSTORE_URL} to get your key."
86
+ )
87
+
88
+ save_credentials(api_key, tenant_id)
89
+ print(
90
+ f"✓ ctxstore.ai: account provisioned! Key saved to {CREDENTIALS_FILE}",
91
+ file=sys.stderr,
92
+ )
93
+ return api_key
94
+
95
+
96
+ def get_or_provision() -> str:
97
+ """
98
+ Return the API key, auto-provisioning if needed.
99
+
100
+ Priority:
101
+ 1. TENANT_API_KEY environment variable
102
+ 2. ~/.ctxstore/credentials.json
103
+ 3. Auto-provision via ctxstore.ai (free, no signup)
104
+ """
105
+ # 1. Env var always wins
106
+ env_key = os.getenv("TENANT_API_KEY", "").strip()
107
+ if env_key:
108
+ return env_key
109
+
110
+ # 2. Saved credentials
111
+ creds = load_credentials()
112
+ if creds:
113
+ return creds["api_key"]
114
+
115
+ # 3. Auto-provision
116
+ return provision()
117
+
118
+
119
+ # Alias for server.py import
120
+ resolve_api_key = get_or_provision
@@ -0,0 +1,303 @@
1
+ """
2
+ ctxstore MCP Server — thin client that proxies to ctxstore.ai API.
3
+
4
+ All embedding generation and vector storage happens server-side.
5
+ This package just translates MCP tool calls to HTTP API calls.
6
+
7
+ Environment variables:
8
+ TENANT_API_KEY — Your ctxstore.ai API key (required)
9
+ CTXSTORE_URL — API base URL (default: https://ctxstore.ai)
10
+ """
11
+
12
+ import logging
13
+ import os
14
+ import sys
15
+ from typing import Any
16
+
17
+ import httpx
18
+ from mcp.server import Server
19
+ from mcp.server.stdio import stdio_server
20
+ from mcp.types import TextContent, Tool
21
+
22
+ from .auth import resolve_api_key
23
+
24
+ logger = logging.getLogger("ctxstore.mcp")
25
+
26
+ BASE_URL = os.getenv("CTXSTORE_URL", "https://ctxstore.ai").rstrip("/")
27
+
28
+ # Resolved lazily on first use so startup auto-provision prints before the
29
+ # server enters the stdio event loop.
30
+ _API_KEY: str | None = None
31
+
32
+
33
+ def _get_api_key() -> str:
34
+ global _API_KEY
35
+ if _API_KEY is None:
36
+ _API_KEY = resolve_api_key()
37
+ return _API_KEY
38
+
39
+ app = Server("ctxstore")
40
+
41
+
42
+ def _headers() -> dict:
43
+ return {
44
+ "Authorization": f"Bearer {_get_api_key()}",
45
+ "Content-Type": "application/json",
46
+ "User-Agent": "ctxstore-mcp/1.0.0",
47
+ }
48
+
49
+
50
+ async def _api(method: str, path: str, json: dict = None) -> dict:
51
+ """Make an authenticated API call to ctxstore.ai."""
52
+ async with httpx.AsyncClient(timeout=30.0) as client:
53
+ resp = await client.request(
54
+ method,
55
+ f"{BASE_URL}{path}",
56
+ headers=_headers(),
57
+ json=json,
58
+ )
59
+ if resp.status_code == 401:
60
+ return {"error": "Invalid API key. Check your TENANT_API_KEY."}
61
+ if resp.status_code == 429:
62
+ return {"error": "Rate limit reached. Upgrade your plan at ctxstore.ai."}
63
+ if resp.status_code >= 400:
64
+ try:
65
+ return resp.json()
66
+ except Exception:
67
+ return {"error": f"API error: {resp.status_code}"}
68
+ return resp.json()
69
+
70
+
71
+ @app.list_tools()
72
+ async def list_tools() -> list[Tool]:
73
+ return [
74
+ Tool(
75
+ name="search_context",
76
+ description=(
77
+ "Search memory — all ingested conversations and sessions. "
78
+ "Returns semantically relevant context with temporal weighting "
79
+ "(recent = higher rank)."
80
+ ),
81
+ inputSchema={
82
+ "type": "object",
83
+ "properties": {
84
+ "query": {
85
+ "type": "string",
86
+ "description": "Natural language search query",
87
+ },
88
+ "top_k": {
89
+ "type": "integer",
90
+ "description": "Number of results (default 20, max 100)",
91
+ "default": 20,
92
+ },
93
+ "source": {
94
+ "type": "string",
95
+ "description": "Filter by source: 'chatgpt' or 'claude'",
96
+ "enum": ["chatgpt", "claude"],
97
+ },
98
+ "days_back": {
99
+ "type": "integer",
100
+ "description": "Only search within last N days",
101
+ },
102
+ },
103
+ "required": ["query"],
104
+ },
105
+ ),
106
+ Tool(
107
+ name="search_facts",
108
+ description=(
109
+ "Search extracted facts, preferences, and decisions. "
110
+ "Facts are tagged by category: preference, decision, identity, "
111
+ "technical, relationship. Permanent facts bypass temporal decay."
112
+ ),
113
+ inputSchema={
114
+ "type": "object",
115
+ "properties": {
116
+ "query": {
117
+ "type": "string",
118
+ "description": "What to search for",
119
+ },
120
+ "category": {
121
+ "type": "string",
122
+ "description": "Filter by category",
123
+ "enum": ["preference", "decision", "identity", "technical", "relationship"],
124
+ },
125
+ "top_k": {
126
+ "type": "integer",
127
+ "description": "Number of results",
128
+ "default": 10,
129
+ },
130
+ },
131
+ "required": ["query"],
132
+ },
133
+ ),
134
+ Tool(
135
+ name="store_fact",
136
+ description=(
137
+ "Store a new fact for permanent retention. Use this when the user "
138
+ "states a preference, makes a decision, or shares information that "
139
+ "should persist across sessions."
140
+ ),
141
+ inputSchema={
142
+ "type": "object",
143
+ "properties": {
144
+ "text": {
145
+ "type": "string",
146
+ "description": "The fact to store",
147
+ },
148
+ "category": {
149
+ "type": "string",
150
+ "description": "Fact category",
151
+ "enum": ["preference", "decision", "identity", "technical", "relationship"],
152
+ },
153
+ "is_permanent": {
154
+ "type": "boolean",
155
+ "description": "Whether this fact should bypass temporal decay",
156
+ "default": True,
157
+ },
158
+ },
159
+ "required": ["text", "category"],
160
+ },
161
+ ),
162
+ Tool(
163
+ name="delete_fact",
164
+ description="Delete a stored fact by its ID.",
165
+ inputSchema={
166
+ "type": "object",
167
+ "properties": {
168
+ "fact_id": {
169
+ "type": "string",
170
+ "description": "The UUID of the fact to delete",
171
+ },
172
+ },
173
+ "required": ["fact_id"],
174
+ },
175
+ ),
176
+ Tool(
177
+ name="get_stats",
178
+ description=(
179
+ "Get statistics about your context store — total vectors, "
180
+ "collection sizes, usage vs plan limits."
181
+ ),
182
+ inputSchema={
183
+ "type": "object",
184
+ "properties": {},
185
+ },
186
+ ),
187
+ ]
188
+
189
+
190
+ @app.call_tool()
191
+ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
192
+ if not _get_api_key():
193
+ return [TextContent(
194
+ type="text",
195
+ text="No API key configured. Set TENANT_API_KEY in your MCP config. "
196
+ "Get a free key at https://ctxstore.ai",
197
+ )]
198
+
199
+ if name == "search_context":
200
+ result = await _api("POST", "/api/v1/search", {
201
+ "query": arguments["query"],
202
+ "top_k": arguments.get("top_k", 20),
203
+ "source": arguments.get("source"),
204
+ "days_back": arguments.get("days_back"),
205
+ })
206
+ if "error" in result:
207
+ return [TextContent(type="text", text=result["error"])]
208
+ return [TextContent(type="text", text=_format_search_results(result))]
209
+
210
+ elif name == "search_facts":
211
+ result = await _api("POST", "/api/v1/facts/search", {
212
+ "query": arguments["query"],
213
+ "category": arguments.get("category"),
214
+ "top_k": arguments.get("top_k", 10),
215
+ })
216
+ if "error" in result:
217
+ return [TextContent(type="text", text=result["error"])]
218
+ return [TextContent(type="text", text=_format_fact_results(result))]
219
+
220
+ elif name == "store_fact":
221
+ result = await _api("POST", "/api/v1/facts", {
222
+ "text": arguments["text"],
223
+ "category": arguments["category"],
224
+ "is_permanent": arguments.get("is_permanent", True),
225
+ })
226
+ if "error" in result:
227
+ return [TextContent(type="text", text=result["error"])]
228
+ return [TextContent(
229
+ type="text",
230
+ text=f"Fact stored (id={result.get('fact_id', '?')}, "
231
+ f"category={arguments['category']}, "
232
+ f"permanent={arguments.get('is_permanent', True)}): "
233
+ f"{arguments['text']}",
234
+ )]
235
+
236
+ elif name == "delete_fact":
237
+ result = await _api("DELETE", f"/api/v1/facts/{arguments['fact_id']}")
238
+ if "error" in result:
239
+ return [TextContent(type="text", text=result["error"])]
240
+ return [TextContent(type="text", text=f"Fact {arguments['fact_id']} deleted.")]
241
+
242
+ elif name == "get_stats":
243
+ result = await _api("GET", "/api/v1/stats")
244
+ if "error" in result:
245
+ return [TextContent(type="text", text=result["error"])]
246
+ return [TextContent(type="text", text=_format_stats(result))]
247
+
248
+ return [TextContent(type="text", text=f"Unknown tool: {name}")]
249
+
250
+
251
+ def _format_search_results(data: dict) -> str:
252
+ results = data.get("results", [])
253
+ if not results:
254
+ return "No relevant context found."
255
+ formatted = []
256
+ for r in results:
257
+ header = f"[{r.get('source', '?')}|{r.get('age', '?')}|score:{r.get('score', 0):.3f}]"
258
+ title = r.get("conversation_title", "")
259
+ if title:
260
+ header += f" ({title})"
261
+ formatted.append(f"{header}\n{r.get('text', '')}\n")
262
+ return "\n---\n".join(formatted)
263
+
264
+
265
+ def _format_fact_results(data: dict) -> str:
266
+ results = data.get("results", [])
267
+ if not results:
268
+ return "No matching facts found."
269
+ formatted = []
270
+ for r in results:
271
+ perm = "permanent" if r.get("is_permanent") else "decaying"
272
+ formatted.append(
273
+ f"[{r.get('category', '?')}|{perm}|score:{r.get('score', 0):.3f}] "
274
+ f"{r.get('text', '')}"
275
+ )
276
+ return "\n".join(formatted)
277
+
278
+
279
+ def _format_stats(data: dict) -> str:
280
+ import json
281
+ return json.dumps(data, indent=2)
282
+
283
+
284
+ async def main():
285
+ logging.basicConfig(
286
+ level=logging.INFO,
287
+ format="%(asctime)s | %(name)s | %(levelname)s | %(message)s",
288
+ stream=sys.stderr,
289
+ )
290
+
291
+ # Resolve API key on startup (triggers auto-provision if needed)
292
+ api_key = _get_api_key()
293
+ if not api_key:
294
+ logger.warning("No API key resolved — tools will prompt for signup")
295
+
296
+ logger.info(f"ctxstore MCP server starting (endpoint: {BASE_URL})")
297
+
298
+ async with stdio_server() as (read_stream, write_stream):
299
+ await app.run(
300
+ read_stream,
301
+ write_stream,
302
+ app.create_initialization_options(),
303
+ )
@@ -0,0 +1,156 @@
1
+ """
2
+ ctxstore-setup — one-command MCP client configuration.
3
+
4
+ Detects installed AI clients, injects ctxstore MCP config, provisions tenant.
5
+ Usage: ctxstore-setup
6
+ """
7
+
8
+ import json
9
+ import os
10
+ import shutil
11
+ import sys
12
+ from pathlib import Path
13
+ from typing import Optional
14
+
15
+ from .auth import get_or_provision
16
+
17
+ CTXSTORE_URL = os.getenv("CTXSTORE_URL", "https://ctxstore.ai").rstrip("/")
18
+
19
+ # ── MCP client config locations ────────────────────────────────────────────────
20
+
21
+ MCP_CLIENTS = [
22
+ {
23
+ "name": "Claude Desktop",
24
+ "paths": [
25
+ Path.home() / "Library" / "Application Support" / "Claude" / "claude_desktop_config.json",
26
+ Path.home() / ".config" / "claude" / "claude_desktop_config.json",
27
+ ],
28
+ "wrapper": "mcpServers",
29
+ },
30
+ {
31
+ "name": "Claude Code",
32
+ "paths": [Path.home() / ".claude.json"],
33
+ "wrapper": "mcpServers",
34
+ },
35
+ {
36
+ "name": "Cursor",
37
+ "paths": [Path.home() / ".cursor" / "mcp.json"],
38
+ "wrapper": "mcpServers",
39
+ },
40
+ {
41
+ "name": "VS Code",
42
+ "paths": [Path.home() / ".vscode" / "mcp.json"],
43
+ "wrapper": "servers",
44
+ },
45
+ {
46
+ "name": "Windsurf",
47
+ "paths": [Path.home() / ".windsurf" / "mcp.json"],
48
+ "wrapper": "mcpServers",
49
+ },
50
+ ]
51
+
52
+
53
+ def _inject_config(config_path: Path, wrapper: str, api_key: str) -> bool:
54
+ """
55
+ Non-destructively inject ctxstore entry into an MCP client config.
56
+ Backs up the original file before modifying.
57
+ Returns True on success.
58
+ """
59
+ try:
60
+ with open(config_path) as f:
61
+ try:
62
+ config = json.load(f)
63
+ except json.JSONDecodeError:
64
+ config = {}
65
+
66
+ if wrapper not in config:
67
+ config[wrapper] = {}
68
+
69
+ config[wrapper]["ctxstore"] = {
70
+ "command": "ctxstore-mcp",
71
+ "env": {"TENANT_API_KEY": api_key},
72
+ }
73
+
74
+ # Backup
75
+ shutil.copy2(config_path, str(config_path) + ".bak")
76
+
77
+ with open(config_path, "w") as f:
78
+ json.dump(config, f, indent=2)
79
+ f.write("\n")
80
+
81
+ return True
82
+ except (OSError, json.JSONDecodeError) as e:
83
+ print(f" ✗ Error modifying {config_path}: {e}", file=sys.stderr)
84
+ return False
85
+
86
+
87
+ def _health_check() -> bool:
88
+ """Check if ctxstore.ai is reachable."""
89
+ try:
90
+ import httpx
91
+ resp = httpx.get(f"{CTXSTORE_URL}/api/health", timeout=10.0)
92
+ return resp.status_code == 200 and "ok" in resp.text.lower()
93
+ except Exception:
94
+ return False
95
+
96
+
97
+ def main() -> None:
98
+ print("\n✦ ctxstore-setup\n")
99
+
100
+ # Step 1: Get/provision API key
101
+ print("→ Checking credentials...")
102
+ try:
103
+ api_key = get_or_provision()
104
+ print(f" ✓ API key ready")
105
+ except RuntimeError as e:
106
+ print(f" ✗ {e}", file=sys.stderr)
107
+ sys.exit(1)
108
+
109
+ # Step 2: Detect and configure MCP clients
110
+ print("\n→ Detecting MCP clients...")
111
+ configured = []
112
+ found_any = False
113
+
114
+ for client in MCP_CLIENTS:
115
+ for config_path in client["paths"]:
116
+ if config_path.exists():
117
+ found_any = True
118
+ print(f" Found: {client['name']} ({config_path})")
119
+ success = _inject_config(config_path, client["wrapper"], api_key)
120
+ if success:
121
+ configured.append(client["name"])
122
+ print(f" ✓ Configured {client['name']}")
123
+ break # Only configure first found path per client
124
+
125
+ if not found_any:
126
+ print(" No MCP clients detected.")
127
+ print("\n Add this to your client's MCP config manually:")
128
+ print("""
129
+ {
130
+ "mcpServers": {
131
+ "ctxstore": {
132
+ "command": "ctxstore-mcp",
133
+ "env": { "TENANT_API_KEY": \"""" + api_key + """\" }
134
+ }
135
+ }
136
+ }""")
137
+
138
+ # Step 3: Health check
139
+ print("\n→ Health check...")
140
+ if _health_check():
141
+ print(" ✓ ctxstore.ai is reachable")
142
+ else:
143
+ print(" ⚠ Could not reach ctxstore.ai — check your connection")
144
+
145
+ # Step 4: Summary
146
+ print("\n" + "━" * 50)
147
+ print("✓ ctxstore-setup complete!\n")
148
+ if configured:
149
+ print(f" Configured: {', '.join(configured)}")
150
+ print(" → Restart your AI client to activate memory")
151
+ print(f"\n Docs: {CTXSTORE_URL}/docs")
152
+ print("━" * 50 + "\n")
153
+
154
+
155
+ if __name__ == "__main__":
156
+ main()
@@ -0,0 +1,27 @@
1
+ Metadata-Version: 2.4
2
+ Name: ctxstore-mcp
3
+ Version: 1.0.0
4
+ Summary: Persistent AI memory via MCP. Give your AI agent long-term memory in 30 seconds.
5
+ Author-email: "ctxstore.ai" <hello@ctxstore.ai>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://ctxstore.ai
8
+ Project-URL: Documentation, https://ctxstore.ai/docs
9
+ Project-URL: Repository, https://github.com/ctxstore-ai/ctxstore-mcp
10
+ Project-URL: Bug Tracker, https://github.com/ctxstore-ai/ctxstore-mcp/issues
11
+ Keywords: mcp,ai,memory,context,vector,llm,claude,cursor
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.10
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: mcp>=1.0.0
24
+ Requires-Dist: httpx>=0.25.0
25
+ Dynamic: license-file
26
+
27
+ # ctxstore-mcp
@@ -0,0 +1,14 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ ctxstore_mcp/__init__.py
5
+ ctxstore_mcp/__main__.py
6
+ ctxstore_mcp/auth.py
7
+ ctxstore_mcp/server.py
8
+ ctxstore_mcp/setup_cli.py
9
+ ctxstore_mcp.egg-info/PKG-INFO
10
+ ctxstore_mcp.egg-info/SOURCES.txt
11
+ ctxstore_mcp.egg-info/dependency_links.txt
12
+ ctxstore_mcp.egg-info/entry_points.txt
13
+ ctxstore_mcp.egg-info/requires.txt
14
+ ctxstore_mcp.egg-info/top_level.txt
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ ctxstore-mcp = ctxstore_mcp:main
3
+ ctxstore-setup = ctxstore_mcp.setup_cli:main
@@ -0,0 +1,2 @@
1
+ mcp>=1.0.0
2
+ httpx>=0.25.0
@@ -0,0 +1 @@
1
+ ctxstore_mcp
@@ -0,0 +1,39 @@
1
+ [project]
2
+ name = "ctxstore-mcp"
3
+ version = "1.0.0"
4
+ description = "Persistent AI memory via MCP. Give your AI agent long-term memory in 30 seconds."
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ requires-python = ">=3.10"
8
+ authors = [
9
+ {name = "ctxstore.ai", email = "hello@ctxstore.ai"},
10
+ ]
11
+ keywords = ["mcp", "ai", "memory", "context", "vector", "llm", "claude", "cursor"]
12
+ classifiers = [
13
+ "Development Status :: 5 - Production/Stable",
14
+ "Intended Audience :: Developers",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.10",
17
+ "Programming Language :: Python :: 3.11",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
20
+ "Topic :: Software Development :: Libraries :: Python Modules",
21
+ ]
22
+ dependencies = [
23
+ "mcp>=1.0.0",
24
+ "httpx>=0.25.0",
25
+ ]
26
+
27
+ [project.scripts]
28
+ ctxstore-mcp = "ctxstore_mcp:main"
29
+ ctxstore-setup = "ctxstore_mcp.setup_cli:main"
30
+
31
+ [project.urls]
32
+ Homepage = "https://ctxstore.ai"
33
+ Documentation = "https://ctxstore.ai/docs"
34
+ Repository = "https://github.com/ctxstore-ai/ctxstore-mcp"
35
+ "Bug Tracker" = "https://github.com/ctxstore-ai/ctxstore-mcp/issues"
36
+
37
+ [build-system]
38
+ requires = ["setuptools>=61.0", "wheel"]
39
+ build-backend = "setuptools.build_meta"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+