naas-abi-core 1.4.1__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.
Files changed (124) hide show
  1. assets/favicon.ico +0 -0
  2. assets/logo.png +0 -0
  3. naas_abi_core/__init__.py +1 -0
  4. naas_abi_core/apps/api/api.py +245 -0
  5. naas_abi_core/apps/api/api_test.py +281 -0
  6. naas_abi_core/apps/api/openapi_doc.py +144 -0
  7. naas_abi_core/apps/mcp/Dockerfile.mcp +35 -0
  8. naas_abi_core/apps/mcp/mcp_server.py +243 -0
  9. naas_abi_core/apps/mcp/mcp_server_test.py +163 -0
  10. naas_abi_core/apps/terminal_agent/main.py +555 -0
  11. naas_abi_core/apps/terminal_agent/terminal_style.py +175 -0
  12. naas_abi_core/engine/Engine.py +87 -0
  13. naas_abi_core/engine/EngineProxy.py +109 -0
  14. naas_abi_core/engine/Engine_test.py +6 -0
  15. naas_abi_core/engine/IEngine.py +91 -0
  16. naas_abi_core/engine/conftest.py +45 -0
  17. naas_abi_core/engine/engine_configuration/EngineConfiguration.py +216 -0
  18. naas_abi_core/engine/engine_configuration/EngineConfiguration_Deploy.py +7 -0
  19. naas_abi_core/engine/engine_configuration/EngineConfiguration_GenericLoader.py +49 -0
  20. naas_abi_core/engine/engine_configuration/EngineConfiguration_ObjectStorageService.py +159 -0
  21. naas_abi_core/engine/engine_configuration/EngineConfiguration_ObjectStorageService_test.py +26 -0
  22. naas_abi_core/engine/engine_configuration/EngineConfiguration_SecretService.py +138 -0
  23. naas_abi_core/engine/engine_configuration/EngineConfiguration_SecretService_test.py +74 -0
  24. naas_abi_core/engine/engine_configuration/EngineConfiguration_TripleStoreService.py +224 -0
  25. naas_abi_core/engine/engine_configuration/EngineConfiguration_TripleStoreService_test.py +109 -0
  26. naas_abi_core/engine/engine_configuration/EngineConfiguration_VectorStoreService.py +76 -0
  27. naas_abi_core/engine/engine_configuration/EngineConfiguration_VectorStoreService_test.py +33 -0
  28. naas_abi_core/engine/engine_configuration/EngineConfiguration_test.py +9 -0
  29. naas_abi_core/engine/engine_configuration/utils/PydanticModelValidator.py +15 -0
  30. naas_abi_core/engine/engine_loaders/EngineModuleLoader.py +302 -0
  31. naas_abi_core/engine/engine_loaders/EngineOntologyLoader.py +16 -0
  32. naas_abi_core/engine/engine_loaders/EngineServiceLoader.py +47 -0
  33. naas_abi_core/integration/__init__.py +7 -0
  34. naas_abi_core/integration/integration.py +28 -0
  35. naas_abi_core/models/Model.py +198 -0
  36. naas_abi_core/models/OpenRouter.py +18 -0
  37. naas_abi_core/models/OpenRouter_test.py +36 -0
  38. naas_abi_core/module/Module.py +252 -0
  39. naas_abi_core/module/ModuleAgentLoader.py +50 -0
  40. naas_abi_core/module/ModuleUtils.py +20 -0
  41. naas_abi_core/modules/templatablesparqlquery/README.md +196 -0
  42. naas_abi_core/modules/templatablesparqlquery/__init__.py +39 -0
  43. naas_abi_core/modules/templatablesparqlquery/ontologies/TemplatableSparqlQueryOntology.ttl +116 -0
  44. naas_abi_core/modules/templatablesparqlquery/workflows/GenericWorkflow.py +48 -0
  45. naas_abi_core/modules/templatablesparqlquery/workflows/TemplatableSparqlQueryLoader.py +192 -0
  46. naas_abi_core/pipeline/__init__.py +6 -0
  47. naas_abi_core/pipeline/pipeline.py +70 -0
  48. naas_abi_core/services/__init__.py +0 -0
  49. naas_abi_core/services/agent/Agent.py +1619 -0
  50. naas_abi_core/services/agent/AgentMemory_test.py +28 -0
  51. naas_abi_core/services/agent/Agent_test.py +214 -0
  52. naas_abi_core/services/agent/IntentAgent.py +1179 -0
  53. naas_abi_core/services/agent/IntentAgent_test.py +139 -0
  54. naas_abi_core/services/agent/beta/Embeddings.py +181 -0
  55. naas_abi_core/services/agent/beta/IntentMapper.py +120 -0
  56. naas_abi_core/services/agent/beta/LocalModel.py +88 -0
  57. naas_abi_core/services/agent/beta/VectorStore.py +89 -0
  58. naas_abi_core/services/agent/test_agent_memory.py +278 -0
  59. naas_abi_core/services/agent/test_postgres_integration.py +145 -0
  60. naas_abi_core/services/cache/CacheFactory.py +31 -0
  61. naas_abi_core/services/cache/CachePort.py +63 -0
  62. naas_abi_core/services/cache/CacheService.py +246 -0
  63. naas_abi_core/services/cache/CacheService_test.py +85 -0
  64. naas_abi_core/services/cache/adapters/secondary/CacheFSAdapter.py +39 -0
  65. naas_abi_core/services/object_storage/ObjectStorageFactory.py +57 -0
  66. naas_abi_core/services/object_storage/ObjectStoragePort.py +47 -0
  67. naas_abi_core/services/object_storage/ObjectStorageService.py +41 -0
  68. naas_abi_core/services/object_storage/adapters/secondary/ObjectStorageSecondaryAdapterFS.py +52 -0
  69. naas_abi_core/services/object_storage/adapters/secondary/ObjectStorageSecondaryAdapterNaas.py +131 -0
  70. naas_abi_core/services/object_storage/adapters/secondary/ObjectStorageSecondaryAdapterS3.py +171 -0
  71. naas_abi_core/services/ontology/OntologyPorts.py +36 -0
  72. naas_abi_core/services/ontology/OntologyService.py +17 -0
  73. naas_abi_core/services/ontology/adaptors/secondary/OntologyService_SecondaryAdaptor_NERPort.py +37 -0
  74. naas_abi_core/services/secret/Secret.py +138 -0
  75. naas_abi_core/services/secret/SecretPorts.py +45 -0
  76. naas_abi_core/services/secret/Secret_test.py +65 -0
  77. naas_abi_core/services/secret/adaptors/secondary/Base64Secret.py +57 -0
  78. naas_abi_core/services/secret/adaptors/secondary/Base64Secret_test.py +39 -0
  79. naas_abi_core/services/secret/adaptors/secondary/NaasSecret.py +88 -0
  80. naas_abi_core/services/secret/adaptors/secondary/NaasSecret_test.py +25 -0
  81. naas_abi_core/services/secret/adaptors/secondary/dotenv_secret_secondaryadaptor.py +29 -0
  82. naas_abi_core/services/triple_store/TripleStoreFactory.py +116 -0
  83. naas_abi_core/services/triple_store/TripleStorePorts.py +223 -0
  84. naas_abi_core/services/triple_store/TripleStoreService.py +419 -0
  85. naas_abi_core/services/triple_store/adaptors/secondary/AWSNeptune.py +1300 -0
  86. naas_abi_core/services/triple_store/adaptors/secondary/AWSNeptune_test.py +284 -0
  87. naas_abi_core/services/triple_store/adaptors/secondary/Oxigraph.py +597 -0
  88. naas_abi_core/services/triple_store/adaptors/secondary/Oxigraph_test.py +1474 -0
  89. naas_abi_core/services/triple_store/adaptors/secondary/TripleStoreService__SecondaryAdaptor__Filesystem.py +223 -0
  90. naas_abi_core/services/triple_store/adaptors/secondary/TripleStoreService__SecondaryAdaptor__ObjectStorage.py +234 -0
  91. naas_abi_core/services/triple_store/adaptors/secondary/base/TripleStoreService__SecondaryAdaptor__FileBase.py +18 -0
  92. naas_abi_core/services/vector_store/IVectorStorePort.py +101 -0
  93. naas_abi_core/services/vector_store/IVectorStorePort_test.py +189 -0
  94. naas_abi_core/services/vector_store/VectorStoreFactory.py +47 -0
  95. naas_abi_core/services/vector_store/VectorStoreService.py +171 -0
  96. naas_abi_core/services/vector_store/VectorStoreService_test.py +185 -0
  97. naas_abi_core/services/vector_store/__init__.py +13 -0
  98. naas_abi_core/services/vector_store/adapters/QdrantAdapter.py +251 -0
  99. naas_abi_core/services/vector_store/adapters/QdrantAdapter_test.py +57 -0
  100. naas_abi_core/tests/test_services_imports.py +69 -0
  101. naas_abi_core/utils/Expose.py +55 -0
  102. naas_abi_core/utils/Graph.py +182 -0
  103. naas_abi_core/utils/JSON.py +49 -0
  104. naas_abi_core/utils/LazyLoader.py +44 -0
  105. naas_abi_core/utils/Logger.py +12 -0
  106. naas_abi_core/utils/OntologyReasoner.py +141 -0
  107. naas_abi_core/utils/OntologyYaml.py +681 -0
  108. naas_abi_core/utils/SPARQL.py +256 -0
  109. naas_abi_core/utils/Storage.py +33 -0
  110. naas_abi_core/utils/StorageUtils.py +398 -0
  111. naas_abi_core/utils/String.py +52 -0
  112. naas_abi_core/utils/Workers.py +114 -0
  113. naas_abi_core/utils/__init__.py +0 -0
  114. naas_abi_core/utils/onto2py/README.md +0 -0
  115. naas_abi_core/utils/onto2py/__init__.py +10 -0
  116. naas_abi_core/utils/onto2py/__main__.py +29 -0
  117. naas_abi_core/utils/onto2py/onto2py.py +611 -0
  118. naas_abi_core/utils/onto2py/tests/ttl2py_test.py +271 -0
  119. naas_abi_core/workflow/__init__.py +5 -0
  120. naas_abi_core/workflow/workflow.py +48 -0
  121. naas_abi_core-1.4.1.dist-info/METADATA +630 -0
  122. naas_abi_core-1.4.1.dist-info/RECORD +124 -0
  123. naas_abi_core-1.4.1.dist-info/WHEEL +4 -0
  124. naas_abi_core-1.4.1.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,35 @@
