nia-mcp-server 1.0.0__tar.gz → 1.0.1__tar.gz

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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nia-mcp-server
3
- Version: 1.0.0
3
+ Version: 1.0.1
4
4
  Summary: NIA Knowledge Agent - MCP server for intelligent codebase search
5
5
  Project-URL: Homepage, https://trynia.ai
6
6
  Project-URL: Documentation, https://docs.trynia.ai
@@ -20,6 +20,7 @@ Classifier: Programming Language :: Python :: 3.10
20
20
  Classifier: Programming Language :: Python :: 3.11
21
21
  Classifier: Programming Language :: Python :: 3.12
22
22
  Requires-Python: >=3.8
23
+ Requires-Dist: exa-py>=1.0.8
23
24
  Requires-Dist: httpx>=0.24.0
24
25
  Requires-Dist: mcp>=0.1.0
25
26
  Requires-Dist: pydantic>=2.0.0
@@ -90,6 +91,39 @@ Find the authentication logic in my repositories
90
91
  What are the best practices for error handling according to the docs?
91
92
  ```
92
93
 
94
+ ### Search and index new content
95
+ ```
96
+ Find the best RAG implementations out there
97
+ ```
98
+ Claude will:
99
+ 1. Use the `nia_web_search` tool to find trending RAG repos
100
+ 2. Show you the results with summaries
101
+ 3. Prompt you to index the ones you want
102
+ 4. You say "Index the first two" and it indexes them!
103
+
104
+ ```
105
+ What are the hottest new Rust web frameworks this week?
106
+ ```
107
+ Claude searches trending repos and guides you through indexing them.
108
+
109
+ Advanced search examples:
110
+ ```
111
+ Find GitHub repos similar to langchain/langchain
112
+
113
+ Search for AI papers published in the last 30 days
114
+
115
+ What are the trending machine learning frameworks this month?
116
+ ```
117
+
118
+ ### Deep research questions
119
+ ```
120
+ Compare the top 3 vector databases for RAG applications
121
+
122
+ What are the pros and cons of different LLM orchestration frameworks?
123
+
124
+ Research the latest developments in AI agent architectures
125
+ ```
126
+
93
127
  ### List your resources
94
128
  ```
95
129
  Show me all my indexed repositories and documentation
@@ -97,6 +131,21 @@ Show me all my indexed repositories and documentation
97
131
 
98
132
  ## Available Tools
99
133
 
134
+ ### Search & Research
135
+ - **`nia_web_search`** - AI-powered search of repositories, documentation, and content
136
+ - Finds trending GitHub repos, relevant documentation, and more
137
+ - Returns structured results that guide you to index the best content
138
+ - Advanced options:
139
+ - `category`: Filter by type (github, company, research paper, news, etc.)
140
+ - `days_back`: Find content from the last N days (great for trending)
141
+ - `find_similar_to`: Search for content similar to a given URL
142
+ - Built into NIA's advanced search capabilities
143
+
144
+ - **`nia_deep_research_agent`** - Multi-step AI research for complex questions
145
+ - Best for comparative analysis, comprehensive overviews
146
+ - Returns structured data with citations
147
+ - Examples: "Compare top RAG frameworks", "Analyze trends in AI safety"
148
+
100
149
  ### Repository Management
101
150
  - **`index_repository`** - Index a GitHub repository
102
151
  - **`list_repositories`** - List all indexed repositories
@@ -62,6 +62,39 @@ Find the authentication logic in my repositories
62
62
  What are the best practices for error handling according to the docs?
63
63
  ```
64
64
 
65
+ ### Search and index new content
66
+ ```
67
+ Find the best RAG implementations out there
68
+ ```
69
+ Claude will:
70
+ 1. Use the `nia_web_search` tool to find trending RAG repos
71
+ 2. Show you the results with summaries
72
+ 3. Prompt you to index the ones you want
73
+ 4. You say "Index the first two" and it indexes them!
74
+
75
+ ```
76
+ What are the hottest new Rust web frameworks this week?
77
+ ```
78
+ Claude searches trending repos and guides you through indexing them.
79
+
80
+ Advanced search examples:
81
+ ```
82
+ Find GitHub repos similar to langchain/langchain
83
+
84
+ Search for AI papers published in the last 30 days
85
+
86
+ What are the trending machine learning frameworks this month?
87
+ ```
88
+
89
+ ### Deep research questions
90
+ ```
91
+ Compare the top 3 vector databases for RAG applications
92
+
93
+ What are the pros and cons of different LLM orchestration frameworks?
94
+
95
+ Research the latest developments in AI agent architectures
96
+ ```
97
+
65
98
  ### List your resources
66
99
  ```
67
100
  Show me all my indexed repositories and documentation
@@ -69,6 +102,21 @@ Show me all my indexed repositories and documentation
69
102
 
70
103
  ## Available Tools
71
104
 
105
+ ### Search & Research
106
+ - **`nia_web_search`** - AI-powered search of repositories, documentation, and content
107
+ - Finds trending GitHub repos, relevant documentation, and more
108
+ - Returns structured results that guide you to index the best content
109
+ - Advanced options:
110
+ - `category`: Filter by type (github, company, research paper, news, etc.)
111
+ - `days_back`: Find content from the last N days (great for trending)
112
+ - `find_similar_to`: Search for content similar to a given URL
113
+ - Built into NIA's advanced search capabilities
114
+
115
+ - **`nia_deep_research_agent`** - Multi-step AI research for complex questions
116
+ - Best for comparative analysis, comprehensive overviews
117
+ - Returns structured data with citations
118
+ - Examples: "Compare top RAG frameworks", "Analyze trends in AI safety"
119
+
72
120
  ### Repository Management
73
121
  - **`index_repository`** - Index a GitHub repository
74
122
  - **`list_repositories`** - List all indexed repositories
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "nia-mcp-server"
7
- version = "1.0.0"
7
+ version = "1.0.1"
8
8
  description = "NIA Knowledge Agent - MCP server for intelligent codebase search"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -30,6 +30,7 @@ dependencies = [
30
30
  "httpx>=0.24.0",
31
31
  "pydantic>=2.0.0",
32
32
  "python-dotenv>=1.0.0",
33
+ "exa-py>=1.0.8",
33
34
  ]
34
35
 
35
36
  [project.urls]
@@ -2,4 +2,4 @@
2
2
  NIA MCP Server - Proxy server for NIA Knowledge Agent
