Skip to content

Commit 34ea482

Browse files
authored
Create toolbox_server.py
1 parent af65b31 commit 34ea482

File tree

1 file changed

+101
-0
lines changed

1 file changed

+101
-0
lines changed

main/api/mcp/toolbox_server.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import os
2+
import subprocess
3+
import logging
4+
from fastapi import FastAPI, HTTPException, Depends
5+
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
6+
from pydantic import BaseModel
7+
import yaml
8+
from typing import List, Dict, Any
9+
import psycopg2
10+
from redis import Redis
11+
import opentelemetry.trace as trace
12+
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
13+
from opentelemetry.sdk.trace import TracerProvider
14+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
15+
16+
# Setup OpenTelemetry
17+
trace.set_tracer_provider(TracerProvider())
18+
tracer = trace.get_tracer(__name__)
19+
otlp_exporter = OTLPSpanExporter(endpoint="http://localhost:4318/v1/traces")
20+
trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(otlp_exporter))
21+
22+
# FastAPI app
23+
app = FastAPI(title="Vial MCP Toolbox Server")
24+
security = HTTPBearer()
25+
26+
# Load tools.yaml
27+
with open(os.path.join(os.path.dirname(__file__), '../../config/tools.yaml'), 'r') as f:
28+
config = yaml.safe_load(f)
29+
30+
# Database connections
31+
db_connections = {}
32+
redis_client = None
33+
34+
def init_connections():
35+
global redis_client
36+
for source_name, source_config in config['sources'].items():
37+
if source_config['kind'] == 'postgres':
38+
db_connections[source_name] = psycopg2.connect(
39+
host=source_config['host'],
40+
port=source_config['port'],
41+
database=source_config['database'],
42+
user=source_config['user'],
43+
password=source_config['password'],
44+
sslmode=source_config.get('ssl', 'require')
45+
)
46+
elif source_config['kind'] == 'redis':
47+
redis_client = Redis.from_url(source_config['url'], decode_responses=True)
48+
49+
# Pydantic models
50+
class ToolParameter(BaseModel):
51+
name: str
52+
value: str
53+
54+
class ToolExecutionRequest(BaseModel):
55+
parameters: List[ToolParameter]
56+
57+
# Authentication
58+
async def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
59+
expected_key = os.getenv('API_KEY')
60+
if credentials.credentials != expected_key:
61+
raise HTTPException(status_code=401, detail="Invalid API key")
62+
return credentials.credentials
63+
64+
# Health check
65+
@app.get("/health")
66+
async def health_check():
67+
return {"status": "healthy"}
68+
69+
# Get toolsets
70+
@app.get("/mcp/toolsets")
71+
async def get_toolsets(token: str = Depends(verify_token)):
72+
with tracer.start_as_current_span("get_toolsets"):
73+
return {"tools": list(config['tools'].keys()), "toolsets": config['toolsets']}
74+
75+
# Execute tool
76+
@app.post("/mcp/tools/{tool_name}/execute")
77+
async def execute_tool(tool_name: str, request: ToolExecutionRequest, token: str = Depends(verify_token)):
78+
with tracer.start_as_current_span(f"execute_tool_{tool_name}"):
79+
if tool_name not in config['tools']:
80+
raise HTTPException(status_code=404, detail="Tool not found")
81+
tool = config['tools'][tool_name]
82+
source = db_connections.get(tool['source'])
83+
if not source:
84+
raise HTTPException(status_code=400, detail="Invalid source")
85+
try:
86+
with source.cursor() as cursor:
87+
params = [param.value for param in request.parameters]
88+
cursor.execute(tool['statement'], params)
89+
result = cursor.fetchall()
90+
source.commit()
91+
return {"result": result}
92+
except Exception as e:
93+
source.rollback()
94+
logging.error(f"Tool execution failed: {str(e)}")
95+
raise HTTPException(status_code=500, detail=str(e))
96+
97+
# Start server
98+
if __name__ == "__main__":
99+
init_connections()
100+
import uvicorn
101+
uvicorn.run(app, host="127.0.0.1", port=5000)

0 commit comments

Comments
 (0)