1
+ # Production Dockerfile for ABI MCP Server
2
+ FROM python:3.13-slim
3
+
4
+ WORKDIR /app
5
+
6
+ # Install system dependencies
7
+ RUN apt-get update && apt-get install -y \
8
+ curl \
9
+ && rm -rf /var/lib/apt/lists/*
10
+
11
+
12
+ # Copy application code
13
+ COPY libs/naas-abi-core/naas_abi_core/apps/mcp/mcp_server.py ./
14
+ # COPY lib/ ./lib/
15
+ # COPY src/ ./src/
16
+ COPY .env* ./
17
+
18
+ # Copy requirements and install Python dependencies
19
+ COPY README.md ./
20
+ # COPY pyproject.toml ./
21
+ RUN pip install fastmcp>=2.11.2 python-dotenv
22
+
23
+ # Set environment for production MCP deployment
24
+ ENV MCP_TRANSPORT=sse
25
+ ENV ABI_API_BASE=http://api:9879
26
+
27
+ # Expose the MCP HTTP server port
28
+ EXPOSE 8000
29
+
30
+ # Health check
31
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
32
+ CMD curl -f http://localhost:8000/ || exit 1
33
+
34
+ # Run the MCP server
35
+ CMD ["python", "mcp_server.py"]
@@ -0,0 +1,243 @@
1
+ """
2
+ ABI MCP Server - Lightweight HTTP-based Implementation
3
+ Exposes ABI agents as MCP tools with fast startup (no heavy imports)
4
+ """
5
+
6
+ import asyncio
7
+ import os
8
+ import re
9
+ from typing import Any, Dict, List
10
+
11
+ import httpx
12
+ from dotenv import load_dotenv
13
+ from mcp.server.fastmcp import FastMCP
14
+
15
+ # Load environment variables from .env file if present
16
+ load_dotenv()
17
+
18
+ # Initialize FastMCP server
19
+ mcp = FastMCP("abi")
20
+
21
+ # Constants - Default to localhost for development, can be overridden by env var
22
+ ABI_API_BASE = os.environ.get("ABI_API_BASE", "http://localhost:9879")
23
+
24
+
25
+ def get_api_key() -> str:
26
+ """Get the API key from environment variables"""
27
+ api_key = os.environ.get("ABI_API_KEY")
28
+ if not api_key:
29
+ print("āŒ ABI_API_KEY not found in environment")
30
+ print("šŸ“ Please add it to your .env file:")
31
+ print(" ABI_API_KEY=your_key_here")
32
+ exit(1)
33
+ return api_key
34
+
35
+
36
+ async def fetch_openapi_spec() -> Dict[str, Any]:
37
+ """Fetch the OpenAPI specification to discover available agents"""
38
+ try:
39
+ async with httpx.AsyncClient(timeout=10.0) as client:
40
+ response = await client.get(f"{ABI_API_BASE}/openapi.json")
41
+ response.raise_for_status()
42
+ return response.json()
43
+ except Exception as e:
44
+ print(f"āŒ Failed to fetch OpenAPI spec: {e}")
45
+ return {}
46
+
47
+
48
+ def extract_agents_from_openapi(openapi_spec: Dict[str, Any]) -> List[Dict[str, str]]:
49
+ """Extract agent information from OpenAPI specification"""
50
+ agents = []
51
+ paths = openapi_spec.get("paths", {})
52
+
53
+ for path, methods in paths.items():
54
+ # Look for agent completion endpoints
55
+ if "/agents/" in path and path.endswith("/completion"):
56
+ # Extract agent name from path like /agents/{agent_name}/completion
57
+ agent_name = path.split("/agents/")[1].split("/completion")[0]
58
+
59
+ # Get description from POST method
60
+ post_method = methods.get("post", {})
61
+ description = post_method.get("summary", f"{agent_name} agent completion")
62
+
63
+ agents.append(
64
+ {
65
+ "name": agent_name,
66
+ "description": description,
67
+ "function_name": agent_name_to_function_name(agent_name),
68
+ }
69
+ )
70
+
71
+ return agents
72
+
73
+
74
+ def agent_name_to_function_name(agent_name: str) -> str:
75
+ """Convert agent name to valid Python function name"""
76
+ # Replace spaces and special chars with underscores, convert to lowercase
77
+ function_name = re.sub(r"[^a-zA-Z0-9_]", "_", agent_name.lower())
78
+ # Remove multiple consecutive underscores
79
+ function_name = re.sub(r"_+", "_", function_name)
80
+ # Remove leading/trailing underscores
81
+ function_name = function_name.strip("_")
82
+ # Ensure it doesn't start with a number
83
+ if function_name and function_name[0].isdigit():
84
+ function_name = f"agent_{function_name}"
85
+ return function_name or "unknown_agent"
86
+
87
+
88
+ async def call_abi_agent_http(agent_name: str, prompt: str, thread_id: int = 1) -> str:
89
+ """Call ABI agents via HTTP to avoid heavy module imports"""
90
+ try:
91
+ headers = {
92
+ "Authorization": f"Bearer {get_api_key()}",
93
+ "Content-Type": "application/json",
94
+ "User-Agent": "ABI-MCP/1.0",
95
+ }
96
+
97
+ data = {"prompt": prompt, "thread_id": thread_id}
98
+
99
+ url = f"{ABI_API_BASE}/agents/{agent_name}/completion"
100
+
101
+ async with httpx.AsyncClient(timeout=30.0) as client:
102
+ response = await client.post(url, json=data, headers=headers)
103
+ response.raise_for_status()
104
+ return response.text.strip('"') # Remove JSON quotes
105
+
106
+ except httpx.ConnectError:
107
+ return f"āŒ ABI API server not running at {ABI_API_BASE}. Please start it first with: uv run api"
108
+ except httpx.TimeoutException:
109
+ return f"ā±ļø Timeout calling {agent_name} agent. The agent might be processing a complex request."
110
+ except httpx.HTTPStatusError as e:
111
+ if e.response.status_code == 401:
112
+ return (
113
+ "šŸ”’ Authentication failed. Check your ABI_API_KEY environment variable."
114
+ )
115
+ elif e.response.status_code == 404:
116
+ return f"ā“ Agent '{agent_name}' not found. Please check available agents via OpenAPI spec."
117
+ else:
118
+ return f"āŒ HTTP {e.response.status_code} error calling {agent_name} agent: {e.response.text}"
119
+ except Exception as e:
120
+ return f"āŒ Error calling {agent_name} agent: {str(e)}"
121
+
122
+
123
+ def create_agent_function(agent_name: str, description: str):
124
+ """Create a dynamic agent function"""
125
+
126
+ async def agent_function(prompt: str, thread_id: int = 1) -> str:
127
+ return await call_abi_agent_http(agent_name, prompt, thread_id)
128
+
129
+ # Set function metadata
130
+ agent_function.__name__ = agent_name_to_function_name(agent_name)
131
+ agent_function.__doc__ = f"""{description}
132
+
133
+ Args:
134
+ prompt: Your question or request for the {agent_name} agent
135
+ thread_id: Thread ID for conversation context (default: 1)
136
+ """
137
+
138
+ return agent_function
139
+
140
+
141
+ async def wait_for_api():
142
+ """Wait for the API to be available before starting"""
143
+ max_retries = 30 # Wait up to 5 minutes (30 * 10 seconds)
144
+ retry_delay = 10 # seconds
145
+
146
+ for attempt in range(max_retries):
147
+ try:
148
+ async with httpx.AsyncClient(timeout=5.0) as client:
149
+ response = await client.get(f"{ABI_API_BASE}")
150
+ if response.status_code == 200:
151
+ print("āœ… API is ready!")
152
+ return True
153
+ except Exception:
154
+ pass
155
+
156
+ if attempt < max_retries - 1:
157
+ print(
158
+ f"ā³ Waiting for API to be ready... (attempt {attempt + 1}/{max_retries})"
159
+ )
160
+ await asyncio.sleep(retry_delay)
161
+
162
+ print("āŒ API did not become ready in time")
163
+ return False
164
+
165
+
166
+ async def register_agents_dynamically():
167
+ """Discover and register agents from OpenAPI specification"""
168
+ print("šŸ” Discovering agents from OpenAPI specification...")
169
+
170
+ # Wait for API to be ready if not running locally
171
+ if not ABI_API_BASE.startswith("http://localhost"):
172
+ api_ready = await wait_for_api()
173
+ if not api_ready:
174
+ print("āš ļø API not ready, using fallback configuration")
175
+ return
176
+
177
+ openapi_spec = await fetch_openapi_spec()
178
+ if not openapi_spec:
179
+ print("āš ļø Failed to fetch OpenAPI spec, falling back to basic agents")
180
+ return
181
+
182
+ agents = extract_agents_from_openapi(openapi_spec)
183
+
184
+ if not agents:
185
+ print("āš ļø No agents found in OpenAPI spec")
186
+ return
187
+
188
+ print(f"šŸ“” Found {len(agents)} agents:")
189
+
190
+ for agent_info in agents:
191
+ agent_name = agent_info["name"]
192
+ description = agent_info["description"]
193
+ function_name = agent_info["function_name"]
194
+
195
+ print(f" • {agent_name} -> {function_name}()")
196
+
197
+ # Create and register the agent function
198
+ agent_function = create_agent_function(agent_name, description)
199
+ mcp.tool()(agent_function)
200
+
201
+ print("āœ… All agents registered successfully!")
202
+
203
+
204
+ async def setup():
205
+ """Setup function to initialize the server"""
206
+ # Quick startup - no heavy imports!
207
+ print("šŸš€ Starting lightweight ABI MCP Server...")
208
+ print(f"šŸ“” Will connect to ABI API at: {ABI_API_BASE}")
209
+
210
+ # Validate API key exists
211
+ get_api_key()
212
+ print("šŸ”‘ API key loaded successfully")
213
+
214
+ # Dynamically discover and register agents
215
+ await register_agents_dynamically()
216
+
217
+
218
+ def run():
219
+ """Entry point for the script"""
220
+ # Run setup first
221
+ asyncio.run(setup())
222
+
223
+ # Determine transport type from environment
224
+ transport = os.environ.get("MCP_TRANSPORT", "stdio")
225
+
226
+ if transport == "sse":
227
+ # SSE transport for web deployment
228
+ print(
229
+ "🌐 Starting MCP server with SSE (Server-Sent Events) transport on port 8000"
230
+ )
231
+ mcp.run(transport="sse")
232
+ elif transport == "http":
233
+ # HTTP transport using streamable-http
234
+ print("🌐 Starting MCP server with streamable HTTP transport")
235
+ mcp.run(transport="streamable-http")
236
+ else:
237
+ # STDIO transport for local Claude Desktop integration
238
+ print("šŸ“‹ Starting MCP server with STDIO transport")
239
+ mcp.run(transport="stdio")
240
+
241
+
242
+ if __name__ == "__main__":
243
+ run()
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Test script to validate MCP server functionality
4
+ """
5
+
6
+ import asyncio
7
+ import httpx
8
+ import os
9
+ import sys
10
+ from typing import Dict, Any
11
+
12
+ # Test configuration
13
+ API_BASE = os.environ.get("ABI_API_BASE", "http://localhost:9879")
14
+ API_KEY = os.environ.get("ABI_API_KEY", "")
15
+
16
+ async def test_api_health() -> bool:
17
+ """Test if the API is healthy"""
18
+ print(f"šŸ” Testing API health at {API_BASE}...")
19
+ try:
20
+ async with httpx.AsyncClient(timeout=5.0) as client:
21
+ response = await client.get(f"{API_BASE}/openapi.json")
22
+ if response.status_code == 200:
23
+ print("āœ… API is healthy")
24
+ return True
25
+ else:
26
+ print(f"āŒ API returned status {response.status_code}")
27
+ return False
28
+ except Exception as e:
29
+ print(f"āŒ Failed to connect to API: {e}")
30
+ return False
31
+
32
+ async def test_openapi_spec() -> Dict[str, Any]:
33
+ """Test if OpenAPI spec is accessible and contains agents"""
34
+ print(f"\nšŸ” Fetching OpenAPI spec from {API_BASE}/openapi.json...")
35
+ try:
36
+ async with httpx.AsyncClient(timeout=10.0) as client:
37
+ response = await client.get(f"{API_BASE}/openapi.json")
38
+ response.raise_for_status()
39
+ spec = response.json()
40
+ print("āœ… OpenAPI spec fetched successfully")
41
+ return spec
42
+ except Exception as e:
43
+ print(f"āŒ Failed to fetch OpenAPI spec: {e}")
44
+ return {}
45
+
46
+ def analyze_agents(openapi_spec: Dict[str, Any]) -> list:
47
+ """Extract and display agent information from OpenAPI spec"""
48
+ print("\nšŸ“‹ Analyzing available agents...")
49
+ agents = []
50
+ paths = openapi_spec.get("paths", {})
51
+
52
+ for path, methods in paths.items():
53
+ if "/agents/" in path and path.endswith("/completion"):
54
+ agent_name = path.split("/agents/")[1].split("/completion")[0]
55
+ post_method = methods.get("post", {})
56
+ description = post_method.get("summary", f"{agent_name} agent")
57
+ agents.append({
58
+ "name": agent_name,
59
+ "description": description,
60
+ "endpoint": path
61
+ })
62
+
63
+ if agents:
64
+ print(f"āœ… Found {len(agents)} agents:")
65
+ for agent in agents:
66
+ print(f" • {agent['name']}: {agent['description']}")
67
+ else:
68
+ print("āŒ No agents found in OpenAPI spec")
69
+
70
+ return agents
71
+
72
+ async def test_agent_call(agent_name: str) -> bool:
73
+ """Test calling a specific agent"""
74
+ if not API_KEY:
75
+ print("\nāš ļø Skipping agent call test - ABI_API_KEY not set")
76
+ return False
77
+
78
+ print(f"\nšŸ” Testing agent call to '{agent_name}'...")
79
+ try:
80
+ headers = {
81
+ "Authorization": f"Bearer {API_KEY}",
82
+ "Content-Type": "application/json"
83
+ }
84
+ data = {
85
+ "prompt": "Hello, this is a test message. Please respond briefly.",
86
+ "thread_id": 1
87
+ }
88
+
89
+ async with httpx.AsyncClient(timeout=30.0) as client:
90
+ response = await client.post(
91
+ f"{API_BASE}/agents/{agent_name}/completion",
92
+ json=data,
93
+ headers=headers
94
+ )
95
+
96
+ if response.status_code == 200:
97
+ print(f"āœ… Successfully called {agent_name} agent")
98
+ print(f" Response: {response.text[:100]}...")
99
+ return True
100
+ else:
101
+ print(f"āŒ Agent call failed with status {response.status_code}")
102
+ print(f" Error: {response.text}")
103
+ return False
104
+
105
+ except Exception as e:
106
+ print(f"āŒ Failed to call agent: {e}")
107
+ return False
108
+
109
+ async def test_mcp_http_server() -> bool:
110
+ """Test if MCP server is running in HTTP mode"""
111
+ print("\nšŸ” Testing MCP HTTP server at http://localhost:3000...")
112
+ try:
113
+ async with httpx.AsyncClient(timeout=5.0) as client:
114
+ response = await client.get("http://localhost:3000/")
115
+ if response.status_code == 200:
116
+ print("āœ… MCP HTTP server is running")
117
+ return True
118
+ else:
119
+ print(f"āŒ MCP server returned status {response.status_code}")
120
+ return False
121
+ except Exception:
122
+ print("āš ļø MCP HTTP server not running (this is OK if testing STDIO mode)")
123
+ return False
124
+
125
+ async def main():
126
+ """Run all tests"""
127
+ print("šŸš€ MCP Server Validation Tests")
128
+ print("=" * 50)
129
+
130
+ # Test 1: API Health
131
+ api_healthy = await test_api_health()
132
+ if not api_healthy:
133
+ print("\nāš ļø API is not running. Please start it with: uv run api")
134
+ sys.exit(1)
135
+
136
+ # Test 2: OpenAPI Spec
137
+ openapi_spec = await test_openapi_spec()
138
+ if not openapi_spec:
139
+ sys.exit(1)
140
+
141
+ # Test 3: Analyze Agents
142
+ agents = analyze_agents(openapi_spec)
143
+ if not agents:
144
+ print("\nāš ļø No agents found. Check your API configuration.")
145
+ sys.exit(1)
146
+
147
+ # Test 4: Test Agent Call (if API key is set)
148
+ if agents:
149
+ # Test the first available agent
150
+ await test_agent_call(agents[0]["name"])
151
+
152
+ # Test 5: MCP HTTP Server (optional)
153
+ await test_mcp_http_server()
154
+
155
+ print("\n" + "=" * 50)
156
+ print("āœ… Validation complete!")
157
+ print("\nNext steps:")
158
+ print("1. Start MCP server locally: python mcp_server.py")
159
+ print("2. For HTTP mode: MCP_TRANSPORT=http python mcp_server.py")
160
+ print("3. For Claude Desktop integration, add to MCP settings")
161
+
162
+ if __name__ == "__main__":
163
+ asyncio.run(main())