memgraph-context-graph 0.8.0__tar.gz → 0.9.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.
Files changed (83) hide show
  1. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/PKG-INFO +33 -4
  2. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/README.md +32 -3
  3. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/docs/command-hooks.md +11 -10
  4. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/pyproject.toml +1 -1
  5. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/_identity.py +42 -4
  6. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/cli.py +73 -74
  7. memgraph_context_graph-0.9.0/src/memgraph_context_graph/connectors.py +71 -0
  8. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/hooks/cli.py +6 -3
  9. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/hooks/runner.py +18 -105
  10. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/setup/agents.py +24 -2
  11. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/setup/cli.py +40 -1
  12. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/setup/engine.py +6 -4
  13. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/setup/tool.py +29 -12
  14. memgraph_context_graph-0.9.0/src/memgraph_context_graph/setup/update.py +124 -0
  15. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/conftest.py +24 -0
  16. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/fake_agent_cli.py +6 -1
  17. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_capture_log.py +5 -2
  18. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_config_cli.py +22 -0
  19. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_hook_cli.py +37 -4
  20. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_identity.py +32 -0
  21. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_setup.py +114 -4
  22. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_tools.py +2 -1
  23. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/.gitignore +0 -0
  24. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/CONTEXT.md +0 -0
  25. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/LICENSE +0 -0
  26. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/docs/adr/0001-runtime-adapter-terminology.md +0 -0
  27. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/docs/adr/0002-config-file-only-hook-resolution.md +0 -0
  28. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/docs/adr/0003-config-path-override.md +0 -0
  29. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/docs/adr/0004-user-id-in-session-start-event.md +0 -0
  30. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/docs/adr/0005-config-file-covers-llm-key-and-subprocess-env.md +0 -0
  31. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/docs/adr/0006-memory-tool-writes-explicit-memory.md +0 -0
  32. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/docs/research/2026-10-codex-memory.md +0 -0
  33. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/__init__.py +0 -0
  34. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/__init__.py +0 -0
  35. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/_opencode_plugin.js +0 -0
  36. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/_spec.py +0 -0
  37. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/agent_mcp.py +0 -0
  38. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/agent_memory.py +0 -0
  39. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/antigravity_cli.py +0 -0
  40. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/claude.py +0 -0
  41. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/claude_code.py +0 -0
  42. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/claude_code_memory.py +0 -0
  43. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/codex.py +0 -0
  44. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/codex_memory.py +0 -0
  45. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/copilot_cli.py +0 -0
  46. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/cursor.py +0 -0
  47. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/grok.py +0 -0
  48. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/openai.py +0 -0
  49. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/adapters/opencode.py +0 -0
  50. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/capture_log.py +0 -0
  51. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/events.py +0 -0
  52. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/hooks/__init__.py +0 -0
  53. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/hooks/runtime_plugin.py +0 -0
  54. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/link.py +0 -0
  55. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/mcp_server.py +0 -0
  56. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/memory_backend.py +0 -0
  57. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/protocols.py +0 -0
  58. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/setup/__init__.py +0 -0
  59. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/setup/memgraph.py +0 -0
  60. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/setup/prompts.py +0 -0
  61. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/setup/uninstall.py +0 -0
  62. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/spaces.py +0 -0
  63. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/terminal.py +0 -0
  64. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/src/memgraph_context_graph/tools.py +0 -0
  65. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/__init__.py +0 -0
  66. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_agent_memory_wiring.py +0 -0
  67. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_antigravity_cli_adapter.py +0 -0
  68. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_claude_code_adapter.py +0 -0
  69. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_codex_adapter.py +0 -0
  70. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_context_before_tool.py +0 -0
  71. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_copilot_cli_adapter.py +0 -0
  72. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_cursor_adapter.py +0 -0
  73. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_dev_scripts.py +0 -0
  74. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_events.py +0 -0
  75. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_grok_adapter.py +0 -0
  76. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_link.py +0 -0
  77. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_opencode_adapter.py +0 -0
  78. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_plugin_hooks.py +0 -0
  79. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_runner_sessions_graph_connector.py +0 -0
  80. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_runtime_plugin.py +0 -0
  81. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_setup_docker.py +0 -0
  82. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_spaces.py +0 -0
  83. {memgraph_context_graph-0.8.0 → memgraph_context_graph-0.9.0}/tests/test_terminal.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: memgraph-context-graph
3
- Version: 0.8.0
3
+ Version: 0.9.0
4
4
  Summary: Connect agent SDKs to context-graph components (actions-graph, skills-graph, etc.)
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -202,6 +202,7 @@ memgraph-context-graph setup # interactive on a terminal; pl
202
202
  memgraph-context-graph setup claude-code codex # just these agents
203
203
  memgraph-context-graph setup cursor --project-dir ~/work/billing
204
204
  memgraph-context-graph setup --plan # print the plan, change nothing
205
+ memgraph-context-graph update [runtime ...] # upgrade the package, refresh the wiring and the schema
205
206
  memgraph-context-graph uninstall [runtime ...] # remove the wiring; the graph and config stay
206
207
  ```
207
208
 
@@ -211,6 +212,15 @@ wires each agent, and runs `doctor`'s checks. Every step checks before it acts,
211
212
  missing. The TUI starts only when stdin and stdout are both terminals; the plugins' `scripts/bootstrap.sh`
212
213
  runs `setup <runtime> --no-tui`.
213
214
 
215
+ **Updating.** Upgrading the package moves the code hooks and the MCP server run, but what setup wrote once stays
216
+ as it was. `update` upgrades the uv tool and its connectors, then, with the new release, refreshes everything
217
+ that is wired: Claude Code's and Codex's plugin (marketplace refreshed, plugin updated), each per-project agent's
218
+ hook file in `--project-dir` (default: the current directory) and its MCP registration, and the graph's indexes
219
+ and constraints. It then runs `doctor`'s checks. Name runtimes to refresh only those; `--no-upgrade` skips the
220
+ package step. Your own hooks, the config and the graph's data are untouched, and running it again changes nothing.
221
+ Running agent sessions keep the hooks they loaded: restart them. Outside the uv tool (a virtualenv, say) the
222
+ package step only warns, and you upgrade it yourself first.
223
+
214
224
  **Identity and connection.** setup writes `~/.config/context-graph/config.toml`; hooks read their configuration from that file at runtime (see [Configuration](#configuration) — env vars are **not** read at hook time). To change your identity later:
215
225
 
216
226
  ```bash
@@ -268,6 +278,10 @@ database = "memgraph"
268
278
  [llm]
269
279
  openai_api_key = ""
270
280
  anthropic_api_key = ""
281
+ derive_model = "claude-sonnet-5-5"
282
+
283
+ [capture]
284
+ connectors = "skills-graph, actions-graph, sessions-graph"
271
285
 
272
286
  [reconcile]
273
287
  auto_reconcile = true
@@ -275,12 +289,21 @@ auto_reconcile = true
275
289
  [recall]
276
290
  embedding_model = "BAAI/bge-small-en-v1.5"
277
291
  turns_k = "8"
292
+
293
+ [extraction]
294
+ model = "fastino/gliner2.5-base-v1"
295
+ entity_threshold = "0.3"
278
296
  ```
279
297
 
280
298
  `[reconcile]` turns on sessions-graph's auto-trigger reconciliation (see
281
299
  [sessions-graph § reconciliation](../sessions-graph/README.md#session-reconciliation)),
282
300
  which needs no LLM. `[llm]` is optional: a key only lets reconciliation learn
283
- the user's own ontology (`sessions-graph derive`).
301
+ the user's own ontology (`sessions-graph derive`), and `derive_model` picks
302
+ the model it calls (default `claude-sonnet-5-5` with Anthropic, `gpt-5` with OpenAI).
303
+ `[capture] connectors` is which graph connectors a hook enables when its
304
+ command names no `--connector`, from `skills-graph`, `actions-graph`,
305
+ `sessions-graph` and `resources-graph`; unset means the first three, and
306
+ an empty value enables none. Setup installs the ones it names.
284
307
  `[reconcile]` is omitted entirely from a file setup wrote — absent
285
308
  means "never configured," distinct from an explicit `auto_reconcile = false`.
286
309
  `[recall]` is optional too: without it, recall runs with the widths the
@@ -288,6 +311,10 @@ benchmark measured; see [Recall](#recall-memory-for-the-harnesss-model).
288
311
  `[ontology]` is optional as well: `path` names a schema file the user's
289
312
  sessions are extracted under and `derive` is `extend` (the default) or
290
313
  `off`; see sessions-graph's [Ontology versions](../sessions-graph/README.md#ontology-versions).
314
+ `[extraction]` tunes how reconciliation, re-extraction and derivation read
315
+ sessions, all optional: `model` (a GLiNER2 checkpoint), `entity_threshold` and
316
+ `relation_threshold` (drop extractions below that confidence, 0 to 1), and
317
+ `chunk_size`/`chunk_overlap` (words per window); unset keys keep GLiNER2's defaults.
291
318
 
292
319
  Manage it with:
293
320
 
@@ -296,8 +323,9 @@ memgraph-context-graph config show
296
323
  memgraph-context-graph config get memgraph.url
297
324
  memgraph-context-graph config set <key> <value>
298
325
  # keys: identity.user_id, memgraph.{url,user,password,database},
299
- # llm.{openai_api_key,anthropic_api_key}, reconcile.auto_reconcile,
300
- # recall.embedding_model, ontology.{path,derive}, memory.backend
326
+ # llm.{openai_api_key,anthropic_api_key,derive_model}, capture.connectors,
327
+ # reconcile.auto_reconcile, recall.embedding_model, ontology.{path,derive}, memory.backend
328
+ # [recall] widths and [extraction] are edited in the file.
301
329
  ```
302
330
 
303
331
  Hooks never read environment variables for these values, and neither does setup: it writes only
@@ -355,6 +383,7 @@ from the tool call, so a model can only read its own user's sessions.
355
383
  | `facts_k`, `fact_turns_k` | `15`, `8` | Facts matched, and the messages they were read from |
356
384
  | `user_fact_types`, `user_facts_k` | `2`, `30` | Relation types gathered whole, for counting questions |
