code-graph-builder 0.2.0__py3-none-any.whl

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 (93) hide show
  1. code_graph_builder/__init__.py +82 -0
  2. code_graph_builder/builder.py +366 -0
  3. code_graph_builder/cgb_cli.py +32 -0
  4. code_graph_builder/cli.py +564 -0
  5. code_graph_builder/commands_cli.py +1288 -0
  6. code_graph_builder/config.py +340 -0
  7. code_graph_builder/constants.py +708 -0
  8. code_graph_builder/embeddings/__init__.py +40 -0
  9. code_graph_builder/embeddings/qwen3_embedder.py +573 -0
  10. code_graph_builder/embeddings/vector_store.py +584 -0
  11. code_graph_builder/examples/__init__.py +0 -0
  12. code_graph_builder/examples/example_configuration.py +276 -0
  13. code_graph_builder/examples/example_kuzu_usage.py +109 -0
  14. code_graph_builder/examples/example_semantic_search_full.py +347 -0
  15. code_graph_builder/examples/generate_wiki.py +915 -0
  16. code_graph_builder/examples/graph_export_example.py +100 -0
  17. code_graph_builder/examples/rag_example.py +206 -0
  18. code_graph_builder/examples/test_cli_demo.py +129 -0
  19. code_graph_builder/examples/test_embedding_api.py +153 -0
  20. code_graph_builder/examples/test_kuzu_local.py +190 -0
  21. code_graph_builder/examples/test_rag_redis.py +390 -0
  22. code_graph_builder/graph_updater.py +605 -0
  23. code_graph_builder/guidance/__init__.py +1 -0
  24. code_graph_builder/guidance/agent.py +123 -0
  25. code_graph_builder/guidance/prompts.py +74 -0
  26. code_graph_builder/guidance/toolset.py +264 -0
  27. code_graph_builder/language_spec.py +536 -0
  28. code_graph_builder/mcp/__init__.py +21 -0
  29. code_graph_builder/mcp/api_doc_generator.py +764 -0
  30. code_graph_builder/mcp/file_editor.py +207 -0
  31. code_graph_builder/mcp/pipeline.py +777 -0
  32. code_graph_builder/mcp/server.py +161 -0
  33. code_graph_builder/mcp/tools.py +1800 -0
  34. code_graph_builder/models.py +115 -0
  35. code_graph_builder/parser_loader.py +344 -0
  36. code_graph_builder/parsers/__init__.py +7 -0
  37. code_graph_builder/parsers/call_processor.py +306 -0
  38. code_graph_builder/parsers/call_resolver.py +139 -0
  39. code_graph_builder/parsers/definition_processor.py +796 -0
  40. code_graph_builder/parsers/factory.py +119 -0
  41. code_graph_builder/parsers/import_processor.py +293 -0
  42. code_graph_builder/parsers/structure_processor.py +145 -0
  43. code_graph_builder/parsers/type_inference.py +143 -0
  44. code_graph_builder/parsers/utils.py +134 -0
  45. code_graph_builder/rag/__init__.py +68 -0
  46. code_graph_builder/rag/camel_agent.py +429 -0
  47. code_graph_builder/rag/client.py +298 -0
  48. code_graph_builder/rag/config.py +239 -0
  49. code_graph_builder/rag/cypher_generator.py +67 -0
  50. code_graph_builder/rag/llm_backend.py +210 -0
  51. code_graph_builder/rag/markdown_generator.py +352 -0
  52. code_graph_builder/rag/prompt_templates.py +440 -0
  53. code_graph_builder/rag/rag_engine.py +640 -0
  54. code_graph_builder/rag/review_report.md +172 -0
  55. code_graph_builder/rag/tests/__init__.py +3 -0
  56. code_graph_builder/rag/tests/test_camel_agent.py +313 -0
  57. code_graph_builder/rag/tests/test_client.py +221 -0
  58. code_graph_builder/rag/tests/test_config.py +177 -0
  59. code_graph_builder/rag/tests/test_markdown_generator.py +240 -0
  60. code_graph_builder/rag/tests/test_prompt_templates.py +160 -0
  61. code_graph_builder/services/__init__.py +39 -0
  62. code_graph_builder/services/graph_service.py +465 -0
  63. code_graph_builder/services/kuzu_service.py +665 -0
  64. code_graph_builder/services/memory_service.py +171 -0
  65. code_graph_builder/settings.py +75 -0
  66. code_graph_builder/tests/ACCEPTANCE_CRITERIA_PHASE2.md +401 -0
  67. code_graph_builder/tests/__init__.py +1 -0
  68. code_graph_builder/tests/run_acceptance_check.py +378 -0
  69. code_graph_builder/tests/test_api_find.py +231 -0
  70. code_graph_builder/tests/test_api_find_integration.py +226 -0
  71. code_graph_builder/tests/test_basic.py +78 -0
  72. code_graph_builder/tests/test_c_api_extraction.py +388 -0
  73. code_graph_builder/tests/test_call_resolution_scenarios.py +504 -0
  74. code_graph_builder/tests/test_embedder.py +411 -0
  75. code_graph_builder/tests/test_integration_semantic.py +434 -0
  76. code_graph_builder/tests/test_mcp_protocol.py +298 -0
  77. code_graph_builder/tests/test_mcp_user_flow.py +190 -0
  78. code_graph_builder/tests/test_rag.py +404 -0
  79. code_graph_builder/tests/test_settings.py +135 -0
  80. code_graph_builder/tests/test_step1_graph_build.py +264 -0
  81. code_graph_builder/tests/test_step2_api_docs.py +323 -0
  82. code_graph_builder/tests/test_step3_embedding.py +278 -0
  83. code_graph_builder/tests/test_vector_store.py +552 -0
  84. code_graph_builder/tools/__init__.py +40 -0
  85. code_graph_builder/tools/graph_query.py +495 -0
  86. code_graph_builder/tools/semantic_search.py +387 -0
  87. code_graph_builder/types.py +333 -0
  88. code_graph_builder/utils/__init__.py +0 -0
  89. code_graph_builder/utils/path_utils.py +30 -0
  90. code_graph_builder-0.2.0.dist-info/METADATA +321 -0
  91. code_graph_builder-0.2.0.dist-info/RECORD +93 -0
  92. code_graph_builder-0.2.0.dist-info/WHEEL +4 -0
  93. code_graph_builder-0.2.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,161 @@
