guohanghui commited on
Commit
1f2bcd4
·
verified ·
1 Parent(s): dc18577

Update autodE/mcp_output/mcp_plugin/mcp_service.py

Browse files
autodE/mcp_output/mcp_plugin/mcp_service.py CHANGED
@@ -1,148 +1,423 @@
 
 
 
 
 
 
 
 
1
  from fastmcp import FastMCP
2
 
 
 
 
 
 
 
 
 
 
3
  # Create the FastMCP service application
4
  mcp = FastMCP("autode_service")
5
 
6
- @mcp.tool(name="optimize_geometry", description="Optimize the geometry of a molecule")
7
- def optimize_geometry(molecule: dict) -> dict:
 
 
 
 
8
  """
9
- Optimize the geometry of a molecule using autode.
10
 
11
  Parameters:
12
- - molecule: A dictionary representing the molecule.
 
13
 
14
  Returns:
15
- - dict: Optimized geometry and energy.
16
  """
 
 
 
17
  try:
18
- from autode.species.molecule import Molecule
19
- from autode.opt.optimisers import BFGS
20
-
21
- mol = Molecule(**molecule)
22
- mol.optimise(method=BFGS())
23
-
24
  return {
25
  "success": True,
26
- "optimized_geometry": mol.coordinates,
27
- "energy": mol.energy
 
 
 
 
28
  }
29
  except Exception as e:
30
  return {"success": False, "error": str(e)}
31
 
32
- @mcp.tool(name="calculate_reaction_energy", description="Calculate the reaction energy for a given reaction")
33
- def calculate_reaction_energy(reactants: list, products: list) -> dict:
 
34
  """
35
- Calculate the reaction energy for a given reaction.
36
 
37
  Parameters:
38
- - reactants: A list of reactant molecules.
39
- - products: A list of product molecules.
40
 
41
  Returns:
42
- - dict: Reaction energy.
43
  """
 
 
 
44
  try:
45
- from autode.reactions.reaction import Reaction
46
- from autode.species.molecule import Molecule
47
-
48
- reactant_mols = [Molecule(**r) for r in reactants]
49
- product_mols = [Molecule(**p) for p in products]
50
-
51
- reaction = Reaction(*reactant_mols, *product_mols)
52
- reaction.calculate_reaction_energy()
53
-
54
  return {
55
  "success": True,
56
- "reaction_energy": reaction.reaction_energy
 
 
 
57
  }
58
  except Exception as e:
59
  return {"success": False, "error": str(e)}
60
 
61
- @mcp.tool(name="generate_conformers", description="Generate conformers for a molecule")
62
- def generate_conformers(molecule: dict) -> dict:
 
 
 
 
 
 
63
  """
64
- Generate conformers for a molecule.
65
 
66
  Parameters:
67
- - molecule: A dictionary representing the molecule.
 
 
 
68
 
69
  Returns:
70
- - dict: Generated conformers.
71
  """
 
 
 
72
  try:
73
- from autode.conformers.conformers import Conformers
74
- from autode.species.molecule import Molecule
75
-
76
- mol = Molecule(**molecule)
77
- conformers = Conformers.generate(mol)
78
 
79
  return {
80
  "success": True,
81
- "conformers": [conf.coordinates for conf in conformers]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  }
83
  except Exception as e:
84
  return {"success": False, "error": str(e)}
85
 
86
- @mcp.tool(name="locate_transition_state", description="Locate the transition state for a reaction")
87
- def locate_transition_state(reactants: list, products: list) -> dict:
 
88
  """
89
- Locate the transition state for a reaction.
90
 
91
  Parameters:
92
- - reactants: A list of reactant molecules.
93
- - products: A list of product molecules.
94
 
95
  Returns:
96
- - dict: Transition state geometry and energy.
97
  """
 
 
 
98
  try:
99
- from autode.transition_states.transition_state import TransitionState
100
- from autode.species.molecule import Molecule
101
 
