codegraphcontext 0.5.2__py3-none-any.whl → 0.5.4__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 (119) hide show
  1. codegraphcontext/api/app.py +24 -5
  2. codegraphcontext/api/auth.py +123 -0
  3. codegraphcontext/api/mcp_sse.py +49 -2
  4. codegraphcontext/api/router.py +9 -5
  5. codegraphcontext/cli/cli_helpers.py +288 -75
  6. codegraphcontext/cli/config_manager.py +59 -14
  7. codegraphcontext/cli/hook_manager.py +26 -4
  8. codegraphcontext/cli/main.py +217 -20
  9. codegraphcontext/cli/project_config.py +218 -0
  10. codegraphcontext/cli/registry_commands.py +6 -6
  11. codegraphcontext/cli/setup_wizard.py +5 -0
  12. codegraphcontext/cli/simulator.py +343 -0
  13. codegraphcontext/core/__init__.py +70 -48
  14. codegraphcontext/core/bundle_registry.py +45 -6
  15. codegraphcontext/core/cgc_bundle.py +130 -34
  16. codegraphcontext/core/cgcignore.py +29 -11
  17. codegraphcontext/core/database.py +5 -1
  18. codegraphcontext/core/database_embedded_kuzu.py +49 -3
  19. codegraphcontext/core/database_falkordb.py +178 -40
  20. codegraphcontext/core/database_falkordb_remote.py +31 -11
  21. codegraphcontext/core/database_nornic.py +5 -1
  22. codegraphcontext/core/falkor_worker.py +16 -3
  23. codegraphcontext/core/simulator.py +589 -0
  24. codegraphcontext/core/watcher.py +77 -14
  25. codegraphcontext/prompts.py +52 -2
  26. codegraphcontext/server.py +77 -4
  27. codegraphcontext/tool_definitions.py +183 -22
  28. codegraphcontext/tools/advanced_language_query_tool.py +2 -0
  29. codegraphcontext/tools/code_finder.py +54 -38
  30. codegraphcontext/tools/graph_builder.py +50 -14
  31. codegraphcontext/tools/handlers/analysis_handlers.py +28 -9
  32. codegraphcontext/tools/handlers/management_handlers.py +33 -7
  33. codegraphcontext/tools/handlers/query_handlers.py +10 -4
  34. codegraphcontext/tools/indexing/discovery.py +6 -2
  35. codegraphcontext/tools/indexing/embeddings.py +8 -0
  36. codegraphcontext/tools/indexing/persistence/writer.py +132 -137
  37. codegraphcontext/tools/indexing/pipeline.py +103 -21
  38. codegraphcontext/tools/indexing/pre_scan.py +20 -2
  39. codegraphcontext/tools/indexing/resolution/calls.py +31 -7
  40. codegraphcontext/tools/indexing/resolution/inheritance.py +16 -2
  41. codegraphcontext/tools/indexing/resolution/post_resolution.py +20 -20
  42. codegraphcontext/tools/indexing/schema.py +89 -47
  43. codegraphcontext/tools/indexing/schema_contract.py +1 -0
  44. codegraphcontext/tools/languages/c.py +1 -1
  45. codegraphcontext/tools/languages/cpp.py +1 -1
  46. codegraphcontext/tools/languages/css.py +1 -1
  47. codegraphcontext/tools/languages/dart.py +1 -1
  48. codegraphcontext/tools/languages/elisp.py +1 -1
  49. codegraphcontext/tools/languages/elixir.py +3 -3
  50. codegraphcontext/tools/languages/go.py +3 -3
  51. codegraphcontext/tools/languages/html.py +1 -1
  52. codegraphcontext/tools/languages/javascript.py +32 -27
  53. codegraphcontext/tools/languages/lua.py +1 -1
  54. codegraphcontext/tools/languages/perl.py +1 -1
  55. codegraphcontext/tools/languages/python.py +78 -44
  56. codegraphcontext/tools/languages/ruby.py +2 -2
  57. codegraphcontext/tools/languages/rust.py +1 -1
  58. codegraphcontext/tools/languages/sfc_common.py +240 -0
  59. codegraphcontext/tools/languages/solidity.py +1069 -0
  60. codegraphcontext/tools/languages/solidity_remappings.py +157 -0
  61. codegraphcontext/tools/languages/svelte.py +38 -0
  62. codegraphcontext/tools/languages/typescript.py +31 -27
  63. codegraphcontext/tools/languages/typescriptjsx.py +3 -3
  64. codegraphcontext/tools/languages/vue.py +38 -0
  65. codegraphcontext/tools/query_tool_languages/cpp_toolkit.py +22 -18
  66. codegraphcontext/tools/query_tool_languages/java_toolkit.py +1 -1
  67. codegraphcontext/tools/query_tool_languages/solidity_toolkit.py +70 -0
  68. codegraphcontext/tools/report_generator.py +7 -7
  69. codegraphcontext/tools/system.py +13 -16
  70. codegraphcontext/tools/tree_sitter_parser.py +3 -0
  71. codegraphcontext/utils/cypher_ddl.py +80 -0
  72. codegraphcontext/utils/cypher_readonly.py +32 -6
  73. codegraphcontext/utils/tree_sitter_manager.py +7 -0
  74. codegraphcontext/viz/dist/assets/__vite-browser-external-9wXp6ZBx.js +1 -0
  75. codegraphcontext/viz/dist/assets/function-calls-BtRHrqa2.png +0 -0
  76. codegraphcontext/viz/dist/assets/graph-total-D1fBAugo.png +0 -0
  77. codegraphcontext/viz/dist/assets/hero-graph-2voMJp2a.jpg +0 -0
  78. codegraphcontext/viz/dist/assets/hierarchy-DGADo0YT.png +0 -0
  79. codegraphcontext/viz/dist/assets/index-C-187lf0.js +5587 -0
  80. codegraphcontext/viz/dist/assets/index-fNAa6jgv.css +1 -0
  81. codegraphcontext/viz/dist/assets/parser-pyodide.worker-BgsDfaad.js +370 -0
  82. codegraphcontext/viz/dist/assets/parser.worker-_nvrecvj.js +233 -0
  83. codegraphcontext/viz/dist/assets/tree-sitter-qKYAACSa.wasm +0 -0
  84. codegraphcontext/viz/dist/cgcIcon.png +0 -0
  85. codegraphcontext/viz/dist/favicon.ico +0 -0
  86. codegraphcontext/viz/dist/index.html +32 -0
  87. codegraphcontext/viz/dist/logo-icon.svg +85 -0
  88. codegraphcontext/viz/dist/logo.svg +100 -0
  89. codegraphcontext/viz/dist/placeholder.svg +1 -0
  90. codegraphcontext/viz/dist/preview-image.png +0 -0
  91. codegraphcontext/viz/dist/robots.txt +14 -0
  92. codegraphcontext/viz/dist/wasm/tree-sitter-c.wasm +0 -0
  93. codegraphcontext/viz/dist/wasm/tree-sitter-c_sharp.wasm +0 -0
  94. codegraphcontext/viz/dist/wasm/tree-sitter-core.js +1 -0
  95. codegraphcontext/viz/dist/wasm/tree-sitter-cpp.wasm +0 -0
  96. codegraphcontext/viz/dist/wasm/tree-sitter-dart.wasm +0 -0
  97. codegraphcontext/viz/dist/wasm/tree-sitter-go.wasm +0 -0
  98. codegraphcontext/viz/dist/wasm/tree-sitter-java.wasm +0 -0
  99. codegraphcontext/viz/dist/wasm/tree-sitter-javascript.wasm +0 -0
  100. codegraphcontext/viz/dist/wasm/tree-sitter-kotlin.wasm +0 -0
  101. codegraphcontext/viz/dist/wasm/tree-sitter-perl.wasm +1 -0
  102. codegraphcontext/viz/dist/wasm/tree-sitter-php.wasm +0 -0
  103. codegraphcontext/viz/dist/wasm/tree-sitter-python.wasm +0 -0
  104. codegraphcontext/viz/dist/wasm/tree-sitter-ruby.wasm +0 -0
  105. codegraphcontext/viz/dist/wasm/tree-sitter-rust.wasm +0 -0
  106. codegraphcontext/viz/dist/wasm/tree-sitter-swift.wasm +0 -0
  107. codegraphcontext/viz/dist/wasm/tree-sitter-tsx.wasm +0 -0
  108. codegraphcontext/viz/dist/wasm/tree-sitter-typescript.wasm +0 -0
  109. codegraphcontext/viz/dist/wasm/tree-sitter.wasm +0 -0
  110. codegraphcontext/viz/dist/wasm/web-tree-sitter.js +4007 -0
  111. codegraphcontext/viz/dist/wasm/web-tree-sitter.wasm +0 -0
  112. codegraphcontext/viz/server.py +54 -12
  113. {codegraphcontext-0.5.2.dist-info → codegraphcontext-0.5.4.dist-info}/METADATA +53 -7
  114. codegraphcontext-0.5.4.dist-info/RECORD +181 -0
  115. codegraphcontext-0.5.2.dist-info/RECORD +0 -132
  116. {codegraphcontext-0.5.2.dist-info → codegraphcontext-0.5.4.dist-info}/WHEEL +0 -0
  117. {codegraphcontext-0.5.2.dist-info → codegraphcontext-0.5.4.dist-info}/entry_points.txt +0 -0
  118. {codegraphcontext-0.5.2.dist-info → codegraphcontext-0.5.4.dist-info}/licenses/LICENSE +0 -0
  119. {codegraphcontext-0.5.2.dist-info → codegraphcontext-0.5.4.dist-info}/top_level.txt +0 -0