1
+ """MCP server entry-point for Code Graph Builder.
2
+
3
+ Reads workspace path from environment and starts an MCP stdio server
4
+ that exposes code graph pipeline and query tools.
5
+
6
+ Environment variables:
7
+ CGB_WORKSPACE Workspace directory (default: ~/.code-graph-builder/)
8
+ Stores all indexed repos, graphs, embeddings, and wikis.
9
+
10
+ Optional (for LLM-backed tools — first match wins):
11
+ LLM_API_KEY Generic LLM API key (highest priority)
12
+ LLM_BASE_URL LLM API base URL
13
+ LLM_MODEL LLM model name
14
+ OPENAI_API_KEY OpenAI (or compatible) API key
15
+ OPENAI_BASE_URL OpenAI-compatible base URL
16
+ OPENAI_MODEL OpenAI model name
17
+ MOONSHOT_API_KEY Moonshot / Kimi API key (legacy)
18
+ MOONSHOT_MODEL Moonshot model name (default: kimi-k2.5)
19
+ DASHSCOPE_API_KEY DashScope API key (required for semantic_search embeddings)
20
+
21
+ Usage:
22
+ CGB_WORKSPACE=~/.code-graph-builder python3 -m code_graph_builder.mcp.server
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import asyncio
28
+ import json
29
+ import os
30
+ from pathlib import Path
31
+
32
+ from dotenv import load_dotenv
33
+
34
+ # Load .env from workspace first (written by setup wizard), then local .env
35
+ _ws = Path(os.environ.get("CGB_WORKSPACE", Path.home() / ".code-graph-builder"))
36
+ load_dotenv(_ws.expanduser() / ".env", override=False)
37
+ load_dotenv(override=False)
38
+
39
+ from ..settings import load_settings # noqa: E402
40
+
41
+ load_settings()
42
+
43
+ from loguru import logger
44
+ from mcp.server import Server
45
+ from mcp.server.stdio import stdio_server
46
+ from mcp.types import TextContent, Tool
47
+
48
+ from .tools import MCPToolsRegistry, ToolError
49
+
50
+ SERVER_NAME = "code-graph-builder"
51
+
52
+
53
+ async def main() -> None:
54
+ workspace = Path(
55
+ os.environ.get("CGB_WORKSPACE", Path.home() / ".code-graph-builder")
56
+ ).expanduser().resolve()
57
+
58
+ registry = MCPToolsRegistry(workspace=workspace)
59
+
60
+ server = Server(SERVER_NAME)
61
+
62
+ @server.list_tools()
63
+ async def list_tools() -> list[Tool]:
64
+ return [
65
+ Tool(
66
+ name=t.name,
67
+ description=t.description,
68
+ inputSchema=t.input_schema,
69
+ )
70
+ for t in registry.tools()
71
+ ]
72
+
73
+ @server.call_tool()
74
+ async def call_tool(name: str, arguments: dict) -> list[TextContent]:
75
+ handler = registry.get_handler(name)
76
+ if handler is None:
77
+ raise ValueError(f"Unknown tool: {name}")
78
+
79
+ kwargs = dict(arguments or {})
80
+
81
+ # Tools that run long and support progress callbacks
82
+ _PROGRESS_TOOLS = {
83
+ "initialize_repository",
84
+ "build_graph",
85
+ "generate_api_docs",
86
+ "rebuild_embeddings",
87
+ "generate_wiki",
88
+ }
89
+
90
+ if name in _PROGRESS_TOOLS:
91
+ session = server.request_context.session
92
+
93
+ # Extract progress token from request metadata (if client supports it)
94
+ progress_token = None
95
+ meta = getattr(server.request_context, "meta", None)
96
+ if meta is not None:
97
+ progress_token = getattr(meta, "progressToken", None)
98
+
99
+ async def _progress_cb(msg: str, pct: float = 0.0) -> None:
100
+ try:
101
+ await session.send_log_message(
102
+ level="info",
103
+ data=f"[{pct:.0f}%] {msg}" if pct > 0 else msg,
104
+ logger="code-graph-builder",
105
+ )
106
+ except Exception:
107
+ pass
108
+
109
+ # Send MCP progress notification (rendered as progress bar
110
+ # by clients that support it, e.g. Claude Code)
111
+ if progress_token is not None:
112
+ try:
113
+ await session.send_progress_notification(
114
+ progress_token=progress_token,
115
+ progress=pct,
116
+ total=100.0,
117
+ message=msg,
118
+ )
119
+ except Exception:
120
+ pass
121
+
122
+ kwargs["_progress_cb"] = _progress_cb
123
+
124
+ try:
125
+ result = await handler(**kwargs)
126
+ except ToolError:
127
+ # ToolError already carries structured JSON in str(exc).
128
+ # Re-raise so the MCP framework returns isError=True to the agent.
129
+ raise
130
+ except Exception as exc:
131
+ # Unexpected exception — wrap into ToolError for consistent handling.
132
+ logger.exception(f"Tool '{name}' raised an unhandled exception")
133
+ raise ToolError({"error": str(exc), "tool": name}) from exc
134
+
135
+ # Notify client that tool list may have changed after state-changing ops
136
+ _STATE_CHANGING_TOOLS = {"initialize_repository", "build_graph", "switch_repository"}
137
+ if name in _STATE_CHANGING_TOOLS and isinstance(result, dict) and result.get("status") == "success":
138
+ try:
139
+ await server.request_context.session.send_tools_list_changed()
140
+ except Exception:
141
+ pass
142
+
143
+ if isinstance(result, (dict, list)):
144
+ text = json.dumps(result, ensure_ascii=False, indent=2, default=str)
145
+ else:
146
+ text = str(result)
147
+ return [TextContent(type="text", text=text)]
148
+
149
+ try:
150
+ async with stdio_server() as (read_stream, write_stream):
151
+ await server.run(
152
+ read_stream,
153
+ write_stream,
154
+ server.create_initialization_options(),
155
+ )
156
+ finally:
157
+ registry.close()
158
+
159
+
160
+ if __name__ == "__main__":
161
+ asyncio.run(main())