102
- reactant_mols = [Molecule(**r) for r in reactants]
103
- product_mols = [Molecule(**p) for p in products]
104
-
105
- ts = TransitionState(reactants=reactant_mols, products=product_mols)
106
- ts.locate()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
 
108
  return {
109
  "success": True,
110
- "ts_geometry": ts.coordinates,
111
- "ts_energy": ts.energy
 
112
  }
113
  except Exception as e:
114
  return {"success": False, "error": str(e)}
115
 
116
- @mcp.tool(name="calculate_hessian", description="Calculate the Hessian matrix for a molecule")
117
- def calculate_hessian(molecule: dict) -> dict:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  """
119
- Calculate the Hessian matrix for a molecule.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
  Parameters:
122
- - molecule: A dictionary representing the molecule.
 
 
 
123
 
124
  Returns:
125
- - dict: Hessian matrix.
126
  """
 
 
 
127
  try:
128
- from autode.hessians.hessians import Hessian
129
- from autode.species.molecule import Molecule
 
 
 
 
 
 
 
 
130
 
131
- mol = Molecule(**molecule)
132
- hessian = Hessian(mol)
133
 
134
  return {
135
  "success": True,
136
- "hessian_matrix": hessian.matrix
 
 
 
137
  }
138
  except Exception as e:
139
  return {"success": False, "error": str(e)}
140
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  def create_app() -> FastMCP:
142
  """
143
  Create and return the FastMCP application instance.
144
 
145
  Returns:
146
- - FastMCP: The FastMCP application instance.
147
  """
148
  return mcp
 
1
+ import os
2
+ import sys
3
+
4
+ # Add the local source directory to sys.path
5
+ source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
6
+ if source_path not in sys.path:
7
+ sys.path.insert(0, source_path)
8
+
9
  from fastmcp import FastMCP
10
 
11
+ # Check if autodE is available
12
+ try:
13
+ import autode as ade
14
+ from autode import Atom, Molecule, Reactant, Product, Reaction, Config
15
+ from autode import Species
16
+ AUTODE_AVAILABLE = True
17
+ except ImportError:
18
+ AUTODE_AVAILABLE = False
19
+
20
  # Create the FastMCP service application
21
  mcp = FastMCP("autode_service")
22
 
23
+ # ============================================================================
24
+ # Molecule and Species Creation Tools
25
+ # ============================================================================
26
+
27
+ @mcp.tool()
28
+ def create_molecule_from_smiles(smiles: str, name: str = "molecule") -> dict:
29
  """
30
+ Create a molecule from a SMILES string.
31
 
32
  Parameters:
33
+ - smiles: SMILES string representation (e.g., 'CCO' for ethanol)
34
+ - name: Name for the molecule (default 'molecule')
35
 
36
  Returns:
37
+ - dict: Molecule information including atoms count and charge
38
  """
39
+ if not AUTODE_AVAILABLE:
40
+ return {"success": False, "error": "autodE not installed. Requires: rdkit, numpy, networkx, and more"}
41
+
42
  try:
43
+ mol = Molecule(smiles=smiles, name=name)
 
 
 
 
 
44
  return {
45
  "success": True,
46
+ "name": mol.name,
47
+ "smiles": smiles,
48
+ "n_atoms": mol.n_atoms,
49
+ "charge": mol.charge,
50
+ "mult": mol.mult,
51
+ "formula": str(mol.formula) if hasattr(mol, 'formula') else None
52
  }
53
  except Exception as e:
54
  return {"success": False, "error": str(e)}
55
 
56
+
57
+ @mcp.tool()
58
+ def get_atom_properties(element: str) -> dict:
59
  """
60
+ Get properties of an atom given its element symbol.
61
 
62
  Parameters:
63
+ - element: Chemical symbol (e.g., 'H', 'C', 'O', 'N')
 
64
 
65
  Returns:
66
+ - dict: Atom properties including mass and atomic number
67
  """
68
+ if not AUTODE_AVAILABLE:
69
+ return {"success": False, "error": "autodE not installed"}
70
+
71
  try:
