Skip to content

Commit d465dbf

Browse files
committed
fix linter
Signed-off-by: Felipe Garay <[email protected]>
1 parent eb3d4ea commit d465dbf

File tree

2 files changed

+140
-121
lines changed

2 files changed

+140
-121
lines changed

openroad/mcp/BUILD.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
load("@openroad_rules_python//python:defs.bzl", "py_binary")
21
load("@openroad-pip//:requirements.bzl", "requirement")
2+
load("@openroad_rules_python//python:defs.bzl", "py_binary")
33

44
py_binary(
55
name = "server",

openroad/mcp/server.py

Lines changed: 139 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -8,153 +8,173 @@
88

99
# Create an MCP server
1010
mcp = FastMCP(
11-
name = "OpenROAD",
12-
instructions = """
11+
name="OpenROAD",
12+
instructions="""
1313
This server provides an instance of OpenROAD.
1414
OpenROAD's unified application implementing an RTL-to-GDS Flow. Documentation at https://openroad.readthedocs.io/en/latest/
1515
This MCP server expose several tools that you can use to interact with the current instance.
1616
You don't need to write tcl files, you can direclty use run_tcl to send commands to openroad.
1717
At the begining, check what is the current path OpenROAD is running in with "pwd" and change it if needed with "cd".
18-
"""
18+
""",
1919
)
2020

2121
tech = Tech()
2222
design = Design(tech)
2323

24+
2425
def eval_tcl(command):
25-
# Create a temporary file to capture stdout
26-
with tempfile.TemporaryFile(mode='w+') as tmp:
27-
# Flush stdout to ensure order
28-
sys.stdout.flush()
29-
30-
# Save original stdout
31-
original_stdout_fd = os.dup(1)
32-
33-
try:
34-
# Redirect stdout to the temporary file
35-
os.dup2(tmp.fileno(), 1)
36-
37-
# Run the command
38-
tcl_result = design.evalTclString(command)
39-
finally:
40-
# Restore stdout
41-
os.dup2(original_stdout_fd, 1)
42-
os.close(original_stdout_fd)
43-
44-
# Read the captured output
45-
tmp.seek(0)
46-
stdout_output = tmp.read()
47-
48-
if stdout_output:
49-
return stdout_output
50-
return tcl_result
26+
# Create a temporary file to capture stdout
27+
with tempfile.TemporaryFile(mode="w+") as tmp:
28+
# Flush stdout to ensure order
29+
sys.stdout.flush()
30+
31+
# Save original stdout
32+
original_stdout_fd = os.dup(1)
33+
34+
try:
35+
# Redirect stdout to the temporary file
36+
os.dup2(tmp.fileno(), 1)
37+
38+
# Run the command
39+
tcl_result = design.evalTclString(command)
40+
finally:
41+
# Restore stdout
42+
os.dup2(original_stdout_fd, 1)
43+
os.close(original_stdout_fd)
44+
45+
# Read the captured output
46+
tmp.seek(0)
47+
stdout_output = tmp.read()
48+
49+
if stdout_output:
50+
return stdout_output
51+
return tcl_result
52+
5153

5254
@mcp.tool
5355
def source(filename: str) -> str:
54-
"""Sources a tcl file and runs it into OpenROAD
56+
"""Sources a tcl file and runs it into OpenROAD
5557
56-
Args:
57-
filename: The absolute path to the tcl file to source and run
5858
59-
Returns
60-
The status of the command.
61-
"""
59+
Args:
60+
filename: The absolute path to the tcl file to source and run
61+
62+
63+
Returns
64+
The status of the command.
65+
"""
66+
67+
return eval_tcl("source %s" % filename)
6268

63-
return eval_tcl("source %s" %filename)
6469

6570
@mcp.tool
6671
def cd(path: str) -> str:
67-
"""Changes the current folder where OpenROAD is running
72+
"""Changes the current folder where OpenROAD is running
6873
69-
Args:
70-
path: The new path were OpenROAD will run from
7174
72-
Returns
73-
The status of the command.
74-
"""
75+
Args:
76+
path: The new path were OpenROAD will run from
77+
78+
79+
Returns
80+
The status of the command.
81+
"""
82+
83+
return eval_tcl("cd %s" % path)
7584

76-
return eval_tcl("cd %s" %path)
7785

7886
@mcp.tool
7987
def pwd() -> str:
80-
"""Returns the current path where OpenROAD is running
88+
"""Returns the current path where OpenROAD is running
8189
82-
Returns
83-
The path where OpenROAD is running
84-
"""
8590
86-
return eval_tcl("pwd")
91+
Returns
92+
The path where OpenROAD is running
93+
"""
94+
95+
return eval_tcl("pwd")
8796

8897

8998
@mcp.tool
9099
def run_tcl(command: str) -> str:
91-
"""Runs any tcl command available on OpenROAD
100+
"""Runs any tcl command available on OpenROAD
92101
93-
Args:
94-
command: A tcl command to run on this OpenROAD instance
95102
96-
Returns:
97-
The text response of the command.
98-
"""
99-
return eval_tcl(command)
103+
Args:
104+
command: A tcl command to run on this OpenROAD instance
105+
106+
107+
Returns:
108+
The text response of the command.
109+
"""
110+
return eval_tcl(command)
111+
100112

101113
@mcp.tool
102114
def read_lef(filename: str, tech: bool, library: bool) -> str:
103-
"""Reads Library Exchange Format (.lef) files or tech files or library (.lib) files
115+
"""Reads Library Exchange Format (.lef) files or tech files or library (.lib) files
104116
105-
Args:
106-
filename: The absolute path to the file to read.
107-
tech: boolean if we are reading a tech file
108-
library: boolean if we are reading a library file
109117
110-
Returns
111-
The status of the command.
118+
Args:
119+
filename: The absolute path to the file to read.
120+
tech: boolean if we are reading a tech file
121+
library: boolean if we are reading a library file
122+
123+
124+
Returns
125+
The status of the command.
112126
113-
"""
114127
115-
if tech:
116-
return eval_tcl("read_lef -tech %s" %filename)
117-
if library:
118-
return eval_tcl("read_lef -library %s" %filename)
119-
return eval_tcl("read_lef %s" %filename)
128+
"""
129+
130+
if tech:
131+
return eval_tcl("read_lef -tech %s" % filename)
132+
if library:
133+
return eval_tcl("read_lef -library %s" % filename)
134+
return eval_tcl("read_lef %s" % filename)
120135

121136

122137
@mcp.tool
123138
def read_def(filename: str) -> str:
124-
"""Read Design Exchange Format (.def) files.
139+
"""Read Design Exchange Format (.def) files.
125140
126-
Args:
127-
filename: The absolute path to the file to read.
128141
129-
Returns
130-
The status of the command.
131-
"""
142+
Args:
143+
filename: The absolute path to the file to read.
132144
133-
return eval_tcl("read_def %s" %filename)
145+
146+
Returns
147+
The status of the command.
148+
"""
149+
150+
return eval_tcl("read_def %s" % filename)
134151

135152

136153
@mcp.tool
137154
def read_verilog(filename: str) -> str:
138-
"""Read Verilog (.v) input file.
155+
"""Read Verilog (.v) input file.
139156
140-
Args:
141-
filename: The absolute path to the verilog file to read.
142157
143-
Returns
144-
The status of the command.
145-
"""
158+
Args:
159+
filename: The absolute path to the verilog file to read.
160+
161+
162+
Returns
163+
The status of the command.
164+
"""
165+
166+
return eval_tcl("read_verilog %s" % filename)
146167

147-
return eval_tcl("read_verilog %s" %filename)
148168

149169
@mcp.tool()
150170
def get_property(
151171
object_name: str,
152172
object_type: Literal["cell", "net", "pin", "port", "lib_cell"],
153-
property_name: str
173+
property_name: str,
154174
) -> str:
155175
"""
156176
Queries a specific property of a design object in the OpenROAD database.
157-
177+
158178
Use this to inspect specific details of the design, such as the area of a cell,
159179
the capacitance of a net, or the reference name of an instance.
160180
@@ -171,27 +191,23 @@ def get_property(
171191
- For nets: "capacitance", "weight"
172192
- For pins: "direction", "arrival_window"
173193
"""
174-
194+
175195
selector_map = {
176196
"cell": "get_cells",
177197
"net": "get_nets",
178198
"pin": "get_pins",
179199
"port": "get_ports",
180-
"lib_cell": "get_lib_cells"
200+
"lib_cell": "get_lib_cells",
181201
}
182-
202+
183203
selector_cmd = selector_map.get(object_type)
184-
204+
185205
tcl_command = f"get_property [{selector_cmd} {{{object_name}}}] {property_name}"
186206
return eval_tcl(tcl_command)
187207

188208

189209
@mcp.tool()
190-
def get_cells(
191-
pattern: str = "*",
192-
hierarchical: bool = False,
193-
limit: int = 20
194-
) -> str:
210+
def get_cells(pattern: str = "*", hierarchical: bool = False, limit: int = 20) -> str:
195211
"""
196212
Searches for cell instances in the design matching a specific name pattern.
197213
Returns a list of instance names (e.g., "u_core/u_alu/u_adder").
@@ -238,53 +254,56 @@ def get_cells(
238254

239255
return eval_tcl(tcl_command)
240256

257+
241258
@mcp.tool
242259
def reset() -> str:
243-
"""
244-
Resets OpenROAD dropping the database.
245-
WARNING: You will need to start from the begining loading all the libraries and designs again.
246-
Use this command if you need to change something and OpenROAD does not allow you to do it after loading a design like re-defining corners.
247-
"""
248-
tcl_command = """
260+
"""
261+
Resets OpenROAD dropping the database.
262+
WARNING: You will need to start from the begining loading all the libraries and designs again.
263+
Use this command if you need to change something and OpenROAD does not allow you to do it after loading a design like re-defining corners.
264+
"""
265+
tcl_command = """
249266
set db [::ord::get_db]
250267
$db clear
251268
dbDatabase_destroy $db
252269
set db [dbDatabase_create]
253270
"""
254-
return eval_tcl(tcl_command)
271+
return eval_tcl(tcl_command)
255272

256273

257274
# Timing tools
258275
@mcp.tool
259276
def report_wns() -> str:
260-
"""
261-
Calculates and reports the Worst Negative Slack (WNS) for the currently
262-
loaded Verilog design in OpenROAD.
277+
"""
278+
Calculates and reports the Worst Negative Slack (WNS) for the currently
279+
loaded Verilog design in OpenROAD.
280+
281+
WNS is the critical metric for timing closure; a negative value indicates
282+
that the design does not meet frequency constraints.
283+
"""
284+
return eval_tcl("report_wns")
263285

264-
WNS is the critical metric for timing closure; a negative value indicates
265-
that the design does not meet frequency constraints.
266-
"""
267-
return eval_tcl("report_wns")
268286

269287
@mcp.tool()
270288
def report_tns(digits: int = 2) -> str:
271-
"""
272-
Calculates and reports the Total Negative Slack (TNS) for the currently
273-
loaded Verilog design in OpenROAD.
274-
275-
TNS is the sum of all negative slack across all failing endpoints in the
276-
design. While WNS (Worst Negative Slack) indicates the speed limit, TNS
277-
indicates the *magnitude* of the failing paths. A large TNS value suggests
278-
many paths are failing and the design may need significant architectural
279-
changes or different optimization strategies.
280-
"""
281-
return eval_tcl("report_tns")
289+
"""
290+
Calculates and reports the Total Negative Slack (TNS) for the currently
291+
loaded Verilog design in OpenROAD.
292+
293+
TNS is the sum of all negative slack across all failing endpoints in the
294+
design. While WNS (Worst Negative Slack) indicates the speed limit, TNS
295+
indicates the *magnitude* of the failing paths. A large TNS value suggests
296+
many paths are failing and the design may need significant architectural
297+
changes or different optimization strategies.
298+
"""
299+
return eval_tcl("report_tns")
300+
282301

283302
@mcp.tool()
284303
def report_checks(
285304
path_delay: Literal["min", "max", "min_max"] = "max",
286305
group_count: int = 1,
287-
detailed: bool = False
306+
detailed: bool = False,
288307
) -> str:
289308
"""
290309
Performs detailed timing path analysis (report_checks) on the current design.
@@ -320,4 +339,4 @@ def report_checks(
320339
if __name__ == "__main__":
321340
print(os.getcwd())
322341
# Run the server
323-
mcp.run(transport="http", host="127.0.0.1", port=8000)
342+
mcp.run(transport="http", host="127.0.0.1", port=8000)

0 commit comments

Comments
 (0)