nia-mcp-server 1.0.26__py3-none-any.whl → 1.0.27__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.
Potentially problematic release.
This version of nia-mcp-server might be problematic. Click here for more details.
- nia_mcp_server/__init__.py +1 -1
- nia_mcp_server/api_client.py +124 -36
- nia_mcp_server/server.py +1456 -344
- {nia_mcp_server-1.0.26.dist-info → nia_mcp_server-1.0.27.dist-info}/METADATA +1 -1
- {nia_mcp_server-1.0.26.dist-info → nia_mcp_server-1.0.27.dist-info}/RECORD +8 -8
- {nia_mcp_server-1.0.26.dist-info → nia_mcp_server-1.0.27.dist-info}/WHEEL +0 -0
- {nia_mcp_server-1.0.26.dist-info → nia_mcp_server-1.0.27.dist-info}/entry_points.txt +0 -0
- {nia_mcp_server-1.0.26.dist-info → nia_mcp_server-1.0.27.dist-info}/licenses/LICENSE +0 -0
nia_mcp_server/__init__.py
CHANGED
nia_mcp_server/api_client.py
CHANGED
|
@@ -28,7 +28,7 @@ class NIAApiClient:
|
|
|
28
28
|
self.client = httpx.AsyncClient(
|
|
29
29
|
headers={
|
|
30
30
|
"Authorization": f"Bearer {api_key}",
|
|
31
|
-
"User-Agent": "nia-mcp-server/1.0.
|
|
31
|
+
"User-Agent": "nia-mcp-server/1.0.27",
|
|
32
32
|
"Content-Type": "application/json"
|
|
33
33
|
},
|
|
34
34
|
timeout=720.0 # 12 minute timeout for deep research operations
|
|
@@ -432,15 +432,31 @@ class NIAApiClient:
|
|
|
432
432
|
except Exception as e:
|
|
433
433
|
logger.error(f"Failed to rename repository: {e}")
|
|
434
434
|
raise APIError(f"Failed to rename repository: {str(e)}")
|
|
435
|
-
|
|
436
|
-
async def
|
|
435
|
+
|
|
436
|
+
async def get_github_tree(
|
|
437
437
|
self,
|
|
438
438
|
owner_repo: str,
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
439
|
+
branch: Optional[str] = None,
|
|
440
|
+
include_paths: Optional[List[str]] = None,
|
|
441
|
+
exclude_paths: Optional[List[str]] = None,
|
|
442
|
+
file_extensions: Optional[List[str]] = None,
|
|
443
|
+
exclude_extensions: Optional[List[str]] = None,
|
|
444
|
+
show_full_paths: bool = False
|
|
442
445
|
) -> Dict[str, Any]:
|
|
443
|
-
"""Get
|
|
446
|
+
"""Get file tree directly from GitHub API (no FalkorDB dependency).
|
|
447
|
+
|
|
448
|
+
Args:
|
|
449
|
+
owner_repo: Repository in owner/repo format or repository ID
|
|
450
|
+
branch: Optional branch name (defaults to repository's default branch)
|
|
451
|
+
include_paths: Only include files in these paths (e.g., ["src/", "lib/"])
|
|
452
|
+
exclude_paths: Exclude files in these paths (e.g., ["node_modules/", "dist/"])
|
|
453
|
+
file_extensions: Only include these file extensions (e.g., [".py", ".js"])
|
|
454
|
+
exclude_extensions: Exclude these file extensions (e.g., [".md", ".lock"])
|
|
455
|
+
show_full_paths: Show full file paths instead of hierarchical tree
|
|
456
|
+
|
|
457
|
+
Returns:
|
|
458
|
+
GitHub tree structure with files, directories, and stats
|
|
459
|
+
"""
|
|
444
460
|
try:
|
|
445
461
|
# Check if this looks like owner/repo format (contains /)
|
|
446
462
|
if '/' in owner_repo:
|
|
@@ -462,38 +478,46 @@ class NIAApiClient:
|
|
|
462
478
|
if not repo_id:
|
|
463
479
|
raise APIError(f"No repository ID found for {owner_repo}", 404)
|
|
464
480
|
|
|
465
|
-
# Get
|
|
466
|
-
params = {
|
|
467
|
-
|
|
468
|
-
"
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
481
|
+
# Get tree using the ID
|
|
482
|
+
params = {}
|
|
483
|
+
if branch:
|
|
484
|
+
params["branch"] = branch
|
|
485
|
+
if include_paths:
|
|
486
|
+
params["include_paths"] = ",".join(include_paths)
|
|
487
|
+
if exclude_paths:
|
|
488
|
+
params["exclude_paths"] = ",".join(exclude_paths)
|
|
489
|
+
if file_extensions:
|
|
490
|
+
params["file_extensions"] = ",".join(file_extensions)
|
|
491
|
+
if exclude_extensions:
|
|
492
|
+
params["exclude_extensions"] = ",".join(exclude_extensions)
|
|
493
|
+
if show_full_paths:
|
|
494
|
+
params["show_full_paths"] = "true"
|
|
474
495
|
|
|
475
496
|
response = await self.client.get(
|
|
476
|
-
f"{self.base_url}/v2/repositories/{repo_id}/
|
|
477
|
-
params=params
|
|
478
|
-
headers=headers
|
|
497
|
+
f"{self.base_url}/v2/repositories/{repo_id}/github-tree",
|
|
498
|
+
params=params
|
|
479
499
|
)
|
|
480
500
|
response.raise_for_status()
|
|
481
501
|
return response.json()
|
|
482
502
|
else:
|
|
483
503
|
# Assume it's already a repository ID
|
|
484
|
-
params = {
|
|
485
|
-
|
|
486
|
-
"
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
504
|
+
params = {}
|
|
505
|
+
if branch:
|
|
506
|
+
params["branch"] = branch
|
|
507
|
+
if include_paths:
|
|
508
|
+
params["include_paths"] = ",".join(include_paths)
|
|
509
|
+
if exclude_paths:
|
|
510
|
+
params["exclude_paths"] = ",".join(exclude_paths)
|
|
511
|
+
if file_extensions:
|
|
512
|
+
params["file_extensions"] = ",".join(file_extensions)
|
|
513
|
+
if exclude_extensions:
|
|
514
|
+
params["exclude_extensions"] = ",".join(exclude_extensions)
|
|
515
|
+
if show_full_paths:
|
|
516
|
+
params["show_full_paths"] = "true"
|
|
492
517
|
|
|
493
518
|
response = await self.client.get(
|
|
494
|
-
f"{self.base_url}/v2/repositories/{owner_repo}/
|
|
495
|
-
params=params
|
|
496
|
-
headers=headers
|
|
519
|
+
f"{self.base_url}/v2/repositories/{owner_repo}/github-tree",
|
|
520
|
+
params=params
|
|
497
521
|
)
|
|
498
522
|
response.raise_for_status()
|
|
499
523
|
return response.json()
|
|
@@ -503,8 +527,8 @@ class NIAApiClient:
|
|
|
503
527
|
except APIError:
|
|
504
528
|
raise
|
|
505
529
|
except Exception as e:
|
|
506
|
-
logger.error(f"Failed to get
|
|
507
|
-
raise APIError(f"Failed to get
|
|
530
|
+
logger.error(f"Failed to get GitHub tree: {e}")
|
|
531
|
+
raise APIError(f"Failed to get GitHub tree: {str(e)}")
|
|
508
532
|
|
|
509
533
|
# Data Source methods
|
|
510
534
|
|
|
@@ -729,22 +753,86 @@ class NIAApiClient:
|
|
|
729
753
|
payload = {
|
|
730
754
|
"query": query,
|
|
731
755
|
}
|
|
732
|
-
|
|
756
|
+
|
|
733
757
|
if output_format:
|
|
734
758
|
payload["output_format"] = output_format
|
|
735
|
-
|
|
759
|
+
|
|
736
760
|
response = await self.client.post(
|
|
737
761
|
f"{self.base_url}/v2/deep-research",
|
|
738
762
|
json=payload
|
|
739
763
|
)
|
|
740
764
|
response.raise_for_status()
|
|
741
765
|
return response.json()
|
|
742
|
-
|
|
766
|
+
|
|
743
767
|
except httpx.HTTPStatusError as e:
|
|
744
768
|
raise self._handle_api_error(e)
|
|
745
769
|
except Exception as e:
|
|
746
770
|
raise APIError(f"Deep research failed: {str(e)}")
|
|
747
|
-
|
|
771
|
+
|
|
772
|
+
async def regex_search(
|
|
773
|
+
self,
|
|
774
|
+
repositories: List[str],
|
|
775
|
+
query: str,
|
|
776
|
+
pattern: Optional[str] = None,
|
|
777
|
+
file_extensions: Optional[List[str]] = None,
|
|
778
|
+
languages: Optional[List[str]] = None,
|
|
779
|
+
max_results: int = 50,
|
|
780
|
+
include_context: bool = True,
|
|
781
|
+
context_lines: int = 3
|
|
782
|
+
) -> Dict[str, Any]:
|
|
783
|
+
"""
|
|
784
|
+
Perform regex pattern search over indexed repository source code.
|
|
785
|
+
|
|
786
|
+
Args:
|
|
787
|
+
repositories: List of repositories to search (owner/repo format)
|
|
788
|
+
query: Natural language query or regex pattern
|
|
789
|
+
pattern: Optional explicit regex pattern (overrides query extraction)
|
|
790
|
+
file_extensions: File extensions to filter (e.g., [".js", ".tsx"])
|
|
791
|
+
languages: Programming languages to filter
|
|
792
|
+
max_results: Maximum number of results to return
|
|
793
|
+
include_context: Include surrounding context lines
|
|
794
|
+
context_lines: Number of context lines before/after match
|
|
795
|
+
|
|
796
|
+
Returns:
|
|
797
|
+
Search results with matched patterns and locations
|
|
798
|
+
"""
|
|
799
|
+
try:
|
|
800
|
+
# Build repository list
|
|
801
|
+
repo_list = []
|
|
802
|
+
for repo in repositories:
|
|
803
|
+
if isinstance(repo, dict):
|
|
804
|
+
repo_list.append(repo)
|
|
805
|
+
else:
|
|
806
|
+
repo_list.append({"repository": repo})
|
|
807
|
+
|
|
808
|
+
payload = {
|
|
809
|
+
"repositories": repo_list,
|
|
810
|
+
"query": query,
|
|
811
|
+
"max_results": max_results,
|
|
812
|
+
"include_context": include_context,
|
|
813
|
+
"context_lines": context_lines
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
# Add optional parameters
|
|
817
|
+
if pattern:
|
|
818
|
+
payload["pattern"] = pattern
|
|
819
|
+
if file_extensions:
|
|
820
|
+
payload["file_extensions"] = file_extensions
|
|
821
|
+
if languages:
|
|
822
|
+
payload["languages"] = languages
|
|
823
|
+
|
|
824
|
+
response = await self.client.post(
|
|
825
|
+
f"{self.base_url}/v2/regex-search",
|
|
826
|
+
json=payload
|
|
827
|
+
)
|
|
828
|
+
response.raise_for_status()
|
|
829
|
+
return response.json()
|
|
830
|
+
|
|
831
|
+
except httpx.HTTPStatusError as e:
|
|
832
|
+
raise self._handle_api_error(e)
|
|
833
|
+
except Exception as e:
|
|
834
|
+
raise APIError(f"Regex search failed: {str(e)}")
|
|
835
|
+
|
|
748
836
|
async def get_source_content(
|
|
749
837
|
self,
|
|
750
838
|
source_type: str,
|