Skip to content

Commit 2ecbabb

Browse files
committed
add a debug setting and rename to tools for clarity
1 parent 9967f6a commit 2ecbabb

File tree

4 files changed

+68
-40
lines changed

4 files changed

+68
-40
lines changed

README.md

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ An [MCP Server](https://modelcontextprotocol.io/introduction) to utilize Codelog
66

77
### Tools
88

9-
The server implements one tool:
9+
The server implements two tools:
1010

11-
- get-impact: Pulls an impact assessment from the codelogic server's API's for your code
12-
- Takes the given "method" that you're working on and it's associated "class"
11+
- **codelogic-method-impact**: Pulls an impact assessment from the CodeLogic server's APIs for your code.
12+
- Takes the given "method" that you're working on and its associated "class".
13+
- **codelogic-database-impact**: Analyzes impacts between code and database entities.
14+
- Takes the database entity type (column, table, or view) and its name.
1315

1416
### Install
1517

@@ -67,16 +69,38 @@ Add the following configuration to your file:
6769
}
6870
```
6971

70-
Add a **global rule** to help windsurf call the tool, create or modify the `~/.codeium/windsurf/memories/global_rules.md` markdown file.
72+
Add a **global rule** to help Windsurf call the tools, create or modify the `~/.codeium/windsurf/memories/global_rules.md` markdown file.
7173

7274
Add the following or something similar:
7375

7476
```markdown
75-
When I ask you to modify existing code, try running the get-impact mcp tool against the code I've provided and any methods or functions that you are changing. Make sure the results sent back from the tool are highlighted as impacts for the given method or function.
77+
When I ask you to modify existing code, try running the codelogic-method-impact and codelogic-database-impact MCP tools against the code I've provided and any methods or functions that you are changing. Make sure the results sent back from the tool are highlighted as impacts for the given method or function.
7678
```
7779

7880
After adding the configuration, restart Windsurf IDE or refresh the tools to apply the changes.
7981

82+
### Environment Variables
83+
84+
The following environment variables can be configured to customize the behavior of the server:
85+
86+
- `CODELOGIC_SERVER_HOST`: The URL of the CodeLogic server.
87+
- `CODELOGIC_USERNAME`: Your CodeLogic username.
88+
- `CODELOGIC_PASSWORD`: Your CodeLogic password.
89+
- `CODELOGIC_MV_NAME`: The name of the materialized view to use.
90+
- `CODELOGIC_DEBUG_MODE`: Set to `true` to enable debug mode. When enabled, additional debug files such as `timing_log.txt` and `impact_data*.json` will be generated. Defaults to `false`.
91+
92+
#### Example Configuration
93+
94+
```json
95+
"env": {
96+
"CODELOGIC_SERVER_HOST": "<url to the server e.g. https://myco.app.codelogic.com>",
97+
"CODELOGIC_USERNAME": "<my username>",
98+
"CODELOGIC_PASSWORD": "<my password>",
99+
"CODELOGIC_MV_NAME": "<my materialized view>",
100+
"CODELOGIC_DEBUG_MODE": "true"
101+
}
102+
```
103+
80104
#### Pinning the version
81105

82106
instead of using the **latest** version of the server, you can pin to a specific version by changing the **args** field to match the version in [pypi](https://pypi.org/project/codelogic-mcp-server/) e.g.

src/codelogic_mcp_server/handlers.py

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import time
2222
from datetime import datetime
2323

24+
DEBUG_MODE = os.getenv("CODELOGIC_DEBUG_MODE", "false").lower() == "true"
25+
2426

2527
@server.list_tools()
2628
async def handle_list_tools() -> list[types.Tool]:
@@ -30,7 +32,7 @@ async def handle_list_tools() -> list[types.Tool]:
3032
"""
3133
return [
3234
types.Tool(
33-
name="get-impact",
35+
name="codelogic-method-impact",
3436
description="Analyze impacts of modifying a specific method within a given class or type.\n"
3537
"Recommended workflow:\n"
3638
"1. Use this tool before implementing code changes\n"
@@ -47,7 +49,7 @@ async def handle_list_tools() -> list[types.Tool]:
4749
},
4850
),
4951
types.Tool(
50-
name="database-impact",
52+
name="codelogic-database-impact",
5153
description="Analyze impacts between code and database entities.\n"
5254
"Recommended workflow:\n"
5355
"1. Use this tool before implementing code or database changes\n"
@@ -80,9 +82,9 @@ async def handle_call_tool(
8082
Tools can modify server state and notify clients of changes.
8183
"""
8284
try:
83-
if name == "get-impact":
85+
if name == "codelogic-method-impact":
8486
return await handle_method_impact(arguments)
85-
elif name == "database-impact":
87+
elif name == "codelogic-database-impact":
8688
return await handle_database_impact(arguments)
8789
else:
8890
sys.stderr.write(f"Unknown tool: {name}\n")
@@ -127,8 +129,9 @@ async def handle_method_impact(arguments: dict | None) -> list[types.TextContent
127129
end_time = time.time()
128130
duration = end_time - start_time
129131
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
130-
with open("timing_log.txt", "a") as log_file:
131-
log_file.write(f"{timestamp} - get_method_nodes for method '{method_name}' in class '{class_name}' took {duration:.4f} seconds\n")
132+
if DEBUG_MODE:
133+
with open("timing_log.txt", "a") as log_file:
134+
log_file.write(f"{timestamp} - get_method_nodes for method '{method_name}' in class '{class_name}' took {duration:.4f} seconds\n")
132135

133136
# Check if nodes is empty due to timeout or server error
134137
if not nodes:
@@ -167,10 +170,11 @@ async def handle_method_impact(arguments: dict | None) -> list[types.TextContent
167170
end_time = time.time()
168171
duration = end_time - start_time
169172
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
170-
with open("timing_log.txt", "a") as log_file:
171-
log_file.write(f"{timestamp} - get_impact for node '{node['name']}' took {duration:.4f} seconds\n")
172-
with open("impact_data.json", "w") as impact_file:
173-
json.dump(impact, impact_file, indent=4)
173+
if DEBUG_MODE:
174+
with open("timing_log.txt", "a") as log_file:
175+
log_file.write(f"{timestamp} - get_impact for node '{node['name']}' took {duration:.4f} seconds\n")
176+
with open("impact_data.json", "w") as impact_file:
177+
json.dump(impact, impact_file, indent=4)
174178
impact_data = json.loads(impact)
175179
nodes = extract_nodes(impact_data)
176180
relationships = extract_relationships(impact_data)
@@ -527,9 +531,9 @@ async def handle_database_impact(arguments: dict | None) -> list[types.TextConte
527531
end_time = time.time()
528532
duration = end_time - start_time
529533
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
530-
531-
with open("timing_log.txt", "a") as log_file:
532-
log_file.write(f"{timestamp} - search_database_entity for {entity_type} '{name}' took {duration:.4f} seconds\n")
534+
if DEBUG_MODE:
535+
with open("timing_log.txt", "a") as log_file:
536+
log_file.write(f"{timestamp} - search_database_entity for {entity_type} '{name}' took {duration:.4f} seconds\n")
533537

534538
if not search_results:
535539
table_view_text = f" in {table_or_view}" if table_or_view else ""
@@ -555,11 +559,11 @@ async def handle_database_impact(arguments: dict | None) -> list[types.TextConte
555559
duration = end_time - start_time
556560
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
557561

558-
with open("timing_log.txt", "a") as log_file:
559-
log_file.write(f"{timestamp} - get_impact for {entity_type} '{entity_name}' took {duration:.4f} seconds\n")
560-
561-
with open(f"impact_data_{entity_type}_{entity_name}.json", "w") as impact_file:
562-
json.dump(json.loads(impact), impact_file, indent=4)
562+
if DEBUG_MODE:
563+
with open("timing_log.txt", "a") as log_file:
564+
log_file.write(f"{timestamp} - get_impact for {entity_type} '{entity_name}' took {duration:.4f} seconds\n")
565+
with open(f"impact_data_{entity_type}_{entity_name}.json", "w") as impact_file:
566+
json.dump(json.loads(impact), impact_file, indent=4)
563567
impact_data = json.loads(impact)
564568
impact_summary = process_database_entity_impact(
565569
impact_data, entity_type, entity_name, entity_schema

src/codelogic_mcp_server/server.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ async def main():
5656
notification_options=NotificationOptions(),
5757
experimental_capabilities={},
5858
),
59-
instructions=("When asked to modify an existing code method, invoke one or both of get-impact and database-impact mcp tools against "
60-
"the code provided and any methods that you are proposing changes to. Make sure the results sent back from the tool are "
61-
"highlighted as impacts for the given method."
59+
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."
6262
),
6363
),
6464
)

src/codelogic_mcp_server/utils.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -613,53 +613,53 @@ def extract_dependent_applications(dependent_code, impact_data):
613613
def find_database_applications(node_id, impact_data):
614614
"""
615615
Find applications that directly or indirectly group the database entity.
616-
616+
617617
This function traverses both direct grouping relationships and indirect
618618
relationships through containment chains to identify all applications
619619
that might be affected by changes to the database entity.
620-
620+
621621
Args:
622622
node_id (str): ID of the database entity node
623623
impact_data (dict): Impact analysis data from the API
624-
624+
625625
Returns:
626626
list: Names of applications that group this database entity
627627
"""
628628
applications = []
629629
processed_nodes = set() # Track processed nodes to avoid infinite recursion
630-
630+
631631
# Find all application nodes and create lookup map
632632
app_nodes = {}
633633
for node in impact_data.get('data', {}).get('nodes', []):
634634
if node.get('primaryLabel') == 'Application':
635635
app_nodes[node.get('id')] = node.get('name')
636-
636+
637637
# Recursive function to traverse containment and grouping relationships
638638
def traverse_relationships(current_id):
639639
if current_id in processed_nodes:
640640
return # Avoid cycles
641-
641+
642642
processed_nodes.add(current_id)
643-
643+
644644
# Check direct grouping by applications
645645
for rel in impact_data.get('data', {}).get('relationships', []):
646646
# If an application directly groups this node
647647
if rel.get('type') == 'GROUPS' and rel.get('endId') == current_id and rel.get('startId') in app_nodes:
648648
app_name = app_nodes[rel.get('startId')]
649649
if app_name not in applications:
650650
applications.append(app_name)
651-
651+
652652
# Check relationships that refer to or contain this node
653653
# These can lead us to components that might be grouped by applications
654654
if rel.get('endId') == current_id:
655655
# Follow containment and reference relationships up the chain
656656
if rel.get('type').startswith('CONTAINS_') or rel.get('type') in ['REFERENCES', 'REFERENCES_TABLE']:
657657
# Recursively check the parent node
658658
traverse_relationships(rel.get('startId'))
659-
659+
660660
# Start traversal from the target node
661661
traverse_relationships(node_id)
662-
662+
663663
# Additional check for database-specific structures
664664
# Some applications might group the database or schema containing our entity
665665
current_node = find_node_by_id(impact_data.get('data', {}).get('nodes', []), node_id)
@@ -670,11 +670,11 @@ def traverse_relationships(current_id):
670670
# This might be a schema containing our table or a table containing our column
671671
container_id = rel.get('startId')
672672
container_node = find_node_by_id(impact_data.get('data', {}).get('nodes', []), container_id)
673-
673+
674674
if container_node and container_node.get('primaryLabel') in ['Table', 'Schema', 'Database']:
675675
# Recursively process this container to find applications
676676
traverse_relationships(container_id)
677-
677+
678678
return applications
679679

680680

@@ -706,15 +706,15 @@ def generate_combined_database_report(entity_type, search_name, table_or_view, s
706706
for i, impact in enumerate(all_impacts):
707707
entity_name = impact.get("name", "Unknown")
708708
entity_schema = impact.get("schema", "Unknown")
709-
709+
710710
# Format the entity identifier differently based on entity type
711711
if entity_type == "column":
712712
parent_table = impact.get("parent_table", {})
713713
table_name = parent_table.get("name", "Unknown") if parent_table else "Unknown"
714714
entity_id = f"`{entity_schema}.{table_name}.{entity_name}`"
715715
else:
716716
entity_id = f"`{entity_schema}.{entity_name}`"
717-
717+
718718
report += f"### {i + 1}. {entity_type.capitalize()}: {entity_id}\n\n"
719719

720720
# For columns, show the parent table information

0 commit comments

Comments
 (0)