sunholo 0.144.1__py3-none-any.whl → 0.144.2__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.
- sunholo/agents/fastapi/vac_routes.py +374 -134
- sunholo/mcp/extensible_mcp_server.py +271 -0
- sunholo/mcp/vac_mcp_server_fastmcp.py +74 -137
- sunholo/mcp/vac_tools.py +249 -0
- {sunholo-0.144.1.dist-info → sunholo-0.144.2.dist-info}/METADATA +1 -1
- {sunholo-0.144.1.dist-info → sunholo-0.144.2.dist-info}/RECORD +10 -8
- {sunholo-0.144.1.dist-info → sunholo-0.144.2.dist-info}/WHEEL +0 -0
- {sunholo-0.144.1.dist-info → sunholo-0.144.2.dist-info}/entry_points.txt +0 -0
- {sunholo-0.144.1.dist-info → sunholo-0.144.2.dist-info}/licenses/LICENSE.txt +0 -0
- {sunholo-0.144.1.dist-info → sunholo-0.144.2.dist-info}/top_level.txt +0 -0
@@ -58,7 +58,7 @@ except ImportError:
|
|
58
58
|
MCPClientManager = None
|
59
59
|
|
60
60
|
try:
|
61
|
-
from ...mcp.
|
61
|
+
from ...mcp.vac_mcp_server_fastmcp import VACMCPServer
|
62
62
|
except ImportError:
|
63
63
|
VACMCPServer = None
|
64
64
|
|
@@ -81,31 +81,205 @@ class VACRequest(BaseModel):
|
|
81
81
|
|
82
82
|
class VACRoutesFastAPI:
|
83
83
|
"""
|
84
|
-
FastAPI implementation of VAC routes with streaming support.
|
84
|
+
FastAPI implementation of VAC routes with streaming support and extensible MCP integration.
|
85
85
|
|
86
|
-
This class provides a FastAPI
|
87
|
-
|
86
|
+
This class provides a comprehensive FastAPI application with:
|
87
|
+
- VAC (Virtual Agent Computer) endpoints for AI chat and streaming
|
88
|
+
- OpenAI-compatible API endpoints
|
89
|
+
- Extensible MCP (Model Context Protocol) server integration for Claude Desktop/Code
|
90
|
+
- MCP client support for connecting to external MCP servers
|
91
|
+
- A2A (Agent-to-Agent) protocol support
|
92
|
+
- Server-Sent Events (SSE) streaming capabilities
|
93
|
+
|
94
|
+
## Key Features
|
95
|
+
|
96
|
+
### 1. VAC Endpoints
|
97
|
+
- `/vac/{vector_name}` - Non-streaming VAC responses
|
98
|
+
- `/vac/streaming/{vector_name}` - Plain text streaming responses
|
99
|
+
- `/vac/streaming/{vector_name}/sse` - Server-Sent Events streaming
|
100
|
+
|
101
|
+
### 2. OpenAI Compatible API
|
102
|
+
- `/openai/v1/chat/completions` - OpenAI-compatible chat completions
|
103
|
+
- Supports both streaming and non-streaming modes
|
104
|
+
|
105
|
+
### 3. MCP Integration
|
106
|
+
- **MCP Server**: Expose your VAC as MCP tools for Claude Desktop/Code
|
107
|
+
- **MCP Client**: Connect to external MCP servers and use their tools
|
108
|
+
- **Custom Tools**: Easily add your own MCP tools using decorators
|
109
|
+
|
110
|
+
### 4. A2A Agent Protocol
|
111
|
+
- Agent discovery and task execution
|
112
|
+
- Compatible with multi-agent workflows
|
113
|
+
|
114
|
+
## Basic Usage
|
88
115
|
|
89
|
-
Usage Example:
|
90
116
|
```python
|
91
117
|
from fastapi import FastAPI
|
92
118
|
from sunholo.agents.fastapi import VACRoutesFastAPI
|
93
119
|
|
94
120
|
app = FastAPI()
|
95
121
|
|
96
|
-
async def
|
97
|
-
#
|
98
|
-
|
122
|
+
async def my_stream_interpreter(question, vector_name, chat_history, callback, **kwargs):
|
123
|
+
# Your streaming VAC logic here
|
124
|
+
# Use callback.async_on_llm_new_token(token) for streaming
|
125
|
+
# Return final result with sources
|
126
|
+
return {"answer": "Response", "sources": []}
|
127
|
+
|
128
|
+
# Create VAC routes with MCP server enabled
|
129
|
+
vac_routes = VACRoutesFastAPI(
|
130
|
+
app=app,
|
131
|
+
stream_interpreter=my_stream_interpreter,
|
132
|
+
enable_mcp_server=True # Enable MCP server for Claude Desktop/Code
|
133
|
+
)
|
134
|
+
|
135
|
+
# Your FastAPI app now includes:
|
136
|
+
# - All VAC endpoints
|
137
|
+
# - MCP server at /mcp (for Claude Desktop/Code to connect)
|
138
|
+
# - Built-in VAC tools: vac_stream, vac_query, list_available_vacs, get_vac_info
|
139
|
+
```
|
140
|
+
|
141
|
+
## Adding Custom MCP Tools
|
142
|
+
|
143
|
+
### Method 1: Using Decorators
|
144
|
+
```python
|
145
|
+
vac_routes = VACRoutesFastAPI(app, stream_interpreter, enable_mcp_server=True)
|
146
|
+
|
147
|
+
@vac_routes.add_mcp_tool
|
148
|
+
async def get_weather(city: str) -> str:
|
149
|
+
'''Get weather information for a city.'''
|
150
|
+
# Your weather API logic
|
151
|
+
return f"Weather in {city}: Sunny, 22°C"
|
152
|
+
|
153
|
+
@vac_routes.add_mcp_tool("custom_search", "Search our database")
|
154
|
+
async def search_database(query: str, limit: int = 10) -> list:
|
155
|
+
'''Search internal database with custom name and description.'''
|
156
|
+
# Your database search logic
|
157
|
+
return [{"result": f"Found: {query}"}]
|
158
|
+
```
|
159
|
+
|
160
|
+
### Method 2: Programmatic Registration
|
161
|
+
```python
|
162
|
+
async def my_business_tool(param: str) -> dict:
|
163
|
+
return {"processed": param}
|
164
|
+
|
165
|
+
# Add tool with custom name and description
|
166
|
+
vac_routes.add_mcp_tool(
|
167
|
+
my_business_tool,
|
168
|
+
"process_business_data",
|
169
|
+
"Process business data with our custom logic"
|
170
|
+
)
|
171
|
+
```
|
172
|
+
|
173
|
+
### Method 3: Advanced MCP Server Access
|
174
|
+
```python
|
175
|
+
# Get direct access to MCP server for advanced customization
|
176
|
+
mcp_server = vac_routes.get_mcp_server()
|
177
|
+
|
178
|
+
@mcp_server.add_tool
|
179
|
+
async def advanced_tool(complex_param: dict) -> str:
|
180
|
+
return f"Advanced processing: {complex_param}"
|
181
|
+
|
182
|
+
# List all registered tools
|
183
|
+
print("Available MCP tools:", vac_routes.list_mcp_tools())
|
184
|
+
```
|
99
185
|
|
100
|
-
|
101
|
-
|
102
|
-
|
186
|
+
## MCP Client Integration
|
187
|
+
|
188
|
+
Connect to external MCP servers and use their tools:
|
189
|
+
|
190
|
+
```python
|
191
|
+
mcp_servers = [
|
192
|
+
{
|
193
|
+
"name": "filesystem-server",
|
194
|
+
"command": "npx",
|
195
|
+
"args": ["@modelcontextprotocol/server-filesystem", "/path/to/files"]
|
196
|
+
}
|
197
|
+
]
|
103
198
|
|
104
199
|
vac_routes = VACRoutesFastAPI(
|
105
|
-
app,
|
106
|
-
|
107
|
-
|
108
|
-
|
200
|
+
app, stream_interpreter,
|
201
|
+
mcp_servers=mcp_servers, # Connect to external MCP servers
|
202
|
+
enable_mcp_server=True # Also expose our own MCP server
|
203
|
+
)
|
204
|
+
|
205
|
+
# External MCP tools available at:
|
206
|
+
# GET /mcp/tools - List all external tools
|
207
|
+
# POST /mcp/call - Call external MCP tools
|
208
|
+
```
|
209
|
+
|
210
|
+
## Claude Desktop Integration
|
211
|
+
|
212
|
+
### Option 1: Remote Integration (Recommended for Development)
|
213
|
+
```python
|
214
|
+
# Run your FastAPI app
|
215
|
+
uvicorn.run(vac_routes.app, host="0.0.0.0", port=8000)
|
216
|
+
|
217
|
+
# Configure Claude Desktop (Settings > Connectors > Add custom connector):
|
218
|
+
# URL: http://localhost:8000/mcp
|
219
|
+
```
|
220
|
+
|
221
|
+
### Option 2: Local Integration
|
222
|
+
Create a standalone script for Claude Desktop:
|
223
|
+
```python
|
224
|
+
# claude_mcp_server.py
|
225
|
+
from sunholo.mcp.extensible_mcp_server import create_mcp_server
|
226
|
+
|
227
|
+
server = create_mcp_server("my-app", include_vac_tools=True)
|
228
|
+
|
229
|
+
@server.add_tool
|
230
|
+
async def my_app_tool(param: str) -> str:
|
231
|
+
return f"My app processed: {param}"
|
232
|
+
|
233
|
+
if __name__ == "__main__":
|
234
|
+
server.run()
|
235
|
+
|
236
|
+
# Install: fastmcp install claude-desktop claude_mcp_server.py --with sunholo[anthropic]
|
237
|
+
```
|
238
|
+
|
239
|
+
## Available Built-in MCP Tools
|
240
|
+
|
241
|
+
When `enable_mcp_server=True`, these tools are automatically available:
|
242
|
+
|
243
|
+
- **`vac_stream`**: Stream responses from any configured VAC
|
244
|
+
- **`vac_query`**: Query VACs with non-streaming responses
|
245
|
+
- **`list_available_vacs`**: List all available VAC configurations
|
246
|
+
- **`get_vac_info`**: Get detailed information about a specific VAC
|
247
|
+
|
248
|
+
## Error Handling and Best Practices
|
249
|
+
|
250
|
+
```python
|
251
|
+
@vac_routes.add_mcp_tool
|
252
|
+
async def robust_tool(user_input: str) -> str:
|
253
|
+
'''Example of robust tool implementation.'''
|
254
|
+
try:
|
255
|
+
# Validate input
|
256
|
+
if not user_input or len(user_input) > 1000:
|
257
|
+
return "Error: Invalid input length"
|
258
|
+
|
259
|
+
# Your business logic
|
260
|
+
result = await process_user_input(user_input)
|
261
|
+
|
262
|
+
return f"Processed: {result}"
|
263
|
+
|
264
|
+
except Exception as e:
|
265
|
+
# Log error and return user-friendly message
|
266
|
+
log.error(f"Tool error: {e}")
|
267
|
+
return f"Error processing request: {str(e)}"
|
268
|
+
```
|
269
|
+
|
270
|
+
## Configuration Options
|
271
|
+
|
272
|
+
```python
|
273
|
+
vac_routes = VACRoutesFastAPI(
|
274
|
+
app=app,
|
275
|
+
stream_interpreter=my_stream_func,
|
276
|
+
vac_interpreter=my_vac_func, # Optional non-streaming function
|
277
|
+
additional_routes=[], # Custom FastAPI routes
|
278
|
+
mcp_servers=[], # External MCP servers to connect to
|
279
|
+
add_langfuse_eval=True, # Enable Langfuse evaluation
|
280
|
+
enable_mcp_server=True, # Enable MCP server for Claude
|
281
|
+
enable_a2a_agent=False, # Enable A2A agent protocol
|
282
|
+
a2a_vac_names=None # VACs available for A2A
|
109
283
|
)
|
110
284
|
```
|
111
285
|
"""
|
@@ -123,18 +297,86 @@ class VACRoutesFastAPI:
|
|
123
297
|
a2a_vac_names: Optional[List[str]] = None
|
124
298
|
):
|
125
299
|
"""
|
126
|
-
Initialize FastAPI VAC routes.
|
300
|
+
Initialize FastAPI VAC routes with comprehensive AI and MCP integration.
|
127
301
|
|
128
302
|
Args:
|
129
|
-
app: FastAPI application instance
|
130
|
-
stream_interpreter:
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
303
|
+
app: FastAPI application instance to register routes on
|
304
|
+
stream_interpreter: Function for streaming VAC responses. Can be async or sync.
|
305
|
+
Called with (question, vector_name, chat_history, callback, **kwargs)
|
306
|
+
vac_interpreter: Optional function for non-streaming VAC responses. If not provided,
|
307
|
+
will use stream_interpreter without streaming callbacks.
|
308
|
+
additional_routes: List of custom route dictionaries to register:
|
309
|
+
[{"path": "/custom", "handler": func, "methods": ["GET"]}]
|
310
|
+
mcp_servers: List of external MCP server configurations to connect to:
|
311
|
+
[{"name": "server-name", "command": "python", "args": ["server.py"]}]
|
312
|
+
add_langfuse_eval: Whether to enable Langfuse evaluation and tracing
|
313
|
+
enable_mcp_server: Whether to enable the MCP server at /mcp endpoint for
|
314
|
+
Claude Desktop/Code integration. When True, automatically
|
315
|
+
includes built-in VAC tools and supports custom tool registration.
|
316
|
+
enable_a2a_agent: Whether to enable A2A (Agent-to-Agent) protocol endpoints
|
317
|
+
a2a_vac_names: List of VAC names available for A2A agent interactions
|
318
|
+
|
319
|
+
## Stream Interpreter Function
|
320
|
+
|
321
|
+
Your stream_interpreter should handle streaming responses:
|
322
|
+
|
323
|
+
```python
|
324
|
+
async def my_stream_interpreter(question: str, vector_name: str,
|
325
|
+
chat_history: list, callback, **kwargs):
|
326
|
+
# Process the question using your AI/RAG pipeline
|
327
|
+
|
328
|
+
# For streaming tokens:
|
329
|
+
await callback.async_on_llm_new_token("partial response...")
|
330
|
+
|
331
|
+
# Return final result with sources:
|
332
|
+
return {
|
333
|
+
"answer": "Final complete answer",
|
334
|
+
"sources": [{"title": "Source 1", "url": "..."}]
|
335
|
+
}
|
336
|
+
```
|
337
|
+
|
338
|
+
## MCP Server Integration
|
339
|
+
|
340
|
+
When enable_mcp_server=True, the following happens:
|
341
|
+
1. MCP server is mounted at /mcp endpoint
|
342
|
+
2. Built-in VAC tools are automatically registered:
|
343
|
+
- vac_stream, vac_query, list_available_vacs, get_vac_info
|
344
|
+
3. You can add custom MCP tools using add_mcp_tool()
|
345
|
+
4. Claude Desktop/Code can connect to http://your-server/mcp
|
346
|
+
|
347
|
+
## Complete Example
|
348
|
+
|
349
|
+
```python
|
350
|
+
app = FastAPI(title="My VAC Application")
|
351
|
+
|
352
|
+
async def my_vac_logic(question, vector_name, chat_history, callback, **kwargs):
|
353
|
+
# Your AI/RAG implementation
|
354
|
+
result = await process_with_ai(question)
|
355
|
+
return {"answer": result, "sources": []}
|
356
|
+
|
357
|
+
# External MCP servers to connect to
|
358
|
+
external_mcp = [
|
359
|
+
{"name": "filesystem", "command": "mcp-server-fs", "args": ["/data"]}
|
360
|
+
]
|
361
|
+
|
362
|
+
vac_routes = VACRoutesFastAPI(
|
363
|
+
app=app,
|
364
|
+
stream_interpreter=my_vac_logic,
|
365
|
+
mcp_servers=external_mcp,
|
366
|
+
enable_mcp_server=True # Enable for Claude integration
|
367
|
+
)
|
368
|
+
|
369
|
+
# Add custom MCP tools for your business logic
|
370
|
+
@vac_routes.add_mcp_tool
|
371
|
+
async def get_customer_info(customer_id: str) -> dict:
|
372
|
+
return await fetch_customer(customer_id)
|
373
|
+
|
374
|
+
# Your app now has:
|
375
|
+
# - VAC endpoints: /vac/{vector_name}, /vac/streaming/{vector_name}
|
376
|
+
# - OpenAI API: /openai/v1/chat/completions
|
377
|
+
# - MCP server: /mcp (with built-in + custom tools)
|
378
|
+
# - MCP client: /mcp/tools, /mcp/call (for external servers)
|
379
|
+
```
|
138
380
|
"""
|
139
381
|
self.app = app
|
140
382
|
self.stream_interpreter = stream_interpreter
|
@@ -152,11 +394,17 @@ class VACRoutesFastAPI:
|
|
152
394
|
# MCP server initialization
|
153
395
|
self.enable_mcp_server = enable_mcp_server
|
154
396
|
self.vac_mcp_server = None
|
397
|
+
self._custom_mcp_tools = []
|
398
|
+
self._custom_mcp_resources = []
|
399
|
+
|
155
400
|
if self.enable_mcp_server and VACMCPServer:
|
156
401
|
self.vac_mcp_server = VACMCPServer(
|
157
|
-
|
158
|
-
|
402
|
+
server_name="sunholo-vac-fastapi-server",
|
403
|
+
include_vac_tools=True
|
159
404
|
)
|
405
|
+
|
406
|
+
# Add any pre-registered custom tools
|
407
|
+
self._register_custom_tools()
|
160
408
|
|
161
409
|
# A2A agent initialization
|
162
410
|
self.enable_a2a_agent = enable_a2a_agent
|
@@ -232,10 +480,15 @@ class VACRoutesFastAPI:
|
|
232
480
|
self.app.get("/mcp/resources")(self.handle_mcp_list_resources)
|
233
481
|
self.app.post("/mcp/resources/read")(self.handle_mcp_read_resource)
|
234
482
|
|
235
|
-
# MCP server endpoint
|
483
|
+
# MCP server endpoint - mount the FastMCP app
|
236
484
|
if self.enable_mcp_server and self.vac_mcp_server:
|
237
|
-
|
238
|
-
|
485
|
+
try:
|
486
|
+
mcp_app = self.vac_mcp_server.get_http_app()
|
487
|
+
self.app.mount("/mcp", mcp_app)
|
488
|
+
log.info("MCP server mounted at /mcp endpoint")
|
489
|
+
except Exception as e:
|
490
|
+
log.error(f"Failed to mount MCP server: {e}")
|
491
|
+
raise RuntimeError(f"MCP server initialization failed: {e}")
|
239
492
|
|
240
493
|
# A2A agent endpoints
|
241
494
|
if self.enable_a2a_agent:
|
@@ -787,109 +1040,6 @@ class VACRoutesFastAPI:
|
|
787
1040
|
except Exception as e:
|
788
1041
|
raise HTTPException(status_code=500, detail=str(e))
|
789
1042
|
|
790
|
-
async def handle_mcp_server(self, request: Request):
|
791
|
-
"""Handle MCP server requests."""
|
792
|
-
if not self.vac_mcp_server:
|
793
|
-
raise HTTPException(status_code=501, detail="MCP server not enabled")
|
794
|
-
|
795
|
-
data = await request.json()
|
796
|
-
log.info(f"MCP server received: {data}")
|
797
|
-
|
798
|
-
# Process MCP request - simplified version
|
799
|
-
# Full implementation would handle all MCP protocol methods
|
800
|
-
method = data.get("method")
|
801
|
-
params = data.get("params", {})
|
802
|
-
request_id = data.get("id")
|
803
|
-
|
804
|
-
try:
|
805
|
-
if method == "initialize":
|
806
|
-
response = {
|
807
|
-
"jsonrpc": "2.0",
|
808
|
-
"result": {
|
809
|
-
"protocolVersion": "2025-06-18",
|
810
|
-
"capabilities": {"tools": {}},
|
811
|
-
"serverInfo": {
|
812
|
-
"name": "sunholo-vac-server",
|
813
|
-
"version": sunholo_version()
|
814
|
-
}
|
815
|
-
},
|
816
|
-
"id": request_id
|
817
|
-
}
|
818
|
-
elif method == "tools/list":
|
819
|
-
tools = [
|
820
|
-
{
|
821
|
-
"name": "vac_stream",
|
822
|
-
"description": "Stream responses from a Sunholo VAC",
|
823
|
-
"inputSchema": {
|
824
|
-
"type": "object",
|
825
|
-
"properties": {
|
826
|
-
"vector_name": {"type": "string"},
|
827
|
-
"user_input": {"type": "string"},
|
828
|
-
"chat_history": {"type": "array", "default": []}
|
829
|
-
},
|
830
|
-
"required": ["vector_name", "user_input"]
|
831
|
-
}
|
832
|
-
}
|
833
|
-
]
|
834
|
-
if self.vac_interpreter:
|
835
|
-
tools.append({
|
836
|
-
"name": "vac_query",
|
837
|
-
"description": "Query a Sunholo VAC (non-streaming)",
|
838
|
-
"inputSchema": {
|
839
|
-
"type": "object",
|
840
|
-
"properties": {
|
841
|
-
"vector_name": {"type": "string"},
|
842
|
-
"user_input": {"type": "string"},
|
843
|
-
"chat_history": {"type": "array", "default": []}
|
844
|
-
},
|
845
|
-
"required": ["vector_name", "user_input"]
|
846
|
-
}
|
847
|
-
})
|
848
|
-
response = {
|
849
|
-
"jsonrpc": "2.0",
|
850
|
-
"result": {"tools": tools},
|
851
|
-
"id": request_id
|
852
|
-
}
|
853
|
-
elif method == "tools/call":
|
854
|
-
tool_name = params.get("name")
|
855
|
-
arguments = params.get("arguments", {})
|
856
|
-
|
857
|
-
if tool_name == "vac_stream":
|
858
|
-
result = await self.vac_mcp_server._handle_vac_stream(arguments)
|
859
|
-
elif tool_name == "vac_query":
|
860
|
-
result = await self.vac_mcp_server._handle_vac_query(arguments)
|
861
|
-
else:
|
862
|
-
raise ValueError(f"Unknown tool: {tool_name}")
|
863
|
-
|
864
|
-
response = {
|
865
|
-
"jsonrpc": "2.0",
|
866
|
-
"result": {"content": [item.model_dump() for item in result]},
|
867
|
-
"id": request_id
|
868
|
-
}
|
869
|
-
else:
|
870
|
-
raise ValueError(f"Unknown method: {method}")
|
871
|
-
|
872
|
-
except Exception as e:
|
873
|
-
response = {
|
874
|
-
"jsonrpc": "2.0",
|
875
|
-
"error": {
|
876
|
-
"code": -32603,
|
877
|
-
"message": str(e)
|
878
|
-
},
|
879
|
-
"id": request_id
|
880
|
-
}
|
881
|
-
|
882
|
-
return JSONResponse(content=response)
|
883
|
-
|
884
|
-
async def handle_mcp_server_info(self):
|
885
|
-
"""Return MCP server information."""
|
886
|
-
return JSONResponse(content={
|
887
|
-
"name": "sunholo-vac-server",
|
888
|
-
"version": "1.0.0",
|
889
|
-
"transport": "http",
|
890
|
-
"endpoint": "/mcp",
|
891
|
-
"tools": ["vac_stream", "vac_query"] if self.vac_interpreter else ["vac_stream"]
|
892
|
-
})
|
893
1043
|
|
894
1044
|
def _get_or_create_a2a_agent(self, request: Request):
|
895
1045
|
"""Get or create the A2A agent instance with current request context."""
|
@@ -1039,4 +1189,94 @@ class VACRoutesFastAPI:
|
|
1039
1189
|
"id": data.get("id") if 'data' in locals() else None
|
1040
1190
|
},
|
1041
1191
|
status_code=500
|
1042
|
-
)
|
1192
|
+
)
|
1193
|
+
|
1194
|
+
# MCP Tool Registration Methods
|
1195
|
+
|
1196
|
+
def _register_custom_tools(self):
|
1197
|
+
"""Register any custom tools that were added before MCP server initialization."""
|
1198
|
+
if self.vac_mcp_server:
|
1199
|
+
for tool_func, name, description in self._custom_mcp_tools:
|
1200
|
+
self.vac_mcp_server.add_tool(tool_func, name, description)
|
1201
|
+
for resource_func, name, description in self._custom_mcp_resources:
|
1202
|
+
self.vac_mcp_server.add_resource(resource_func, name, description)
|
1203
|
+
|
1204
|
+
def add_mcp_tool(self, func: Callable, name: str = None, description: str = None):
|
1205
|
+
"""
|
1206
|
+
Add a custom MCP tool to the server.
|
1207
|
+
|
1208
|
+
Args:
|
1209
|
+
func: The tool function
|
1210
|
+
name: Optional custom name for the tool
|
1211
|
+
description: Optional description (uses docstring if not provided)
|
1212
|
+
|
1213
|
+
Example:
|
1214
|
+
@app.add_mcp_tool
|
1215
|
+
async def my_custom_tool(param: str) -> str:
|
1216
|
+
'''Custom tool that does something useful.'''
|
1217
|
+
return f"Result: {param}"
|
1218
|
+
|
1219
|
+
# Or with custom name and description
|
1220
|
+
app.add_mcp_tool(my_function, "custom_name", "Custom description")
|
1221
|
+
"""
|
1222
|
+
if self.vac_mcp_server:
|
1223
|
+
self.vac_mcp_server.add_tool(func, name, description)
|
1224
|
+
else:
|
1225
|
+
# Store for later registration
|
1226
|
+
self._custom_mcp_tools.append((func, name, description))
|
1227
|
+
|
1228
|
+
return func # Allow use as decorator
|
1229
|
+
|
1230
|
+
def add_mcp_resource(self, func: Callable, name: str = None, description: str = None):
|
1231
|
+
"""
|
1232
|
+
Add a custom MCP resource to the server.
|
1233
|
+
|
1234
|
+
Args:
|
1235
|
+
func: The resource function
|
1236
|
+
name: Optional custom name for the resource
|
1237
|
+
description: Optional description (uses docstring if not provided)
|
1238
|
+
|
1239
|
+
Example:
|
1240
|
+
@app.add_mcp_resource
|
1241
|
+
async def my_custom_resource(uri: str) -> str:
|
1242
|
+
'''Custom resource that provides data.'''
|
1243
|
+
return f"Resource data for: {uri}"
|
1244
|
+
"""
|
1245
|
+
if self.vac_mcp_server:
|
1246
|
+
self.vac_mcp_server.add_resource(func, name, description)
|
1247
|
+
else:
|
1248
|
+
# Store for later registration
|
1249
|
+
self._custom_mcp_resources.append((func, name, description))
|
1250
|
+
|
1251
|
+
return func # Allow use as decorator
|
1252
|
+
|
1253
|
+
def get_mcp_server(self):
|
1254
|
+
"""
|
1255
|
+
Get the MCP server instance for advanced customization.
|
1256
|
+
|
1257
|
+
Returns:
|
1258
|
+
VACMCPServer instance or None if MCP server is not enabled
|
1259
|
+
"""
|
1260
|
+
return self.vac_mcp_server
|
1261
|
+
|
1262
|
+
def list_mcp_tools(self) -> List[str]:
|
1263
|
+
"""
|
1264
|
+
List all registered MCP tools.
|
1265
|
+
|
1266
|
+
Returns:
|
1267
|
+
List of tool names
|
1268
|
+
"""
|
1269
|
+
if self.vac_mcp_server:
|
1270
|
+
return self.vac_mcp_server.list_tools()
|
1271
|
+
return []
|
1272
|
+
|
1273
|
+
def list_mcp_resources(self) -> List[str]:
|
1274
|
+
"""
|
1275
|
+
List all registered MCP resources.
|
1276
|
+
|
1277
|
+
Returns:
|
1278
|
+
List of resource names
|
1279
|
+
"""
|
1280
|
+
if self.vac_mcp_server:
|
1281
|
+
return self.vac_mcp_server.list_resources()
|
1282
|
+
return []
|