3
3
  """
4
4
 
5
- __version__ = "1.0.0"
5
+ __version__ = "1.0.1"
@@ -7,10 +7,17 @@ import json
7
7
  import asyncio
8
8
  from typing import List, Optional, Dict, Any
9
9
  from datetime import datetime
10
+ from urllib.parse import urlparse
10
11
 
11
12
  from mcp.server.fastmcp import FastMCP
12
13
  from mcp.types import TextContent, Resource
13
14
  from .api_client import NIAApiClient, APIError
15
+ from dotenv import load_dotenv
16
+
17
+ # Load .env from parent directory (nia-app/.env)
18
+ from pathlib import Path
19
+ env_path = Path(__file__).parent.parent.parent.parent / ".env"
20
+ load_dotenv(env_path)
14
21
 
15
22
  # Configure logging
16
23
  logging.basicConfig(
@@ -19,6 +26,20 @@ logging.basicConfig(
19
26
  )
20
27
  logger = logging.getLogger(__name__)
21
28
 
29
+ # TOOL SELECTION GUIDE FOR AI ASSISTANTS:
30
+ #
31
+ # Use 'nia_web_search' for:
32
+ # - "Find RAG libraries" → Simple search
33
+ # - "What's trending in Rust?" → Quick discovery
34
+ # - "Show me repos like LangChain" → Similarity search
35
+ #
36
+ # Use 'nia_deep_research_agent' for:
37
+ # - "Compare RAG vs GraphRAG approaches" → Comparative analysis
38
+ # - "What are the best vector databases for production?" → Evaluation needed
39
+ # - "Analyze the pros and cons of different LLM frameworks" → Structured analysis
40
+ #
41
+ # The AI should assess query complexity and choose accordingly.
42
+
22
43
  # Create the MCP server
23
44
  mcp = FastMCP("nia-knowledge-agent")
24
45
 
@@ -164,7 +185,7 @@ async def search_codebase(
164
185
  messages=messages,
165
186
  repositories=repositories,
166
187
  data_sources=[], # No documentation sources
167
- search_mode="unified", # Use unified for full answers
188
+ search_mode="repositories", # Use repositories mode to exclude external sources
168
189
  stream=True,
169
190
  include_sources=include_sources
170
191
  ):
@@ -699,6 +720,453 @@ async def delete_repository(repository: str) -> List[TextContent]:
699
720
  text=f"❌ Error deleting repository: {str(e)}"
700
721
  )]
701
722
 
723
+ @mcp.tool()
724
+ async def nia_web_search(
725
+ query: str,
726
+ num_results: int = 5,
727
+ category: Optional[str] = None,
728
+ days_back: Optional[int] = None,
729
+ find_similar_to: Optional[str] = None
730
+ ) -> List[TextContent]:
731
+ """
732
+ Search repositories, documentation, and other content using AI-powered search.
733
+ Returns results formatted to guide next actions.
734
+
735
+ USE THIS TOOL WHEN:
736
+ - Finding specific repos/docs/content ("find X library", "trending Y frameworks")
737
+ - Looking for examples or implementations
738
+ - Searching for what's available on a topic
739
+ - Simple, direct searches that need quick results
740
+ - Finding similar content to a known URL
741
+
742
+ DON'T USE THIS FOR:
743
+ - Comparative analysis (use nia_deep_research_agent instead)
744
+ - Complex multi-faceted questions (use nia_deep_research_agent instead)
745
+ - Questions requiring synthesis of multiple sources (use nia_deep_research_agent instead)
746
+
747
+ Args:
748
+ query: Natural language search query (e.g., "best RAG implementations", "trending rust web frameworks")
749
+ num_results: Number of results to return (default: 5, max: 10)
750
+ category: Filter by category: "github", "company", "research paper", "news", "tweet", "pdf"
751
+ days_back: Only show results from the last N days (for trending content)
752
+ find_similar_to: URL to find similar content to
753
+
754
+ Returns:
755
+ Search results with actionable next steps
756
+ """
757
+ try:
758
+ # Check for API key
759
+ api_key = os.getenv("EXA_API_KEY")
760
+ if not api_key:
761
+ return [TextContent(
762
+ type="text",
763
+ text="❌ NIA Web Search unavailable.\n\n"
764
+ "This feature requires additional configuration. "
765
+ "Please contact support for access to advanced search features."
766
+ )]
767
+
768
+ # Import client
769
+ try:
770
+ from exa_py import Exa
771
+ except ImportError:
772
+ return [TextContent(
773
+ type="text",
774
+ text="❌ NIA Web Search unavailable. Please update the NIA MCP server."
775
+ )]
776
+
777
+ # Initialize client
778
+ client = Exa(api_key)
779
+
780
+ # Limit results to reasonable number
781
+ num_results = min(num_results, 10)
782
+
783
+ logger.info(f"Searching content for query: {query}")
784
+
785
+ # Build search parameters
786
+ search_params = {
787
+ "num_results": num_results * 2, # Get more to filter
788
+ "type": "auto", # Automatically choose best search type
789
+ "text": True,
790
+ "highlights": True
791
+ }
792
+
793
+ # Add category filter if specified
794
+ if category:
795
+ # Map user-friendly categories to Exa categories
796
+ category_map = {
797
+ "github": "github",
798
+ "company": "company",
799
+ "research": "research paper",
800
+ "news": "news",
801
+ "tweet": "tweet",
802
+ "pdf": "pdf",
803
+ "blog": "personal site"
804
+ }
805
+ if category.lower() in category_map:
806
+ search_params["category"] = category_map[category.lower()]
807
+
808
+ # Add date filter for trending content
809
+ if days_back:
810
+ from datetime import datetime, timedelta
811
+ start_date = (datetime.now() - timedelta(days=days_back)).strftime("%Y-%m-%d")
812
+ search_params["start_published_date"] = start_date
813
+
814
+ # Use similarity search if URL provided
815
+ if find_similar_to:
816
+ results = client.find_similar_and_contents(
817
+ find_similar_to,
818
+ **search_params
819
+ )
820
+ else:
821
+ results = client.search_and_contents(
822
+ query,
823
+ **search_params
824
+ )
825
+
826
+ # Separate results by type
827
+ github_repos = []
828
+ documentation = []
829
+ other_content = []
830
+
831
+ for result in results.results:
832
+ url = result.url
833
+
834
+ # Categorize based on URL patterns
835
+ if "github.com" in url and "/tree/" not in url and "/blob/" not in url:
836
+ # It's a GitHub repo (not a specific file)
837
+ # Extract owner/repo from URL
838
+ try:
839
+ parsed = urlparse(url)
840
+ # Ensure we have a valid GitHub URL
841
+ if parsed.hostname == "github.com":
842
+ # Remove leading/trailing slashes and split the path
843
+ path_parts = parsed.path.strip("/").split("/")
844
+ # Verify we have at least owner and repo in the path
845
+ if len(path_parts) >= 2 and path_parts[0] and path_parts[1]:
846
+ # Extract only the owner and repo, ignoring any additional path components
847
+ owner_repo = f"{path_parts[0]}/{path_parts[1]}"
848
+ github_repos.append({
849
+ "url": url,
850
+ "owner_repo": owner_repo,
851
+ "title": result.title or owner_repo,
852
+ "summary": result.text[:200] if result.text else "",
853
+ "highlights": result.highlights[:2] if result.highlights else [],
854
+ "published_date": getattr(result, 'published_date', None)
855
+ })
856
+ except:
857
+ pass
858
+ elif any(doc_pattern in url for doc_pattern in ["docs.", "documentation", ".readthedocs.", "/docs/"]):
859
+ # It's documentation
860
+ documentation.append({
861
+ "url": url,
862
+ "title": result.title or "Documentation",
863
+ "summary": result.text[:200] if result.text else "",
864
+ "highlights": result.highlights[:2] if result.highlights else []
865
+ })
866
+ else:
867
+ # Other content
868
+ other_content.append({
869
+ "url": url,
870
+ "title": result.title or "Content",
871
+ "summary": result.text[:200] if result.text else ""
872
+ })
873
+
874
+ # Format response to naturally guide next actions
875
+ response = f"## 🔍 NIA Web Search Results for: \"{query}\"\n\n"
876
+
877
+ if days_back:
878
+ response += f"*Showing results from the last {days_back} days*\n\n"
879
+
880
+ if find_similar_to:
881
+ response += f"*Finding content similar to: {find_similar_to}*\n\n"
882
+
883
+ # GitHub Repositories Section
884
+ if github_repos:
885
+ response += f"### 📦 GitHub Repositories ({len(github_repos)} found)\n\n"
886
+
887
+ for i, repo in enumerate(github_repos[:num_results], 1):
888
+ response += f"**{i}. {repo['title']}**\n"
889
+ response += f" 📍 `{repo['url']}`\n"
890
+ if repo.get('published_date'):
891
+ response += f" 📅 Updated: {repo['published_date']}\n"
892
+ if repo['summary']:
893
+ response += f" 📝 {repo['summary']}...\n"
894
+ if repo['highlights']:
895
+ response += f" ✨ Key features: {', '.join(repo['highlights'])}\n"
896
+ response += "\n"
897
+
898
+ # Be more aggressive based on query specificity
899
+ if len(github_repos) == 1 or any(specific_word in query.lower() for specific_word in ["specific", "exact", "particular", "find me", "looking for"]):
900
+ response += "**🚀 RECOMMENDED ACTION - Index this repository with NIA:**\n"
901
+ response += f"```\nIndex {github_repos[0]['owner_repo']}\n```\n"
902
+ response += "✨ This will enable AI-powered code search, understanding, and analysis!\n\n"
903
+ else:
904
+ response += "**🚀 Make these repositories searchable with NIA's AI:**\n"
905
+ response += f"- **Quick start:** Say \"Index {github_repos[0]['owner_repo']}\"\n"
906
+ response += "- **Index multiple:** Say \"Index all repositories\"\n"
907
+ response += "- **Benefits:** AI-powered code search, architecture understanding, implementation details\n\n"
908
+
909
+ # Documentation Section
910
+ if documentation:
911
+ response += f"### 📚 Documentation ({len(documentation)} found)\n\n"
912
+
913
+ for i, doc in enumerate(documentation[:num_results], 1):
914
+ response += f"**{i}. {doc['title']}**\n"
915
+ response += f" 📍 `{doc['url']}`\n"
916
+ if doc['summary']:
917
+ response += f" 📝 {doc['summary']}...\n"
918
+ if doc.get('highlights'):
919
+ response += f" ✨ Key topics: {', '.join(doc['highlights'])}\n"
920
+ response += "\n"
921
+
922
+ # Be more aggressive for documentation too
923
+ if len(documentation) == 1 or any(specific_word in query.lower() for specific_word in ["docs", "documentation", "guide", "tutorial", "reference"]):
924
+ response += "**📖 RECOMMENDED ACTION - Index this documentation with NIA:**\n"
925
+ response += f"```\nIndex documentation {documentation[0]['url']}\n```\n"
926
+ response += "✨ NIA will make this fully searchable with AI-powered Q&A!\n\n"
927
+ else:
928
+ response += "**📖 Make this documentation AI-searchable with NIA:**\n"
929
+ response += f"- **Quick start:** Say \"Index documentation {documentation[0]['url']}\"\n"
930
+ response += "- **Index all:** Say \"Index all documentation\"\n"
931
+ response += "- **Benefits:** Instant answers, smart search, code examples extraction\n\n"
932
+
933
+ # Other Content Section
934
+ if other_content and not github_repos and not documentation:
935
+ response += f"### 🌐 Other Content ({len(other_content)} found)\n\n"
936
+
937
+ for i, content in enumerate(other_content[:num_results], 1):
938
+ response += f"**{i}. {content['title']}**\n"
939
+ response += f" 📍 `{content['url']}`\n"
940
+ if content['summary']:
941
+ response += f" 📝 {content['summary']}...\n"
942
+ response += "\n"
943
+
944
+ # No results found
945
+ if not github_repos and not documentation and not other_content:
946
+ response = f"No results found for '{query}'. Try:\n"
947
+ response += "- Using different keywords\n"
948
+ response += "- Being more specific (e.g., 'Python RAG implementation')\n"
949
+ response += "- Including technology names (e.g., 'LangChain', 'TypeScript')\n"
950
+
951
+ # Add prominent call-to-action if we found indexable content
952
+ if github_repos or documentation:
953
+ response += "\n## 🎯 **Ready to unlock NIA's AI capabilities?**\n"
954
+ response += "The repositories and documentation above can be indexed for:\n"
955
+ response += "- 🤖 AI-powered code understanding and search\n"
956
+ response += "- 💡 Instant answers to technical questions\n"
957
+ response += "- 🔍 Deep architectural insights\n"
958
+ response += "- 📚 Smart documentation Q&A\n\n"
959
+ response += "**Just copy and paste the index commands above!**\n"
960
+
961
+ # Add search metadata
962
+ response += f"\n---\n"
963
+ response += f"*Searched {len(results.results)} sources using NIA Web Search*"
964
+
965
+ return [TextContent(type="text", text=response)]
966
+
967
+ except Exception as e:
968
+ logger.error(f"Error in NIA web search: {str(e)}")
969
+ return [TextContent(
970
+ type="text",
971
+ text=f"❌ NIA Web Search error: {str(e)}\n\n"
972
+ "This might be due to:\n"
973
+ "- Network connectivity issues\n"
974
+ "- Service temporarily unavailable"
975
+ )]
976
+
977
+ @mcp.tool()
978
+ async def nia_deep_research_agent(
979
+ query: str,
980
+ output_format: Optional[str] = None
981
+ ) -> List[TextContent]:
982
+ """
983
+ Perform deep, multi-step research on a topic using advanced AI research capabilities.
984
+ Best for complex questions that need comprehensive analysis.
985
+
986
+ USE THIS TOOL WHEN:
987
+ - Comparing multiple options ("compare X vs Y vs Z")
988
+ - Analyzing pros and cons
989
+ - Questions with "best", "top", "which is better"
990
+ - Needing structured analysis or synthesis
991
+ - Complex questions requiring multiple sources
992
+ - Questions about trends, patterns, or developments
993
+ - Requests for comprehensive overviews
994
+
995
+ DON'T USE THIS FOR:
996
+ - Simple lookups (use nia_web_search instead)
997
+ - Finding a specific known item (use nia_web_search instead)
998
+ - Quick searches for repos/docs (use nia_web_search instead)
999
+
1000
+ COMPLEXITY INDICATORS:
1001
+ - Words like: compare, analyze, evaluate, pros/cons, trade-offs
1002
+ - Multiple criteria mentioned
1003
+ - Asking for recommendations based on context
1004
+ - Needing structured output (tables, lists, comparisons)
1005
+
1006
+ Args:
1007
+ query: Research question (e.g., "Compare top 3 RAG frameworks with pros/cons")
1008
+ output_format: Optional structure hint (e.g., "comparison table", "pros and cons list")
1009
+
1010
+ Returns:
1011
+ Comprehensive research results with citations
1012
+ """
1013
+ try:
1014
+ # Check for API key
1015
+ api_key = os.getenv("EXA_API_KEY")
1016
+ if not api_key:
1017
+ return [TextContent(
1018
+ type="text",
1019
+ text="❌ Deep research unavailable. This advanced feature requires additional configuration."
1020
+ )]
1021
+
1022
+ # Import client
1023
+ try:
1024
+ from exa_py import Exa
1025
+ import json
1026
+ except ImportError:
1027
+ return [TextContent(
1028
+ type="text",
1029
+ text="❌ Research service unavailable. Please update the NIA MCP server."
1030
+ )]
1031
+
1032
+ # Initialize client
1033
+ client = Exa(api_key)
1034
+
1035
+ logger.info(f"Starting deep research for: {query}")
1036
+
1037
+ # Create a research task
1038
+ try:
1039
+ # Let the AI infer the schema based on the query
1040
+ task = client.research.create_task(
1041
+ instructions=query,
1042
+ infer_schema=True,
1043
+ model="exa-research-pro" # Use the pro model
1044
+ )
1045
+
1046
+ logger.info(f"Research task created: {task.id}")
1047
+
1048
+ # Poll for completion (max 2 minutes)
1049
+ result = client.research.poll_task(
1050
+ task.id,
1051
+ poll_interval=3,
1052
+ max_wait_time=120
1053
+ )
1054
+
1055
+ if result.status == "failed":
1056
+ return [TextContent(
1057
+ type="text",
1058
+ text=f"❌ Research failed. Please try rephrasing your question."
1059
+ )]
1060
+
1061
+ # Format the research results
1062
+ response = f"## 🔬 NIA Deep Research Agent Results\n\n"
1063
+ response += f"**Query:** {query}\n\n"
1064
+
1065
+ if result.data:
1066
+ response += "### 📊 Research Findings:\n\n"
1067
+
1068
+ # Pretty print the JSON data
1069
+ formatted_data = json.dumps(result.data, indent=2)
1070
+ response += f"```json\n{formatted_data}\n```\n\n"
1071
+
1072
+ # Add citations if available
1073
+ if result.citations:
1074
+ response += "### 📚 Sources & Citations:\n\n"
1075
+ citation_num = 1
1076
+ for field, citations in result.citations.items():
1077
+ if citations:
1078
+ response += f"**{field}:**\n"
1079
+ for citation in citations[:3]: # Limit to 3 citations per field
1080
+ response += f"{citation_num}. [{citation.get('title', 'Source')}]({citation.get('url', '#')})\n"
1081
+ if citation.get('snippet'):
1082
+ response += f" > {citation['snippet'][:150]}...\n"
1083
+ citation_num += 1
1084
+ response += "\n"
1085
+
1086
+ response += "### 💡 RECOMMENDED NEXT ACTIONS WITH NIA:\n\n"
1087
+
1088
+ # Extract potential repos and docs from the research data
1089
+ repos_found = []
1090
+ docs_found = []
1091
+
1092
+ # Helper function to extract URLs from nested data structures
1093
+ def extract_urls_from_data(data, urls_list=None):
1094
+ if urls_list is None:
1095
+ urls_list = []
1096
+
1097
+ if isinstance(data, dict):
1098
+ for value in data.values():
1099
+ extract_urls_from_data(value, urls_list)
1100
+ elif isinstance(data, list):
1101
+ for item in data:
1102
+ extract_urls_from_data(item, urls_list)
1103
+ elif isinstance(data, str):
1104
+ # Check if this string is a URL
1105
+ if data.startswith(('http://', 'https://')):
1106
+ urls_list.append(data)
1107
+
1108
+ return urls_list
1109
+
1110
+ # Extract all URLs from the data
1111
+ all_urls = extract_urls_from_data(result.data)
1112
+
1113
+ # Filter for GitHub repos and documentation
1114
+ import re
1115
+ github_pattern = r'github\.com/([a-zA-Z0-9-]+/[a-zA-Z0-9-_.]+)'
1116
+
1117
+ for url in all_urls:
1118
+ # Check for GitHub repos
1119
+ github_match = re.search(github_pattern, url)
1120
+ if github_match and '/tree/' not in url and '/blob/' not in url:
1121
+ repos_found.append(github_match.group(1))
1122
+ # Check for documentation URLs
1123
+ elif any(doc_indicator in url.lower() for doc_indicator in ['docs', 'documentation', '.readthedocs.', '/guide', '/tutorial']):
1124
+ docs_found.append(url)
1125
+
1126
+ # Remove duplicates and limit results
1127
+ repos_found = list(set(repos_found))[:3]
1128
+ docs_found = list(set(docs_found))[:3]
1129
+
1130
+ if repos_found:
1131
+ response += "**🚀 DISCOVERED REPOSITORIES - Index with NIA for deep analysis:**\n"
1132
+ for repo in repos_found:
1133
+ response += f"```\nIndex {repo}\n```\n"
1134
+ response += "✨ Enable AI-powered code search and architecture understanding!\n\n"
1135
+
1136
+ if docs_found:
1137
+ response += "**📖 DISCOVERED DOCUMENTATION - Index with NIA for smart search:**\n"
1138
+ for doc in docs_found[:2]: # Limit to 2 for readability
1139
+ response += f"```\nIndex documentation {doc}\n```\n"
1140
+ response += "✨ Make documentation instantly searchable with AI Q&A!\n\n"
1141
+
1142
+ if not repos_found and not docs_found:
1143
+ response += "**🔍 Manual indexing options:**\n"
1144
+ response += "- If you see any GitHub repos mentioned: Say \"Index [owner/repo]\"\n"
1145
+ response += "- If you see any documentation sites: Say \"Index documentation [url]\"\n"
1146
+ response += "- These will unlock NIA's powerful AI search capabilities!\n\n"
1147
+
1148
+ response += "**📊 Other actions:**\n"
1149
+ response += "- Ask follow-up questions about the research\n"
1150
+ response += "- Request a different analysis format\n"
1151
+ response += "- Search for more specific information\n"
1152
+ else:
1153
+ response += "No structured data returned. The research may need a more specific query."
1154
+
1155
+ return [TextContent(type="text", text=response)]
1156
+
1157
+ except Exception as e:
1158
+ # Fallback to regular search if research API fails
1159
+ logger.warning(f"Research API failed, falling back to search: {e}")
1160
+ return await nia_web_search(query, num_results=10)
1161
+
1162
+ except Exception as e:
1163
+ logger.error(f"Error in deep research: {str(e)}")
1164
+ return [TextContent(
1165
+ type="text",
1166
+ text=f"❌ Research error: {str(e)}\n\n"
1167
+ "Try simplifying your question or using the regular nia_web_search tool."
1168
+ )]
1169
+
702
1170
  # Resources
703
1171
 
704
1172
  # Note: FastMCP doesn't have list_resources or read_resource decorators
@@ -1,23 +0,0 @@
1
- #!/bin/bash
2
-
3
- # Development installation script
4
-
5
- echo "Installing NIA MCP Server for development..."
6
-
7
- # Create virtual environment
8
- python -m venv venv
9
-
10
- # Activate virtual environment
11
- source venv/bin/activate
12
-
13
- # Install in editable mode
14
- pip install -e .
15
-
16
- echo ""
17
- echo "✅ Installation complete!"
18
- echo ""
19
- echo "To use the server:"
20
- echo "1. Set your API key: export NIA_API_KEY=your-api-key-here"
21
- echo "2. Run: ./run_local.sh"
22
- echo ""
23
- echo "To test connection: python test_connection.py"
@@ -1,24 +0,0 @@
1
- #!/bin/bash
2
-
3
- # Local development runner for NIA MCP Server
4
-
5
- # Colors
6
- GREEN='\033[0;32m'
7
- YELLOW='\033[1;33m'
8
- RED='\033[0;31m'
9
- NC='\033[0m'
10
-
11
- echo -e "${GREEN}NIA MCP Server - Local Development${NC}"
12
- echo "===================================="
13
-
14
- # Check for API key
15
- if [ -z "$NIA_API_KEY" ]; then
16
- echo -e "${YELLOW}Warning: NIA_API_KEY not set${NC}"
17
- echo "Set it with: export NIA_API_KEY=your-api-key-here"
18
- echo "Get your API key at: https://trynia.ai/api-keys"
19
- exit 1
20
- fi
21
-
22
- # Run the server
23
- echo -e "${GREEN}Starting MCP server...${NC}"
24
- python -m nia_mcp_server
@@ -1,64 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Test script to verify NIA MCP Server connection
4
- """
5
- import os
6
- import asyncio
7
- from src.nia_mcp_server.api_client import NIAApiClient, APIError
8
-
9
- async def test_connection():
10
- """Test the connection to NIA API."""
11
- api_key = os.getenv("NIA_API_KEY")
12
- if not api_key:
13
- print("❌ NIA_API_KEY environment variable not set")
14
- print(" Set it with: export NIA_API_KEY=your-api-key-here")
15
- return
16
-
17
- api_url = os.getenv("NIA_API_URL", "https://api.trynia.ai")
18
- print(f"🔄 Testing connection to NIA API at {api_url}...")
19
-
20
- try:
21
- client = NIAApiClient(api_key, base_url=api_url)
22
-
23
- # Test API key validation
24
- if await client.validate_api_key():
25
- print("✅ API key is valid!")
26
-
27
- # Try to list repositories
28
- try:
29
- repos = await client.list_repositories()
30
- print(f"\n📚 You have {len(repos)} indexed repositories")
31
-
32
- for repo in repos[:3]: # Show first 3
33
- print(f" - {repo['repository']} ({repo.get('status', 'unknown')})")
34
-
35
- if len(repos) > 3:
36
- print(f" ... and {len(repos) - 3} more")
37
-
38
- except APIError as e:
39
- print(f"\n❌ {str(e)}")
40
- if e.status_code == 403 and "lifetime limit" in str(e).lower():
41
- print("\n💡 You've reached the free tier limit of 25 API requests.")
42
- print(" Upgrade to Pro at https://trynia.ai/billing for unlimited access.")
43
-
44
- else:
45
- print("❌ API key validation failed")
46
- print(" This could be due to:")
47
- print(" - Invalid API key")
48
- print(" - Exceeded usage limits (free tier: 25 lifetime requests)")
49
- print(" Check your API key at: https://trynia.ai/api-keys")
50
-
51
- await client.close()
52
-
53
- except APIError as e:
54
- print(f"❌ {str(e)}")
55
- if e.status_code == 403:
56
- print("\n💡 Access forbidden. This usually means:")
57
- print(" - You've exceeded your usage limits")
58
- print(" - Your subscription has expired")
59
- print(" Visit https://trynia.ai/billing to check your account")
60
- except Exception as e:
61
- print(f"❌ Error: {e}")
62
-
63
- if __name__ == "__main__":
64
- asyncio.run(test_connection())
@@ -1,92 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Test script for unified search functionality
4
- """
5
- import os
6
- import asyncio
7
- from src.nia_mcp_server.api_client import NIAApiClient
8
-
9
- async def test_unified_search():
10
- """Test the unified search functionality."""
11
- api_key = os.getenv("NIA_API_KEY")
12
- if not api_key:
13
- print("❌ NIA_API_KEY environment variable not set")
14
- print(" Set it with: export NIA_API_KEY=your-api-key-here")
15
- return
16
-
17
- api_url = os.getenv("NIA_API_URL", "https://api.trynia.ai")
18
- print(f"🔄 Testing unified search at {api_url}...")
19
-
20
- try:
21
- client = NIAApiClient(api_key, base_url=api_url)
22
-
23
- # First, list repositories and data sources
24
- print("\n📚 Listing repositories...")
25
- repos = await client.list_repositories()
26
- print(f"Found {len(repos)} repositories")
27
- for repo in repos[:3]:
28
- print(f" - {repo['repository']} ({repo.get('status', 'unknown')})")
29
-
30
- print("\n📄 Listing documentation sources...")
31
- sources = await client.list_data_sources()
32
- print(f"Found {len(sources)} documentation sources")
33
- for source in sources[:3]:
34
- print(f" - {source.get('url', 'Unknown')} ({source.get('status', 'unknown')})")
35
-
36
- # Test unified search
37
- if repos or sources:
38
- print("\n🔍 Testing unified search...")
39
- query = "authentication"
40
- messages = [{"role": "user", "content": query}]
41
-
42
- # Get first completed repo and source
43
- repo_list = [r["repository"] for r in repos if r.get("status") == "completed"][:1]
44
- source_list = [s["id"] for s in sources if s.get("status") == "completed"][:1]
45
-
46
- if repo_list or source_list:
47
- print(f"Searching for '{query}' across:")
48
- if repo_list:
49
- print(f" - Repository: {repo_list[0]}")
50
- if source_list:
51
- print(f" - Documentation: {sources[0].get('url', 'Unknown')}")
52
-
53
- # Perform unified search
54
- response_text = ""
55
- source_count = 0
56
-
57
- async for chunk in client.query_unified(
58
- messages=messages,
59
- repositories=repo_list,
60
- data_sources=source_list,
61
- search_mode="unified",
62
- stream=True,
63
- include_sources=True
64
- ):
65
- try:
66
- import json
67
- data = json.loads(chunk)
68
- if "content" in data:
69
- response_text += data["content"]
70
- if "sources" in data:
71
- source_count += len(data["sources"])
72
- except:
73
- continue
74
-
75
- print(f"\n✅ Unified search successful!")
76
- print(f"Response preview: {response_text[:200]}...")
77
- print(f"Found {source_count} source references")
78
- else:
79
- print("⚠️ No completed repositories or documentation sources to search")
80
- else:
81
- print("\n⚠️ No repositories or documentation sources found")
82
- print(" Index some content first using the MCP tools")
83
-
84
- await client.close()
85
-
86
- except Exception as e:
87
- print(f"❌ Error: {e}")
88
- import traceback
89
- traceback.print_exc()
90
-
91
- if __name__ == "__main__":
92
- asyncio.run(test_unified_search())
File without changes