nia-mcp-server 1.0.0__tar.gz → 1.0.3__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.
- {nia_mcp_server-1.0.0 → nia_mcp_server-1.0.3}/PKG-INFO +49 -1
- {nia_mcp_server-1.0.0 → nia_mcp_server-1.0.3}/README.md +48 -0
- {nia_mcp_server-1.0.0 → nia_mcp_server-1.0.3}/pyproject.toml +1 -1
- {nia_mcp_server-1.0.0 → nia_mcp_server-1.0.3}/src/nia_mcp_server/__init__.py +1 -1
- {nia_mcp_server-1.0.0 → nia_mcp_server-1.0.3}/src/nia_mcp_server/api_client.py +63 -2
- {nia_mcp_server-1.0.0 → nia_mcp_server-1.0.3}/src/nia_mcp_server/server.py +370 -1
- nia_mcp_server-1.0.0/install_dev.sh +0 -23
- nia_mcp_server-1.0.0/run_local.sh +0 -24
- nia_mcp_server-1.0.0/test_connection.py +0 -64
- nia_mcp_server-1.0.0/test_unified_search.py +0 -92
- {nia_mcp_server-1.0.0 → nia_mcp_server-1.0.3}/.gitignore +0 -0
- {nia_mcp_server-1.0.0 → nia_mcp_server-1.0.3}/ARCHITECTURE.md +0 -0
- {nia_mcp_server-1.0.0 → nia_mcp_server-1.0.3}/LICENSE +0 -0
- {nia_mcp_server-1.0.0 → nia_mcp_server-1.0.3}/src/nia_mcp_server/__main__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nia-mcp-server
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.3
|
|
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
|
|
@@ -90,6 +90,39 @@ Find the authentication logic in my repositories
|
|
|
90
90
|
What are the best practices for error handling according to the docs?
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
+
### Search and index new content
|
|
94
|
+
```
|
|
95
|
+
Find the best RAG implementations out there
|
|
96
|
+
```
|
|
97
|
+
Claude will:
|
|
98
|
+
1. Use the `nia_web_search` tool to find trending RAG repos
|
|
99
|
+
2. Show you the results with summaries
|
|
100
|
+
3. Prompt you to index the ones you want
|
|
101
|
+
4. You say "Index the first two" and it indexes them!
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
What are the hottest new Rust web frameworks this week?
|
|
105
|
+
```
|
|
106
|
+
Claude searches trending repos and guides you through indexing them.
|
|
107
|
+
|
|
108
|
+
Advanced search examples:
|
|
109
|
+
```
|
|
110
|
+
Find GitHub repos similar to langchain/langchain
|
|
111
|
+
|
|
112
|
+
Search for AI papers published in the last 30 days
|
|
113
|
+
|
|
114
|
+
What are the trending machine learning frameworks this month?
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Deep research questions
|
|
118
|
+
```
|
|
119
|
+
Compare the top 3 vector databases for RAG applications
|
|
120
|
+
|
|
121
|
+
What are the pros and cons of different LLM orchestration frameworks?
|
|
122
|
+
|
|
123
|
+
Research the latest developments in AI agent architectures
|
|
124
|
+
```
|
|
125
|
+
|
|
93
126
|
### List your resources
|
|
94
127
|
```
|
|
95
128
|
Show me all my indexed repositories and documentation
|
|
@@ -97,6 +130,21 @@ Show me all my indexed repositories and documentation
|
|
|
97
130
|
|
|
98
131
|
## Available Tools
|
|
99
132
|
|
|
133
|
+
### Search & Research
|
|
134
|
+
- **`nia_web_search`** - AI-powered search of repositories, documentation, and content
|
|
135
|
+
- Finds trending GitHub repos, relevant documentation, and more
|
|
136
|
+
- Returns structured results that guide you to index the best content
|
|
137
|
+
- Advanced options:
|
|
138
|
+
- `category`: Filter by type (github, company, research paper, news, etc.)
|
|
139
|
+
- `days_back`: Find content from the last N days (great for trending)
|
|
140
|
+
- `find_similar_to`: Search for content similar to a given URL
|
|
141
|
+
- Built into NIA's advanced search capabilities
|
|
142
|
+
|
|
143
|
+
- **`nia_deep_research_agent`** - Multi-step AI research for complex questions
|
|
144
|
+
- Best for comparative analysis, comprehensive overviews
|
|
145
|
+
- Returns structured data with citations
|
|
146
|
+
- Examples: "Compare top RAG frameworks", "Analyze trends in AI safety"
|
|
147
|
+
|
|
100
148
|
### Repository Management
|
|
101
149
|
- **`index_repository`** - Index a GitHub repository
|
|
102
150
|
- **`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
|
|
@@ -31,7 +31,7 @@ class NIAApiClient:
|
|
|
31
31
|
"User-Agent": "nia-mcp-server/1.0.0",
|
|
32
32
|
"Content-Type": "application/json"
|
|
33
33
|
},
|
|
34
|
-
timeout=
|
|
34
|
+
timeout=720.0 # 12 minute timeout for deep research operations
|
|
35
35
|
)
|
|
36
36
|
|
|
37
37
|
async def close(self):
|
|
@@ -474,4 +474,65 @@ class NIAApiClient:
|
|
|
474
474
|
except httpx.HTTPStatusError as e:
|
|
475
475
|
raise self._handle_api_error(e)
|
|
476
476
|
except Exception as e:
|
|
477
|
-
raise APIError(f"Query failed: {str(e)}")
|
|
477
|
+
raise APIError(f"Query failed: {str(e)}")
|
|
478
|
+
|
|
479
|
+
async def web_search(
|
|
480
|
+
self,
|
|
481
|
+
query: str,
|
|
482
|
+
num_results: int = 5,
|
|
483
|
+
category: Optional[str] = None,
|
|
484
|
+
days_back: Optional[int] = None,
|
|
485
|
+
find_similar_to: Optional[str] = None
|
|
486
|
+
) -> Dict[str, Any]:
|
|
487
|
+
"""Perform AI-powered web search."""
|
|
488
|
+
try:
|
|
489
|
+
payload = {
|
|
490
|
+
"query": query,
|
|
491
|
+
"num_results": min(num_results, 10),
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
# Add optional parameters
|
|
495
|
+
if category:
|
|
496
|
+
payload["category"] = category
|
|
497
|
+
if days_back:
|
|
498
|
+
payload["days_back"] = days_back
|
|
499
|
+
if find_similar_to:
|
|
500
|
+
payload["find_similar_to"] = find_similar_to
|
|
501
|
+
|
|
502
|
+
response = await self.client.post(
|
|
503
|
+
f"{self.base_url}/v2/web-search",
|
|
504
|
+
json=payload
|
|
505
|
+
)
|
|
506
|
+
response.raise_for_status()
|
|
507
|
+
return response.json()
|
|
508
|
+
|
|
509
|
+
except httpx.HTTPStatusError as e:
|
|
510
|
+
raise self._handle_api_error(e)
|
|
511
|
+
except Exception as e:
|
|
512
|
+
raise APIError(f"Web search failed: {str(e)}")
|
|
513
|
+
|
|
514
|
+
async def deep_research(
|
|
515
|
+
self,
|
|
516
|
+
query: str,
|
|
517
|
+
output_format: Optional[str] = None
|
|
518
|
+
) -> Dict[str, Any]:
|
|
519
|
+
"""Perform deep research using AI agent."""
|
|
520
|
+
try:
|
|
521
|
+
payload = {
|
|
522
|
+
"query": query,
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if output_format:
|
|
526
|
+
payload["output_format"] = output_format
|
|
527
|
+
|
|
528
|
+
response = await self.client.post(
|
|
529
|
+
f"{self.base_url}/v2/deep-research",
|
|
530
|
+
json=payload
|
|
531
|
+
)
|
|
532
|
+
response.raise_for_status()
|
|
533
|
+
return response.json()
|
|
534
|
+
|
|
535
|
+
except httpx.HTTPStatusError as e:
|
|
536
|
+
raise self._handle_api_error(e)
|
|
537
|
+
except Exception as e:
|
|
538
|
+
raise APIError(f"Deep research failed: {str(e)}")
|
|
@@ -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="
|
|
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,354 @@ 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
|
+
client = await ensure_api_client()
|
|
759
|
+
|
|
760
|
+
logger.info(f"Searching content for query: {query}")
|
|
761
|
+
|
|
762
|
+
# Use the API client method instead of direct HTTP call
|
|
763
|
+
result = await client.web_search(
|
|
764
|
+
query=query,
|
|
765
|
+
num_results=num_results,
|
|
766
|
+
category=category,
|
|
767
|
+
days_back=days_back,
|
|
768
|
+
find_similar_to=find_similar_to
|
|
769
|
+
)
|
|
770
|
+
|
|
771
|
+
# Extract results
|
|
772
|
+
github_repos = result.get("github_repos", [])
|
|
773
|
+
documentation = result.get("documentation", [])
|
|
774
|
+
other_content = result.get("other_content", [])
|
|
775
|
+
|
|
776
|
+
# Format response to naturally guide next actions
|
|
777
|
+
response_text = f"## 🔍 NIA Web Search Results for: \"{query}\"\n\n"
|
|
778
|
+
|
|
779
|
+
if days_back:
|
|
780
|
+
response_text += f"*Showing results from the last {days_back} days*\n\n"
|
|
781
|
+
|
|
782
|
+
if find_similar_to:
|
|
783
|
+
response_text += f"*Finding content similar to: {find_similar_to}*\n\n"
|
|
784
|
+
|
|
785
|
+
# GitHub Repositories Section
|
|
786
|
+
if github_repos:
|
|
787
|
+
response_text += f"### 📦 GitHub Repositories ({len(github_repos)} found)\n\n"
|
|
788
|
+
|
|
789
|
+
for i, repo in enumerate(github_repos[:num_results], 1):
|
|
790
|
+
response_text += f"**{i}. {repo['title']}**\n"
|
|
791
|
+
response_text += f" 📍 `{repo['url']}`\n"
|
|
792
|
+
if repo.get('published_date'):
|
|
793
|
+
response_text += f" 📅 Updated: {repo['published_date']}\n"
|
|
794
|
+
if repo['summary']:
|
|
795
|
+
response_text += f" 📝 {repo['summary']}...\n"
|
|
796
|
+
if repo['highlights']:
|
|
797
|
+
response_text += f" ✨ Key features: {', '.join(repo['highlights'])}\n"
|
|
798
|
+
response_text += "\n"
|
|
799
|
+
|
|
800
|
+
# Be more aggressive based on query specificity
|
|
801
|
+
if len(github_repos) == 1 or any(specific_word in query.lower() for specific_word in ["specific", "exact", "particular", "find me", "looking for"]):
|
|
802
|
+
response_text += "**🚀 RECOMMENDED ACTION - Index this repository with NIA:**\n"
|
|
803
|
+
response_text += f"```\nIndex {github_repos[0]['owner_repo']}\n```\n"
|
|
804
|
+
response_text += "✨ This will enable AI-powered code search, understanding, and analysis!\n\n"
|
|
805
|
+
else:
|
|
806
|
+
response_text += "**🚀 Make these repositories searchable with NIA's AI:**\n"
|
|
807
|
+
response_text += f"- **Quick start:** Say \"Index {github_repos[0]['owner_repo']}\"\n"
|
|
808
|
+
response_text += "- **Index multiple:** Say \"Index all repositories\"\n"
|
|
809
|
+
response_text += "- **Benefits:** AI-powered code search, architecture understanding, implementation details\n\n"
|
|
810
|
+
|
|
811
|
+
# Documentation Section
|
|
812
|
+
if documentation:
|
|
813
|
+
response_text += f"### 📚 Documentation ({len(documentation)} found)\n\n"
|
|
814
|
+
|
|
815
|
+
for i, doc in enumerate(documentation[:num_results], 1):
|
|
816
|
+
response_text += f"**{i}. {doc['title']}**\n"
|
|
817
|
+
response_text += f" 📍 `{doc['url']}`\n"
|
|
818
|
+
if doc['summary']:
|
|
819
|
+
response_text += f" 📝 {doc['summary']}...\n"
|
|
820
|
+
if doc.get('highlights'):
|
|
821
|
+
response_text += f" ✨ Key topics: {', '.join(doc['highlights'])}\n"
|
|
822
|
+
response_text += "\n"
|
|
823
|
+
|
|
824
|
+
# Be more aggressive for documentation too
|
|
825
|
+
if len(documentation) == 1 or any(specific_word in query.lower() for specific_word in ["docs", "documentation", "guide", "tutorial", "reference"]):
|
|
826
|
+
response_text += "**📖 RECOMMENDED ACTION - Index this documentation with NIA:**\n"
|
|
827
|
+
response_text += f"```\nIndex documentation {documentation[0]['url']}\n```\n"
|
|
828
|
+
response_text += "✨ NIA will make this fully searchable with AI-powered Q&A!\n\n"
|
|
829
|
+
else:
|
|
830
|
+
response_text += "**📖 Make this documentation AI-searchable with NIA:**\n"
|
|
831
|
+
response_text += f"- **Quick start:** Say \"Index documentation {documentation[0]['url']}\"\n"
|
|
832
|
+
response_text += "- **Index all:** Say \"Index all documentation\"\n"
|
|
833
|
+
response_text += "- **Benefits:** Instant answers, smart search, code examples extraction\n\n"
|
|
834
|
+
|
|
835
|
+
# Other Content Section
|
|
836
|
+
if other_content and not github_repos and not documentation:
|
|
837
|
+
response_text += f"### 🌐 Other Content ({len(other_content)} found)\n\n"
|
|
838
|
+
|
|
839
|
+
for i, content in enumerate(other_content[:num_results], 1):
|
|
840
|
+
response_text += f"**{i}. {content['title']}**\n"
|
|
841
|
+
response_text += f" 📍 `{content['url']}`\n"
|
|
842
|
+
if content['summary']:
|
|
843
|
+
response_text += f" 📝 {content['summary']}...\n"
|
|
844
|
+
response_text += "\n"
|
|
845
|
+
|
|
846
|
+
# No results found
|
|
847
|
+
if not github_repos and not documentation and not other_content:
|
|
848
|
+
response_text = f"No results found for '{query}'. Try:\n"
|
|
849
|
+
response_text += "- Using different keywords\n"
|
|
850
|
+
response_text += "- Being more specific (e.g., 'Python RAG implementation')\n"
|
|
851
|
+
response_text += "- Including technology names (e.g., 'LangChain', 'TypeScript')\n"
|
|
852
|
+
|
|
853
|
+
# Add prominent call-to-action if we found indexable content
|
|
854
|
+
if github_repos or documentation:
|
|
855
|
+
response_text += "\n## 🎯 **Ready to unlock NIA's AI capabilities?**\n"
|
|
856
|
+
response_text += "The repositories and documentation above can be indexed for:\n"
|
|
857
|
+
response_text += "- 🤖 AI-powered code understanding and search\n"
|
|
858
|
+
response_text += "- 💡 Instant answers to technical questions\n"
|
|
859
|
+
response_text += "- 🔍 Deep architectural insights\n"
|
|
860
|
+
response_text += "- 📚 Smart documentation Q&A\n\n"
|
|
861
|
+
response_text += "**Just copy and paste the index commands above!**\n"
|
|
862
|
+
|
|
863
|
+
# Add search metadata
|
|
864
|
+
response_text += f"\n---\n"
|
|
865
|
+
response_text += f"*Searched {result.get('total_results', 0)} sources using NIA Web Search*"
|
|
866
|
+
|
|
867
|
+
return [TextContent(type="text", text=response_text)]
|
|
868
|
+
|
|
869
|
+
except APIError as e:
|
|
870
|
+
logger.error(f"API Error in web search: {e}")
|
|
871
|
+
if e.status_code == 403 or "free tier limit" in str(e).lower() or "free api requests" in str(e).lower():
|
|
872
|
+
if e.detail and "25 free API requests" in e.detail:
|
|
873
|
+
return [TextContent(
|
|
874
|
+
type="text",
|
|
875
|
+
text=f"❌ {e.detail}\n\n💡 Tip: Upgrade to Pro at https://trynia.ai/billing for unlimited API access."
|
|
876
|
+
)]
|
|
877
|
+
else:
|
|
878
|
+
return [TextContent(
|
|
879
|
+
type="text",
|
|
880
|
+
text=f"❌ {str(e)}\n\n💡 Tip: You've reached the free tier limit. Upgrade to Pro for unlimited access."
|
|
881
|
+
)]
|
|
882
|
+
else:
|
|
883
|
+
return [TextContent(type="text", text=f"❌ {str(e)}")]
|
|
884
|
+
except Exception as e:
|
|
885
|
+
logger.error(f"Error in NIA web search: {str(e)}")
|
|
886
|
+
return [TextContent(
|
|
887
|
+
type="text",
|
|
888
|
+
text=f"❌ NIA Web Search error: {str(e)}\n\n"
|
|
889
|
+
"This might be due to:\n"
|
|
890
|
+
"- Network connectivity issues\n"
|
|
891
|
+
"- Service temporarily unavailable"
|
|
892
|
+
)]
|
|
893
|
+
|
|
894
|
+
@mcp.tool()
|
|
895
|
+
async def nia_deep_research_agent(
|
|
896
|
+
query: str,
|
|
897
|
+
output_format: Optional[str] = None
|
|
898
|
+
) -> List[TextContent]:
|
|
899
|
+
"""
|
|
900
|
+
Perform deep, multi-step research on a topic using advanced AI research capabilities.
|
|
901
|
+
Best for complex questions that need comprehensive analysis.
|
|
902
|
+
|
|
903
|
+
USE THIS TOOL WHEN:
|
|
904
|
+
- Comparing multiple options ("compare X vs Y vs Z")
|
|
905
|
+
- Analyzing pros and cons
|
|
906
|
+
- Questions with "best", "top", "which is better"
|
|
907
|
+
- Needing structured analysis or synthesis
|
|
908
|
+
- Complex questions requiring multiple sources
|
|
909
|
+
- Questions about trends, patterns, or developments
|
|
910
|
+
- Requests for comprehensive overviews
|
|
911
|
+
|
|
912
|
+
DON'T USE THIS FOR:
|
|
913
|
+
- Simple lookups (use nia_web_search instead)
|
|
914
|
+
- Finding a specific known item (use nia_web_search instead)
|
|
915
|
+
- Quick searches for repos/docs (use nia_web_search instead)
|
|
916
|
+
|
|
917
|
+
COMPLEXITY INDICATORS:
|
|
918
|
+
- Words like: compare, analyze, evaluate, pros/cons, trade-offs
|
|
919
|
+
- Multiple criteria mentioned
|
|
920
|
+
- Asking for recommendations based on context
|
|
921
|
+
- Needing structured output (tables, lists, comparisons)
|
|
922
|
+
|
|
923
|
+
Args:
|
|
924
|
+
query: Research question (e.g., "Compare top 3 RAG frameworks with pros/cons")
|
|
925
|
+
output_format: Optional structure hint (e.g., "comparison table", "pros and cons list")
|
|
926
|
+
|
|
927
|
+
Returns:
|
|
928
|
+
Comprehensive research results with citations
|
|
929
|
+
"""
|
|
930
|
+
try:
|
|
931
|
+
client = await ensure_api_client()
|
|
932
|
+
|
|
933
|
+
logger.info(f"Starting deep research for: {query}")
|
|
934
|
+
|
|
935
|
+
# Use the API client method with proper timeout handling
|
|
936
|
+
try:
|
|
937
|
+
result = await asyncio.wait_for(
|
|
938
|
+
client.deep_research(query=query, output_format=output_format),
|
|
939
|
+
timeout=720.0 # 12 minutes to allow for longer research tasks
|
|
940
|
+
)
|
|
941
|
+
except asyncio.TimeoutError:
|
|
942
|
+
logger.error(f"Deep research timed out after 12 minutes for query: {query}")
|
|
943
|
+
return [TextContent(
|
|
944
|
+
type="text",
|
|
945
|
+
text="❌ Research timed out. The query may be too complex. Try:\n"
|
|
946
|
+
"- Breaking it into smaller questions\n"
|
|
947
|
+
"- Using more specific keywords\n"
|
|
948
|
+
"- Trying the nia_web_search tool for simpler queries"
|
|
949
|
+
)]
|
|
950
|
+
|
|
951
|
+
# Format the research results
|
|
952
|
+
response_text = f"## 🔬 NIA Deep Research Agent Results\n\n"
|
|
953
|
+
response_text += f"**Query:** {query}\n\n"
|
|
954
|
+
|
|
955
|
+
if result.get("data"):
|
|
956
|
+
response_text += "### 📊 Research Findings:\n\n"
|
|
957
|
+
|
|
958
|
+
# Pretty print the JSON data
|
|
959
|
+
import json
|
|
960
|
+
formatted_data = json.dumps(result["data"], indent=2)
|
|
961
|
+
response_text += f"```json\n{formatted_data}\n```\n\n"
|
|
962
|
+
|
|
963
|
+
# Add citations if available
|
|
964
|
+
if result.get("citations"):
|
|
965
|
+
response_text += "### 📚 Sources & Citations:\n\n"
|
|
966
|
+
citation_num = 1
|
|
967
|
+
for field, citations in result["citations"].items():
|
|
968
|
+
if citations:
|
|
969
|
+
response_text += f"**{field}:**\n"
|
|
970
|
+
for citation in citations[:3]: # Limit to 3 citations per field
|
|
971
|
+
response_text += f"{citation_num}. [{citation.get('title', 'Source')}]({citation.get('url', '#')})\n"
|
|
972
|
+
if citation.get('snippet'):
|
|
973
|
+
response_text += f" > {citation['snippet'][:150]}...\n"
|
|
974
|
+
citation_num += 1
|
|
975
|
+
response_text += "\n"
|
|
976
|
+
|
|
977
|
+
response_text += "### 💡 RECOMMENDED NEXT ACTIONS WITH NIA:\n\n"
|
|
978
|
+
|
|
979
|
+
# Extract potential repos and docs from the research data
|
|
980
|
+
repos_found = []
|
|
981
|
+
docs_found = []
|
|
982
|
+
|
|
983
|
+
# Helper function to extract URLs from nested data structures
|
|
984
|
+
def extract_urls_from_data(data, urls_list=None):
|
|
985
|
+
if urls_list is None:
|
|
986
|
+
urls_list = []
|
|
987
|
+
|
|
988
|
+
if isinstance(data, dict):
|
|
989
|
+
for value in data.values():
|
|
990
|
+
extract_urls_from_data(value, urls_list)
|
|
991
|
+
elif isinstance(data, list):
|
|
992
|
+
for item in data:
|
|
993
|
+
extract_urls_from_data(item, urls_list)
|
|
994
|
+
elif isinstance(data, str):
|
|
995
|
+
# Check if this string is a URL
|
|
996
|
+
if data.startswith(('http://', 'https://')):
|
|
997
|
+
urls_list.append(data)
|
|
998
|
+
|
|
999
|
+
return urls_list
|
|
1000
|
+
|
|
1001
|
+
# Extract all URLs from the data
|
|
1002
|
+
all_urls = extract_urls_from_data(result["data"])
|
|
1003
|
+
|
|
1004
|
+
# Filter for GitHub repos and documentation
|
|
1005
|
+
import re
|
|
1006
|
+
github_pattern = r'github\.com/([a-zA-Z0-9-]+/[a-zA-Z0-9-_.]+)'
|
|
1007
|
+
|
|
1008
|
+
for url in all_urls:
|
|
1009
|
+
# Check for GitHub repos
|
|
1010
|
+
github_match = re.search(github_pattern, url)
|
|
1011
|
+
if github_match and '/tree/' not in url and '/blob/' not in url:
|
|
1012
|
+
repos_found.append(github_match.group(1))
|
|
1013
|
+
# Check for documentation URLs
|
|
1014
|
+
elif any(doc_indicator in url.lower() for doc_indicator in ['docs', 'documentation', '.readthedocs.', '/guide', '/tutorial']):
|
|
1015
|
+
docs_found.append(url)
|
|
1016
|
+
|
|
1017
|
+
# Remove duplicates and limit results
|
|
1018
|
+
repos_found = list(set(repos_found))[:3]
|
|
1019
|
+
docs_found = list(set(docs_found))[:3]
|
|
1020
|
+
|
|
1021
|
+
if repos_found:
|
|
1022
|
+
response_text += "**🚀 DISCOVERED REPOSITORIES - Index with NIA for deep analysis:**\n"
|
|
1023
|
+
for repo in repos_found:
|
|
1024
|
+
response_text += f"```\nIndex {repo}\n```\n"
|
|
1025
|
+
response_text += "✨ Enable AI-powered code search and architecture understanding!\n\n"
|
|
1026
|
+
|
|
1027
|
+
if docs_found:
|
|
1028
|
+
response_text += "**📖 DISCOVERED DOCUMENTATION - Index with NIA for smart search:**\n"
|
|
1029
|
+
for doc in docs_found[:2]: # Limit to 2 for readability
|
|
1030
|
+
response_text += f"```\nIndex documentation {doc}\n```\n"
|
|
1031
|
+
response_text += "✨ Make documentation instantly searchable with AI Q&A!\n\n"
|
|
1032
|
+
|
|
1033
|
+
if not repos_found and not docs_found:
|
|
1034
|
+
response_text += "**🔍 Manual indexing options:**\n"
|
|
1035
|
+
response_text += "- If you see any GitHub repos mentioned: Say \"Index [owner/repo]\"\n"
|
|
1036
|
+
response_text += "- If you see any documentation sites: Say \"Index documentation [url]\"\n"
|
|
1037
|
+
response_text += "- These will unlock NIA's powerful AI search capabilities!\n\n"
|
|
1038
|
+
|
|
1039
|
+
response_text += "**📊 Other actions:**\n"
|
|
1040
|
+
response_text += "- Ask follow-up questions about the research\n"
|
|
1041
|
+
response_text += "- Request a different analysis format\n"
|
|
1042
|
+
response_text += "- Search for more specific information\n"
|
|
1043
|
+
else:
|
|
1044
|
+
response_text += "No structured data returned. The research may need a more specific query."
|
|
1045
|
+
|
|
1046
|
+
return [TextContent(type="text", text=response_text)]
|
|
1047
|
+
|
|
1048
|
+
except APIError as e:
|
|
1049
|
+
logger.error(f"API Error in deep research: {e}")
|
|
1050
|
+
if e.status_code == 403 or "free tier limit" in str(e).lower() or "free api requests" in str(e).lower():
|
|
1051
|
+
if e.detail and "25 free API requests" in e.detail:
|
|
1052
|
+
return [TextContent(
|
|
1053
|
+
type="text",
|
|
1054
|
+
text=f"❌ {e.detail}\n\n💡 Tip: Upgrade to Pro at https://trynia.ai/billing for unlimited API access."
|
|
1055
|
+
)]
|
|
1056
|
+
else:
|
|
1057
|
+
return [TextContent(
|
|
1058
|
+
type="text",
|
|
1059
|
+
text=f"❌ {str(e)}\n\n💡 Tip: You've reached the free tier limit. Upgrade to Pro for unlimited access."
|
|
1060
|
+
)]
|
|
1061
|
+
else:
|
|
1062
|
+
return [TextContent(type="text", text=f"❌ {str(e)}")]
|
|
1063
|
+
except Exception as e:
|
|
1064
|
+
logger.error(f"Error in deep research: {str(e)}")
|
|
1065
|
+
return [TextContent(
|
|
1066
|
+
type="text",
|
|
1067
|
+
text=f"❌ Research error: {str(e)}\n\n"
|
|
1068
|
+
"Try simplifying your question or using the regular nia_web_search tool."
|
|
1069
|
+
)]
|
|
1070
|
+
|
|
702
1071
|
# Resources
|
|
703
1072
|
|
|
704
1073
|
# 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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|