From fac947f9cddaf915318a5211e6d5d80562cb2c23 Mon Sep 17 00:00:00 2001 From: hanishkvc Date: Sun, 7 Dec 2025 00:22:45 +0530 Subject: [PATCH] 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. --- .../local.tools/simplemcp.py | 27 +++++++++++-------- .../public_simplechat/local.tools/toolcall.py | 20 +++++++++++--- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/tools/server/public_simplechat/local.tools/simplemcp.py b/tools/server/public_simplechat/local.tools/simplemcp.py index 2d1e098a7f..34c7a9cf15 100644 --- a/tools/server/public_simplechat/local.tools/simplemcp.py +++ b/tools/server/public_simplechat/local.tools/simplemcp.py @@ -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") diff --git a/tools/server/public_simplechat/local.tools/toolcall.py b/tools/server/public_simplechat/local.tools/toolcall.py index 39d40820eb..102b058bd9 100644 --- a/tools/server/public_simplechat/local.tools/toolcall.py +++ b/tools/server/public_simplechat/local.tools/toolcall.py @@ -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: