SimpleSallap:SimpleMCP:tools/list

Fix a oversight wrt ToolManager.meta, where I had created a dict
of name-keyed toolcall metas, instead of a simple list of toolcall
metas. Rather I blindly duplicated structure I used for storing the
tool calls in the tc_switch in the anveshika sallap client side
code.

Add dataclasses to mimic the MCP tools/list response. However wrt
the 2 odd differences between the MCP structure and OpenAi tools
handshake structure, for now I have retained the OpenAi tools hs
structure.

Add a common helper send_mcp to ProxyHandler given that both
mcp_toolscall and mcp_toolslist and even others like mcp_initialise
in future require a common response mechanism.

With above and bit more implement initial go at tools/list response.
This commit is contained in:
hanishkvc 2025-12-07 00:22:45 +05:30
parent 69be7f2029
commit fac947f9cd
2 changed files with 32 additions and 15 deletions

View File

@ -86,13 +86,21 @@ class ProxyHandler(http.server.BaseHTTPRequestHandler):
return mTC.TCOutResponse(False, 400, "WARN:Invalid auth")
return mTC.TCOutResponse(True, 200, "Auth Ok")
def send_mcp(self, statusCode: int, statusMessage: str, body: Any):
self.send_response(statusCode, statusMessage)
self.send_header('Content-Type', "application/json")
# Add CORS for browser fetch, just in case
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
self.wfile.write(json.dumps(body).encode('utf-8'))
def mcp_toolscall(self, oRPC: Any):
"""
If authorisation is ok for the request, run the specified handler.
"""
try:
if not gMe.op.toolManager:
raise RuntimeError("DBUG:PH:TCRun:ToolManager uninitialised")
raise RuntimeError("DBUG:PH:MCPToolsCall:ToolManager uninitialised")
resp = gMe.op.toolManager.tc_handle(oRPC["id"], oRPC["params"]["name"], oRPC["params"]["arguments"], self.headers)
if not resp.response.callOk:
self.send_error(resp.response.statusCode, resp.response.statusMsg)
@ -104,24 +112,21 @@ class ProxyHandler(http.server.BaseHTTPRequestHandler):
mTC.MCPTCRContentText(resp.response.contentData.decode('utf-8'))
])
)
self.send_response(resp.response.statusCode, resp.response.statusMsg)
self.send_header('Content-Type', "application/json")
# Add CORS for browser fetch, just in case
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
self.wfile.write(json.dumps(tcresp).encode('utf-8'))
self.send_mcp(resp.response.statusCode, resp.response.statusMsg, tcresp)
except Exception as e:
self.send_error(400, f"ERRR:PH:{e}")
def mcp_toolslist(self):
pass
def mcp_toolslist(self, oRPC: Any):
if not gMe.op.toolManager:
raise RuntimeError("DBUG:PH:MCPToolsList:ToolManager uninitialised")
tcl = mTC.MCPToolsList(oRPC["id"], mTC.MCPTLResult(gMe.op.toolManager.meta()))
self.send_mcp(200, "tools/list follows", tcl)
def mcp_run(self, oRPC: Any):
if oRPC["method"] == "tools/call":
self.mcp_toolscall(oRPC)
elif oRPC["method"] == "tools/list":
self.mcp_toolslist()
self.mcp_toolslist(oRPC)
else:
self.send_error(400, f"ERRR:PH:MCP:Unknown")

View File

@ -54,10 +54,10 @@ class TCInParameters():
class TCFunction():
name: str
description: str
parameters: TCInParameters
parameters: TCInParameters ### Delta wrt naming btw OpenAi Tools HS (parameters) and MCP(inputSchema)
@dataclass
class ToolCallMeta():
class ToolCallMeta(): ### Delta wrt tree btw OpenAi Tools HS (Needs this wrapper) and MCP (directly use TCFunction)
type: str = "function"
function: TCFunction|None = None
@ -112,6 +112,17 @@ class ToolCall():
return ToolCallMeta("function", tcf)
@dataclass
class MCPTLResult:
tools: list[ToolCallMeta]
@dataclass
class MCPToolsList:
id: str
result: MCPTLResult
jsonrpc: str = "2.0"
class ToolManager():
def __init__(self) -> None:
@ -121,9 +132,10 @@ class ToolManager():
self.toolcalls[fName] = tc
def meta(self):
oMeta = {}
lMeta: list[ToolCallMeta]= []
for tcName in self.toolcalls.keys():
oMeta[tcName] = self.toolcalls[tcName].meta()
lMeta.append(self.toolcalls[tcName].meta())
return lMeta
def tc_handle(self, callId: str, tcName: str, tcArgs: TCInArgs, inHeaders: HttpHeaders) -> ToolCallResponseEx:
try: