nia-mcp-server 1.0.23__py3-none-any.whl → 1.0.24__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.

@@ -3,4 +3,4 @@ NIA MCP Server - Proxy server for NIA Knowledge Agent
3
3
  """
4
4
 
5
5
 
6
- __version__ = "1.0.23"
6
+ __version__ = "1.0.24"
@@ -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.23",
31
+ "User-Agent": "nia-mcp-server/1.0.24",
32
32
  "Content-Type": "application/json"
33
33
  },
34
34
  timeout=720.0 # 12 minute timeout for deep research operations
@@ -926,7 +926,9 @@ class NIAApiClient:
926
926
  content: str,
927
927
  agent_source: str,
928
928
  tags: List[str] = None,
929
- metadata: Dict[str, Any] = None
929
+ metadata: Dict[str, Any] = None,
930
+ nia_references: Optional[Dict[str, Any]] = None,
931
+ edited_files: Optional[List[Dict[str, Any]]] = None
930
932
  ) -> Dict[str, Any]:
931
933
  """Save a conversation context for cross-agent sharing."""
932
934
  try:
@@ -939,6 +941,12 @@ class NIAApiClient:
939
941
  "metadata": metadata or {}
940
942
  }
941
943
 
944
+ # Add new structured fields if provided
945
+ if nia_references is not None:
946
+ payload["nia_references"] = nia_references
947
+ if edited_files is not None:
948
+ payload["edited_files"] = edited_files
949
+
942
950
  response = await self.client.post(
943
951
  f"{self.base_url}/v2/contexts",
944
952
  json=payload
nia_mcp_server/server.py CHANGED
@@ -2826,160 +2826,6 @@ async def nia_package_search_read_file(
2826
2826
  f"- The line range is valid (1-based, max 200 lines)"
2827
2827
  )]
2828
2828
 
2829
- # @mcp.tool()
2830
- async def visualize_codebase(
2831
- repository: str
2832
- ) -> List[TextContent]:
2833
- """
2834
- Open the graph visualization for an indexed repository in a browser.
2835
-
2836
- This tool launches a browser with the interactive graph visualization
2837
- that shows the code structure, relationships, and dependencies of
2838
- the indexed codebase.
2839
-
2840
- Args:
2841
- repository: Repository in owner/repo format (e.g., "facebook/react")
2842
-
2843
- Returns:
2844
- Status message with the URL that was opened
2845
-
2846
- Examples:
2847
- - visualize_codebase("facebook/react")
2848
- - visualize_codebase("langchain-ai/langchain")
2849
- """
2850
- try:
2851
- client = await ensure_api_client()
2852
-
2853
- logger.info(f"Looking up repository: {repository}")
2854
-
2855
- # List all repositories to find the matching one
2856
- repositories = await client.list_repositories()
2857
-
2858
- # Find the repository by name
2859
- matching_repo = None
2860
- for repo in repositories:
2861
- if repo.get("repository") == repository:
2862
- matching_repo = repo
2863
- break
2864
-
2865
- if not matching_repo:
2866
- # Try case-insensitive match as fallback
2867
- repository_lower = repository.lower()
2868
- for repo in repositories:
2869
- if repo.get("repository", "").lower() == repository_lower:
2870
- matching_repo = repo
2871
- break
2872
-
2873
- if not matching_repo:
2874
- return [TextContent(
2875
- type="text",
2876
- text=f"❌ Repository '{repository}' not found.\n\n"
2877
- f"Available repositories:\n" +
2878
- "\n".join(f"- {r.get('repository')}" for r in repositories if r.get('repository')) +
2879
- "\n\nUse `list_repositories` to see all indexed repositories."
2880
- )]
2881
-
2882
- # Check if the repository is fully indexed
2883
- status = matching_repo.get("status", "unknown")
2884
- # Use the actual project ID if available, fall back to repository_id
2885
- repository_id = matching_repo.get("id") or matching_repo.get("repository_id")
2886
-
2887
- if not repository_id:
2888
- return [TextContent(
2889
- type="text",
2890
- text=f"❌ No repository ID found for '{repository}'. This may be a data inconsistency."
2891
- )]
2892
-
2893
- if status != "completed":
2894
- warning_msg = f"⚠️ Note: Repository '{repository}' is currently {status}.\n"
2895
- if status == "indexing":
2896
- warning_msg += "The visualization may show incomplete data.\n\n"
2897
- elif status == "error":
2898
- error_msg = matching_repo.get("error", "Unknown error")
2899
- warning_msg += f"Error: {error_msg}\n\n"
2900
- else:
2901
- warning_msg += "The visualization may not be available.\n\n"
2902
- else:
2903
- warning_msg = ""
2904
-
2905
- # Determine the base URL based on the API URL
2906
- api_base_url = client.base_url
2907
- if "localhost" in api_base_url or "127.0.0.1" in api_base_url:
2908
- # Local development
2909
- app_base_url = "http://localhost:3000"
2910
- else:
2911
- # Production
2912
- app_base_url = "https://app.trynia.ai"
2913
-
2914
- # Construct the visualization URL
2915
- visualization_url = f"{app_base_url}/visualize/{repository_id}"
2916
-
2917
- # Try to open the browser
2918
- try:
2919
- webbrowser.open(visualization_url)
2920
- browser_opened = True
2921
- open_msg = "✅ Opening graph visualization in your default browser..."
2922
- except Exception as e:
2923
- logger.warning(f"Failed to open browser: {e}")
2924
- browser_opened = False
2925
- open_msg = "⚠️ Could not automatically open browser."
2926
-
2927
- # Format the response
2928
- response_lines = [
2929
- f"# Graph Visualization: {repository}",
2930
- "",
2931
- warning_msg if warning_msg else "",
2932
- open_msg,
2933
- "",
2934
- f"**URL:** {visualization_url}",
2935
- "",
2936
- ]
2937
-
2938
- if matching_repo.get("display_name"):
2939
- response_lines.append(f"**Display Name:** {matching_repo['display_name']}")
2940
-
2941
- response_lines.extend([
2942
- f"**Branch:** {matching_repo.get('branch', 'main')}",
2943
- f"**Status:** {status}",
2944
- "",
2945
- "## Features Available:",
2946
- "- 🔍 Interactive force-directed graph",
2947
- "- 🎨 Color-coded node types (functions, classes, files, etc.)",
2948
- "- 🔗 Relationship visualization (calls, imports, inherits, etc.)",
2949
- "- 💬 Click on any node to chat with that specific code element",
2950
- "- 🔎 Search and filter capabilities",
2951
- "- 📊 Graph statistics and insights"
2952
- ])
2953
-
2954
- if not browser_opened:
2955
- response_lines.extend([
2956
- "",
2957
- "**Manual Access:**",
2958
- f"Copy and paste this URL into your browser: {visualization_url}"
2959
- ])
2960
-
2961
- return [TextContent(
2962
- type="text",
2963
- text="\n".join(response_lines)
2964
- )]
2965
-
2966
- except APIError as e:
2967
- logger.error(f"API Error in visualize_codebase: {e}")
2968
- if e.status_code == 403 or "free tier limit" in str(e).lower():
2969
- return [TextContent(
2970
- type="text",
2971
- text=f"❌ {str(e)}\n\n💡 Tip: Upgrade to Pro at https://trynia.ai/billing for unlimited access."
2972
- )]
2973
- else:
2974
- return [TextContent(type="text", text=f"❌ {str(e)}")]
2975
- except Exception as e:
2976
- logger.error(f"Error in visualize_codebase: {e}")
2977
- return [TextContent(
2978
- type="text",
2979
- text=f"❌ Error opening visualization: {str(e)}"
2980
- )]
2981
-
2982
-
2983
2829
  @mcp.tool()
2984
2830
  async def nia_bug_report(
2985
2831
  description: str,
@@ -3094,7 +2940,9 @@ async def save_context(
3094
2940
  content: str,
3095
2941
  agent_source: str,
3096
2942
  tags: Optional[List[str]] = None,
3097
- metadata: Optional[dict] = None
2943
+ metadata: Optional[dict] = None,
2944
+ nia_references: Optional[dict] = None,
2945
+ edited_files: Optional[List[dict]] = None
3098
2946
  ) -> List[TextContent]:
3099
2947
  """