357
385
  | `turn_chars` | `1500` | Characters shown per message |
386
+ | `query_instruction` | per model | Prefix embedded with the question; bge's English models get their retrieval instruction, other models none |
358
387
 
359
388
  Recall needs Memgraph with MAGE (`memgraph/memgraph-mage`) for its vector lanes;
360
389
  without it, it answers from text search and says so. `doctor` checks both
@@ -177,6 +177,7 @@ memgraph-context-graph setup # interactive on a terminal; pl
177
177
  memgraph-context-graph setup claude-code codex # just these agents
178
178
  memgraph-context-graph setup cursor --project-dir ~/work/billing
179
179
  memgraph-context-graph setup --plan # print the plan, change nothing
180
+ memgraph-context-graph update [runtime ...] # upgrade the package, refresh the wiring and the schema
180
181
  memgraph-context-graph uninstall [runtime ...] # remove the wiring; the graph and config stay
181
182
  ```
182
183
 
@@ -186,6 +187,15 @@ wires each agent, and runs `doctor`'s checks. Every step checks before it acts,
186
187
  missing. The TUI starts only when stdin and stdout are both terminals; the plugins' `scripts/bootstrap.sh`
187
188
  runs `setup <runtime> --no-tui`.
188
189
 
190
+ **Updating.** Upgrading the package moves the code hooks and the MCP server run, but what setup wrote once stays
191
+ as it was. `update` upgrades the uv tool and its connectors, then, with the new release, refreshes everything
192
+ that is wired: Claude Code's and Codex's plugin (marketplace refreshed, plugin updated), each per-project agent's
193
+ hook file in `--project-dir` (default: the current directory) and its MCP registration, and the graph's indexes
194
+ and constraints. It then runs `doctor`'s checks. Name runtimes to refresh only those; `--no-upgrade` skips the
195
+ package step. Your own hooks, the config and the graph's data are untouched, and running it again changes nothing.
196
+ Running agent sessions keep the hooks they loaded: restart them. Outside the uv tool (a virtualenv, say) the
197
+ package step only warns, and you upgrade it yourself first.
198
+
189
199
  **Identity and connection.** setup writes `~/.config/context-graph/config.toml`; hooks read their configuration from that file at runtime (see [Configuration](#configuration) — env vars are **not** read at hook time). To change your identity later:
190
200
 
191
201
  ```bash
@@ -243,6 +253,10 @@ database = "memgraph"
243
253
  [llm]
244
254
  openai_api_key = ""
245
255
  anthropic_api_key = ""
256
+ derive_model = "claude-sonnet-5-5"
257
+
258
+ [capture]
259
+ connectors = "skills-graph, actions-graph, sessions-graph"
246
260
 
247
261
  [reconcile]
248
262
  auto_reconcile = true
@@ -250,12 +264,21 @@ auto_reconcile = true
250
264
  [recall]
251
265
  embedding_model = "BAAI/bge-small-en-v1.5"
252
266
  turns_k = "8"
267
+
268
+ [extraction]
269
+ model = "fastino/gliner2.5-base-v1"
270
+ entity_threshold = "0.3"
253
271
  ```
254
272
 
255
273
  `[reconcile]` turns on sessions-graph's auto-trigger reconciliation (see
256
274
  [sessions-graph § reconciliation](../sessions-graph/README.md#session-reconciliation)),
257
275
  which needs no LLM. `[llm]` is optional: a key only lets reconciliation learn
258
- the user's own ontology (`sessions-graph derive`).
276
+ the user's own ontology (`sessions-graph derive`), and `derive_model` picks
277
+ the model it calls (default `claude-sonnet-5-5` with Anthropic, `gpt-5` with OpenAI).
278
+ `[capture] connectors` is which graph connectors a hook enables when its
279
+ command names no `--connector`, from `skills-graph`, `actions-graph`,
280
+ `sessions-graph` and `resources-graph`; unset means the first three, and
281
+ an empty value enables none. Setup installs the ones it names.
259
282
  `[reconcile]` is omitted entirely from a file setup wrote — absent
260
283
  means "never configured," distinct from an explicit `auto_reconcile = false`.
261
284
  `[recall]` is optional too: without it, recall runs with the widths the
@@ -263,6 +286,10 @@ benchmark measured; see [Recall](#recall-memory-for-the-harnesss-model).
263
286
  `[ontology]` is optional as well: `path` names a schema file the user's
264
287
  sessions are extracted under and `derive` is `extend` (the default) or
265
288
  `off`; see sessions-graph's [Ontology versions](../sessions-graph/README.md#ontology-versions).
289
+ `[extraction]` tunes how reconciliation, re-extraction and derivation read
290
+ sessions, all optional: `model` (a GLiNER2 checkpoint), `entity_threshold` and
291
+ `relation_threshold` (drop extractions below that confidence, 0 to 1), and
292
+ `chunk_size`/`chunk_overlap` (words per window); unset keys keep GLiNER2's defaults.
266
293
 
267
294
  Manage it with:
268
295
 
@@ -271,8 +298,9 @@ memgraph-context-graph config show
271
298
  memgraph-context-graph config get memgraph.url
272
299
  memgraph-context-graph config set <key> <value>
273
300
  # keys: identity.user_id, memgraph.{url,user,password,database},
274
- # llm.{openai_api_key,anthropic_api_key}, reconcile.auto_reconcile,
275
- # recall.embedding_model, ontology.{path,derive}, memory.backend
301
+ # llm.{openai_api_key,anthropic_api_key,derive_model}, capture.connectors,
302
+ # reconcile.auto_reconcile, recall.embedding_model, ontology.{path,derive}, memory.backend
303
+ # [recall] widths and [extraction] are edited in the file.
276
304
  ```
277
305
 
278
306
  Hooks never read environment variables for these values, and neither does setup: it writes only
@@ -330,6 +358,7 @@ from the tool call, so a model can only read its own user's sessions.
330
358
  | `facts_k`, `fact_turns_k` | `15`, `8` | Facts matched, and the messages they were read from |
331
359
  | `user_fact_types`, `user_facts_k` | `2`, `30` | Relation types gathered whole, for counting questions |
332
360
  | `turn_chars` | `1500` | Characters shown per message |
361
+ | `query_instruction` | per model | Prefix embedded with the question; bge's English models get their retrieval instruction, other models none |
333
362
 
334
363
  Recall needs Memgraph with MAGE (`memgraph/memgraph-mage`) for its vector lanes;
335
364
  without it, it answers from text search and says so. `doctor` checks both
@@ -5,12 +5,13 @@ Protocol, then routes those events to graph connectors. The installed command
5
5
  is the same for every runtime:
6
6
 
7
7
  ```bash
8
- memgraph-context-graph hook run <runtime> \
9
- --connector skills-graph \
10
- --connector actions-graph \
11
- --connector sessions-graph
8
+ memgraph-context-graph hook run <runtime>
12
9
  ```
13
10
 
11
+ It enables the graph connectors the config file's `[capture] connectors`
12
+ names (unset: `skills-graph`, `actions-graph`, `sessions-graph`). Repeated
13
+ `--connector` flags override that for one hook command.
14
+
14
15
  Runtime registrations are discovered through the
15
16
  `memgraph_context_graph.runtimes` Python entry-point group.
16
17
 
@@ -36,8 +37,9 @@ memgraph-context-graph hook init antigravity-cli
36
37
  memgraph-context-graph hook init grok
37
38
  ```
38
39
 
39
- `hook init` enables all three built-in connectors by default. Use repeated
40
- `--connector` flags to select a subset. Existing Copilot, Cursor, Antigravity, and Grok
40
+ `hook init` writes hook commands without `--connector` flags, so they enable
41
+ `[capture] connectors`. Repeated `--connector` flags pin a set into the
42
+ generated commands instead. Existing Copilot, Cursor, Antigravity, and Grok
41
43
  JSON documents are merged without removing unrelated settings or hooks.
42
44
  OpenCode's generated plugin is replaced only with `--force`.
43
45
 
@@ -246,12 +248,11 @@ memgraph-context-graph setup antigravity-cli --project-dir "$PWD"
246
248
  Smoke-test a registration and its configured connectors:
247
249
 
248
250
  ```bash
249
- memgraph-context-graph doctor --runtime cursor \
250
- --connector skills-graph \
251
- --connector actions-graph \
252
- --connector sessions-graph
251
+ memgraph-context-graph doctor --runtime cursor
253
252
  ```
254
253
 
254
+ `doctor` checks the `[capture] connectors` set unless `--connector` flags name others.
255
+
255
256
  Each runtime provides its own native probe payload; `doctor` does not assume
256
257
  that every runtime calls its session-end event `Stop`.
257
258
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "memgraph-context-graph"
3
- version = "0.8.0"
3
+ version = "0.9.0"
4
4
  description = "Connect agent SDKs to context-graph components (actions-graph, skills-graph, etc.)"
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -85,6 +85,11 @@ class HookConfig:
85
85
  memgraph_database: str = _MEMGRAPH_DEFAULTS["database"]
86
86
  openai_api_key: str = _LLM_DEFAULTS["openai_api_key"]
87
87
  anthropic_api_key: str = _LLM_DEFAULTS["anthropic_api_key"]
88
+ #: ``[llm] derive_model``: the model ontology derivation calls; None means the provider's default.
89
+ derive_model: str | None = None
90
+ #: ``[capture] connectors``: the graph connectors a hook enables when its command names none;
91
+ #: None (never set) means memgraph_context_graph.connectors.DEFAULT_CONNECTORS.
92
+ connectors: tuple[str, ...] | None = None
88
93
  auto_reconcile: bool | None = None
89
94
  #: The model recall embeds with; None means the consumer's own default.
90
95
  embedding_model: str | None = None
@@ -95,6 +100,9 @@ class HookConfig:
95
100
  ontology_path: str | None = None
96
101
  #: ``[ontology] derive``: "extend" (the default) or "off"; see sessions-graph's ontology module.
97
102
  ontology_derive: str | None = None
103
+ #: ``[extraction]`` keys (GLiNER2 checkpoint, thresholds, chunking), as written: sessions-graph
104
+ #: validates them, this module only keeps them across rewrites.
105
+ extraction_settings: dict[str, str] = field(default_factory=dict)
98
106
  #: ``[github] token``: overrides the ``gh`` login resources-graph's Sweep otherwise fetches with.
99
107
  github_token: str | None = None
100
108
  #: ``[memory] backend``: ``"context-graph"`` when the user has handed their
@@ -186,7 +194,7 @@ def resolve_auto_reconcile() -> bool:
186
194
  """Resolve whether sessions-graph should auto-trigger reconciliation on SESSION_END.
187
195
 
188
196
  Config file only (ADR 0002); never configured means ``False``, matching
189
- ``SessionsGraphConnector``'s default, since reconciliation costs LLM calls.
197
+ ``SessionsGraphConnector``'s default.
190
198
  """
191
199
  return bool(load_config().auto_reconcile)
192
200
 
@@ -196,6 +204,16 @@ def resolve_embedding_model() -> str | None:
196
204
  return load_config().embedding_model
197
205
 
198
206
 
207
+ def resolve_extraction() -> dict[str, str]:
208
+ """The configured ``[extraction]`` keys, as written; sessions-graph owns their meaning."""
209
+ return dict(load_config().extraction_settings)
210
+
211
+
212
+ def resolve_derive_model() -> str | None:
213
+ """The configured ``[llm] derive_model``, or None to leave the provider's default."""
214
+ return load_config().derive_model
215
+
216
+
199
217
  def resolve_ontology() -> tuple[str | None, str | None]:
200
218
  """The configured ``[ontology]`` (path, derive), each None when unset; sessions-graph owns their meaning."""
201
219
  config = load_config()
@@ -219,6 +237,8 @@ def write_config(
219
237
  memgraph_database: str | None = None,
220
238
  openai_api_key: str | None = None,
221
239
  anthropic_api_key: str | None = None,
240
+ derive_model: str | None = None,
241
+ connectors: tuple[str, ...] | None = None,
222
242
  auto_reconcile: bool | None = None,
223
243
  embedding_model: str | None = None,
224
244
  ontology_path: str | None = None,
@@ -241,6 +261,8 @@ def write_config(
241
261
  "memgraph_database": memgraph_database,
242
262
  "openai_api_key": openai_api_key,
243
263
  "anthropic_api_key": anthropic_api_key,
264
+ "derive_model": derive_model,
265
+ "connectors": connectors,
244
266
  "auto_reconcile": auto_reconcile,
245
267
  "embedding_model": embedding_model,
246
268
  "ontology_path": ontology_path,
@@ -266,8 +288,8 @@ def write_full_config(
266
288
  ) -> Path:
267
289
  """Write a complete config file, resetting identity, Memgraph and LLM settings to what is given.
268
290
 
269
- Every other section (``[reconcile]``, ``[recall]``, ``[ontology]``, ``[github]``, ``[memory]``,
270
- ``[session_start]``) and the edition are kept from the existing file, since only ``config set`` or a hand edit ever sets them.
291
+ Every other section (``[capture]``, ``[reconcile]``, ``[recall]``, ``[ontology]``, ``[extraction]``,
292
+ ``[github]``, ``[memory]``, ``[session_start]``), ``[llm] derive_model`` and the edition are kept from the existing file, since only ``config set`` or a hand edit ever sets them.
271
293
  ``auto_reconcile`` is kept likewise unless given here. :func:`write_config` is the partial update.
272
294
  """
273
295
  existing = _read_config_file()
@@ -313,9 +335,11 @@ def _read_config_file() -> HookConfig:
313
335
  identity = sections.get("identity", {})
314
336
  memgraph = sections.get("memgraph", {})
315
337
  llm = sections.get("llm", {})
338
+ capture = sections.get("capture", {})
316
339
  reconcile = sections.get("reconcile", {})
317
340
  recall = sections.get("recall", {})
318
341
  ontology = sections.get("ontology", {})
342
+ extraction = sections.get("extraction", {})
319
343
  github = sections.get("github", {})
320
344
  memory = sections.get("memory", {})
321
345
  session_start = sections.get("session_start", {})
@@ -330,11 +354,14 @@ def _read_config_file() -> HookConfig:
330
354
  memgraph_database=memgraph.get("database") or _MEMGRAPH_DEFAULTS["database"],
331
355
  openai_api_key=llm.get("openai_api_key", _LLM_DEFAULTS["openai_api_key"]),
332
356
  anthropic_api_key=llm.get("anthropic_api_key", _LLM_DEFAULTS["anthropic_api_key"]),
357
+ derive_model=llm.get("derive_model") or None,
358
+ connectors=_parse_list(capture["connectors"]) if "connectors" in capture else None,
333
359
  auto_reconcile=parse_bool_flag(auto_reconcile_raw) if auto_reconcile_raw is not None else None,
334
360
  embedding_model=recall.get("embedding_model") or None,
335
361
  recall_settings={key: value for key, value in recall.items() if key != "embedding_model"},
336
362
  ontology_path=ontology.get("path") or None,
337
363
  ontology_derive=ontology.get("derive") or None,
364
+ extraction_settings=dict(extraction),
338
365
  github_token=github.get("token") or None,
339
366
  memory_backend=memory.get("backend") or None,
340
367
  session_resume=parse_bool_flag(session_start["resume"]) if session_start.get("resume") else None,
@@ -348,6 +375,11 @@ def _read_config_file() -> HookConfig:
348
375
  )
349
376
 
350
377
 
378
+ def _parse_list(value: str) -> tuple[str, ...]:
379
+ """A comma-separated value (``"a, b"``) as its non-blank items; the parser has no arrays."""
380
+ return tuple(item.strip() for item in value.split(",") if item.strip())
381
+
382
+
351
383
  def _parse_toml(path: Path, problems: list[str] | None = None) -> dict[str, dict[str, str]]:
352
384
  """Minimal TOML parser — handles [section] and key = "value" pairs.
353
385
 
@@ -387,7 +419,8 @@ def _render_config(config: HookConfig) -> str:
387
419
 
388
420
  ``[reconcile]`` is omitted while ``auto_reconcile`` is ``None`` so a re-read keeps "never configured"
389
421
  distinct from an explicit ``false``, and ``[session_start]`` likewise while ``session_resume`` is;
390
- ``[recall]``, ``[ontology]``, ``[github]`` and ``[memory]`` are omitted while they hold nothing.
422
+ ``[capture]`` likewise while ``connectors`` is; ``[recall]``, ``[ontology]``, ``[extraction]``, ``[github]``
423
+ and ``[memory]`` are omitted while they hold nothing.
391
424
  """
392
425
  user_id = config.user_id or ""
393
426
  lines = [
@@ -407,7 +440,10 @@ def _render_config(config: HookConfig) -> str:
407
440
  "[llm]",
408
441
  f'openai_api_key = "{config.openai_api_key}"',
409
442
  f'anthropic_api_key = "{config.anthropic_api_key}"',
443
+ *([f'derive_model = "{config.derive_model}"'] if config.derive_model else []),
410
444
  ]
445
+ if config.connectors is not None:
446
+ lines += ["", "[capture]", f'connectors = "{", ".join(config.connectors)}"']
411
447
  if config.auto_reconcile is not None:
412
448
  lines += ["", "[reconcile]", f"auto_reconcile = {'true' if config.auto_reconcile else 'false'}"]
413
449
  recall = (
@@ -422,6 +458,8 @@ def _render_config(config: HookConfig) -> str:
422
458
  }
423
459
  if ontology:
424
460
  lines += ["", "[ontology]", *(f'{key} = "{value}"' for key, value in ontology.items())]
461
+ if config.extraction_settings:
462
+ lines += ["", "[extraction]", *(f'{key} = "{value}"' for key, value in config.extraction_settings.items())]
425
463
  if config.github_token:
426
464
  lines += ["", "[github]", f'token = "{config.github_token}"']
427
465
  if config.memory_backend:
@@ -9,10 +9,16 @@ import sys
9
9
  from importlib import metadata
10
10
  from typing import TYPE_CHECKING, Any, TypedDict, cast
11
11
 
12
+ from memgraph_context_graph.connectors import (
13
+ COMPONENTS,
14
+ DEFAULT_CONNECTORS,
15
+ configured_connectors,
16
+ supported_connectors,
17
+ )
12
18
  from memgraph_context_graph.tools import normalize_connector_name
13
19
 
14
20
  if TYPE_CHECKING:
15
- from collections.abc import Sequence
21
+ from collections.abc import Callable, Sequence
16
22
 
17
23
  _HELP = """usage: memgraph-context-graph <command> [options]
18
24
 
@@ -20,6 +26,7 @@ Commands:
20
26
  setup [runtime ...] Install Context Graph for your agents (interactive on a terminal);
21
27
  --memory-backend context-graph|native switches whose memory they use.
22
28
  uninstall [runtime ...] Remove the agents' wiring; your graph and config stay.
29
+ update [runtime ...] Upgrade the package and refresh the agents' wiring and the graph's schema.
23
30
  config Get or set persistent configuration values.
24
31
  doctor Check runtime hook dependencies and Memgraph connectivity.
25
32
  join <url> <login> Use an Enterprise Memgraph as <login> (edition = enterprise); --password or prompt.
@@ -54,6 +61,11 @@ def main(argv: Sequence[str] | None = None) -> int:
54
61
 
55
62
  return uninstall_main(args[1:])
56
63
 
64
+ if command == "update":
65
+ from memgraph_context_graph.setup.cli import update_main
66
+
67
+ return update_main(args[1:])
68
+
57
69
  if command == "config":
58
70
  return _config(args[1:])
59
71
 
@@ -106,6 +118,8 @@ def _config(argv: list[str]) -> int:
106
118
  "memgraph.database": "memgraph_database",
107
119
  "llm.openai_api_key": "openai_api_key",
108
120
  "llm.anthropic_api_key": "anthropic_api_key",
121
+ "llm.derive_model": "derive_model",
122
+ "capture.connectors": "connectors",
109
123
  "reconcile.auto_reconcile": "auto_reconcile",
110
124
  "recall.embedding_model": "embedding_model",
111
125
  "ontology.path": "ontology_path",
@@ -143,6 +157,14 @@ def _config(argv: list[str]) -> int:
143
157
  print(f"memgraph.database = {config.memgraph_database!r}")
144
158
  print(f"llm.openai_api_key = {'***' if config.openai_api_key else repr('')}")
145
159
  print(f"llm.anthropic_api_key = {'***' if config.anthropic_api_key else repr('')}")
160
+ if config.derive_model is None:
161
+ print("llm.derive_model = unset (defaults to the provider's model)")
162
+ else:
163
+ print(f"llm.derive_model = {config.derive_model!r}")
164
+ if config.connectors is None:
165
+ print(f"capture.connectors = unset (defaults to {', '.join(DEFAULT_CONNECTORS)})")
166
+ else:
167
+ print(f"capture.connectors = {', '.join(config.connectors)!r}")
146
168
  if config.auto_reconcile is None:
147
169
  print("reconcile.auto_reconcile = unset (defaults to false)")
148
170
  else:
@@ -156,6 +178,8 @@ def _config(argv: list[str]) -> int:
156
178
  print("ontology.derive = unset (defaults to extend)")
157
179
  else:
158
180
  print(f"ontology.derive = {config.ontology_derive!r}")
181
+ for key, value in config.extraction_settings.items():
182
+ print(f"extraction.{key} = {value!r}")
159
183
  print(f"github.token = {'***' if config.github_token else 'unset'}")
160
184
  print(
161
185
  f"memory.backend = {config.memory_backend!r}"
@@ -189,7 +213,7 @@ def _config(argv: list[str]) -> int:
189
213
  else:
190
214
  value = argv[2]
191
215
 
192
- write_value: str | bool = value
216
+ write_value: str | bool | tuple[str, ...] = value
193
217
  if key in _BOOL_KEYS:
194
218
  normalized = value.strip().lower()
195
219
  if normalized not in TRUTHY_VALUES | FALSY_VALUES:
@@ -205,6 +229,12 @@ def _config(argv: list[str]) -> int:
205
229
  if key == "ontology.derive" and value not in {"extend", "off"}:
206
230
  print(f"Invalid value for {key}: {value!r} (expected extend/off)", file=sys.stderr)
207
231
  return 2
232
+ if key == "capture.connectors":
233
+ try:
234
+ write_value = tuple(supported_connectors(value.split(",")))
235
+ except ValueError as exc:
236
+ print(f"Invalid value for {key}: {exc}", file=sys.stderr)
237
+ return 2
208
238
 
209
239
  # The key/value pairing is only known at runtime (bool iff key is in
210
240
  # _BOOL_KEYS, str otherwise); write_config's own parameters are precisely
@@ -214,6 +244,8 @@ def _config(argv: list[str]) -> int:
214
244
  display_value = "***"
215
245
  elif key in _BOOL_KEYS:
216
246
  display_value = str(write_value).lower()
247
+ elif isinstance(write_value, tuple):
248
+ display_value = repr(", ".join(write_value))
217
249
  else:
218
250
  display_value = repr(write_value)
219
251
  print(f"Wrote {key} = {display_value} to {config_path}")
@@ -236,6 +268,8 @@ def _config(argv: list[str]) -> int:
236
268
  return 1
237
269
  if key in _SECRET_KEYS:
238
270
  print("***")
271
+ elif isinstance(value, tuple):
272
+ print(", ".join(value))
239
273
  else:
240
274
  print(value)
241
275
  return 0
@@ -370,7 +404,7 @@ def _doctor(argv: list[str]) -> int:
370
404
  "--connector",
371
405
  action="append",
372
406
  default=None,
373
- help="Graph connector to enable. Defaults to skills-graph.",
407
+ help="Graph connector to check, repeatable. Defaults to the config file's [capture] connectors.",
374
408
  )
375
409
  parser.add_argument(
376
410
  "--json",
@@ -379,7 +413,7 @@ def _doctor(argv: list[str]) -> int:
379
413
  )
380
414
  args = parser.parse_args(argv)
381
415
 
382
- checks = doctor_checks([args.runtime], args.connector or ["skills-graph"])
416
+ checks = doctor_checks([args.runtime], args.connector or configured_connectors())
383
417
  ok = all(check["ok"] for check in checks)
384
418
  payload: _DoctorPayload = {"ok": ok, "checks": checks}
385
419
  if args.json:
@@ -519,82 +553,47 @@ def _check_package(package_name: str) -> _CheckResult:
519
553
 
520
554
  def _check_connector(connector_name: str) -> _CheckResult:
521
555
  normalized = normalize_connector_name(connector_name)
522
- if normalized == "skills-graph":
523
- graph = None
524
- try:
525
- from skills_graph import SkillGraph
556
+ if normalized not in COMPONENTS:
557
+ return {"name": f"connector:{connector_name}", "ok": False, "detail": "unsupported connector"}
526
558
 
527
- graph = SkillGraph()
528
- version = _package_version("skills-graph")
529
- return {"name": "connector:skills-graph", "ok": True, "detail": f"installed={version}; memgraph=reachable"}
530
- except Exception as exc:
531
- return {"name": "connector:skills-graph", "ok": False, "detail": f"{type(exc).__name__}: {exc}"}
532
- finally:
533
- _close(graph and graph._db)
559
+ from memgraph_context_graph.adapters._identity import resolve_memgraph_env
560
+ from memgraph_context_graph.hooks.runner import memgraph_kwargs
534
561
 
535
- if normalized == "actions-graph":
536
- graph = None
537
- try:
538
- from actions_graph import ActionsGraph
562
+ name = f"connector:{normalized}"
563
+ graph = None
564
+ try:
565
+ graph = COMPONENTS[normalized].graph()(**memgraph_kwargs(resolve_memgraph_env()))
566
+ detail = f"installed={_package_version(normalized)}; memgraph=reachable"
567
+ extra = _CONNECTOR_CHECKS.get(normalized)
568
+ ok, more = extra() if extra is not None else (True, "")
569
+ return {"name": name, "ok": ok, "detail": f"{detail}; {more}" if ok and more else more or detail}
570
+ except Exception as exc:
571
+ return {"name": name, "ok": False, "detail": f"{type(exc).__name__}: {exc}"}
572
+ finally:
573
+ _close(graph and graph._db)
539
574
 
540
- graph = ActionsGraph()
541
- version = _package_version("actions-graph")
542
- return {"name": "connector:actions-graph", "ok": True, "detail": f"installed={version}; memgraph=reachable"}
543
- except Exception as exc:
544
- return {"name": "connector:actions-graph", "ok": False, "detail": f"{type(exc).__name__}: {exc}"}
545
- finally:
546
- _close(graph and graph._db)
547
575
 
548
- if normalized == "sessions-graph":
549
- graph = None
550
- try:
551
- from sessions_graph import SessionsGraph
552
-
553
- from memgraph_context_graph.adapters._identity import resolve_memgraph_env, resolve_user_id
554
- from memgraph_context_graph.hooks.runner import memgraph_kwargs
555
-
556
- graph = SessionsGraph(**memgraph_kwargs(resolve_memgraph_env()))
557
- version = _package_version("sessions-graph")
558
- user_id = resolve_user_id({})
559
- if not user_id:
560
- return {
561
- "name": "connector:sessions-graph",
562
- "ok": False,
563
- "detail": "user_id not resolved — run: memgraph-context-graph config set identity.user_id <your-name>",
564
- }
565
- return {
566
- "name": "connector:sessions-graph",
567
- "ok": True,
568
- "detail": f"installed={version}; memgraph=reachable; user_id={user_id!r}",
569
- }
570
- except Exception as exc:
571
- return {"name": "connector:sessions-graph", "ok": False, "detail": f"{type(exc).__name__}: {exc}"}
572
- finally:
573
- _close(graph and graph._db)
576
+ def _check_sessions_graph() -> tuple[bool, str]:
577
+ from memgraph_context_graph.adapters._identity import resolve_user_id
574
578
 
575
- if normalized == "resources-graph":
576
- graph = None
577
- try:
578
- from memgraph_context_graph.adapters._identity import load_config, resolve_memgraph_env
579
- from memgraph_context_graph.hooks.runner import memgraph_kwargs
580
- from resources_graph import ResourcesGraph
581
-
582
- graph = ResourcesGraph(**memgraph_kwargs(resolve_memgraph_env()))
583
- version = _package_version("resources-graph")
584
- # Touches are recorded without a token; the Sweep falls back to the user's gh login.
585
- token = "set" if load_config().github_token else "unset (the Sweep uses the gh login)"
586
- return {
587
- "name": "connector:resources-graph",
588
- "ok": True,
589
- "detail": f"installed={version}; memgraph=reachable; github.token {token}",
590
- }
591
- except Exception as exc:
592
- return {"name": "connector:resources-graph", "ok": False, "detail": f"{type(exc).__name__}: {exc}"}
593
- finally:
594
- _close(graph and graph._db)
579
+ user_id = resolve_user_id({})
580
+ if not user_id:
581
+ return False, "user_id not resolved — run: memgraph-context-graph config set identity.user_id <your-name>"
582
+ return True, f"user_id={user_id!r}"
595
583
 
596
- else:
597
- return {"name": f"connector:{connector_name}", "ok": False, "detail": "unsupported connector"}
584
+
585
+ def _check_resources_graph() -> tuple[bool, str]:
586
+ from memgraph_context_graph.adapters._identity import load_config
587
+
588
+ # Touches are recorded without a token; the Sweep falls back to the user's gh login.
589
+ return True, f"github.token {'set' if load_config().github_token else 'unset (the Sweep uses the gh login)'}"
590
+
591
+
592
+ #: What a connector needs beyond a reachable Memgraph: (ok, what its check line adds, or why it fails).
593
+ _CONNECTOR_CHECKS: dict[str, Callable[[], tuple[bool, str]]] = {
594
+ "sessions-graph": _check_sessions_graph,
595
+ "resources-graph": _check_resources_graph,
596
+ }
598
597
 
599
598
 
600
599
  def _check_embeddings() -> _CheckResult: