1111import json
1212import pathlib
1313import sys
14+ import uuid
1415
16+ from finecode .wm_client import ApiClient
17+ from finecode .wm_server import wm_lifecycle
1518from loguru import logger
1619from mcp .server import Server
1720from mcp .server .stdio import stdio_server
1821from mcp .types import TextContent , Tool
1922
20- from finecode .wm_client import ApiClient
21- from finecode .wm_server import wm_lifecycle
22-
23-
2423_wm_client = ApiClient ()
2524server = Server ("FineCode" )
2625
26+ _partial_result_queues : dict [str , asyncio .Queue ] = {}
27+
28+
29+ def _setup_partial_result_forwarding () -> None :
30+ """Register the WM partial-result notification handler.
31+
32+ Must be called once after ``_wm_client.connect()``. Each ``actions/partialResult``
33+ notification is routed by token to the matching per-call asyncio.Queue.
34+ """
35+
36+ async def _on_partial_result (params : dict ) -> None :
37+ token = params .get ("token" )
38+ value = params .get ("value" )
39+ if token and value is not None :
40+ queue = _partial_result_queues .get (token )
41+ if queue is not None :
42+ queue .put_nowait (value )
43+
44+ _wm_client .on_notification ("actions/partialResult" , _on_partial_result )
45+
46+
47+ async def _run_with_progress (
48+ action : str ,
49+ project : str ,
50+ params : dict ,
51+ options : dict ,
52+ session ,
53+ ) -> dict :
54+ """Run a WM action with streaming partial results forwarded as MCP log messages.
55+
56+ ``project`` may be ``""`` to run across all projects that expose the action.
57+ Each ``actions/partialResult`` notification is forwarded to the MCP client as a
58+ ``notifications/message`` log message while the call blocks waiting for the final result.
59+ """
60+ token = str (uuid .uuid4 ())
61+ queue : asyncio .Queue = asyncio .Queue ()
62+ _partial_result_queues [token ] = queue
63+
64+ async def _forward () -> None :
65+ try :
66+ while True :
67+ value = await queue .get ()
68+ await session .send_log_message (
69+ level = "info" , data = value , logger = "finecode"
70+ )
71+ except asyncio .CancelledError :
72+ pass
73+
74+ result_task = asyncio .create_task (
75+ _wm_client .run_action_with_partial_results (
76+ action , project , token , params , options
77+ )
78+ )
79+ forward_task = asyncio .create_task (_forward ())
80+ try :
81+ return await result_task
82+ finally :
83+ forward_task .cancel ()
84+ await asyncio .gather (forward_task , return_exceptions = True )
85+ _partial_result_queues .pop (token , None )
86+
2787
2888@server .list_tools ()
2989async def list_tools () -> list [Tool ]:
@@ -167,22 +227,22 @@ async def call_tool(name: str, arguments: dict) -> list[TextContent]:
167227 params = {
168228 "source_file_path" : str (project_path / "pyproject.toml" ),
169229 "project_raw_config" : raw_config ,
170- "target_file_path" : str (project_path / "finecode_config_dump" / "pyproject.toml" ),
230+ "target_file_path" : str (
231+ project_path / "finecode_config_dump" / "pyproject.toml"
232+ ),
171233 },
172234 options = {"resultFormats" : ["json" ], "trigger" : "user" , "devEnv" : "ai" },
173235 )
174236 return [TextContent (type = "text" , text = json .dumps (result ))]
175237
238+ from mcp .server .lowlevel .server import request_ctx
239+
240+ session = request_ctx .get ().session
176241 project = arguments .pop ("project" , None )
177242 options = {"resultFormats" : ["json" ], "trigger" : "user" , "devEnv" : "ai" }
178- if project is not None :
179- result = await _wm_client .run_action (
180- name , project , params = arguments or None , options = options
181- )
182- else :
183- result = await _wm_client .run_batch (
184- [name ], params = arguments or None , options = options
185- )
243+ result = await _run_with_progress (
244+ name , project or "" , arguments or {}, options , session
245+ )
186246 return [TextContent (type = "text" , text = json .dumps (result ))]
187247
188248
@@ -209,10 +269,13 @@ def start(workdir: pathlib.Path, port_file: pathlib.Path | None = None) -> None:
209269
210270 async def _run () -> None :
211271 try :
212- await _wm_client .connect ("127.0.0.1" , port )
272+ await _wm_client .connect ("127.0.0.1" , port , client_id = "mcp" )
213273 except (ConnectionRefusedError , OSError ) as exc :
214- logger .error (f"Could not connect to FineCode WM server on port { port } : { exc } " )
274+ logger .error (
275+ f"Could not connect to FineCode WM server on port { port } : { exc } "
276+ )
215277 sys .exit (1 )
278+ _setup_partial_result_forwarding ()
216279 logger .debug (f"Add dir to API Client: { workdir } " )
217280 await _wm_client .add_dir (workdir )
218281 logger .debug ("Added dir" )
0 commit comments