3100
2948
  Save a conversation context for cross-agent sharing.
@@ -3108,8 +2956,16 @@ async def save_context(
3108
2956
  summary: Brief summary of the conversation (10-1000 characters)
3109
2957
  content: Full conversation context - the agent should compact the conversation history but keep all important parts togethers, as well as code snippets. No excuses.
3110
2958
  agent_source: Which agent is creating this context (e.g., "cursor", "claude-code", "windsurf")
3111
- tags: Optional list of searchable tags (up to 10 tags)
2959
+ tags: Optional list of searchable tags
3112
2960
  metadata: Optional metadata like file paths, repositories discussed, etc.
2961
+ nia_references: Structured data about NIA resources used during conversation
2962
+ Format: {
2963
+ "indexed_resources": [{"identifier": "owner/repo", "resource_type": "repository", "purpose": "Used for authentication patterns"}],
2964
+ "search_queries": [{"query": "JWT implementation", "query_type": "codebase", "resources_searched": ["owner/repo"], "key_findings": "Found JWT utils in auth folder"}],
2965
+ "session_summary": "Used NIA to explore authentication patterns and API design"
2966
+ }
2967
+ edited_files: List of files that were modified during conversation
2968
+ Format: [{"file_path": "src/auth.ts", "operation": "modified", "changes_description": "Added JWT validation", "key_changes": ["Added validate() function"]}]
3113
2969
 
3114
2970
  Returns:
3115
2971
  Confirmation of successful context save with context ID
@@ -3121,7 +2977,11 @@ async def save_context(
3121
2977
  content="User asked about implementing streaming... [agent should include conversation]",
3122
2978
  agent_source="cursor",
3123
2979
  tags=["streaming", "ai-sdk", "implementation"],
3124
- metadata={"files": ["src/api/chat.ts"], "repos": ["myproject"]}
2980
+ nia_references={
2981
+ "indexed_resources": [{"identifier": "vercel/ai", "resource_type": "repository", "purpose": "Reference for streaming implementation"}],
2982
+ "search_queries": [{"query": "streaming API", "query_type": "documentation", "key_findings": "Found useChat hook with streaming"}]
2983
+ },
2984
+ edited_files=[{"file_path": "src/chat.ts", "operation": "created", "changes_description": "Added streaming chat component"}]
3125
2985
  )
3126
2986
  """
3127
2987
  try:
@@ -3144,9 +3004,6 @@ async def save_context(
3144
3004
  if not agent_source or not agent_source.strip():
3145
3005
  return [TextContent(type="text", text="❌ Error: Agent source is required")]
3146
3006
 
3147
- if tags and len(tags) > 10:
3148
- return [TextContent(type="text", text="❌ Error: Maximum 10 tags allowed")]
3149
-
3150
3007
  client = await ensure_api_client()
3151
3008
 
3152
3009
  logger.info(f"Saving context: title='{title}', agent={agent_source}, content_length={len(content)}")
@@ -3157,7 +3014,9 @@ async def save_context(
3157
3014
  content=content,
3158
3015
  agent_source=agent_source.strip(),
3159
3016
  tags=tags or [],
3160
- metadata=metadata or {}
3017
+ metadata=metadata or {},
3018
+ nia_references=nia_references,
3019
+ edited_files=edited_files or []
3161
3020
  )
3162
3021
 
3163
3022
  context_id = result.get("id")
@@ -3360,10 +3219,78 @@ async def retrieve_context(context_id: str) -> List[TextContent]:
3360
3219
 
3361
3220
  response += f"\n📝 **Summary:**\n{context['summary']}\n\n"
3362
3221
 
3222
+ # Add NIA References - CRITICAL for context handoffs
3223
+ nia_references = context.get('nia_references', {})
3224
+ if nia_references:
3225
+ response += "🧠 **NIA RESOURCES USED - RECOMMENDED ACTIONS:**\n"
3226
+
3227
+ indexed_resources = nia_references.get('indexed_resources', [])
3228
+ if indexed_resources:
3229
+ response += "**📦 Re-index these resources:**\n"
3230
+ for resource in indexed_resources:
3231
+ identifier = resource.get('identifier', 'Unknown')
3232
+ resource_type = resource.get('resource_type', 'unknown')
3233
+ purpose = resource.get('purpose', 'No purpose specified')
3234
+
3235
+ if resource_type == 'repository':
3236
+ response += f"• `Index {identifier}` - {purpose}\n"
3237
+ elif resource_type == 'documentation':
3238
+ response += f"• `Index documentation {identifier}` - {purpose}\n"
3239
+ else:
3240
+ response += f"• `Index {identifier}` ({resource_type}) - {purpose}\n"
3241
+ response += "\n"
3242
+
3243
+ search_queries = nia_references.get('search_queries', [])
3244
+ if search_queries:
3245
+ response += "**🔍 Useful search queries to re-run:**\n"
3246
+ for query in search_queries:
3247
+ query_text = query.get('query', 'Unknown query')
3248
+ query_type = query.get('query_type', 'search')
3249
+ key_findings = query.get('key_findings', 'No findings specified')
3250
+ resources_searched = query.get('resources_searched', [])
3251
+
3252
+ response += f"• **Query:** `{query_text}` ({query_type})\n"
3253
+ if resources_searched:
3254
+ response += f" **Resources:** {', '.join(resources_searched)}\n"
3255
+ response += f" **Key Findings:** {key_findings}\n"
3256
+ response += "\n"
3257
+
3258
+ session_summary = nia_references.get('session_summary')
3259
+ if session_summary:
3260
+ response += f"**📋 NIA Session Summary:** {session_summary}\n\n"
3261
+
3262
+ # Add Edited Files - CRITICAL for code handoffs
3263
+ edited_files = context.get('edited_files', [])
3264
+ if edited_files:
3265
+ response += "📝 **FILES MODIFIED - READ THESE TO GET UP TO SPEED:**\n"
3266
+ for file_info in edited_files:
3267
+ file_path = file_info.get('file_path', 'Unknown file')
3268
+ operation = file_info.get('operation', 'modified')
3269
+ changes_desc = file_info.get('changes_description', 'No description')
3270
+ key_changes = file_info.get('key_changes', [])
3271
+ language = file_info.get('language', '')
3272
+
3273
+ operation_emoji = {
3274
+ 'created': '🆕',
3275
+ 'modified': '✏️',
3276
+ 'deleted': '🗑️'
3277
+ }.get(operation, '📄')
3278
+
3279
+ response += f"• {operation_emoji} **`{file_path}`** ({operation})\n"
3280
+ response += f" **Changes:** {changes_desc}\n"
3281
+
3282
+ if key_changes:
3283
+ response += f" **Key Changes:** {', '.join(key_changes)}\n"
3284
+ if language:
3285
+ response += f" **Language:** {language}\n"
3286
+
3287
+ response += f" **💡 Action:** Read this file with: `Read {file_path}`\n"
3288
+ response += "\n"
3289
+
3363
3290
  # Add metadata if available
3364
3291
  metadata = context.get('metadata', {})
3365
3292
  if metadata:
3366
- response += f"📊 **Metadata:**\n"
3293
+ response += f"📊 **Additional Metadata:**\n"
3367
3294
  for key, value in metadata.items():
3368
3295
  if isinstance(value, list):
3369
3296
  response += f"• **{key}:** {', '.join(map(str, value))}\n"
@@ -3374,10 +3301,15 @@ async def retrieve_context(context_id: str) -> List[TextContent]:
3374
3301
  response += f"📄 **Full Context:**\n\n{context['content']}\n\n"
3375
3302
 
3376
3303
  response += f"---\n"
3377
- response += f"💡 **Tips:**\n"
3378
- response += f"• This context was created by {context['agent_source']}\n"
3379
- response += f"• Use this information to understand the strategic planning\n"
3380
- response += f"• You can now implement based on this context!"
3304
+ response += f"🚀 **NEXT STEPS FOR SEAMLESS HANDOFF:**\n"
3305
+ response += f"• This context was created by **{context['agent_source']}**\n"
3306
+
3307
+ if nia_references.get('search_queries'):
3308
+ response += f"• **RECOMMENDED:** Re-run the search queries to get the same insights\n"
3309
+ if edited_files:
3310
+ response += f"• **ESSENTIAL:** Read the modified files above to understand code changes\n"
3311
+
3312
+ response += f"• Use the summary and full context to understand the strategic planning\n"
3381
3313
 
3382
3314
  return [TextContent(type="text", text=response)]
3383
3315
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nia-mcp-server
3
- Version: 1.0.23
3
+ Version: 1.0.24
4
4
  Summary: Nia Knowledge Agent
5
5
  Project-URL: Homepage, https://trynia.ai
6
6
  Project-URL: Documentation, https://docs.trynia.ai
@@ -1,19 +1,19 @@
1
- nia_mcp_server/__init__.py,sha256=fRGM5mMWuUp59DtNCXNq3BQKxw30WI3zptbKFGTkt_A,87
1
+ nia_mcp_server/__init__.py,sha256=VOORKLeNTKA2FspOIRTpNpZQPszzLz8RVGVzrInPjew,87
2
2
  nia_mcp_server/__main__.py,sha256=YQSpFtDeKp18r8mKr084cHnRFV4416_EKCu9FTM8_ik,394
3
- nia_mcp_server/api_client.py,sha256=LOFKnadDmtwRLm5QhUkKn8X7Be5GlZf0WaGnahsQ9tQ,41177
3
+ nia_mcp_server/api_client.py,sha256=Flk54a3E73TCZmrChwq-sdBaDRxaEtcMC3p2APOHg_0,41546
4
4
  nia_mcp_server/cli.py,sha256=32VSPNIocXtDgVBDZNZsxvj3kytBn54_a1pIE84vOdY,1834
5
5
  nia_mcp_server/profiles.py,sha256=2DD8PFRr5Ij4IK4sPUz0mH8aKjkrEtkKLC1R0iki2bA,7221
6
6
  nia_mcp_server/project_init.py,sha256=T0-ziJhofL4L8APwnM43BLhxtlmOHaYH-V9PF2yXLw4,7138
7
7
  nia_mcp_server/rule_transformer.py,sha256=wCxoQ1Kl_rI9mUFnh9kG5iCXYU4QInrmFQOReZfAFVo,11000
8
- nia_mcp_server/server.py,sha256=0y0feDisTv-hhQeqWDByqo2yaDJMvTILXZFYZULs248,157233
8
+ nia_mcp_server/server.py,sha256=oDV-HZCyLlsPpAXU6IwMLDR9YjfduB1AsVDLQLRDoxU,156315
9
9
  nia_mcp_server/setup.py,sha256=nJXVY8NHGtWROtoH8DW-3uOgyuPs4F9dW0cNhcbCLrM,5355
10
10
  nia_mcp_server/assets/rules/claude_rules.md,sha256=HNL5GJMUbFxSpNbIAJUQWqAywjMl4lf530I1in69aNY,7380
11
11
  nia_mcp_server/assets/rules/cursor_rules.md,sha256=hd6lhzNrK1ULQUYIEVeOnyKnuLKq4hmwZPbMqGUI1Lk,1720
12
12
  nia_mcp_server/assets/rules/nia_rules.md,sha256=l6sx000uqoczoHYqOPp4hnNgyfpnhvO9NyT0fVx5nU0,8059
13
13
  nia_mcp_server/assets/rules/vscode_rules.md,sha256=fqn4aJO_bhftaCGkVoquruQHf3EaREQJQWHXq6a4FOk,6967
14
14
  nia_mcp_server/assets/rules/windsurf_rules.md,sha256=PzU2as5gaiVsV6PAzg8T_-GR7VCyRQGMjAHcSzYF_ms,3354
15
- nia_mcp_server-1.0.23.dist-info/METADATA,sha256=UB-GuIDpM_9P32kz5bG98aEfHEb51rA3rPliKMtI9fs,1324
16
- nia_mcp_server-1.0.23.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
17
- nia_mcp_server-1.0.23.dist-info/entry_points.txt,sha256=V74FQEp48pfWxPCl7B9mihtqvIJNVjCSbRfCz4ww77I,64
18
- nia_mcp_server-1.0.23.dist-info/licenses/LICENSE,sha256=IrdVKi3bsiB2MTLM26MltBRpwyNi-8P6Cy0EnmAN76A,1557
19
- nia_mcp_server-1.0.23.dist-info/RECORD,,
15
+ nia_mcp_server-1.0.24.dist-info/METADATA,sha256=HTPMQsCgZ4qkwpwk0P9inx85JSnCGewM3_yO_JIqCUc,1324
16
+ nia_mcp_server-1.0.24.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
17
+ nia_mcp_server-1.0.24.dist-info/entry_points.txt,sha256=V74FQEp48pfWxPCl7B9mihtqvIJNVjCSbRfCz4ww77I,64
18
+ nia_mcp_server-1.0.24.dist-info/licenses/LICENSE,sha256=IrdVKi3bsiB2MTLM26MltBRpwyNi-8P6Cy0EnmAN76A,1557
19
+ nia_mcp_server-1.0.24.dist-info/RECORD,,