1010from starlette .requests import Request
1111from starlette .responses import JSONResponse
1212
13+ from core .config import config
1314from models .models import MCPResponse , ToolDefinitionModel , ToolParameterModel
1415from core .logging_decorator import log_execution
1516from core .telemetry_decorator import telemetry_tool
2829_MAX_POLL_SECONDS = 600
2930
3031
32+ def get_user_id_from_context (ctx : Context ) -> str | None :
33+ """Read user_id from request-scoped context in remote-hosted mode."""
34+ if not config .http_remote_hosted :
35+ return None
36+
37+ get_state = getattr (ctx , "get_state" , None )
38+ if not callable (get_state ):
39+ return None
40+
41+ try :
42+ user_id = get_state ("user_id" )
43+ except Exception :
44+ return None
45+
46+ return user_id if isinstance (user_id , str ) and user_id else None
47+
48+
3149class RegisterToolsPayload (BaseModel ):
3250 project_id : str
3351 project_hash : str | None = None
@@ -84,30 +102,40 @@ async def register_tools(request: Request) -> JSONResponse:
84102 return JSONResponse (response .model_dump ())
85103
86104 # --- Public API for MCP tools ---------------------------------------
87- async def list_registered_tools (self , project_id : str ) -> list [ToolDefinitionModel ]:
105+ async def list_registered_tools (
106+ self ,
107+ project_id : str ,
108+ user_id : str | None = None ,
109+ ) -> list [ToolDefinitionModel ]:
88110 legacy = list (self ._project_tools .get (project_id , {}).values ())
89- hub_tools = await PluginHub .get_tools_for_project (project_id )
111+ hub_tools = await PluginHub .get_tools_for_project (project_id , user_id = user_id )
90112 return legacy + hub_tools
91113
92- async def get_tool_definition (self , project_id : str , tool_name : str ) -> ToolDefinitionModel | None :
114+ async def get_tool_definition (
115+ self ,
116+ project_id : str ,
117+ tool_name : str ,
118+ user_id : str | None = None ,
119+ ) -> ToolDefinitionModel | None :
93120 tool = self ._project_tools .get (project_id , {}).get (tool_name )
94121 if tool :
95122 return tool
96- return await PluginHub .get_tool_definition (project_id , tool_name )
123+ return await PluginHub .get_tool_definition (project_id , tool_name , user_id = user_id )
97124
98125 async def execute_tool (
99126 self ,
100127 project_id : str ,
101128 tool_name : str ,
102129 unity_instance : str | None ,
103130 params : dict [str , object ] | None = None ,
131+ user_id : str | None = None ,
104132 ) -> MCPResponse :
105133 params = params or {}
106134 logger .info (
107135 f"Executing tool '{ tool_name } ' for project '{ project_id } ' (instance={ unity_instance } ) with params: { params } "
108136 )
109137
110- definition = await self .get_tool_definition (project_id , tool_name )
138+ definition = await self .get_tool_definition (project_id , tool_name , user_id = user_id )
111139 if definition is None :
112140 return MCPResponse (
113141 success = False ,
@@ -119,6 +147,7 @@ async def execute_tool(
119147 unity_instance ,
120148 tool_name ,
121149 params ,
150+ user_id = user_id ,
122151 )
123152
124153 if not definition .requires_polling :
@@ -132,6 +161,7 @@ async def execute_tool(
132161 params ,
133162 response ,
134163 definition .poll_action or "status" ,
164+ user_id = user_id ,
135165 )
136166 logger .info (f"Tool '{ tool_name } ' polled response: { result } " )
137167 return result
@@ -156,6 +186,7 @@ async def _poll_until_complete(
156186 initial_params : dict [str , object ],
157187 initial_response ,
158188 poll_action : str ,
189+ user_id : str | None = None ,
159190 ) -> MCPResponse :
160191 poll_params = dict (initial_params )
161192 poll_params ["action" ] = poll_action or "status"
@@ -180,7 +211,11 @@ async def _poll_until_complete(
180211
181212 try :
182213 response = await send_with_unity_instance (
183- async_send_command_with_retry , unity_instance , tool_name , poll_params
214+ async_send_command_with_retry ,
215+ unity_instance ,
216+ tool_name ,
217+ poll_params ,
218+ user_id = user_id ,
184219 )
185220 except Exception as exc : # pragma: no cover - network/domain reload variability
186221 logger .debug (f"Polling { tool_name } failed, will retry: { exc } " )
@@ -347,8 +382,15 @@ async def _handler(ctx: Context, **kwargs) -> MCPResponse:
347382 )
348383
349384 params = {k : v for k , v in kwargs .items () if v is not None }
385+ user_id = get_user_id_from_context (ctx )
350386 service = CustomToolService .get_instance ()
351- return await service .execute_tool (project_id , definition .name , unity_instance , params )
387+ return await service .execute_tool (
388+ project_id ,
389+ definition .name ,
390+ unity_instance ,
391+ params ,
392+ user_id = user_id ,
393+ )
352394
353395 _handler .__name__ = f"custom_tool_{ definition .name } "
354396 _handler .__doc__ = definition .description or ""
0 commit comments