bgts-context-engine 0.2.3__tar.gz → 0.2.5__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 (131) hide show
  1. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/CHANGELOG.md +77 -1
  2. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/MANIFEST.in +1 -0
  3. {bgts_context_engine-0.2.3/src/bgts_context_engine.egg-info → bgts_context_engine-0.2.5}/PKG-INFO +31 -8
  4. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/README.md +30 -7
  5. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/README.tr.md +30 -5
  6. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/pyproject.toml +1 -0
  7. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/__init__.py +1 -1
  8. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/mcp/server.py +5 -2
  9. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/mcp/tools.py +33 -8
  10. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/schemas.py +7 -3
  11. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/cli.py +130 -3
  12. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/config.py +14 -0
  13. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/assembler/assembler.py +16 -11
  14. bgts_context_engine-0.2.5/src/bce/core/defaults.py +19 -0
  15. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/embedder/encoder.py +14 -5
  16. bgts_context_engine-0.2.5/src/bce/integrations/__init__.py +10 -0
  17. bgts_context_engine-0.2.5/src/bce/integrations/agents.py +271 -0
  18. bgts_context_engine-0.2.5/src/bce/integrations/precontext.py +168 -0
  19. bgts_context_engine-0.2.5/src/bce/integrations/templates/claude_section.md +16 -0
  20. bgts_context_engine-0.2.5/src/bce/integrations/templates/cursor_rule.mdc +20 -0
  21. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/tools/layer3/__init__.py +3 -0
  22. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/tools/layer3/orchestration.py +34 -8
  23. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5/src/bgts_context_engine.egg-info}/PKG-INFO +31 -8
  24. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bgts_context_engine.egg-info/SOURCES.txt +6 -0
  25. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/LICENSE +0 -0
  26. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/setup.cfg +0 -0
  27. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/__init__.py +0 -0
  28. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/mcp/__init__.py +0 -0
  29. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/__init__.py +0 -0
  30. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/app.py +0 -0
  31. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/deps.py +0 -0
  32. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/routes.py +0 -0
  33. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/static/assets/index-Cc4pUpSE.js +0 -0
  34. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/static/assets/index-DrLRaTMU.css +0 -0
  35. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/static/favicon.svg +0 -0
  36. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/static/index.html +0 -0
  37. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/ui/__init__.py +0 -0
  38. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/ui/queries.py +0 -0
  39. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/ui/router.py +0 -0
  40. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/ui/schemas.py +0 -0
  41. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/ui/trace.py +0 -0
  42. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/bench/__init__.py +0 -0
  43. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/bench/pr_ablation.py +0 -0
  44. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/bench/runner.py +0 -0
  45. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/__init__.py +0 -0
  46. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/assembler/__init__.py +0 -0
  47. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/auth/__init__.py +0 -0
  48. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/auth/scope.py +0 -0
  49. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/coverage/__init__.py +0 -0
  50. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/coverage/confidence.py +0 -0
  51. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/i18n/__init__.py +0 -0
  52. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/i18n/catalogs/en.json +0 -0
  53. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/i18n/catalogs/tr.json +0 -0
  54. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/i18n/locale.py +0 -0
  55. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/i18n/translator.py +0 -0
  56. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/logging.py +0 -0
  57. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/orchestrator/__init__.py +0 -0
  58. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/orchestrator/anchors.py +0 -0
  59. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/orchestrator/bulk.py +0 -0
  60. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/orchestrator/expand.py +0 -0
  61. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/orchestrator/orchestrator.py +0 -0
  62. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/orchestrator/profile.py +0 -0
  63. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/orchestrator/text.py +0 -0
  64. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/scoring/__init__.py +0 -0
  65. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/scoring/engine.py +0 -0
  66. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/domain/__init__.py +0 -0
  67. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/domain/enums.py +0 -0
  68. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/domain/models.py +0 -0
  69. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/__init__.py +0 -0
  70. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/churn.py +0 -0
  71. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/embedder/__init__.py +0 -0
  72. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/embedder/embedder.py +0 -0
  73. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/extractor/__init__.py +0 -0
  74. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/extractor/bridges.py +0 -0
  75. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/extractor/designnote.py +0 -0
  76. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/extractor/extractor.py +0 -0
  77. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/extractor/routes.py +0 -0
  78. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/gitsync/__init__.py +0 -0
  79. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/gitsync/bitbucket.py +0 -0
  80. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/gitsync/local.py +0 -0
  81. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/gitsync/remote.py +0 -0
  82. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/indexer.py +0 -0
  83. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/linker/__init__.py +0 -0
  84. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/linker/linker.py +0 -0
  85. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/__init__.py +0 -0
  86. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/_treesitter.py +0 -0
  87. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/base.py +0 -0
  88. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/languages/__init__.py +0 -0
  89. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/languages/csharp_provider.py +0 -0
  90. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/languages/go_provider.py +0 -0
  91. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/languages/java_provider.py +0 -0
  92. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/languages/jsts_provider.py +0 -0
  93. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/languages/python_provider.py +0 -0
  94. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/registry.py +0 -0
  95. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/scip/__init__.py +0 -0
  96. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/scip/resolver.py +0 -0
  97. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/symbol_id.py +0 -0
  98. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/upserter/__init__.py +0 -0
  99. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/upserter/upserter.py +0 -0
  100. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/jobs/__init__.py +0 -0
  101. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/jobs/store.py +0 -0
  102. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/jobs/worker.py +0 -0
  103. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/__init__.py +0 -0
  104. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/graph/__init__.py +0 -0
  105. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/graph/client.py +0 -0
  106. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/graph/repository.py +0 -0
  107. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/__init__.py +0 -0
  108. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/db.py +0 -0
  109. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrations/0001_extensions_graph.sql +0 -0
  110. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrations/0002_relational.sql +0 -0
  111. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrations/0003_vector.sql +0 -0
  112. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrations/0004_rls.sql +0 -0
  113. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrations/0005_embeddings_dim.sql +0 -0
  114. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrations/0006_fts.sql +0 -0
  115. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrations/0007_jobs.sql +0 -0
  116. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrations/0008_graph_label_indexes.sql +0 -0
  117. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrations/0009_fts_identifier_parts.sql +0 -0
  118. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrations/0010_file_churn.sql +0 -0
  119. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrator.py +0 -0
  120. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/queries.py +0 -0
  121. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/vector/__init__.py +0 -0
  122. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/vector/store.py +0 -0
  123. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/tools/__init__.py +0 -0
  124. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/tools/layer1/__init__.py +0 -0
  125. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/tools/layer1/primitives.py +0 -0
  126. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/tools/layer2/__init__.py +0 -0
  127. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/tools/layer2/search.py +0 -0
  128. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bgts_context_engine.egg-info/dependency_links.txt +0 -0
  129. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bgts_context_engine.egg-info/entry_points.txt +0 -0
  130. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bgts_context_engine.egg-info/requires.txt +0 -0
  131. {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bgts_context_engine.egg-info/top_level.txt +0 -0
@@ -7,6 +7,80 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.5] - 2026-09-24
11
+
12
+ ### Changed
13
+
14
+ - The English and Turkish READMEs link to the project website
15
+ ([bgts-ai-org.github.io/bce-microsite](https://bgts-ai-org.github.io/bce-microsite/))
16
+ from the top navigation.
17
+
18
+ ## [0.2.4] - 2026-09-22
19
+
20
+ This release turns the agent benchmark (a Cursor CLI agent on 14 tasks against a
21
+ React/TypeScript codebase, plain vs. with the engine) into product behaviour. The MCP server alone made a Cursor agent *worse*: it asked the graph late (after
22
+ a round of grep) or eight times per task, and the answer it got back had no file or line.
23
+ With one advertised tool, a rule that says "call it first and treat `file:line` as a
24
+ verified location", the answer handed over before the first turn, and a shorter context
25
+ budget, the same agent on the same 14 tasks, model and machine used 20 % fewer tokens,
26
+ produced 46 % less grep/glob output and 13 % fewer model turns, passed 73/73 checks against
27
+ 72/73, and changed 23 % fewer lines. Everything below is what that took.
28
+
29
+ ### Added
30
+
31
+ - `bce cursor-init [--project DIR] [--repo-id ID]... [--env-file PATH] [--bce-command X]`:
32
+ writes a project's `.cursor/mcp.json` (merged into an existing one; other servers are
33
+ kept) with the absolute path of the running `bce`, `serve-mcp --env-file …` and
34
+ `PYTHONUTF8=1`, plus the `alwaysApply` rule `.cursor/rules/bgts-context-engine.mdc`. The
35
+ rule ships in the wheel as a template, so every install gets the same instruction: call
36
+ `get_context_for_task` first with the full task text, open the returned `file:line`
37
+ entries directly instead of grepping for them, treat the list as places to inspect rather
38
+ than files to edit, never repeat a call with the same text. Idempotent.
39
+ - `bce claude-init …[--no-hook]`: the Claude Code counterpart — `.mcp.json` (merged), a
40
+ section in `CLAUDE.md` between `<!-- bgts-context-engine:begin/end -->` markers (the rest
41
+ of the file is kept), and by default a `UserPromptSubmit` hook in `.claude/settings.json`
42
+ (other hooks kept) that runs `bce precontext` before every prompt. Cursor gets no hook: its
43
+ `beforeSubmitPrompt` event can only allow or block a prompt, so there the rule carries the
44
+ instruction and the agent makes the one call itself.
45
+ - `bce precontext [--task TEXT] [--repo-id ID]...`: the compact "Code context for this
46
+ task" block an agent prompt carries — a file list in rank order, then one line per symbol
47
+ (`path:line kind name distance`) with a snippet of at most 12 lines where the snippet says
48
+ more than the name; about 5 k characters at the default budget. Without `--task` it is the
49
+ Claude Code hook: reads the hook JSON on stdin, skips prompts under 20 characters and slash
50
+ commands, prints `hookSpecificOutput.additionalContext` capped at Claude Code's
51
+ 10 000-character limit, and fails open (an unreachable database is a prompt without
52
+ context, never a blocked prompt). `bce.integrations.precontext.render_context_markdown`
53
+ is the renderer, for other hosts.
54
+ - `BCE_MCP_TOOLS`: comma-separated names the MCP server advertises (default: all read
55
+ tools). Agents fetch a tool's schema before first use and chain small calls when many
56
+ tools are on offer; the benchmark ran with `get_context_for_task` alone. Unknown names
57
+ fail at startup, and a tool not advertised cannot be dispatched either.
58
+ - `BCE_EMBEDDING_QUERY_TIMEOUT` (10 s, two attempts): the wait for embedding a single
59
+ *query* at search time, separate from the indexing timeout (600 s). When it expires,
60
+ `get_context_for_task` and `suggest_change_sites` answer from lexical and structural
61
+ anchors alone — visible as lower `coverage.confidence` — instead of failing after having
62
+ waited past an agent's MCP timeout (Cursor: 60 s). Motivated by a RunPod TCP proxy that
63
+ stalled connects for 19–67 s during the benchmark.
64
+ - Context items carry `name`, `kind`, `file_id` and `line` next to `symbol_id`. Without
65
+ them an agent had to search the tree for the file behind every symbol id (the module path
66
+ does not say `.ts` vs `.tsx`, nor the line) — one tool call, one model turn, per item.
67
+
68
+ ### Changed
69
+
70
+ - **Defaults: `max_candidates` 8 → 20, `max_tokens` 4000 → 1500**, on every surface (CLI
71
+ `bce context`, REST, MCP schema defaults, and the `bce.core.defaults` module they all
72
+ read). K=20 is where recall@K flattened in the PR replay for both fitted profiles and
73
+ what the agent benchmark ran with. The budget is shorter because an agent re-sends the
74
+ context on every turn: at 1500 it travelled as ~1.3 k tokens per turn and still halved
75
+ the agent's search output; 4000 travelled as ~4 k per turn for no further gain. Callers
76
+ wanting longer snippets pass `max_tokens` per request; the assembler still degrades items
77
+ to reference level rather than dropping them.
78
+ - `.env.example` shows `voyage-code-4` (the model the voyage profile was fitted on) as the
79
+ Voyage example and documents `BCE_EMBEDDING_QUERY_TIMEOUT` and `BCE_MCP_TOOLS`. The code
80
+ default stays `voyage-code-3` so indexes built with it keep matching.
81
+ - `server.json` describes the catalog as eight read-only tools plus two write tools rather
82
+ than "14 tools".
83
+
10
84
  ## [0.2.3] - 2026-09-21
11
85
 
12
86
  ### Added
@@ -304,7 +378,9 @@ First public release.
304
378
  - Docker Compose deployment with PostgreSQL, Apache AGE and pgvector preconfigured.
305
379
  - `server.json` manifest describing the stdio server for the official MCP registry.
306
380
 
307
- [Unreleased]: https://github.com/bgts-ai-org/bgts-context-engine/compare/v0.2.3...HEAD
381
+ [Unreleased]: https://github.com/bgts-ai-org/bgts-context-engine/compare/v0.2.5...HEAD
382
+ [0.2.5]: https://github.com/bgts-ai-org/bgts-context-engine/compare/v0.2.4...v0.2.5
383
+ [0.2.4]: https://github.com/bgts-ai-org/bgts-context-engine/compare/v0.2.3...v0.2.4
308
384
  [0.2.3]: https://github.com/bgts-ai-org/bgts-context-engine/compare/v0.2.2...v0.2.3
309
385
  [0.2.2]: https://github.com/bgts-ai-org/bgts-context-engine/compare/v0.2.1...v0.2.2
310
386
  [0.2.1]: https://github.com/bgts-ai-org/bgts-context-engine/compare/v0.2.0...v0.2.1
@@ -4,6 +4,7 @@ include README.tr.md
4
4
  include CHANGELOG.md
5
5
  recursive-include src/bce/core/i18n/catalogs *.json
6
6
  recursive-include src/bce/storage/relational/migrations *.sql
7
+ recursive-include src/bce/integrations/templates *.mdc *.md
7
8
 
8
9
  # Compiled frontend, vendored by scripts/build_ui.py before a release build.
9
10
  recursive-include src/bce/api/rest/static *
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bgts-context-engine
3
- Version: 0.2.3
3
+ Version: 0.2.5
4
4
  Summary: Deterministic code-graph context engine for AI coding agents - PostgreSQL, Apache AGE and pgvector, over MCP and REST
5
5
  Author: Oğuz Öztürk, Enes İyidil
6
6
  Maintainer: BGTS
@@ -69,7 +69,7 @@ that actually answer it — ranked, budgeted, and reproducible.
69
69
  [![MCP](https://img.shields.io/badge/MCP-compatible-000000.svg)](docs/mcp.md)
70
70
  [![Stars](https://img.shields.io/github/stars/bgts-ai-org/bgts-context-engine?style=flat&logo=github)](https://github.com/bgts-ai-org/bgts-context-engine/stargazers)
71
71
 
72
- [Quick start](#quick-start) · [Use it from your agent](#use-it-from-your-agent) · [How it works](#how-it-works) · [Supported models](#supported-models) · [Documentation](#documentation) · [Türkçe](README.tr.md)
72
+ [Quick start](#quick-start) · [Use it from your agent](#use-it-from-your-agent) · [How it works](#how-it-works) · [Supported models](#supported-models) · [Documentation](#documentation) · [Website](https://bgts-ai-org.github.io/bce-microsite/) · [Türkçe](README.tr.md)
73
73
 
74
74
  </div>
75
75
 
@@ -150,9 +150,28 @@ Every MCP client uses the same stdio command plus the database in the environmen
150
150
  leave `bce serve-mcp` running in a terminal for the editor: stdout is the protocol, so the
151
151
  process stays silent, and the IDE starts its own copy.
152
152
 
153
+ **One command per editor.** Run it in the project the agent works on (the repository you
154
+ indexed), pointing at the engine's `.env`:
155
+
156
+ ```bash
157
+ bce --env-file /path/to/engine/.env cursor-init --repo-id my-service # Cursor
158
+ bce --env-file /path/to/engine/.env claude-init --repo-id my-service # Claude Code
159
+ ```
160
+
161
+ `cursor-init` writes `.cursor/mcp.json` (merged into an existing one) and the rule
162
+ `.cursor/rules/bgts-context-engine.mdc`, which tells the agent to call
163
+ `get_context_for_task` *first*, treat its `file:line` entries as verified locations instead
164
+ of grepping for them, and not to edit a file just because it was listed. `claude-init`
165
+ writes `.mcp.json`, a marked section in `CLAUDE.md`, and a `UserPromptSubmit` hook
166
+ (`bce precontext`) that runs the graph once per prompt and hands the agent the answer
167
+ before its first turn — the flow the agent benchmark measured (−20 % tokens, half the search
168
+ output, same or better checks; 14 tasks on a React/TypeScript codebase, same model and machine).
169
+ Cursor's prompt hook cannot add context, so there the rule does that job. `--no-hook`,
170
+ `--repo-id` (repeatable) and `--bce-command` adjust the files; both commands are safe to rerun.
171
+
153
172
  After you add or change the MCP config, **restart Cursor or VS Code** (or Command Palette
154
173
  → “Developer: Reload Window”). The server should then show as enabled with eight tools (ten with indexing enabled).
155
- Setup detail: [docs/mcp.md](docs/mcp.md).
174
+ Setup detail: [docs/mcp.md](docs/mcp.md). The manual equivalents:
156
175
 
157
176
  **Cursor** — user config `~/.cursor/mcp.json` (applies to every project), or a project
158
177
  `.cursor/mcp.json` that stays local (the directory is gitignored):
@@ -217,12 +236,14 @@ Not a list of file paths. A ranked pack, with the reasoning attached:
217
236
  },
218
237
  "context": {
219
238
  "items": [
220
- { "symbol_id": "...refresh_session#88c2", "detail_level": "full",
239
+ { "symbol_id": "...refresh_session#88c2", "name": "refresh_session", "kind": "function",
240
+ "file_id": "my-service:src/auth/session.py", "line": 41, "detail_level": "full",
221
241
  "graph_distance": 0, "score": 11.42, "tokens": 214, "content": "def refresh_session(...)" },
222
- { "symbol_id": "...SESSION_TTL#4b0d", "detail_level": "signature",
242
+ { "symbol_id": "...SESSION_TTL#4b0d", "name": "SESSION_TTL", "kind": "constant",
243
+ "file_id": "my-service:src/auth/config.py", "line": 12, "detail_level": "signature",
223
244
  "graph_distance": 2, "score": 6.10, "tokens": 31, "content": "SESSION_TTL: int" }
224
245
  ],
225
- "used_tokens": 2913, "budget": 4000, "included": 8, "skipped": 0
246
+ "used_tokens": 1388, "budget": 1500, "included": 20, "skipped": 0
226
247
  },
227
248
  "coverage": {
228
249
  "anchor_source_count": 3, "connected_component_ratio": 0.875,
@@ -243,8 +264,10 @@ could not corroborate it — the moment for an agent to ask a follow-up question
243
264
  editing. `commit_mismatch` means the index is behind your working tree.
244
265
 
245
266
  **`detail_level`** falls off with graph distance: the symbol you are changing arrives in
246
- full, its neighbours as signatures, the outer ring as `name @ file:line`. That is how eight
247
- genuinely relevant symbols fit in 4000 tokens.
267
+ full, its neighbours as signatures, the outer ring as `name @ file:line`. That is how twenty
268
+ genuinely relevant symbols fit in 1500 tokens — short enough for an agent to carry on every
269
+ turn. Every item also names its `file_id` and `line`, so the agent opens the file instead of
270
+ searching for the symbol.
248
271
 
249
272
  ## How it works
250
273
 
@@ -16,7 +16,7 @@ that actually answer it — ranked, budgeted, and reproducible.
16
16
  [![MCP](https://img.shields.io/badge/MCP-compatible-000000.svg)](docs/mcp.md)
17
17
  [![Stars](https://img.shields.io/github/stars/bgts-ai-org/bgts-context-engine?style=flat&logo=github)](https://github.com/bgts-ai-org/bgts-context-engine/stargazers)
18
18
 
19
- [Quick start](#quick-start) · [Use it from your agent](#use-it-from-your-agent) · [How it works](#how-it-works) · [Supported models](#supported-models) · [Documentation](#documentation) · [Türkçe](README.tr.md)
19
+ [Quick start](#quick-start) · [Use it from your agent](#use-it-from-your-agent) · [How it works](#how-it-works) · [Supported models](#supported-models) · [Documentation](#documentation) · [Website](https://bgts-ai-org.github.io/bce-microsite/) · [Türkçe](README.tr.md)
20
20
 
21
21
  </div>
22
22
 
@@ -97,9 +97,28 @@ Every MCP client uses the same stdio command plus the database in the environmen
97
97
  leave `bce serve-mcp` running in a terminal for the editor: stdout is the protocol, so the
98
98
  process stays silent, and the IDE starts its own copy.
99
99
 
100
+ **One command per editor.** Run it in the project the agent works on (the repository you
101
+ indexed), pointing at the engine's `.env`:
102
+
103
+ ```bash
104
+ bce --env-file /path/to/engine/.env cursor-init --repo-id my-service # Cursor
105
+ bce --env-file /path/to/engine/.env claude-init --repo-id my-service # Claude Code
106
+ ```
107
+
108
+ `cursor-init` writes `.cursor/mcp.json` (merged into an existing one) and the rule
109
+ `.cursor/rules/bgts-context-engine.mdc`, which tells the agent to call
110
+ `get_context_for_task` *first*, treat its `file:line` entries as verified locations instead
111
+ of grepping for them, and not to edit a file just because it was listed. `claude-init`
112
+ writes `.mcp.json`, a marked section in `CLAUDE.md`, and a `UserPromptSubmit` hook
113
+ (`bce precontext`) that runs the graph once per prompt and hands the agent the answer
114
+ before its first turn — the flow the agent benchmark measured (−20 % tokens, half the search
115
+ output, same or better checks; 14 tasks on a React/TypeScript codebase, same model and machine).
116
+ Cursor's prompt hook cannot add context, so there the rule does that job. `--no-hook`,
117
+ `--repo-id` (repeatable) and `--bce-command` adjust the files; both commands are safe to rerun.
118
+
100
119
  After you add or change the MCP config, **restart Cursor or VS Code** (or Command Palette
101
120
  → “Developer: Reload Window”). The server should then show as enabled with eight tools (ten with indexing enabled).
102
- Setup detail: [docs/mcp.md](docs/mcp.md).
121
+ Setup detail: [docs/mcp.md](docs/mcp.md). The manual equivalents:
103
122
 
104
123
  **Cursor** — user config `~/.cursor/mcp.json` (applies to every project), or a project
105
124
  `.cursor/mcp.json` that stays local (the directory is gitignored):
@@ -164,12 +183,14 @@ Not a list of file paths. A ranked pack, with the reasoning attached:
164
183
  },
165
184
  "context": {
166
185
  "items": [
167
- { "symbol_id": "...refresh_session#88c2", "detail_level": "full",
186
+ { "symbol_id": "...refresh_session#88c2", "name": "refresh_session", "kind": "function",
187
+ "file_id": "my-service:src/auth/session.py", "line": 41, "detail_level": "full",
168
188
  "graph_distance": 0, "score": 11.42, "tokens": 214, "content": "def refresh_session(...)" },
169
- { "symbol_id": "...SESSION_TTL#4b0d", "detail_level": "signature",
189
+ { "symbol_id": "...SESSION_TTL#4b0d", "name": "SESSION_TTL", "kind": "constant",
190
+ "file_id": "my-service:src/auth/config.py", "line": 12, "detail_level": "signature",
170
191
  "graph_distance": 2, "score": 6.10, "tokens": 31, "content": "SESSION_TTL: int" }
171
192
  ],
172
- "used_tokens": 2913, "budget": 4000, "included": 8, "skipped": 0
193
+ "used_tokens": 1388, "budget": 1500, "included": 20, "skipped": 0
173
194
  },
174
195
  "coverage": {
175
196
  "anchor_source_count": 3, "connected_component_ratio": 0.875,
@@ -190,8 +211,10 @@ could not corroborate it — the moment for an agent to ask a follow-up question
190
211
  editing. `commit_mismatch` means the index is behind your working tree.
191
212
 
192
213
  **`detail_level`** falls off with graph distance: the symbol you are changing arrives in
193
- full, its neighbours as signatures, the outer ring as `name @ file:line`. That is how eight
194
- genuinely relevant symbols fit in 4000 tokens.
214
+ full, its neighbours as signatures, the outer ring as `name @ file:line`. That is how twenty
215
+ genuinely relevant symbols fit in 1500 tokens — short enough for an agent to carry on every
216
+ turn. Every item also names its `file_id` and `line`, so the agent opens the file instead of
217
+ searching for the symbol.
195
218
 
196
219
  ## How it works
197
220
 
@@ -14,7 +14,7 @@ veren sekiz sembolü sıralanmış, bütçelenmiş ve yeniden üretilebilir şek
14
14
  [![MCP](https://img.shields.io/badge/MCP-uyumlu-000000.svg)](docs/mcp.md)
15
15
  [![Yıldızlar](https://img.shields.io/github/stars/bgts-ai-org/bgts-context-engine?style=flat&logo=github)](https://github.com/bgts-ai-org/bgts-context-engine/stargazers)
16
16
 
17
- [Hızlı başlangıç](#hızlı-başlangıç) · [Ajanınızdan kullanma](#ajanınızdan-kullanma) · [Nasıl çalışır](#nasıl-çalışır) · [Desteklenen modeller](#desteklenen-modeller) · [Dokümantasyon](#dokümantasyon) · [English](README.md)
17
+ [Hızlı başlangıç](#hızlı-başlangıç) · [Ajanınızdan kullanma](#ajanınızdan-kullanma) · [Nasıl çalışır](#nasıl-çalışır) · [Desteklenen modeller](#desteklenen-modeller) · [Dokümantasyon](#dokümantasyon) · [Site](https://bgts-ai-org.github.io/bce-microsite/) · [English](README.md)
18
18
 
19
19
  </div>
20
20
 
@@ -97,9 +97,31 @@ Her MCP istemcisi aynı stdio komutunu ve ortamındaki veritabanı bağlantısı
97
97
  Editör için terminalde `bce serve-mcp` açık bırakmayın: stdout protokoldür, bu yüzden
98
98
  süreç sessiz kalır; IDE kendi kopyasını başlatır.
99
99
 
100
+ **Editör başına tek komut.** Ajanın çalıştığı projede (indekslediğiniz depoda), motorun
101
+ `.env` dosyasını göstererek çalıştırın:
102
+
103
+ ```bash
104
+ bce --env-file /path/to/engine/.env cursor-init --repo-id my-service # Cursor
105
+ bce --env-file /path/to/engine/.env claude-init --repo-id my-service # Claude Code
106
+ ```
107
+
108
+ `cursor-init`, `.cursor/mcp.json` dosyasını (varsa üzerine birleştirerek) ve
109
+ `.cursor/rules/bgts-context-engine.mdc` kuralını yazar. Kural ajana şunları söyler:
110
+ `get_context_for_task` aracını *ilk iş olarak* çağır, döndürdüğü `dosya:satır`
111
+ girdilerini doğrulanmış konum say ve onları grep ile arama, listelendi diye bir dosyayı
112
+ düzenleme. `claude-init` ise `.mcp.json`, `CLAUDE.md` içinde işaretli bir bölüm ve
113
+ `bce precontext` komutunu çalıştıran bir `UserPromptSubmit` kancası yazar: kanca her
114
+ istemde grafı bir kez sorgular ve yanıtı ajana ilk turundan önce verir. Ajan
115
+ benchmark'ında ölçülen akış budur (−%20 token, yarıya inen arama çıktısı, eşit veya daha
116
+ iyi kontroller; React/TypeScript bir kod tabanında 14 görev, aynı model ve makine).
117
+ Cursor'ın istem kancası bağlam ekleyemediği için orada bu işi kural yapar. `--no-hook`,
118
+ `--repo-id` (tekrarlanabilir) ve `--bce-command` dosyaları ayarlar; iki komut da tekrar
119
+ çalıştırılmaya uygundur.
120
+
100
121
  MCP yapılandırmasını ekledikten veya değiştirdikten sonra **Cursor veya VS Code'u
101
122
  yeniden başlatın** (veya Komut Paleti → “Developer: Reload Window”). Sunucu sekiz araçla
102
123
  (indeksleme açıkken on araçla) etkin görünmelidir. Ayrıntı: [docs/mcp.md](docs/mcp.md).
124
+ Elle yapmanın karşılıkları:
103
125
 
104
126
  **Cursor** — her proje için kullanıcı yapılandırması `~/.cursor/mcp.json`, ya da yerelde
105
127
  kalan bir proje `.cursor/mcp.json` (dizin gitignore'dadır):
@@ -165,12 +187,14 @@ Dosya yolu listesi değil. Gerekçesi ekli, sıralanmış bir paket:
165
187
  },
166
188
  "context": {
167
189
  "items": [
168
- { "symbol_id": "...refresh_session#88c2", "detail_level": "full",
190
+ { "symbol_id": "...refresh_session#88c2", "name": "refresh_session", "kind": "function",
191
+ "file_id": "my-service:src/auth/session.py", "line": 41, "detail_level": "full",
169
192
  "graph_distance": 0, "score": 11.42, "tokens": 214, "content": "def refresh_session(...)" },
170
- { "symbol_id": "...SESSION_TTL#4b0d", "detail_level": "signature",
193
+ { "symbol_id": "...SESSION_TTL#4b0d", "name": "SESSION_TTL", "kind": "constant",
194
+ "file_id": "my-service:src/auth/config.py", "line": 12, "detail_level": "signature",
171
195
  "graph_distance": 2, "score": 6.10, "tokens": 31, "content": "SESSION_TTL: int" }
172
196
  ],
173
- "used_tokens": 2913, "budget": 4000, "included": 8, "skipped": 0
197
+ "used_tokens": 1388, "budget": 1500, "included": 20, "skipped": 0
174
198
  },
175
199
  "coverage": {
176
200
  "anchor_source_count": 3, "connected_component_ratio": 0.875,
@@ -192,7 +216,8 @@ gereken an. `commit_mismatch` ise indeksin çalışma ağacınızın gerisinde k
192
216
 
193
217
  **`detail_level`** graf mesafesiyle azalır: değiştirdiğiniz sembol tam gövdesiyle,
194
218
  komşuları imza olarak, dış halka `name @ dosya:satır` biçiminde gelir. Gerçekten ilgili
195
- sekiz sembolün 4000 token'a sığması bu sayede olur.
219
+ yirmi sembolün 1500 token'a sığması bu sayede olur — bir ajanın her turda taşıyabileceği
220
+ kadar kısa. Her öge `file_id` ve `line` da taşır; ajan sembolü aramak yerine dosyayı açar.
196
221
 
197
222
  ## Nasıl çalışır
198
223
 
@@ -106,6 +106,7 @@ include = ["bce*"]
106
106
  bce = [
107
107
  "core/i18n/catalogs/*.json",
108
108
  "storage/relational/migrations/*.sql",
109
+ "integrations/templates/*",
109
110
  # Compiled frontend served at /ui; vendored by scripts/build_ui.py before a release build.
110
111
  "api/rest/static/**/*",
111
112
  ]
@@ -5,4 +5,4 @@ specification for details. This package is the provider side of the deterministi
5
5
  contains no LLM and produces reproducible context packages.
6
6
  """
7
7
 
8
- __version__ = "0.2.3"
8
+ __version__ = "0.2.5"
@@ -44,8 +44,9 @@ def build_server(name: str = "bgts-context-engine") -> Any:
44
44
  from bce.config import get_settings
45
45
  from bce.storage.relational.db import connection
46
46
 
47
- allow_write = get_settings().mcp_allow_write
48
- specs = available_tool_specs(allow_write=allow_write)
47
+ settings = get_settings()
48
+ allow_write = settings.mcp_allow_write
49
+ specs = available_tool_specs(allow_write=allow_write, allowlist=settings.mcp_tool_allowlist)
49
50
  server = Server(name)
50
51
 
51
52
  @server.list_tools()
@@ -56,6 +57,8 @@ def build_server(name: str = "bgts-context-engine") -> Any:
56
57
  ]
57
58
 
58
59
  def _dispatch_blocking(name: str, arguments: dict[str, Any]) -> dict[str, Any]:
60
+ if name not in specs:
61
+ raise KeyError(f"tool '{name}' is not advertised by this server (BCE_MCP_TOOLS)")
59
62
  with connection() as conn:
60
63
  try:
61
64
  result = dispatch_tool(conn, name, arguments, allow_write=allow_write)
@@ -26,6 +26,7 @@ from typing import Any
26
26
  import psycopg
27
27
 
28
28
  from bce.core.auth.scope import Principal, ScopeFilter
29
+ from bce.core.defaults import DEFAULT_MAX_CANDIDATES, DEFAULT_MAX_TOKENS
29
30
  from bce.core.i18n import get_translator
30
31
  from bce.jobs.store import enqueue_job, get_job, list_jobs
31
32
  from bce.storage.graph.client import GraphClient
@@ -57,10 +58,21 @@ TOOL_SPECS: dict[str, dict[str, Any]] = {
57
58
  ),
58
59
  "schema": _schema(
59
60
  {
60
- "task_text": _STR,
61
+ "task_text": {
62
+ **_STR,
63
+ "description": "The task in prose; pass the full request text, once.",
64
+ },
61
65
  "task_id": _STR,
62
- "max_tokens": _INT,
63
- "max_candidates": _INT,
66
+ "max_tokens": {
67
+ **_INT,
68
+ "default": DEFAULT_MAX_TOKENS,
69
+ "description": "Token budget of the assembled context.",
70
+ },
71
+ "max_candidates": {
72
+ **_INT,
73
+ "default": DEFAULT_MAX_CANDIDATES,
74
+ "description": "How many symbols to return (K).",
75
+ },
64
76
  "commit": _STR,
65
77
  "repo_ids": _STR_LIST,
66
78
  "explicit_symbols": _STR_LIST,
@@ -137,11 +149,24 @@ TOOL_SPECS: dict[str, dict[str, Any]] = {
137
149
  }
138
150
 
139
151
 
140
- def available_tool_specs(*, allow_write: bool) -> dict[str, dict[str, Any]]:
141
- """The catalog to advertise: write tools are hidden unless ``allow_write`` is set."""
142
- if allow_write:
143
- return dict(TOOL_SPECS)
144
- return {name: spec for name, spec in TOOL_SPECS.items() if name not in WRITE_TOOLS}
152
+ def available_tool_specs(
153
+ *, allow_write: bool, allowlist: frozenset[str] | None = None
154
+ ) -> dict[str, dict[str, Any]]:
155
+ """The catalog to advertise: write tools are hidden unless ``allow_write`` is set, and an
156
+ ``allowlist`` (``BCE_MCP_TOOLS``) narrows it further. Unknown names in the allowlist raise, so a
157
+ typo does not silently publish nothing."""
158
+ if allowlist is not None:
159
+ unknown = sorted(allowlist - set(TOOL_SPECS))
160
+ if unknown:
161
+ raise ValueError(
162
+ f"BCE_MCP_TOOLS names unknown tools {unknown}; known: {sorted(TOOL_SPECS)}"
163
+ )
164
+ specs = {
165
+ name: spec
166
+ for name, spec in TOOL_SPECS.items()
167
+ if (allow_write or name not in WRITE_TOOLS) and (allowlist is None or name in allowlist)
168
+ }
169
+ return specs
145
170
 
146
171
 
147
172
  def dispatch_tool(
@@ -11,6 +11,8 @@ from typing import Any
11
11
 
12
12
  from pydantic import BaseModel, Field
13
13
 
14
+ from bce.core.defaults import DEFAULT_MAX_CANDIDATES, DEFAULT_MAX_TOKENS
15
+
14
16
 
15
17
  class IndexRequest(BaseModel):
16
18
  repo_path: str = Field(..., description="Path to a local repository to index.")
@@ -64,9 +66,11 @@ class ContextForTaskRequest(BaseModel):
64
66
  task_id: str | None = Field(
65
67
  default=None, description="Optional Jira task id (audit + history)."
66
68
  )
67
- max_tokens: int = Field(default=4000, ge=1, le=200000, description="Token budget for assembly.")
69
+ max_tokens: int = Field(
70
+ default=DEFAULT_MAX_TOKENS, ge=1, le=200000, description="Token budget for assembly."
71
+ )
68
72
  max_candidates: int = Field(
69
- default=8, ge=1, le=100, description="Narrow to at most N candidates."
73
+ default=DEFAULT_MAX_CANDIDATES, ge=1, le=100, description="Narrow to at most N candidates."
70
74
  )
71
75
  commit: str | None = Field(default=None, description="Pinned commit sha (stage 0).")
72
76
  repo_ids: list[str] | None = Field(
@@ -108,7 +112,7 @@ class AssembleContextRequest(BaseModel):
108
112
  symbol_ids: list[str] = Field(
109
113
  ..., description="Symbols to deduplicate and fit into the budget."
110
114
  )
111
- max_tokens: int = Field(default=4000, ge=1, le=200000)
115
+ max_tokens: int = Field(default=DEFAULT_MAX_TOKENS, ge=1, le=200000)
112
116
 
113
117
 
114
118
  class ToolResponse(BaseModel):
@@ -9,10 +9,16 @@ Commands:
9
9
  - ``bench-prs`` PR replay ablation: Voyage alone vs Voyage + BCE (``prepare`` / ``run``)
10
10
  - ``resolve-symbol`` Layer-1: resolve a symbol by name
11
11
  - ``find-references`` Layer-1: find references to a symbol_id
12
+ - ``context`` Layer-3: assemble the context package for a task
13
+ - ``precontext`` the compact context block an agent prompt carries (also a Claude Code hook)
14
+ - ``cursor-init`` write .cursor/mcp.json + the agent rule into a project
15
+ - ``claude-init`` write .mcp.json, a CLAUDE.md section and the pre-context hook into a project
12
16
  - ``languages`` list supported languages/extensions (no database needed)
13
17
  - ``serve`` run the REST API (FastAPI/uvicorn)
18
+ - ``serve-mcp`` run the MCP server over stdio
14
19
 
15
- Read/write commands need a running PostgreSQL (Apache AGE + pgvector); ``languages`` does not.
20
+ Read/write commands need a running PostgreSQL (Apache AGE + pgvector); ``languages``,
21
+ ``cursor-init`` and ``claude-init`` do not.
16
22
  """
17
23
 
18
24
  from __future__ import annotations
@@ -23,6 +29,8 @@ from pathlib import Path
23
29
 
24
30
  import typer
25
31
 
32
+ from bce.core.defaults import DEFAULT_MAX_CANDIDATES, DEFAULT_MAX_TOKENS
33
+
26
34
  app = typer.Typer(add_completion=False, help="BGTS Context Engine CLI")
27
35
 
28
36
 
@@ -382,9 +390,11 @@ def find_references_cmd(
382
390
  @app.command()
383
391
  def context(
384
392
  task: str = typer.Option(..., "--task", help="Task title + description text"),
385
- max_tokens: int = typer.Option(4000, "--max-tokens", help="Token budget for assembly"),
393
+ max_tokens: int = typer.Option(
394
+ DEFAULT_MAX_TOKENS, "--max-tokens", help="Token budget for assembly"
395
+ ),
386
396
  max_candidates: int = typer.Option(
387
- 8, "--max-candidates", help="Narrow to at most N candidates"
397
+ DEFAULT_MAX_CANDIDATES, "--max-candidates", help="Narrow to at most N candidates (K)"
388
398
  ),
389
399
  commit: str | None = typer.Option(None, "--commit", help="Pinned commit sha (stage 0)"),
390
400
  locale: str | None = typer.Option(None, "--locale", help="Message locale (en/tr)"),
@@ -412,6 +422,123 @@ def context(
412
422
  _echo_json(result["payload"])
413
423
 
414
424
 
425
+ @app.command()
426
+ def precontext(
427
+ task: str | None = typer.Option(
428
+ None,
429
+ "--task",
430
+ help="Task text. Omit it to read a Claude Code UserPromptSubmit hook payload from stdin.",
431
+ ),
432
+ repo_id: list[str] = typer.Option(
433
+ [], "--repo-id", help="Restrict to these repository ids (repeatable)"
434
+ ),
435
+ max_candidates: int = typer.Option(DEFAULT_MAX_CANDIDATES, "--max-candidates", help="K"),
436
+ max_tokens: int = typer.Option(DEFAULT_MAX_TOKENS, "--max-tokens", help="Token budget"),
437
+ ) -> None:
438
+ """The compact "Code context for this task" block an agent starts with (markdown).
439
+
440
+ With ``--task`` it prints the block. Without it, it acts as a Claude Code ``UserPromptSubmit``
441
+ hook: reads the hook JSON on stdin and prints the ``additionalContext`` response, or nothing
442
+ when the prompt is not a task. It never fails the prompt: errors go to stderr, exit code 0.
443
+ """
444
+ from bce.integrations.precontext import claude_hook_response, precontext_for_task
445
+
446
+ if task is not None:
447
+ pre = precontext_for_task(
448
+ task, repo_ids=repo_id or None, max_candidates=max_candidates, max_tokens=max_tokens
449
+ )
450
+ typer.echo(pre["text"], nl=False)
451
+ return
452
+ try:
453
+ payload = json.loads(sys.stdin.read() or "{}")
454
+ except json.JSONDecodeError as exc:
455
+ typer.echo(f"bce precontext: stdin is not JSON ({exc})", err=True)
456
+ return
457
+ response = claude_hook_response(payload, repo_ids=repo_id or None)
458
+ if response is not None:
459
+ typer.echo(json.dumps(response, ensure_ascii=False))
460
+
461
+
462
+ def _agent_init_common(
463
+ project: Path, repo_id: list[str], env_file: Path | None, bce_command: str | None
464
+ ) -> tuple[Path, list[str], str, Path | None]:
465
+ from bce.integrations.agents import resolve_bce_command, resolve_env_file
466
+
467
+ project = project.resolve()
468
+ if not project.is_dir():
469
+ raise typer.BadParameter(f"not a directory: {project}", param_hint="--project")
470
+ repo_ids = [r.strip() for r in repo_id if r.strip()] or [project.name]
471
+ try:
472
+ resolved_env = resolve_env_file(env_file, project)
473
+ except FileNotFoundError as exc:
474
+ raise typer.BadParameter(str(exc), param_hint="--env-file") from None
475
+ return project, repo_ids, resolve_bce_command(bce_command), resolved_env
476
+
477
+
478
+ def _report_setup(result, env_file: Path | None) -> None:
479
+ for p in result.written:
480
+ typer.echo(f"wrote {p}")
481
+ if env_file is None:
482
+ typer.secho(
483
+ "No .env found: the MCP server will read BCE_* from the editor's environment. Pass "
484
+ "--env-file PATH (or run `bce --env-file PATH ...`) to point it at your engine "
485
+ "configuration.",
486
+ err=True,
487
+ fg=typer.colors.YELLOW,
488
+ )
489
+ for n in result.notes:
490
+ typer.echo(f"- {n}")
491
+
492
+
493
+ _INIT_PROJECT_HELP = "Project directory to configure (default: current directory)"
494
+ _INIT_REPO_HELP = "Repository id as indexed in the engine (repeatable; default: the directory name)"
495
+ _INIT_ENV_HELP = "The engine .env the MCP server loads (default: --env-file/BCE_ENV_FILE, then <project>/.env, then ./.env)"
496
+ _INIT_CMD_HELP = "Executable the editor spawns (default: this bce)"
497
+
498
+
499
+ @app.command(name="cursor-init")
500
+ def cursor_init(
501
+ project: Path = typer.Option(Path("."), "--project", help=_INIT_PROJECT_HELP),
502
+ repo_id: list[str] = typer.Option([], "--repo-id", help=_INIT_REPO_HELP),
503
+ env_file: Path | None = typer.Option(None, "--env-file", help=_INIT_ENV_HELP),
504
+ bce_command: str | None = typer.Option(None, "--bce-command", help=_INIT_CMD_HELP),
505
+ ) -> None:
506
+ """Connect a project to the engine for Cursor: .cursor/mcp.json + the agent rule.
507
+
508
+ Merges into an existing mcp.json (other servers are kept) and is safe to rerun.
509
+ """
510
+ from bce.integrations.agents import setup_cursor
511
+
512
+ project, repo_ids, cmd, env = _agent_init_common(project, repo_id, env_file, bce_command)
513
+ _report_setup(setup_cursor(project, repo_ids=repo_ids, bce_cmd=cmd, env_file=env), env)
514
+
515
+
516
+ @app.command(name="claude-init")
517
+ def claude_init(
518
+ project: Path = typer.Option(Path("."), "--project", help=_INIT_PROJECT_HELP),
519
+ repo_id: list[str] = typer.Option([], "--repo-id", help=_INIT_REPO_HELP),
520
+ env_file: Path | None = typer.Option(None, "--env-file", help=_INIT_ENV_HELP),
521
+ bce_command: str | None = typer.Option(None, "--bce-command", help=_INIT_CMD_HELP),
522
+ hook: bool = typer.Option(
523
+ True,
524
+ "--hook/--no-hook",
525
+ help="Install the UserPromptSubmit hook that adds the graph's answer to every prompt",
526
+ ),
527
+ ) -> None:
528
+ """Connect a project to the engine for Claude Code: .mcp.json, a CLAUDE.md section and, by
529
+ default, the pre-computed context hook in .claude/settings.json.
530
+
531
+ Merges into existing files (other servers, hooks and CLAUDE.md content are kept) and is safe
532
+ to rerun.
533
+ """
534
+ from bce.integrations.agents import setup_claude
535
+
536
+ project, repo_ids, cmd, env = _agent_init_common(project, repo_id, env_file, bce_command)
537
+ _report_setup(
538
+ setup_claude(project, repo_ids=repo_ids, bce_cmd=cmd, env_file=env, hook=hook), env
539
+ )
540
+
541
+
415
542
  @app.command()
416
543
  def bench(
417
544
  cases: Path = typer.Option(..., "--cases", help="Path to a JSON file of benchmark cases"),
@@ -100,6 +100,11 @@ class Settings(BaseSettings):
100
100
  )
101
101
  # Seconds to wait for one embedding request (a local model on a laptop can need minutes).
102
102
  embedding_timeout: float = 600.0
103
+ # Seconds to wait when embedding a single *query* at search time (openai provider). Queries are
104
+ # one short text, so a healthy server answers in well under a second; a longer wait only means
105
+ # the network stalled. Kept short so a tool call returns (possibly without its semantic anchor)
106
+ # before an agent's own MCP timeout (Cursor: 60 s) kills it. Each query gets two attempts.
107
+ embedding_query_timeout: float = 10.0
103
108
 
104
109
  # --- Retrieval profile (engine constants that depend on the embedding model) ---
105
110
  # "auto" (default) picks the profile fitted for ``embedding_model`` - "voyage" for voyage-*,
@@ -128,6 +133,15 @@ class Settings(BaseSettings):
128
133
  # only read-only tools and runs no job workers. Turning it on also starts a worker pool inside
129
134
  # the MCP process, so queued indexing runs without a separate ``bce serve``.
130
135
  mcp_allow_write: bool = False
136
+ # Which tools the MCP server advertises (comma-separated names; empty = all). Agents fetch a
137
+ # tool's schema before using it and tend to chain small calls when many tools are on offer, so a
138
+ # narrow catalog (e.g. "get_context_for_task,find_references") costs fewer model turns.
139
+ mcp_tools: str = ""
140
+
141
+ @property
142
+ def mcp_tool_allowlist(self) -> frozenset[str] | None:
143
+ names = frozenset(n.strip() for n in self.mcp_tools.split(",") if n.strip())
144
+ return names or None
131
145
 
132
146
  @property
133
147
  def dsn(self) -> str: