Skip to content

Commit b00eb4f

Browse files
authored
Merge pull request #5 from CodeLogicIncEngineering/g-branch-1
2 parents 3f307fc + dd45419 commit b00eb4f

File tree

6 files changed

+178
-31
lines changed

6 files changed

+178
-31
lines changed

.github/copilot-instructions.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# CodeLogic MCP Server - Copilot Instructions
2+
3+
## About This Codebase
4+
5+
This repository contains a Model Context Protocol (MCP) server that integrates with CodeLogic's knowledge graph APIs. It enables AI programming assistants (like GitHub Copilot) to access dependency data from CodeLogic to analyze code and database impacts.
6+
7+
## Key Technologies
8+
9+
- **Python 3.13+** with extensive use of async/await
10+
- **Model Context Protocol SDK** (`mcp[cli]`)
11+
- **Neo4j** for graph database operations
12+
- **HTTPX** for API requests
13+
- **Environment variables** via dotenv for configuration
14+
15+
## Project Structure
16+
17+
- **src/codelogic_mcp_server/**: Core package
18+
- **`__init__.py`**: Package initialization and entry point
19+
- **`server.py`**: MCP server implementation
20+
- **`handlers.py`**: Tool handlers implementation
21+
- **`utils.py`**: API interaction utilities
22+
23+
## Core Coding Patterns
24+
25+
### MCP Server Pattern
26+
27+
```python
28+
server = Server("codelogic-mcp-server")
29+
30+
@server.list_tools()
31+
async def handle_list_tools() -> list[types.Tool]:
32+
# Define and return tools
33+
34+
@server.call_tool()
35+
async def handle_call_tool(name: str, arguments: dict | None) -> list[types.TextContent]:
36+
# Handle tool execution
37+
```
38+
39+
### Error Handling
40+
41+
```python
42+
try:
43+
# Operations that might fail
44+
except Exception as e:
45+
sys.stderr.write(f"Error: {str(e)}\n")
46+
return [types.TextContent(type="text", text=f"# Error\n\n{str(e)}")]
47+
```
48+
49+
## Style Guidelines
50+
51+
1. **Copyright Headers**: Include MPL 2.0 headers in all Python files
52+
2. **Docstrings**: Google-style docstrings for modules/classes/functions
53+
3. **Type Hints**: Always use Python type hints
54+
4. **Asynchronous**: Keep I/O operations asynchronous
55+
5. **Format Outputs**: Return markdown-formatted text in tool responses
56+
57+
## Tool Implementation Pattern
58+
59+
When implementing new MCP tools:
60+
61+
1. Add to `handle_list_tools()` with descriptive name (prefix: `codelogic-`)
62+
2. Add handler in `handle_call_tool()`
63+
3. Implement handler function with error handling
64+
4. Return results as markdown-formatted text
65+
66+
## Testing Approach
67+
68+
- **Unit Tests**: For functions without external dependencies
69+
- **Integration Tests**: For tests against a real CodeLogic server
70+
- Use the `CODELOGIC_TEST_MODE` environment variable
71+
72+
## Debugging
73+
74+
- Debug Mode: Set `CODELOGIC_DEBUG_MODE=true`
75+
- Remote Debugging: Use debugpy capabilities
76+
77+
## Key Environment Variables
78+
79+
- `CODELOGIC_SERVER_HOST`: CodeLogic server URL
80+
- `CODELOGIC_USERNAME`: Username for authentication
81+
- `CODELOGIC_PASSWORD`: Password for authentication
82+
- `CODELOGIC_MV_NAME`: Materialized view name
83+
- `CODELOGIC_DEBUG_MODE`: Enable debug logging
84+
- `CODELOGIC_TEST_MODE`: Used by test framework

pyproject.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@ description = "Integrates CodeLogic's powerful codebase knowledge graphs with a
55
readme = "README.md"
66
license = "MPL-2.0"
77
requires-python = ">=3.13"
8-
dependencies = [ "debugpy>=1.8.12", "httpx>=0.28.1", "mcp[cli]>=1.3.0", "neo4j>=5.28.1", "pip-licenses>=5.0.0", "python-dotenv>=1.0.1", "tenacity>=9.0.0",]
8+
dependencies = [
9+
"debugpy>=1.8.12",
10+
"httpx>=0.28.1",
11+
"mcp[cli]>=1.3.0",
12+
"pip-licenses>=5.0.0",
13+
"python-dotenv>=1.0.1",
14+
"tenacity>=9.0.0",
15+
]
916
[[project.authors]]
1017
name = "garrmark"
1118

src/codelogic_mcp_server/handlers.py

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ async def handle_list_tools() -> list[types.Tool]:
6464
"1. Use this tool before implementing code or database changes\n"
6565
"2. Search for the relevant database entity\n"
6666
"3. Review the impact analysis to understand which code depends on this database object and vice versa\n"
67-
"Particularly crucial when AI-suggested modifications are being considered.",
67+
"Particularly crucial when AI-suggested modifications are being considered or when modifying SQL code.",
6868
inputSchema={
6969
"type": "object",
7070
"properties": {
@@ -224,6 +224,22 @@ async def handle_method_impact(arguments: dict | None) -> list[types.TextContent
224224
complexity = target_node['properties'].get('statistics.cyclomaticComplexity', 'N/A') if target_node else 'N/A'
225225
instruction_count = target_node['properties'].get('statistics.instructionCount', 'N/A') if target_node else 'N/A'
226226

227+
# Extract code owners and reviewers
228+
code_owners = target_node['properties'].get('codelogic.owners', []) if target_node else []
229+
code_reviewers = target_node['properties'].get('codelogic.reviewers', []) if target_node else []
230+
231+
# If target node doesn't have owners/reviewers, try to find them from the class or file node
232+
if not code_owners or not code_reviewers:
233+
class_node = None
234+
if class_name:
235+
class_node = next((n for n in nodes if n['primaryLabel'].endswith('ClassEntity') and class_name.lower() in n['name'].lower()), None)
236+
237+
if class_node:
238+
if not code_owners:
239+
code_owners = class_node['properties'].get('codelogic.owners', [])
240+
if not code_reviewers:
241+
code_reviewers = class_node['properties'].get('codelogic.reviewers', [])
242+
227243
# Identify dependents (systems that depend on this method)
228244
dependents = []
229245

@@ -391,11 +407,18 @@ async def handle_method_impact(arguments: dict | None) -> list[types.TextContent
391407
## Summary
392408
- **Method**: `{method_name}`
393409
- **Class**: `{class_name or 'N/A'}`
394-
- **Complexity**: {complexity}
395-
- **Instruction Count**: {instruction_count}
396-
- **Affected Applications**: {len(affected_applications)}
397410
"""
398411

412+
# Add code ownership information if available
413+
if code_owners:
414+
impact_description += f"- **Code Owners**: {', '.join(code_owners)}\n"
415+
if code_reviewers:
416+
impact_description += f"- **Code Reviewers**: {', '.join(code_reviewers)}\n"
417+
418+
impact_description += f"- **Complexity**: {complexity}\n"
419+
impact_description += f"- **Instruction Count**: {instruction_count}\n"
420+
impact_description += f"- **Affected Applications**: {len(affected_applications)}\n"
421+
399422
# Add affected REST endpoints to the Summary section
400423
if endpoint_nodes:
401424
impact_description += "\n### Affected REST Endpoints\n"
@@ -461,6 +484,17 @@ async def handle_method_impact(arguments: dict | None) -> list[types.TextContent
461484
impact_description += "\n### REST API Risk Assessment\n"
462485
impact_description += "✅ No direct impact on REST endpoints or API controllers detected\n"
463486

487+
# Ownership-based consultation recommendation
488+
if code_owners or code_reviewers:
489+
impact_description += "\n### Code Ownership\n"
490+
if code_owners:
491+
impact_description += f"👤 **Code Owners**: Changes to this code should be reviewed by: {', '.join(code_owners)}\n"
492+
if code_reviewers:
493+
impact_description += f"👁️ **Preferred Reviewers**: Consider getting reviews from: {', '.join(code_reviewers)}\n"
494+
495+
if code_owners:
496+
impact_description += "\nConsult with the code owners before making significant changes to ensure alignment with original design intent.\n"
497+
464498
impact_description += f"""
465499
## Method Impact
466500
This analysis focuses on systems that depend on `{method_name}`. Modifying this method could affect these dependents:

src/codelogic_mcp_server/server.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,9 @@ async def main():
5757
experimental_capabilities={},
5858
),
5959
instructions=("When asked to modify an existing code method, invoke one or both of codelogic-method-impact and codelogic-database-impact "
60-
"mcp tools against the code provided and any methods that you are proposing changes to. Make sure the results sent back "
61-
"from the tool are highlighted as impacts for the given method."
60+
"mcp tools against the code provided and any methods that you are proposing changes to. When modifying SQL code or database entities, "
61+
"always use the codelogic-database-impact tool to analyze potential impacts. Make sure the results sent back "
62+
"from the tool are highlighted as impacts for the given method or database entity."
6263
),
6364
),
6465
)

src/codelogic_mcp_server/utils.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,33 @@ def process_database_entity_impact(impact_data, entity_type, entity_name, entity
493493
if app not in dependent_applications:
494494
dependent_applications.append(app)
495495

496+
# Extract code owners and reviewers from the dependent code entities
497+
# Since database entities don't typically have ownership metadata directly,
498+
# we'll gather this information from the code entities that reference them
499+
code_owners = set()
500+
code_reviewers = set()
501+
502+
# Check code entities that reference this database entity
503+
for code_item in dependent_code:
504+
code_id = code_item.get("id")
505+
code_node = next((n for n in nodes if n['id'] == code_id), None)
506+
if code_node:
507+
owners = code_node.get('properties', {}).get('codelogic.owners', [])
508+
reviewers = code_node.get('properties', {}).get('codelogic.reviewers', [])
509+
code_owners.update(owners)
510+
code_reviewers.update(reviewers)
511+
512+
# Look for parent classes that might contain ownership info
513+
for rel in impact_data.get('data', {}).get('relationships', []):
514+
if rel.get('type').startswith('CONTAINS_') and rel.get('endId') == code_id:
515+
parent_id = rel.get('startId')
516+
parent_node = find_node_by_id(impact_data.get('data', {}).get('nodes', []), parent_id)
517+
if parent_node and parent_node.get('primaryLabel', '').endswith('ClassEntity'):
518+
parent_owners = parent_node.get('properties', {}).get('codelogic.owners', [])
519+
parent_reviewers = parent_node.get('properties', {}).get('codelogic.reviewers', [])
520+
code_owners.update(parent_owners)
521+
code_reviewers.update(parent_reviewers)
522+
496523
return {
497524
"entity_type": entity_type,
498525
"name": entity_name,
@@ -501,6 +528,8 @@ def process_database_entity_impact(impact_data, entity_type, entity_name, entity
501528
"referencing_tables": referencing_tables,
502529
"dependent_applications": dependent_applications,
503530
"parent_table": parent_table,
531+
"code_owners": list(code_owners),
532+
"code_reviewers": list(code_reviewers),
504533
"nodes": nodes,
505534
"relationships": relationships
506535
}
@@ -721,6 +750,21 @@ def generate_combined_database_report(entity_type, search_name, table_or_view, s
721750

722751
report += f"### {i + 1}. {entity_type.capitalize()}: {entity_id}\n\n"
723752

753+
# Add code ownership information if available
754+
code_owners = impact.get("code_owners", [])
755+
code_reviewers = impact.get("code_reviewers", [])
756+
757+
if code_owners or code_reviewers:
758+
report += "#### Code Ownership\n"
759+
if code_owners:
760+
report += f"👤 **Code Owners**: {', '.join(code_owners)}\n"
761+
if code_reviewers:
762+
report += f"👁️ **Preferred Reviewers**: {', '.join(code_reviewers)}\n"
763+
if code_owners:
764+
report += "\nConsult with the code owners before making significant changes to ensure alignment with original design intent.\n\n"
765+
else:
766+
report += "\n"
767+
724768
# For columns, show the parent table information
725769
parent_table = impact.get("parent_table")
726770
if parent_table and entity_type == "column":

uv.lock

Lines changed: 1 addition & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)