72
+ atom = Atom(element)
 
 
 
 
 
 
 
 
73
  return {
74
  "success": True,
75
+ "element": element,
76
+ "atomic_number": atom.atomic_number,
77
+ "atomic_mass": atom.mass,
78
+ "atomic_symbol": atom.label
79
  }
80
  except Exception as e:
81
  return {"success": False, "error": str(e)}
82
 
83
+
84
+ # ============================================================================
85
+ # Reaction Setup Tools
86
+ # ============================================================================
87
+
88
+ @mcp.tool()
89
+ def create_reaction_from_smiles(reactant_smiles: str, product_smiles: str,
90
+ name: str = "reaction", solvent_name: str = None) -> dict:
91
  """
92
+ Create a reaction from reactant and product SMILES strings.
93
 
94
  Parameters:
95
+ - reactant_smiles: SMILES of reactant(s), separate multiple with '.'
96
+ - product_smiles: SMILES of product(s), separate multiple with '.'
97
+ - name: Name for the reaction (default 'reaction')
98
+ - solvent_name: Solvent name if needed (optional)
99
 
100
  Returns:
101
+ - dict: Reaction information
102
  """
103
+ if not AUTODE_AVAILABLE:
104
+ return {"success": False, "error": "autodE not installed"}
105
+
106
  try:
107
+ # Create reaction using simplified notation
108
+ reaction_smiles = f"{reactant_smiles}>>{product_smiles}"
109
+ rxn = Reaction(reaction_smiles, name=name, solvent_name=solvent_name)
 
 
110
 
111
  return {
112
  "success": True,
113
+ "name": rxn.name,
114
+ "reaction_smiles": reaction_smiles,
115
+ "n_reactants": len(rxn.reacs),
116
+ "n_products": len(rxn.prods),
117
+ "solvent": solvent_name
118
+ }
119
+ except Exception as e:
120
+ return {"success": False, "error": str(e)}
121
+
122
+
123
+ # ============================================================================
124
+ # Configuration Tools
125
+ # ============================================================================
126
+
127
+ @mcp.tool()
128
+ def get_config_info() -> dict:
129
+ """
130
+ Get current autodE configuration settings.
131
+
132
+ Returns:
133
+ - dict: Configuration parameters
134
+ """
135
+ if not AUTODE_AVAILABLE:
136
+ return {"success": False, "error": "autodE not installed"}
137
+
138
+ try:
139
+ return {
140
+ "success": True,
141
+ "n_cores": Config.n_cores,
142
+ "max_core": Config.max_core,
143
+ "hcode": Config.hcode,
144
+ "lcode": Config.lcode,
145
+ "ts_guess_methods": Config.ts_guess_methods if hasattr(Config, 'ts_guess_methods') else None,
146
+ "num_conformers": Config.num_conformers if hasattr(Config, 'num_conformers') else None
147
  }
148
  except Exception as e:
149
  return {"success": False, "error": str(e)}
150
 
151
+
152
+ @mcp.tool()
153
+ def set_n_cores(n_cores: int) -> dict:
154
  """
155
+ Set the number of CPU cores to use for calculations.
156
 
157
  Parameters:
158
+ - n_cores: Number of cores (positive integer)
 
159
 
160
  Returns:
161
+ - dict: Updated configuration
162
  """
163
+ if not AUTODE_AVAILABLE:
164
+ return {"success": False, "error": "autodE not installed"}
165
+
166
  try:
167
+ if n_cores < 1:
168
+ return {"success": False, "error": "n_cores must be at least 1"}
169
 
170
+ Config.n_cores = n_cores
171
+ return {
172
+ "success": True,
173
+ "n_cores": Config.n_cores,
174
+ "message": f"Set to use {n_cores} cores"
175
+ }
176
+ except Exception as e:
177
+ return {"success": False, "error": str(e)}
178
+
179
+
180
+ @mcp.tool()
181
+ def list_available_methods() -> dict:
182
+ """
183
+ List available computational chemistry methods/codes.
184
+
185
+ Returns:
186
+ - dict: Available high-level and low-level methods
187
+ """
188
+ if not AUTODE_AVAILABLE:
189
+ return {"success": False, "error": "autodE not installed"}
190
+
191
+ try:
192
+ # Common methods autodE supports
193
+ available_methods = {
194
+ "high_level": [
195
+ "orca",
196
+ "g09", "g16", # Gaussian
197
+ "qchem",
198
+ "nwchem",
199
+ "psi4"
200
+ ],
201
+ "low_level": [
202
+ "xtb",
203
+ "mopac",
204
+ "orca"
205
+ ],
206
+ "description": {
207
+ "orca": "ORCA - general purpose quantum chemistry",
208
+ "g09/g16": "Gaussian - widely used QM package",
209
+ "xtb": "GFN-xTB - fast semi-empirical method",
210
+ "mopac": "MOPAC - semi-empirical methods",
211
+ "qchem": "Q-Chem",
212
+ "psi4": "Psi4 - open-source QM",
213
+ "nwchem": "NWChem"
214
+ }
215
+ }
216
 
217
  return {
218
  "success": True,
219
+ "methods": available_methods,
220
+ "current_hcode": Config.hcode,
221
+ "current_lcode": Config.lcode
222
  }
223
  except Exception as e:
224
  return {"success": False, "error": str(e)}
225
 
226
+
227
+ # ============================================================================
228
+ # Information and Utility Tools
229
+ # ============================================================================
230
+
231
+ @mcp.tool()
232
+ def get_reaction_example() -> dict:
233
+ """
234
+ Get an example of setting up and running a reaction calculation.
235
+
236
+ Returns:
237
+ - dict: Example code for Diels-Alder reaction
238
+ """
239
+ if not AUTODE_AVAILABLE:
240
+ return {"success": False, "error": "autodE not installed"}
241
+
242
+ example_code = """
243
+ import autode as ade
244
+
245
+ # Configure
246
+ ade.Config.n_cores = 4
247
+
248
+ # Create reaction from SMILES (Diels-Alder)
249
+ rxn = ade.Reaction('C=CC=C.C=C>>C1=CCCCC1', name='diels_alder')
250
+
251
+ # Calculate reaction profile (requires QM software installed)
252
+ # rxn.calculate_reaction_profile()
253
+
254
+ # Access results
255
+ # print(f"Reactants: {rxn.reacs}")
256
+ # print(f"Products: {rxn.prods}")
257
+ # print(f"Transition state: {rxn.ts}")
258
+ """
259
+
260
+ return {
261
+ "success": True,
262
+ "description": "Diels-Alder cycloaddition example",
263
+ "reaction": "Butadiene + Ethylene -> Cyclohexene",
264
+ "code": example_code,
265
+ "note": "Requires external QM software (ORCA, Gaussian, etc.) to be installed"
266
+ }
267
+
268
+
269
+ @mcp.tool()
270
+ def get_sn2_example() -> dict:
271
  """
272
+ Get an example of an SN2 reaction setup.
273
+
274
+ Returns:
275
+ - dict: Example code for SN2 reaction
276
+ """
277
+ if not AUTODE_AVAILABLE:
278
+ return {"success": False, "error": "autodE not installed"}
279
+
280
+ example_code = """
281
+ import autode as ade
282
+
283
+ # SN2 reaction: Cl- + CH3Br -> CH3Cl + Br-
284
+ rxn = ade.Reaction('[Cl-].CBr>>[Br-].CCl', name='sn2')
285
+
286
+ # Calculate reaction profile
287
+ # rxn.calculate_reaction_profile()
288
+
289
+ # Access energies
290
+ # delta_e = rxn.delta('energy')
291
+ # delta_h = rxn.delta('enthalpy')
292
+ # delta_g = rxn.delta('free_energy')
293
+ """
294
+
295
+ return {
296
+ "success": True,
297
+ "description": "SN2 nucleophilic substitution example",
298
+ "reaction": "Cl- + CH3Br -> CH3Cl + Br-",
299
+ "code": example_code
300
+ }
301
+
302
+
303
+ @mcp.tool()
304
+ def get_molecule_from_xyz(xyz_string: str, name: str = "molecule",
305
+ charge: int = 0, mult: int = 1) -> dict:
306
+ """
307
+ Create a molecule from XYZ coordinate string.
308
 
309
  Parameters:
310
+ - xyz_string: XYZ format coordinates (element x y z per line)
311
+ - name: Molecule name (default 'molecule')
312
+ - charge: Molecular charge (default 0)
313
+ - mult: Spin multiplicity (default 1)
314
 
315
  Returns:
316
+ - dict: Molecule information
317
  """