@@ -1,12 +1,17 @@
1
1
  # src/codegraphcontext/api/app.py
2
2
  import os
3
- from fastapi import FastAPI
3
+ from fastapi import Depends, FastAPI
4
4
  from fastapi.responses import HTMLResponse
5
5
  from fastapi.middleware.cors import CORSMiddleware
6
6
  from .router import router
7
+ from .auth import log_auth_status, require_api_key
7
8
  from .mcp_sse import handle_sse, handle_messages
8
9
 
9
10
  def create_app() -> FastAPI:
11
+ # Log whether API-key auth is active. Emits a prominent security warning
12
+ # when the gateway is running unauthenticated (CGC_API_KEY unset).
13
+ log_auth_status()
14
+
10
15
  app = FastAPI(
11
16
  title="CodeGraphContext Gateway",
12
17
  description="HTTP API gateway for CodeGraphContext MCP server. Enables integration with ChatGPT Actions, Claude, and web frontends.",
@@ -32,9 +37,23 @@ def create_app() -> FastAPI:
32
37
  """Liveness probe for load balancers and k8s."""
33
38
  return {"status": "ok"}
34
39
 
35
- # MCP-over-SSE Endpoints
36
- app.add_api_route("/api/v1/mcp/sse", handle_sse, methods=["GET"])
37
- app.add_api_route("/api/v1/mcp/messages", handle_messages, methods=["POST"])
40
+ # MCP-over-SSE Endpoints. These dispatch to the same tools as the REST
41
+ # router (execute_cypher_query, add_code_to_graph, delete_repository), so
42
+ # they need the same API-key dependency the router applies — without it,
43
+ # setting CGC_API_KEY left the SSE transport as an unauthenticated path to
44
+ # every tool.
45
+ app.add_api_route(
46
+ "/api/v1/mcp/sse",
47
+ handle_sse,
48
+ methods=["GET"],
49
+ dependencies=[Depends(require_api_key)],
50
+ )
51
+ app.add_api_route(
52
+ "/api/v1/mcp/messages",
53
+ handle_messages,
54
+ methods=["POST"],
55
+ dependencies=[Depends(require_api_key)],
56
+ )
38
57
 
39
58
  @app.get("/", response_class=HTMLResponse)
40
59
  async def root():
@@ -61,7 +80,7 @@ def create_app() -> FastAPI:
61
80
  <a href="/docs" class="btn">View API Docs</a>
62
81
  <div class="links">
63
82
  <a href="/openapi.json">OpenAPI Spec</a>
64
- <a href="https://github.com/Shashankss1205/CodeGraphContext" target="_blank">GitHub</a>
83
+ <a href="https://github.com/CodeGraphContext/CodeGraphContext" target="_blank">GitHub</a>
65
84
  </div>
66
85
  </div>
67
86
  </body>
@@ -0,0 +1,123 @@
1
+ # src/codegraphcontext/api/auth.py
2
+ """Optional API-key authentication for the CodeGraphContext HTTP API.
3
+
4
+ Backward-compatible and opt-in:
5
+
6
+ * When **no** key is configured, the API behaves exactly as it did before
7
+ (every endpoint is unauthenticated) but a prominent security warning is
8
+ logged at startup so operators know the gateway is wide open.
9
+ * When a key **is** configured (via the ``CGC_API_KEY`` environment variable
10
+ or the standard config mechanism), every protected router endpoint requires
11
+ it, supplied as either ``Authorization: Bearer <key>`` or ``X-API-Key: <key>``.
12
+ Missing/incorrect keys receive an HTTP ``401``.
13
+
14
+ The comparison uses :func:`secrets.compare_digest` to avoid timing side
15
+ channels.
16
+ """
17
+
18
+ import logging
19
+ import os
20
+ import secrets
21
+ from typing import Optional
22
+
23
+ from fastapi import Header, HTTPException
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+ # Config/env key that holds the API key. Reused as the config_manager key so
28
+ # ``cgc config set CGC_API_KEY <value>`` works as well.
29
+ API_KEY_ENV = "CGC_API_KEY"
30
+
31
+
32
+ def get_configured_api_key() -> Optional[str]:
33
+ """Return the configured API key, or ``None`` when auth is disabled.
34
+
35
+ Resolution order (highest priority first):
36
+
37
+ 1. The ``CGC_API_KEY`` environment variable (read directly so auth works
38
+ even when no config file is present).
39
+ 2. The value stored via the existing config mechanism
40
+ (:func:`codegraphcontext.cli.config_manager.get_config_value`).
41
+
42
+ Empty / whitespace-only values are treated as "not configured".
43
+ """
44
+ env_val = os.getenv(API_KEY_ENV)
45
+ if env_val and env_val.strip():
46
+ return env_val.strip()
47
+
48
+ # Fall back to the existing config mechanism (global/local .env, defaults).
49
+ try:
50
+ from codegraphcontext.cli.config_manager import get_config_value
51
+
52
+ cfg_val = get_config_value(API_KEY_ENV)
53
+ if cfg_val and cfg_val.strip():
54
+ return cfg_val.strip()
55
+ except Exception: # pragma: no cover - config lookup is best-effort here
56
+ pass
57
+
58
+ return None
59
+
60
+
61
+ def _extract_provided_key(
62
+ authorization: Optional[str], x_api_key: Optional[str]
63
+ ) -> Optional[str]:
64
+ """Pull the caller-supplied key from the Authorization or X-API-Key header."""
65
+ if authorization:
66
+ value = authorization.strip()
67
+ if value.lower().startswith("bearer "):
68
+ return value[len("bearer ") :].strip()
69
+ # Accept a bare token in the Authorization header as a convenience.
70
+ return value or None
71
+ if x_api_key and x_api_key.strip():
72
+ return x_api_key.strip()
73
+ return None
74
+
75
+
76
+ def _keys_match(provided: str, configured: str) -> bool:
77
+ """Constant-time comparison that never raises on non-ASCII input."""
78
+ try:
79
+ return secrets.compare_digest(
80
+ provided.encode("utf-8"), configured.encode("utf-8")
81
+ )
82
+ except (TypeError, AttributeError):
83
+ return False
84
+
85
+
86
+ async def require_api_key(
87
+ authorization: Optional[str] = Header(default=None),
88
+ x_api_key: Optional[str] = Header(default=None, alias="X-API-Key"),
89
+ ) -> None:
90
+ """FastAPI dependency that enforces the API key when one is configured.
91
+
92
+ No-op (backward compatible) when no key is set; otherwise requires a valid
93
+ key via ``Authorization: Bearer <key>`` or ``X-API-Key: <key>``, returning
94
+ HTTP ``401`` on a missing or incorrect key.
95
+ """
96
+ configured = get_configured_api_key()
97
+ if not configured:
98
+ # Auth disabled -> preserve the pre-existing unauthenticated behavior.
99
+ return
100
+
101
+ provided = _extract_provided_key(authorization, x_api_key)
102
+ if not provided or not _keys_match(provided, configured):
103
+ raise HTTPException(
104
+ status_code=401,
105
+ detail="Invalid or missing API key",
106
+ headers={"WWW-Authenticate": "Bearer"},
107
+ )
108
+
109
+
110
+ def log_auth_status() -> None:
111
+ """Log the authentication status at app startup.
112
+
113
+ Emits a prominent WARNING when the API is unauthenticated so the insecure
114
+ default is impossible to miss in logs.
115
+ """
116
+ if get_configured_api_key():
117
+ logger.info("API key authentication is ENABLED (CGC_API_KEY is set).")
118
+ else:
119
+ logger.warning(
120
+ "⚠️ CodeGraphContext API is running WITHOUT authentication. "
121
+ "Anyone who can reach this server can index code, run Cypher queries "
122
+ "and call tools. Set CGC_API_KEY to require a key."
123
+ )
@@ -48,11 +48,41 @@ async def handle_call_tool(name: str, arguments: dict | None) -> list[TextConten
48
48
  # Create the SSE transport.
49
49
  sse = SseServerTransport("/api/v1/mcp/messages")
50
50
 
51
+
52
+ class _AlreadySentResponse(Response):
53
+ """Placeholder for a response the MCP transport wrote itself.
54
+
55
+ Both handlers below hand the raw ASGI ``send`` to the SDK, which emits the
56
+ complete response. FastAPI still does ``await endpoint(request)`` followed
57
+ by ``await response(...)``, so returning ``None`` raises ``TypeError:
58
+ 'NoneType' object is not callable`` and returning a real ``Response`` starts
59
+ a second response on a finished ASGI cycle. This satisfies FastAPI while
60
+ putting nothing on the wire.
61
+ """
62
+
63
+ async def __call__(self, scope, receive, send) -> None:
64
+ return
65
+
66
+
67
+ class _SendTracker:
68
+ """ASGI ``send`` wrapper that records whether a response was started."""
69
+
70
+ def __init__(self, send):
71
+ self._send = send
72
+ self.response_started = False
73
+
74
+ async def __call__(self, message) -> None:
75
+ if message.get("type") == "http.response.start":
76
+ self.response_started = True
77
+ await self._send(message)
78
+
79
+
51
80
  async def handle_sse(request: Request):
52
81
  """Entry point for the SSE connection."""
53
82
  logger.info("SSE client connected")
83
+ sender = _SendTracker(request._send)
54
84
  try:
55
- async with sse.connect_sse(request.scope, request.receive, request._send) as (read_stream, write_stream):
85
+ async with sse.connect_sse(request.scope, request.receive, sender) as (read_stream, write_stream):
56
86
  await mcp_server.run(
57
87
  read_stream,
58
88
  write_stream,
@@ -70,6 +100,10 @@ async def handle_sse(request: Request):
70
100
  logger.debug("SSE connection closed: %s", type(exc).__name__)
71
101
  finally:
72
102
  logger.info("SSE client disconnected — handler exited, resources freed")
103
+ if sender.response_started:
104
+ return _AlreadySentResponse()
105
+ # connect_sse bailed out before writing anything (e.g. rejected origin).
106
+ return Response(status_code=500, content="SSE connection could not be established")
73
107
 
74
108
 
75
109
  async def handle_messages(request: Request):
@@ -99,7 +133,20 @@ async def handle_messages(request: Request):
99
133
  media_type="application/json"
100
134
  )
101
135
 
136
+ # `handle_post_message` builds its own Request and awaits `.body()` again.
137
+ # Starlette caches a body on the Request instance, not in the scope, so
138
+ # handing it the raw `receive` would make it wait for a body that has
139
+ # already been drained above — the POST hangs until the client gives up and
140
+ # the message never reaches the session. Replay the buffered bytes instead.
141
+ async def replay_receive() -> dict:
142
+ return {"type": "http.request", "body": raw_body, "more_body": False}
143
+
144
+ sender = _SendTracker(request._send)
102
145
  try:
103
- await sse.handle_post_message(request.scope, request.receive, request._send)
146
+ await sse.handle_post_message(request.scope, replay_receive, sender)
104
147
  except Exception as exc:
105
148
  logger.debug("Message handler closed: %s", type(exc).__name__)
149
+
150
+ if sender.response_started:
151
+ return _AlreadySentResponse()
152
+ return Response(status_code=202, content="Accepted")
@@ -5,17 +5,21 @@ from typing import Dict, Any, List
5
5
  from pathlib import Path
6
6
 
7
7
  from .schemas import (
8
- IndexRequest,
9
- QueryRequest,
10
- SearchRequest,
11
- ToolCallRequest,
8
+ IndexRequest,
9
+ QueryRequest,
10
+ SearchRequest,
11
+ ToolCallRequest,
12
12
  ApiResponse
13
13
  )
14
+ from .auth import require_api_key
14
15
  from codegraphcontext.server import MCPServer
15
16
 
16
17
  import socket
17
18
 
18
- router = APIRouter()
19
+ # Optional API-key auth is enforced on every route below via require_api_key.
20
+ # It is a no-op (backward compatible) unless CGC_API_KEY is configured, in
21
+ # which case each request must supply the key. See codegraphcontext.api.auth.
22
+ router = APIRouter(dependencies=[Depends(require_api_key)])
19
23
 
20
24
  # Global server instance (initialized on startup)
21
25
  _server_instance: MCPServer = None