@@ -29,9 +29,9 @@ def start_server( # noqa: C901
2929 port : int = 8000 ,
3030) -> None :
3131 try :
32- from mcp .server .fastmcp import FastMCP
32+ from mcp .server .mcpserver import MCPServer
3333 except ImportError as exc :
34- raise ImportError ("MCP server requires extra dependencies: pip install 'mcp>=1 .0'" ) from exc
34+ raise ImportError ("MCP server requires extra dependencies: pip install 'mcp>=2 .0'" ) from exc
3535
3636 session = ProjectSession (location )
3737 session .build ()
@@ -44,69 +44,71 @@ def start_server( # noqa: C901
4444 repo : RequirementsRepository = session .repo
4545 urn_source_paths = session .urn_source_paths
4646
47- mcp = FastMCP ("reqstool" )
48- mcp .settings .host = host
49- mcp .settings .port = port
47+ mcp = MCPServer (name = "reqstool" )
48+
49+ # SDK 2.x: transport options are run() kwargs instead of server settings.
50+ run_kwargs : dict = {}
51+ if transport in ("sse" , "streamable-http" ):
52+ run_kwargs .update (host = host , port = port )
5053 if transport == "streamable-http" :
51- mcp .settings .json_response = True
52- mcp .settings .stateless_http = True
54+ run_kwargs .update (json_response = True , stateless_http = True )
5355
5456 @mcp .tool ()
55- def list_requirements (urn : str | None = None , lifecycle_state : str | None = None ) -> list [dict ]:
57+ async def list_requirements (urn : str | None = None , lifecycle_state : str | None = None ) -> list [dict ]:
5658 """List requirements with id, title, and lifecycle state.
5759 Filter by urn and/or lifecycle_state (draft|effective|deprecated|obsolete)."""
5860 return get_requirements_list (repo , urn = urn , lifecycle_state = lifecycle_state )
5961
6062 @mcp .tool ()
61- def get_requirement (id : str ) -> dict :
63+ async def get_requirement (id : str ) -> dict :
6264 """Get full details for a requirement by ID (e.g. REQ_010)."""
6365 result = get_requirement_details (id , repo , urn_source_paths )
6466 if result is None :
6567 raise ValueError (f"Requirement { id !r} not found" )
6668 return result
6769
6870 @mcp .tool ()
69- def get_requirements_status (urn : str | None = None , include_post_build : bool = False ) -> list [dict ]:
71+ async def get_requirements_status (urn : str | None = None , include_post_build : bool = False ) -> list [dict ]:
7072 """Batch status for all requirements: id, urn, lifecycle_state, completed, implementation_type,
7173 automated_tests, manual_tests. Use this to find requirements that are incomplete, partially
7274 tested, or not yet implemented. Optionally filter by URN. Set include_post_build=True for
7375 parity with `status --with-post-tests` (scopes to post-build-phase SVCs too)."""
7476 return _get_requirements_status_all (repo , urn = urn , include_post_build = include_post_build )
7577
7678 @mcp .tool ()
77- def list_svcs (urn : str | None = None , lifecycle_state : str | None = None ) -> list [dict ]:
79+ async def list_svcs (urn : str | None = None , lifecycle_state : str | None = None ) -> list [dict ]:
7880 """List SVCs with id, title, lifecycle state, and verification type.
7981 Filter by urn and/or lifecycle_state (draft|effective|deprecated|obsolete)."""
8082 return get_svcs_list (repo , urn = urn , lifecycle_state = lifecycle_state )
8183
8284 @mcp .tool ()
83- def get_svc (id : str ) -> dict :
85+ async def get_svc (id : str ) -> dict :
8486 """Get full details for an SVC by ID (e.g. SVC_010)."""
8587 result = get_svc_details (id , repo , urn_source_paths )
8688 if result is None :
8789 raise ValueError (f"SVC { id !r} not found" )
8890 return result
8991
9092 @mcp .tool ()
91- def list_mvrs (urn : str | None = None , passed : bool | None = None ) -> list [dict ]:
93+ async def list_mvrs (urn : str | None = None , passed : bool | None = None ) -> list [dict ]:
9294 """List MVRs with id and passed status. Filter by urn and/or passed (True|False)."""
9395 return get_mvrs_list (repo , urn = urn , passed = passed )
9496
9597 @mcp .tool ()
96- def get_mvr (id : str ) -> dict :
98+ async def get_mvr (id : str ) -> dict :
9799 """Get full details for an MVR by ID."""
98100 result = get_mvr_details (id , repo , urn_source_paths )
99101 if result is None :
100102 raise ValueError (f"MVR { id !r} not found" )
101103 return result
102104
103105 @mcp .tool ()
104- def get_status () -> dict :
106+ async def get_status () -> dict :
105107 """Get overall traceability status — completion per requirement, test totals."""
106108 return StatisticsService (repo ).to_status_dict ()
107109
108110 @mcp .tool ()
109- def get_requirement_status (id : str , include_post_build : bool = False ) -> dict :
111+ async def get_requirement_status (id : str , include_post_build : bool = False ) -> dict :
110112 """Status check for one requirement: lifecycle_state, completed, implementation_type,
111113 automated_tests, manual_tests. Set include_post_build=True for parity with
112114 `status --with-post-tests` (scopes to post-build-phase SVCs too)."""
@@ -116,7 +118,7 @@ def get_requirement_status(id: str, include_post_build: bool = False) -> dict:
116118 return result
117119
118120 @mcp .tool ()
119- def list_annotations (urn : str | None = None ) -> list [dict ]:
121+ async def list_annotations (urn : str | None = None ) -> list [dict ]:
120122 """List implementation annotations (@Requirements) found in source code. Optionally filter by URN."""
121123 impl_annotations = repo .get_annotations_impls (urn = urn )
122124 result = []
@@ -133,20 +135,20 @@ def list_annotations(urn: str | None = None) -> list[dict]:
133135 return result
134136
135137 @mcp .tool ()
136- def list_urns () -> list [dict ]:
138+ async def list_urns () -> list [dict ]:
137139 """List all URNs in the project graph with variant, title, url, location, and file paths."""
138140 return get_urns_list (repo , urn_source_paths )
139141
140142 @mcp .tool ()
141- def get_urn_details (urn : str ) -> dict :
143+ async def get_urn_details (urn : str ) -> dict :
142144 """Get details for a URN: variant, title, location, file paths, and entity counts."""
143145 result = _get_urn_details (urn , repo , urn_source_paths )
144146 if result is None :
145147 raise ValueError (f"URN { urn !r} not found" )
146148 return result
147149
148150 @mcp .tool ()
149- def enrich_document (content : str , preset : str ) -> str :
151+ async def enrich_document (content : str , preset : str ) -> str :
150152 """Enrich an OpenSpec document by resolving requirement/SVC/MVR IDs.
151153
152154 Injects titles and further fields next to each known ID according to the
@@ -162,6 +164,6 @@ def enrich_document(content: str, preset: str) -> str:
162164
163165 try :
164166 logger .info ("Starting reqstool MCP server (transport=%s, host=%s, port=%s)" , transport , host , port )
165- mcp .run (transport = transport )
167+ mcp .run (transport = transport , ** run_kwargs )
166168 finally :
167169 session .close ()
0 commit comments