318
+ if not AUTODE_AVAILABLE:
319
+ return {"success": False, "error": "autodE not installed"}
320
+
321
  try:
322
+ # Parse XYZ string
323
+ lines = xyz_string.strip().split('\n')
324
+ atoms = []
325
+
326
+ for line in lines:
327
+ parts = line.split()
328
+ if len(parts) >= 4:
329
+ element = parts[0]
330
+ x, y, z = float(parts[1]), float(parts[2]), float(parts[3])
331
+ atoms.append(Atom(element, x=x, y=y, z=z))
332
 
333
+ mol = Molecule(name=name, charge=charge, mult=mult, atoms=atoms)
 
334
 
335
  return {
336
  "success": True,
337
+ "name": mol.name,
338
+ "n_atoms": mol.n_atoms,
339
+ "charge": mol.charge,
340
+ "mult": mol.mult
341
  }
342
  except Exception as e:
343
  return {"success": False, "error": str(e)}
344
 
345
+
346
+ @mcp.tool()
347
+ def list_supported_solvents() -> dict:
348
+ """
349
+ List commonly supported solvent names.
350
+
351
+ Returns:
352
+ - dict: Available solvent names
353
+ """
354
+ if not AUTODE_AVAILABLE:
355
+ return {"success": False, "error": "autodE not installed"}
356
+
357
+ solvents = [
358
+ "water", "h2o",
359
+ "methanol", "meoh",
360
+ "ethanol", "etoh",
361
+ "acetone",
362
+ "acetonitrile", "mecn",
363
+ "dmso",
364
+ "dmf",
365
+ "thf",
366
+ "dichloromethane", "dcm",
367
+ "chloroform", "chcl3",
368
+ "benzene",
369
+ "toluene",
370
+ "hexane",
371
+ "diethyl ether", "ether"
372
+ ]
373
+
374
+ return {
375
+ "success": True,
376
+ "solvents": solvents,
377
+ "note": "Solvent availability depends on the QM method used"
378
+ }
379
+
380
+
381
+ @mcp.tool()
382
+ def get_autode_info() -> dict:
383
+ """
384
+ Get autodE version and installation information.
385
+
386
+ Returns:
387
+ - dict: Version and capabilities
388
+ """
389
+ if not AUTODE_AVAILABLE:
390
+ return {
391
+ "success": False,
392
+ "available": False,
393
+ "message": "autodE not installed. Requires: pip install autode rdkit numpy networkx scipy matplotlib"
394
+ }
395
+
396
+ try:
397
+ return {
398
+ "success": True,
399
+ "available": True,
400
+ "version": ade.__version__,
401
+ "description": "Automated reaction profile generation for organic and organometallic reactions",
402
+ "capabilities": [
403
+ "Automated transition state search",
404
+ "Reaction profile calculation",
405
+ "Conformer generation",
406
+ "NEB calculations",
407
+ "Multistep reactions"
408
+ ],
409
+ "github": "https://github.com/duartegroup/autodE",
410
+ "docs": "https://duartegroup.github.io/autodE/"
411
+ }
412
+ except Exception as e:
413
+ return {"success": False, "error": str(e)}
414
+
415
+
416
  def create_app() -> FastMCP:
417
  """
418
  Create and return the FastMCP application instance.
419
 
420
  Returns:
421
+ - FastMCP: The FastMCP service instance.
422
  """
423
  return mcp