kailash 0.1.4__py3-none-any.whl → 0.2.0__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.
- kailash/__init__.py +1 -1
- kailash/access_control.py +740 -0
- kailash/api/__main__.py +6 -0
- kailash/api/auth.py +668 -0
- kailash/api/custom_nodes.py +285 -0
- kailash/api/custom_nodes_secure.py +377 -0
- kailash/api/database.py +620 -0
- kailash/api/studio.py +915 -0
- kailash/api/studio_secure.py +893 -0
- kailash/mcp/__init__.py +53 -0
- kailash/mcp/__main__.py +13 -0
- kailash/mcp/ai_registry_server.py +712 -0
- kailash/mcp/client.py +447 -0
- kailash/mcp/client_new.py +334 -0
- kailash/mcp/server.py +293 -0
- kailash/mcp/server_new.py +336 -0
- kailash/mcp/servers/__init__.py +12 -0
- kailash/mcp/servers/ai_registry.py +289 -0
- kailash/nodes/__init__.py +4 -2
- kailash/nodes/ai/__init__.py +38 -0
- kailash/nodes/ai/a2a.py +1790 -0
- kailash/nodes/ai/agents.py +116 -2
- kailash/nodes/ai/ai_providers.py +206 -8
- kailash/nodes/ai/intelligent_agent_orchestrator.py +2108 -0
- kailash/nodes/ai/iterative_llm_agent.py +1280 -0
- kailash/nodes/ai/llm_agent.py +324 -1
- kailash/nodes/ai/self_organizing.py +1623 -0
- kailash/nodes/api/http.py +106 -25
- kailash/nodes/api/rest.py +116 -21
- kailash/nodes/base.py +15 -2
- kailash/nodes/base_async.py +45 -0
- kailash/nodes/base_cycle_aware.py +374 -0
- kailash/nodes/base_with_acl.py +338 -0
- kailash/nodes/code/python.py +135 -27
- kailash/nodes/data/readers.py +116 -53
- kailash/nodes/data/writers.py +16 -6
- kailash/nodes/logic/__init__.py +8 -0
- kailash/nodes/logic/async_operations.py +48 -9
- kailash/nodes/logic/convergence.py +642 -0
- kailash/nodes/logic/loop.py +153 -0
- kailash/nodes/logic/operations.py +212 -27
- kailash/nodes/logic/workflow.py +26 -18
- kailash/nodes/mixins/__init__.py +11 -0
- kailash/nodes/mixins/mcp.py +228 -0
- kailash/nodes/mixins.py +387 -0
- kailash/nodes/transform/__init__.py +8 -1
- kailash/nodes/transform/processors.py +119 -4
- kailash/runtime/__init__.py +2 -1
- kailash/runtime/access_controlled.py +458 -0
- kailash/runtime/local.py +106 -33
- kailash/runtime/parallel_cyclic.py +529 -0
- kailash/sdk_exceptions.py +90 -5
- kailash/security.py +845 -0
- kailash/tracking/manager.py +38 -15
- kailash/tracking/models.py +1 -1
- kailash/tracking/storage/filesystem.py +30 -2
- kailash/utils/__init__.py +8 -0
- kailash/workflow/__init__.py +18 -0
- kailash/workflow/convergence.py +270 -0
- kailash/workflow/cycle_analyzer.py +768 -0
- kailash/workflow/cycle_builder.py +573 -0
- kailash/workflow/cycle_config.py +709 -0
- kailash/workflow/cycle_debugger.py +760 -0
- kailash/workflow/cycle_exceptions.py +601 -0
- kailash/workflow/cycle_profiler.py +671 -0
- kailash/workflow/cycle_state.py +338 -0
- kailash/workflow/cyclic_runner.py +985 -0
- kailash/workflow/graph.py +500 -39
- kailash/workflow/migration.py +768 -0
- kailash/workflow/safety.py +365 -0
- kailash/workflow/templates.py +744 -0
- kailash/workflow/validation.py +693 -0
- {kailash-0.1.4.dist-info → kailash-0.2.0.dist-info}/METADATA +446 -13
- kailash-0.2.0.dist-info/RECORD +125 -0
- kailash/nodes/mcp/__init__.py +0 -11
- kailash/nodes/mcp/client.py +0 -554
- kailash/nodes/mcp/resource.py +0 -682
- kailash/nodes/mcp/server.py +0 -577
- kailash-0.1.4.dist-info/RECORD +0 -85
- {kailash-0.1.4.dist-info → kailash-0.2.0.dist-info}/WHEEL +0 -0
- {kailash-0.1.4.dist-info → kailash-0.2.0.dist-info}/entry_points.txt +0 -0
- {kailash-0.1.4.dist-info → kailash-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.1.4.dist-info → kailash-0.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,712 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
AI Registry MCP Server using Anthropic's Official MCP Python SDK.
|
4
|
+
|
5
|
+
This creates a real MCP server that exposes AI Registry tools following
|
6
|
+
the actual Model Context Protocol specification.
|
7
|
+
|
8
|
+
Run as: python -m kailash.mcp.ai_registry_server
|
9
|
+
"""
|
10
|
+
|
11
|
+
import asyncio
|
12
|
+
import json
|
13
|
+
import os
|
14
|
+
from typing import Any, Dict, List, Optional
|
15
|
+
|
16
|
+
from mcp.server import Server
|
17
|
+
from mcp.types import Resource, TextContent, Tool
|
18
|
+
|
19
|
+
|
20
|
+
class AIRegistryServer:
|
21
|
+
"""
|
22
|
+
AI Registry MCP Server providing real ISO/IEC AI use case data.
|
23
|
+
|
24
|
+
This server implements the actual MCP protocol using Anthropic's official SDK,
|
25
|
+
providing 8 real tools for AI use case discovery and analysis.
|
26
|
+
"""
|
27
|
+
|
28
|
+
def __init__(self, registry_file: str = "research/combined_ai_registry.json"):
|
29
|
+
"""Initialize the AI Registry MCP server."""
|
30
|
+
self.server = Server("ai-registry")
|
31
|
+
self.registry_data = self._load_registry_data(registry_file)
|
32
|
+
self._setup_tools()
|
33
|
+
self._setup_resources()
|
34
|
+
|
35
|
+
def _load_registry_data(self, registry_file: str) -> Dict[str, Any]:
|
36
|
+
"""Load AI Registry data from JSON file."""
|
37
|
+
# Handle both absolute and relative paths
|
38
|
+
if not os.path.isabs(registry_file):
|
39
|
+
# Try relative to current working directory first
|
40
|
+
if os.path.exists(registry_file):
|
41
|
+
pass # Use as-is
|
42
|
+
else:
|
43
|
+
# Try relative to this module's directory
|
44
|
+
module_dir = os.path.dirname(os.path.abspath(__file__))
|
45
|
+
project_root = os.path.dirname(
|
46
|
+
os.path.dirname(os.path.dirname(module_dir))
|
47
|
+
)
|
48
|
+
registry_file = os.path.join(project_root, registry_file)
|
49
|
+
|
50
|
+
try:
|
51
|
+
with open(registry_file, "r", encoding="utf-8") as f:
|
52
|
+
return json.load(f)
|
53
|
+
except FileNotFoundError:
|
54
|
+
# Return mock data if file not found
|
55
|
+
return {
|
56
|
+
"registry_info": {
|
57
|
+
"source": "AI Registry MCP Server",
|
58
|
+
"total_cases": 3,
|
59
|
+
"domains": 2,
|
60
|
+
},
|
61
|
+
"use_cases": [
|
62
|
+
{
|
63
|
+
"use_case_id": 42,
|
64
|
+
"name": "Medical Diagnosis Assistant",
|
65
|
+
"application_domain": "Healthcare",
|
66
|
+
"description": "AI-powered diagnostic support system for medical professionals",
|
67
|
+
"ai_methods": [
|
68
|
+
"Machine Learning",
|
69
|
+
"Deep Learning",
|
70
|
+
"Natural Language Processing",
|
71
|
+
],
|
72
|
+
"tasks": [
|
73
|
+
"Classification",
|
74
|
+
"Diagnosis Support",
|
75
|
+
"Risk Assessment",
|
76
|
+
],
|
77
|
+
"status": "PoC",
|
78
|
+
"challenges": "Data privacy, model interpretability, regulatory compliance",
|
79
|
+
"kpis": [
|
80
|
+
"Diagnostic accuracy",
|
81
|
+
"Time to diagnosis",
|
82
|
+
"User satisfaction",
|
83
|
+
],
|
84
|
+
},
|
85
|
+
{
|
86
|
+
"use_case_id": 87,
|
87
|
+
"name": "Clinical Decision Support",
|
88
|
+
"application_domain": "Healthcare",
|
89
|
+
"description": "Evidence-based recommendations for clinical decision making",
|
90
|
+
"ai_methods": ["Expert Systems", "Machine Learning"],
|
91
|
+
"tasks": ["Decision Support", "Risk Assessment"],
|
92
|
+
"status": "Production",
|
93
|
+
"challenges": "Integration with EHR systems, physician adoption",
|
94
|
+
"kpis": [
|
95
|
+
"Decision accuracy",
|
96
|
+
"Time savings",
|
97
|
+
"Physician satisfaction",
|
98
|
+
],
|
99
|
+
},
|
100
|
+
{
|
101
|
+
"use_case_id": 156,
|
102
|
+
"name": "Manufacturing Quality Control",
|
103
|
+
"application_domain": "Manufacturing",
|
104
|
+
"description": "Automated quality inspection using computer vision",
|
105
|
+
"ai_methods": ["Computer Vision", "Deep Learning"],
|
106
|
+
"tasks": ["Detection", "Classification", "Quality Control"],
|
107
|
+
"status": "Production",
|
108
|
+
"challenges": "Real-time processing, accuracy requirements",
|
109
|
+
"kpis": [
|
110
|
+
"Detection accuracy",
|
111
|
+
"Processing speed",
|
112
|
+
"Cost savings",
|
113
|
+
],
|
114
|
+
},
|
115
|
+
],
|
116
|
+
}
|
117
|
+
except Exception as e:
|
118
|
+
raise ValueError(
|
119
|
+
f"Failed to load AI registry data from {registry_file}: {e}"
|
120
|
+
)
|
121
|
+
|
122
|
+
def _setup_tools(self):
|
123
|
+
"""Setup MCP tools using the official SDK."""
|
124
|
+
|
125
|
+
@self.server.list_tools()
|
126
|
+
async def handle_list_tools():
|
127
|
+
"""List all available AI Registry tools."""
|
128
|
+
return [
|
129
|
+
Tool(
|
130
|
+
name="search_use_cases",
|
131
|
+
description="Advanced search across AI use cases with domain and method filters",
|
132
|
+
inputSchema={
|
133
|
+
"type": "object",
|
134
|
+
"properties": {
|
135
|
+
"query": {"type": "string", "description": "Search query"},
|
136
|
+
"domains": {
|
137
|
+
"type": "array",
|
138
|
+
"items": {"type": "string"},
|
139
|
+
"description": "Filter by domains",
|
140
|
+
},
|
141
|
+
"methods": {
|
142
|
+
"type": "array",
|
143
|
+
"items": {"type": "string"},
|
144
|
+
"description": "Filter by AI methods",
|
145
|
+
},
|
146
|
+
"limit": {
|
147
|
+
"type": "integer",
|
148
|
+
"description": "Maximum results",
|
149
|
+
"default": 10,
|
150
|
+
},
|
151
|
+
},
|
152
|
+
"required": ["query"],
|
153
|
+
},
|
154
|
+
),
|
155
|
+
Tool(
|
156
|
+
name="filter_by_domain",
|
157
|
+
description="Get all use cases in a specific application domain",
|
158
|
+
inputSchema={
|
159
|
+
"type": "object",
|
160
|
+
"properties": {
|
161
|
+
"domain": {
|
162
|
+
"type": "string",
|
163
|
+
"description": "Application domain",
|
164
|
+
},
|
165
|
+
"status": {
|
166
|
+
"type": "string",
|
167
|
+
"description": "Optional status filter",
|
168
|
+
},
|
169
|
+
"limit": {
|
170
|
+
"type": "integer",
|
171
|
+
"description": "Maximum results",
|
172
|
+
"default": 20,
|
173
|
+
},
|
174
|
+
},
|
175
|
+
"required": ["domain"],
|
176
|
+
},
|
177
|
+
),
|
178
|
+
Tool(
|
179
|
+
name="get_use_case_details",
|
180
|
+
description="Get complete details for a specific use case by ID",
|
181
|
+
inputSchema={
|
182
|
+
"type": "object",
|
183
|
+
"properties": {
|
184
|
+
"use_case_id": {
|
185
|
+
"type": "integer",
|
186
|
+
"description": "Use case ID",
|
187
|
+
}
|
188
|
+
},
|
189
|
+
"required": ["use_case_id"],
|
190
|
+
},
|
191
|
+
),
|
192
|
+
Tool(
|
193
|
+
name="analyze_domain_trends",
|
194
|
+
description="Analyze trends, methods, and patterns within a specific domain",
|
195
|
+
inputSchema={
|
196
|
+
"type": "object",
|
197
|
+
"properties": {
|
198
|
+
"domain": {
|
199
|
+
"type": "string",
|
200
|
+
"description": "Application domain to analyze",
|
201
|
+
},
|
202
|
+
"include_details": {
|
203
|
+
"type": "boolean",
|
204
|
+
"description": "Include detailed examples",
|
205
|
+
"default": False,
|
206
|
+
},
|
207
|
+
},
|
208
|
+
"required": ["domain"],
|
209
|
+
},
|
210
|
+
),
|
211
|
+
Tool(
|
212
|
+
name="recommend_similar",
|
213
|
+
description="Find similar use cases based on various similarity factors",
|
214
|
+
inputSchema={
|
215
|
+
"type": "object",
|
216
|
+
"properties": {
|
217
|
+
"use_case_id": {
|
218
|
+
"type": "integer",
|
219
|
+
"description": "Reference use case ID",
|
220
|
+
},
|
221
|
+
"similarity_factors": {
|
222
|
+
"type": "array",
|
223
|
+
"items": {"type": "string"},
|
224
|
+
"description": "Factors to consider",
|
225
|
+
},
|
226
|
+
"limit": {
|
227
|
+
"type": "integer",
|
228
|
+
"description": "Maximum similar cases",
|
229
|
+
"default": 5,
|
230
|
+
},
|
231
|
+
},
|
232
|
+
"required": ["use_case_id"],
|
233
|
+
},
|
234
|
+
),
|
235
|
+
Tool(
|
236
|
+
name="estimate_complexity",
|
237
|
+
description="Assess implementation complexity based on methods, challenges, and KPIs",
|
238
|
+
inputSchema={
|
239
|
+
"type": "object",
|
240
|
+
"properties": {
|
241
|
+
"use_case_id": {
|
242
|
+
"type": "integer",
|
243
|
+
"description": "Use case ID to analyze",
|
244
|
+
},
|
245
|
+
"organization_context": {
|
246
|
+
"type": "object",
|
247
|
+
"description": "Optional organization context",
|
248
|
+
},
|
249
|
+
},
|
250
|
+
"required": ["use_case_id"],
|
251
|
+
},
|
252
|
+
),
|
253
|
+
Tool(
|
254
|
+
name="suggest_implementation_path",
|
255
|
+
description="Suggest implementation roadmap and strategy based on use case and organizational context",
|
256
|
+
inputSchema={
|
257
|
+
"type": "object",
|
258
|
+
"properties": {
|
259
|
+
"use_case_id": {
|
260
|
+
"type": "integer",
|
261
|
+
"description": "Use case ID",
|
262
|
+
},
|
263
|
+
"organization_context": {
|
264
|
+
"type": "object",
|
265
|
+
"description": "Organization context for tailored recommendations",
|
266
|
+
},
|
267
|
+
},
|
268
|
+
"required": ["use_case_id"],
|
269
|
+
},
|
270
|
+
),
|
271
|
+
Tool(
|
272
|
+
name="filter_by_method",
|
273
|
+
description="Find use cases using specific AI methods or techniques",
|
274
|
+
inputSchema={
|
275
|
+
"type": "object",
|
276
|
+
"properties": {
|
277
|
+
"method": {
|
278
|
+
"type": "string",
|
279
|
+
"description": "AI method or technique",
|
280
|
+
},
|
281
|
+
"min_maturity": {
|
282
|
+
"type": "string",
|
283
|
+
"description": "Minimum implementation maturity",
|
284
|
+
},
|
285
|
+
"limit": {
|
286
|
+
"type": "integer",
|
287
|
+
"description": "Maximum results",
|
288
|
+
"default": 15,
|
289
|
+
},
|
290
|
+
},
|
291
|
+
"required": ["method"],
|
292
|
+
},
|
293
|
+
),
|
294
|
+
]
|
295
|
+
|
296
|
+
@self.server.call_tool()
|
297
|
+
async def handle_call_tool(name: str, arguments: dict):
|
298
|
+
"""Handle tool execution requests."""
|
299
|
+
if name == "search_use_cases":
|
300
|
+
return [
|
301
|
+
TextContent(
|
302
|
+
type="text",
|
303
|
+
text=json.dumps(self._search_use_cases(**arguments), indent=2),
|
304
|
+
)
|
305
|
+
]
|
306
|
+
elif name == "filter_by_domain":
|
307
|
+
return [
|
308
|
+
TextContent(
|
309
|
+
type="text",
|
310
|
+
text=json.dumps(self._filter_by_domain(**arguments), indent=2),
|
311
|
+
)
|
312
|
+
]
|
313
|
+
elif name == "get_use_case_details":
|
314
|
+
return [
|
315
|
+
TextContent(
|
316
|
+
type="text",
|
317
|
+
text=json.dumps(
|
318
|
+
self._get_use_case_details(**arguments), indent=2
|
319
|
+
),
|
320
|
+
)
|
321
|
+
]
|
322
|
+
elif name == "analyze_domain_trends":
|
323
|
+
return [
|
324
|
+
TextContent(
|
325
|
+
type="text",
|
326
|
+
text=json.dumps(
|
327
|
+
self._analyze_domain_trends(**arguments), indent=2
|
328
|
+
),
|
329
|
+
)
|
330
|
+
]
|
331
|
+
elif name == "recommend_similar":
|
332
|
+
return [
|
333
|
+
TextContent(
|
334
|
+
type="text",
|
335
|
+
text=json.dumps(self._recommend_similar(**arguments), indent=2),
|
336
|
+
)
|
337
|
+
]
|
338
|
+
elif name == "estimate_complexity":
|
339
|
+
return [
|
340
|
+
TextContent(
|
341
|
+
type="text",
|
342
|
+
text=json.dumps(
|
343
|
+
self._estimate_complexity(**arguments), indent=2
|
344
|
+
),
|
345
|
+
)
|
346
|
+
]
|
347
|
+
elif name == "suggest_implementation_path":
|
348
|
+
return [
|
349
|
+
TextContent(
|
350
|
+
type="text",
|
351
|
+
text=json.dumps(
|
352
|
+
self._suggest_implementation_path(**arguments), indent=2
|
353
|
+
),
|
354
|
+
)
|
355
|
+
]
|
356
|
+
elif name == "filter_by_method":
|
357
|
+
return [
|
358
|
+
TextContent(
|
359
|
+
type="text",
|
360
|
+
text=json.dumps(self._filter_by_method(**arguments), indent=2),
|
361
|
+
)
|
362
|
+
]
|
363
|
+
else:
|
364
|
+
raise ValueError(f"Unknown tool: {name}")
|
365
|
+
|
366
|
+
def _setup_resources(self):
|
367
|
+
"""Setup MCP resources using the official SDK."""
|
368
|
+
|
369
|
+
@self.server.list_resources()
|
370
|
+
async def handle_list_resources():
|
371
|
+
"""List all available AI Registry resources."""
|
372
|
+
resources = []
|
373
|
+
|
374
|
+
# Registry overview resource
|
375
|
+
resources.append(
|
376
|
+
Resource(
|
377
|
+
uri="ai-registry://overview",
|
378
|
+
name="AI Registry Overview",
|
379
|
+
description="Overview of the AI use case registry",
|
380
|
+
mimeType="application/json",
|
381
|
+
)
|
382
|
+
)
|
383
|
+
|
384
|
+
# Individual use case resources
|
385
|
+
for use_case in self.registry_data.get("use_cases", []):
|
386
|
+
use_case_id = use_case.get("use_case_id")
|
387
|
+
if use_case_id:
|
388
|
+
resources.append(
|
389
|
+
Resource(
|
390
|
+
uri=f"ai-registry://use-case/{use_case_id}",
|
391
|
+
name=f"Use Case {use_case_id}: {use_case.get('name', 'Unknown')}",
|
392
|
+
description=use_case.get("description", ""),
|
393
|
+
mimeType="application/json",
|
394
|
+
)
|
395
|
+
)
|
396
|
+
|
397
|
+
return resources
|
398
|
+
|
399
|
+
@self.server.read_resource()
|
400
|
+
async def handle_read_resource(uri: str):
|
401
|
+
"""Handle resource read requests."""
|
402
|
+
if uri == "ai-registry://overview":
|
403
|
+
return [
|
404
|
+
TextContent(
|
405
|
+
type="text",
|
406
|
+
text=json.dumps(
|
407
|
+
self.registry_data.get("registry_info", {}), indent=2
|
408
|
+
),
|
409
|
+
)
|
410
|
+
]
|
411
|
+
elif uri.startswith("ai-registry://use-case/"):
|
412
|
+
use_case_id = int(uri.split("/")[-1])
|
413
|
+
use_case = self._get_use_case_by_id(use_case_id)
|
414
|
+
if use_case:
|
415
|
+
return [
|
416
|
+
TextContent(type="text", text=json.dumps(use_case, indent=2))
|
417
|
+
]
|
418
|
+
else:
|
419
|
+
raise ValueError(f"Use case not found: {use_case_id}")
|
420
|
+
else:
|
421
|
+
raise ValueError(f"Unknown resource: {uri}")
|
422
|
+
|
423
|
+
# Tool implementation methods
|
424
|
+
|
425
|
+
def _search_use_cases(
|
426
|
+
self,
|
427
|
+
query: str,
|
428
|
+
domains: Optional[List[str]] = None,
|
429
|
+
methods: Optional[List[str]] = None,
|
430
|
+
limit: int = 10,
|
431
|
+
) -> Dict[str, Any]:
|
432
|
+
"""Search use cases with filters."""
|
433
|
+
use_cases = self.registry_data.get("use_cases", [])
|
434
|
+
results = []
|
435
|
+
|
436
|
+
for use_case in use_cases:
|
437
|
+
# Simple text search
|
438
|
+
score = 0.0
|
439
|
+
search_text = (
|
440
|
+
f"{use_case.get('name', '')} {use_case.get('description', '')}".lower()
|
441
|
+
)
|
442
|
+
if query.lower() in search_text:
|
443
|
+
score += 0.8
|
444
|
+
|
445
|
+
# Domain filter
|
446
|
+
if domains and use_case.get("application_domain") in domains:
|
447
|
+
score += 0.3
|
448
|
+
|
449
|
+
# Method filter
|
450
|
+
if methods:
|
451
|
+
use_case_methods = use_case.get("ai_methods", [])
|
452
|
+
if any(method in use_case_methods for method in methods):
|
453
|
+
score += 0.2
|
454
|
+
|
455
|
+
if score > 0:
|
456
|
+
results.append({"use_case": use_case, "score": score})
|
457
|
+
|
458
|
+
# Sort by score and limit results
|
459
|
+
results.sort(key=lambda x: x["score"], reverse=True)
|
460
|
+
return {"results": results[:limit], "count": len(results), "query": query}
|
461
|
+
|
462
|
+
def _filter_by_domain(
|
463
|
+
self, domain: str, status: Optional[str] = None, limit: int = 20
|
464
|
+
) -> Dict[str, Any]:
|
465
|
+
"""Filter use cases by domain."""
|
466
|
+
use_cases = self.registry_data.get("use_cases", [])
|
467
|
+
filtered = []
|
468
|
+
|
469
|
+
for use_case in use_cases:
|
470
|
+
if use_case.get("application_domain") == domain:
|
471
|
+
if not status or use_case.get("status") == status:
|
472
|
+
filtered.append(use_case)
|
473
|
+
|
474
|
+
return {"domain": domain, "count": len(filtered), "use_cases": filtered[:limit]}
|
475
|
+
|
476
|
+
def _get_use_case_details(self, use_case_id: int) -> Dict[str, Any]:
|
477
|
+
"""Get detailed information for a specific use case."""
|
478
|
+
use_case = self._get_use_case_by_id(use_case_id)
|
479
|
+
if use_case:
|
480
|
+
return {
|
481
|
+
"use_case": use_case,
|
482
|
+
"similar_cases": [], # Could implement similarity search
|
483
|
+
}
|
484
|
+
else:
|
485
|
+
raise ValueError(f"Use case not found: {use_case_id}")
|
486
|
+
|
487
|
+
def _analyze_domain_trends(
|
488
|
+
self, domain: str, include_details: bool = False
|
489
|
+
) -> Dict[str, Any]:
|
490
|
+
"""Analyze trends within a specific domain."""
|
491
|
+
use_cases = [
|
492
|
+
uc
|
493
|
+
for uc in self.registry_data.get("use_cases", [])
|
494
|
+
if uc.get("application_domain") == domain
|
495
|
+
]
|
496
|
+
|
497
|
+
# Analyze methods and statuses
|
498
|
+
methods = {}
|
499
|
+
statuses = {}
|
500
|
+
|
501
|
+
for use_case in use_cases:
|
502
|
+
for method in use_case.get("ai_methods", []):
|
503
|
+
methods[method] = methods.get(method, 0) + 1
|
504
|
+
|
505
|
+
status = use_case.get("status", "Unknown")
|
506
|
+
statuses[status] = statuses.get(status, 0) + 1
|
507
|
+
|
508
|
+
return {
|
509
|
+
"domain": domain,
|
510
|
+
"total_use_cases": len(use_cases),
|
511
|
+
"popular_methods": sorted(
|
512
|
+
methods.items(), key=lambda x: x[1], reverse=True
|
513
|
+
),
|
514
|
+
"status_distribution": statuses,
|
515
|
+
"examples": use_cases[:3] if include_details else [],
|
516
|
+
}
|
517
|
+
|
518
|
+
def _recommend_similar(
|
519
|
+
self,
|
520
|
+
use_case_id: int,
|
521
|
+
similarity_factors: Optional[List[str]] = None,
|
522
|
+
limit: int = 5,
|
523
|
+
) -> Dict[str, Any]:
|
524
|
+
"""Find similar use cases."""
|
525
|
+
reference_case = self._get_use_case_by_id(use_case_id)
|
526
|
+
if not reference_case:
|
527
|
+
raise ValueError(f"Use case not found: {use_case_id}")
|
528
|
+
|
529
|
+
similar_cases = []
|
530
|
+
for use_case in self.registry_data.get("use_cases", []):
|
531
|
+
if use_case.get("use_case_id") != use_case_id:
|
532
|
+
similarity = self._calculate_similarity(reference_case, use_case)
|
533
|
+
if similarity > 0.3: # Threshold
|
534
|
+
similar_cases.append(
|
535
|
+
{"use_case": use_case, "similarity": similarity}
|
536
|
+
)
|
537
|
+
|
538
|
+
similar_cases.sort(key=lambda x: x["similarity"], reverse=True)
|
539
|
+
return {
|
540
|
+
"reference_use_case_id": use_case_id,
|
541
|
+
"similar_cases": similar_cases[:limit],
|
542
|
+
}
|
543
|
+
|
544
|
+
def _estimate_complexity(
|
545
|
+
self, use_case_id: int, organization_context: Optional[Dict] = None
|
546
|
+
) -> Dict[str, Any]:
|
547
|
+
"""Estimate implementation complexity."""
|
548
|
+
use_case = self._get_use_case_by_id(use_case_id)
|
549
|
+
if not use_case:
|
550
|
+
raise ValueError(f"Use case not found: {use_case_id}")
|
551
|
+
|
552
|
+
# Simple complexity scoring
|
553
|
+
score = 0
|
554
|
+
factors = []
|
555
|
+
|
556
|
+
methods = use_case.get("ai_methods", [])
|
557
|
+
for method in methods:
|
558
|
+
if "Deep Learning" in method:
|
559
|
+
score += 4
|
560
|
+
factors.append(f"AI Method: {method} (+4)")
|
561
|
+
elif "Machine Learning" in method:
|
562
|
+
score += 2
|
563
|
+
factors.append(f"AI Method: {method} (+2)")
|
564
|
+
|
565
|
+
domain = use_case.get("application_domain", "")
|
566
|
+
if domain == "Healthcare":
|
567
|
+
score += 4
|
568
|
+
factors.append(f"Domain: {domain} (+4)")
|
569
|
+
|
570
|
+
challenges = use_case.get("challenges", "")
|
571
|
+
if "privacy" in challenges.lower():
|
572
|
+
score += 3
|
573
|
+
factors.append("Challenge: privacy (+3)")
|
574
|
+
|
575
|
+
complexity_level = "Low" if score < 5 else "Medium" if score < 10 else "High"
|
576
|
+
|
577
|
+
return {
|
578
|
+
"use_case_id": use_case_id,
|
579
|
+
"complexity_score": score,
|
580
|
+
"complexity_level": complexity_level,
|
581
|
+
"scoring_factors": factors,
|
582
|
+
"estimates": {
|
583
|
+
"timeline": "6-12 months" if score < 8 else "12-18 months",
|
584
|
+
"team_size": "3-8 people" if score < 8 else "8-15 people",
|
585
|
+
"budget_category": "medium" if score < 8 else "high",
|
586
|
+
},
|
587
|
+
}
|
588
|
+
|
589
|
+
def _suggest_implementation_path(
|
590
|
+
self, use_case_id: int, organization_context: Optional[Dict] = None
|
591
|
+
) -> Dict[str, Any]:
|
592
|
+
"""Suggest implementation roadmap."""
|
593
|
+
use_case = self._get_use_case_by_id(use_case_id)
|
594
|
+
if not use_case:
|
595
|
+
raise ValueError(f"Use case not found: {use_case_id}")
|
596
|
+
|
597
|
+
phases = [
|
598
|
+
{"phase": 1, "name": "Foundation & Planning", "duration": "2-4 weeks"},
|
599
|
+
{"phase": 2, "name": "Proof of Concept", "duration": "6-8 weeks"},
|
600
|
+
{"phase": 3, "name": "Advanced Development", "duration": "12-24 weeks"},
|
601
|
+
{"phase": 4, "name": "Deployment & Monitoring", "duration": "4-8 weeks"},
|
602
|
+
]
|
603
|
+
|
604
|
+
recommendations = [
|
605
|
+
"Start with a well-defined proof of concept",
|
606
|
+
"Ensure data quality and availability early",
|
607
|
+
"Plan for change management and user adoption",
|
608
|
+
]
|
609
|
+
|
610
|
+
# Domain-specific recommendations
|
611
|
+
domain = use_case.get("application_domain", "")
|
612
|
+
if domain == "Healthcare":
|
613
|
+
recommendations.extend(
|
614
|
+
[
|
615
|
+
"Ensure HIPAA compliance and data privacy measures",
|
616
|
+
"Plan for regulatory approval processes",
|
617
|
+
"Consider partnering with medical AI specialists",
|
618
|
+
]
|
619
|
+
)
|
620
|
+
|
621
|
+
return {
|
622
|
+
"use_case_id": use_case_id,
|
623
|
+
"use_case_name": use_case.get("name", ""),
|
624
|
+
"implementation_phases": phases,
|
625
|
+
"key_recommendations": recommendations,
|
626
|
+
}
|
627
|
+
|
628
|
+
def _filter_by_method(
|
629
|
+
self, method: str, min_maturity: Optional[str] = None, limit: int = 15
|
630
|
+
) -> Dict[str, Any]:
|
631
|
+
"""Filter use cases by AI method."""
|
632
|
+
use_cases = self.registry_data.get("use_cases", [])
|
633
|
+
filtered = []
|
634
|
+
|
635
|
+
for use_case in use_cases:
|
636
|
+
methods = use_case.get("ai_methods", [])
|
637
|
+
if any(method.lower() in m.lower() for m in methods):
|
638
|
+
if not min_maturity or self._check_maturity(
|
639
|
+
use_case.get("status", ""), min_maturity
|
640
|
+
):
|
641
|
+
filtered.append(use_case)
|
642
|
+
|
643
|
+
return {"method": method, "count": len(filtered), "use_cases": filtered[:limit]}
|
644
|
+
|
645
|
+
# Helper methods
|
646
|
+
|
647
|
+
def _get_use_case_by_id(self, use_case_id: int) -> Optional[Dict[str, Any]]:
|
648
|
+
"""Get use case by ID."""
|
649
|
+
for use_case in self.registry_data.get("use_cases", []):
|
650
|
+
if use_case.get("use_case_id") == use_case_id:
|
651
|
+
return use_case
|
652
|
+
return None
|
653
|
+
|
654
|
+
def _calculate_similarity(
|
655
|
+
self, case1: Dict[str, Any], case2: Dict[str, Any]
|
656
|
+
) -> float:
|
657
|
+
"""Calculate similarity between two use cases."""
|
658
|
+
score = 0.0
|
659
|
+
|
660
|
+
# Domain similarity
|
661
|
+
if case1.get("application_domain") == case2.get("application_domain"):
|
662
|
+
score += 0.4
|
663
|
+
|
664
|
+
# Method similarity
|
665
|
+
methods1 = set(case1.get("ai_methods", []))
|
666
|
+
methods2 = set(case2.get("ai_methods", []))
|
667
|
+
if methods1 and methods2:
|
668
|
+
overlap = len(methods1.intersection(methods2))
|
669
|
+
total = len(methods1.union(methods2))
|
670
|
+
score += 0.6 * (overlap / total)
|
671
|
+
|
672
|
+
return score
|
673
|
+
|
674
|
+
def _check_maturity(self, status: str, min_maturity: str) -> bool:
|
675
|
+
"""Check if status meets minimum maturity requirement."""
|
676
|
+
maturity_order = ["Idea", "PoC", "PoB", "Production"]
|
677
|
+
try:
|
678
|
+
status_level = maturity_order.index(status)
|
679
|
+
min_level = maturity_order.index(min_maturity)
|
680
|
+
return status_level >= min_level
|
681
|
+
except ValueError:
|
682
|
+
return True # If unknown status, include it
|
683
|
+
|
684
|
+
async def run_stdio(self):
|
685
|
+
"""Run the server with stdio transport."""
|
686
|
+
from mcp.server.stdio import stdio_server
|
687
|
+
|
688
|
+
async with stdio_server() as (read_stream, write_stream):
|
689
|
+
await self.server.run(
|
690
|
+
read_stream, write_stream, self.server.create_initialization_options()
|
691
|
+
)
|
692
|
+
|
693
|
+
|
694
|
+
async def main():
|
695
|
+
"""Main entry point for running the AI Registry MCP server."""
|
696
|
+
# Get registry file from environment or use default
|
697
|
+
registry_file = os.environ.get(
|
698
|
+
"REGISTRY_FILE", "research/combined_ai_registry.json"
|
699
|
+
)
|
700
|
+
|
701
|
+
server = AIRegistryServer(registry_file)
|
702
|
+
await server.run_stdio()
|
703
|
+
|
704
|
+
|
705
|
+
if __name__ == "__main__":
|
706
|
+
asyncio.run(main())
|
707
|
+
|
708
|
+
|
709
|
+
# For module execution
|
710
|
+
def run_server():
|
711
|
+
"""Entry point for python -m kailash.mcp.ai_registry_server"""
|
712
|
+
asyncio.run(main())
|