rowan-mcp 2.0.0__py3-none-any.whl → 2.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of rowan-mcp might be problematic. Click here for more details.

Files changed (31) hide show
  1. rowan_mcp/functions/admet.py +89 -0
  2. rowan_mcp/functions/bde.py +106 -0
  3. rowan_mcp/functions/calculation_retrieve.py +89 -0
  4. rowan_mcp/functions/conformers.py +77 -0
  5. rowan_mcp/functions/descriptors.py +89 -0
  6. rowan_mcp/functions/docking.py +290 -0
  7. rowan_mcp/functions/docking_enhanced.py +174 -0
  8. rowan_mcp/functions/electronic_properties.py +202 -0
  9. rowan_mcp/functions/folder_management.py +130 -0
  10. rowan_mcp/functions/fukui.py +216 -0
  11. rowan_mcp/functions/hydrogen_bond_basicity.py +87 -0
  12. rowan_mcp/functions/irc.py +125 -0
  13. rowan_mcp/functions/macropka.py +120 -0
  14. rowan_mcp/functions/molecular_converter.py +423 -0
  15. rowan_mcp/functions/molecular_dynamics.py +191 -0
  16. rowan_mcp/functions/molecule_lookup.py +57 -0
  17. rowan_mcp/functions/multistage_opt.py +168 -0
  18. rowan_mcp/functions/pdb_handler.py +200 -0
  19. rowan_mcp/functions/pka.py +81 -0
  20. rowan_mcp/functions/redox_potential.py +349 -0
  21. rowan_mcp/functions/scan.py +536 -0
  22. rowan_mcp/functions/scan_analyzer.py +347 -0
  23. rowan_mcp/functions/solubility.py +277 -0
  24. rowan_mcp/functions/spin_states.py +747 -0
  25. rowan_mcp/functions/system_management.py +361 -0
  26. rowan_mcp/functions/tautomers.py +88 -0
  27. rowan_mcp/functions/workflow_management.py +422 -0
  28. {rowan_mcp-2.0.0.dist-info → rowan_mcp-2.0.1.dist-info}/METADATA +3 -18
  29. {rowan_mcp-2.0.0.dist-info → rowan_mcp-2.0.1.dist-info}/RECORD +31 -4
  30. {rowan_mcp-2.0.0.dist-info → rowan_mcp-2.0.1.dist-info}/WHEEL +0 -0
  31. {rowan_mcp-2.0.0.dist-info → rowan_mcp-2.0.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,422 @@
1
+ """
2
+ Rowan workflow management functions for MCP tool integration.
3
+ """
4
+
5
+ from typing import Optional, Dict, Any, Union, List, Literal
6
+ import rowan
7
+
8
+ def safe_get_attr(obj, attr: str, default=None):
9
+ """Safely get an attribute from an object, returning default if it doesn't exist."""
10
+ try:
11
+ return getattr(obj, attr, default)
12
+ except (AttributeError, TypeError):
13
+ return default
14
+
15
+ def rowan_workflow_management(
16
+ action: Literal['create', 'retrieve', 'update', 'stop', 'status', 'is_finished', 'delete', 'list'],
17
+ workflow_uuid: Optional[str] = None,
18
+ name: Optional[str] = None,
19
+ workflow_type: Optional[str] = None,
20
+ initial_molecule: Optional[str] = None,
21
+ parent_uuid: Optional[str] = None,
22
+ notes: Optional[str] = None,
23
+ starred: Optional[bool] = None,
24
+ public: Optional[bool] = None,
25
+ email_when_complete: Optional[bool] = None,
26
+ workflow_data: Optional[Dict[str, Any]] = None,
27
+ name_contains: Optional[str] = None,
28
+ object_status: Optional[int] = None,
29
+ object_type: Optional[str] = None,
30
+ page: int = 0,
31
+ size: int = 50
32
+ ) -> str:
33
+ """Unified workflow management tool for all workflow operations. Available actions: create, retrieve, update, stop, status, is_finished, delete, list.
34
+
35
+ **Available Actions:**
36
+ - **create**: Create a new workflow (requires: name, workflow_type, initial_molecule)
37
+ - **retrieve**: retrieve workflow details (requires: workflow_uuid)
38
+ - **update**: Update workflow properties (requires: workflow_uuid, optional: name, parent_uuid, notes, starred, public, email_when_complete)
39
+ - **stop**: Stop a running workflow (requires: workflow_uuid)
40
+ - **status**: Check workflow status (requires: workflow_uuid)
41
+ - **is_finished**: Check if workflow is finished (requires: workflow_uuid)
42
+ - **delete**: Delete a workflow (requires: workflow_uuid)
43
+ - **list**: List workflows with filters (optional: name_contains, parent_uuid, starred, public, object_status, object_type, page, size)
44
+
45
+ Args:
46
+ action: Action to perform - must be one of: 'create', 'retrieve', 'update', 'stop', 'status', 'is_finished', 'delete', 'list'
47
+ workflow_uuid: UUID of the workflow (required for retrieve, update, stop, status, is_finished, delete)
48
+ name: Workflow name (required for create, optional for update)
49
+ workflow_type: Type of workflow (required for create)
50
+ initial_molecule: Initial molecule SMILES (required for create)
51
+ parent_uuid: Parent folder UUID (optional for create/update)
52
+ notes: Workflow notes (optional for create/update)
53
+ starred: Star the workflow (optional for create/update)
54
+ public: Make workflow public (optional for create/update)
55
+ email_when_complete: Email when complete (optional for create/update)
56
+ workflow_data: Additional workflow data (optional for create)
57
+ name_contains: Filter by name containing text (optional for list)
58
+ object_status: Filter by status (0=queued, 1=running, 2=completed, 3=failed, 4=stopped, optional for list)
59
+ object_type: Filter by workflow type (optional for list)
60
+ page: Page number for pagination (default: 1, for list)
61
+ size: Results per page (default: 50, for list)
62
+
63
+ Returns:
64
+ Results of the workflow operation
65
+ """
66
+
67
+ action = action.lower()
68
+
69
+ try:
70
+ if action == "create":
71
+ if not all([name, workflow_type, initial_molecule]):
72
+ return " Error: 'name', 'workflow_type', and 'initial_molecule' are required for creating a workflow"
73
+
74
+ # Validate workflow type
75
+ VALID_WORKFLOWS = {
76
+ "admet", "basic_calculation", "conformer_search", "descriptors",
77
+ "docking", "electronic_properties", "fukui",
78
+ "irc", "molecular_dynamics", "multistage_opt", "pka", "redox_potential",
79
+ "scan", "solubility", "spin_states", "tautomers"
80
+ }
81
+
82
+ if workflow_type not in VALID_WORKFLOWS:
83
+ error_msg = f" Invalid workflow_type '{workflow_type}'.\n\n"
84
+ error_msg += " **Available Rowan Workflow Types:**\n"
85
+ error_msg += f"{', '.join(sorted(VALID_WORKFLOWS))}"
86
+ return error_msg
87
+
88
+ workflow = rowan.Workflow.create(
89
+ name=name,
90
+ workflow_type=workflow_type,
91
+ initial_molecule=initial_molecule,
92
+ parent_uuid=parent_uuid,
93
+ notes=notes or "",
94
+ starred=starred or False,
95
+ public=public or False,
96
+ email_when_complete=email_when_complete or False,
97
+ workflow_data=workflow_data or {}
98
+ )
99
+
100
+ formatted = f" Workflow '{name}' created successfully!\n\n"
101
+ formatted += f" UUID: {safe_get_attr(workflow, 'uuid', 'N/A')}\n"
102
+ formatted += f" Type: {workflow_type}\n"
103
+ formatted += f" Status: {safe_get_attr(workflow, 'object_status', 'Unknown')}\n"
104
+ formatted += f" Created: {safe_get_attr(workflow, 'created_at', 'N/A')}\n"
105
+ return formatted
106
+
107
+ elif action == "retrieve":
108
+ if not workflow_uuid:
109
+ return " Error: 'workflow_uuid' is required for retrieving a workflow"
110
+
111
+ try:
112
+ workflow = rowan.Workflow.retrieve(uuid=workflow_uuid)
113
+ except Exception as e:
114
+ return f" Error retrieving workflow: {str(e)}"
115
+
116
+ # Handle workflow as dictionary (which is what Rowan API returns)
117
+ def safe_get_dict_value(data, key, default='N/A'):
118
+ """Safely get a value from a dictionary."""
119
+ if isinstance(data, dict):
120
+ return data.get(key, default)
121
+ return safe_get_attr(data, key, default)
122
+
123
+ # Get status and interpret it
124
+ status = safe_get_dict_value(workflow, 'object_status', 'Unknown')
125
+ status_names = {
126
+ 0: "Queued",
127
+ 1: "Running",
128
+ 2: "Completed",
129
+ 3: "Failed",
130
+ 4: "Stopped",
131
+ 5: "Awaiting Queue"
132
+ }
133
+ status_name = status_names.get(status, f"Unknown ({status})")
134
+
135
+ formatted = f" Workflow Details:\n\n"
136
+ formatted += f" Name: {safe_get_dict_value(workflow, 'name', 'N/A')}\n"
137
+ formatted += f" UUID: {safe_get_dict_value(workflow, 'uuid', 'N/A')}\n"
138
+ formatted += f" Type: {safe_get_dict_value(workflow, 'object_type', 'N/A')}\n"
139
+ formatted += f" Status: {status_name} ({status})\n"
140
+ formatted += f" Parent: {safe_get_dict_value(workflow, 'parent_uuid', 'Root')}\n"
141
+ formatted += f" Starred: {'Yes' if safe_get_dict_value(workflow, 'starred', False) else 'No'}\n"
142
+ formatted += f" Public: {'Yes' if safe_get_dict_value(workflow, 'public', False) else 'No'}\n"
143
+ formatted += f" Created: {safe_get_dict_value(workflow, 'created_at', 'N/A')}\n"
144
+ formatted += f" Elapsed: {safe_get_dict_value(workflow, 'elapsed', 0):.2f}s\n"
145
+ formatted += f" Credits: {safe_get_dict_value(workflow, 'credits_charged', 0)}\n"
146
+ formatted += f" Notes: {safe_get_dict_value(workflow, 'notes', 'None')}\n\n"
147
+
148
+ # If workflow is completed (status 2), extract and show results
149
+ if status == 2:
150
+ formatted += f" **Workflow Completed Successfully!**\n\n"
151
+
152
+ # Show basic completion details
153
+ credits_charged = safe_get_dict_value(workflow, 'credits_charged', 0)
154
+ elapsed_time = safe_get_dict_value(workflow, 'elapsed', 0)
155
+ if credits_charged or elapsed_time:
156
+ formatted += f" Workflow used {credits_charged} credits and ran for {elapsed_time:.2f}s\n\n"
157
+
158
+ # Debug: Show all available keys in the workflow dictionary
159
+ if isinstance(workflow, dict):
160
+ workflow_keys = list(workflow.keys())
161
+ formatted += f" **Debug - Available Workflow Keys:**\n"
162
+ formatted += f" {', '.join(workflow_keys)}\n\n"
163
+ else:
164
+ # Fallback for object-based workflows
165
+ workflow_attrs = []
166
+ for attr in dir(workflow):
167
+ if not attr.startswith('_'):
168
+ try:
169
+ value = getattr(workflow, attr)
170
+ if not callable(value):
171
+ workflow_attrs.append(attr)
172
+ except:
173
+ pass
174
+ formatted += f" **Debug - Available Workflow Attributes:**\n"
175
+ formatted += f" {', '.join(workflow_attrs)}\n\n"
176
+
177
+ # Extract actual results from object_data
178
+ object_data = safe_get_dict_value(workflow, 'object_data', {})
179
+ workflow_type = safe_get_dict_value(workflow, 'object_type', '')
180
+
181
+ formatted += f" **Results Analysis:**\n"
182
+ formatted += f" Workflow Type: {workflow_type}\n"
183
+ formatted += f" Object Data Present: {'Yes' if object_data else 'No'}\n"
184
+
185
+ if object_data:
186
+ formatted += f" Object Data Keys: {list(object_data.keys()) if isinstance(object_data, dict) else 'Not a dictionary'}\n\n"
187
+ formatted += extract_workflow_results(workflow_type, object_data)
188
+ else:
189
+ formatted += f" **No results data found in workflow object_data**\n"
190
+ formatted += f" This could mean:\n"
191
+ formatted += f" • The workflow completed but didn't generate data\n"
192
+ formatted += f" • The results are stored in a different attribute\n"
193
+ formatted += f" • There was an issue with the workflow execution\n"
194
+
195
+ elif status == 1: # Running
196
+ formatted += f" **Workflow is currently running...**\n"
197
+ formatted += f" Check back later or use `rowan_workflow_management(action='status', workflow_uuid='{workflow_uuid}')` for updates\n"
198
+ elif status == 0: # Queued
199
+ formatted += f" **Workflow is queued and waiting to start**\n"
200
+ formatted += f" Use `rowan_workflow_management(action='status', workflow_uuid='{workflow_uuid}')` to check progress\n"
201
+ elif status == 3: # Failed
202
+ formatted += f" **Workflow failed**\n"
203
+ formatted += f" Check the workflow details in the Rowan web interface for error messages\n"
204
+ elif status == 4: # Stopped
205
+ formatted += f" **Workflow was stopped**\n"
206
+
207
+ return formatted
208
+
209
+ elif action == "update":
210
+ if not workflow_uuid:
211
+ return " Error: 'workflow_uuid' is required for updating a workflow"
212
+
213
+ # Build update parameters according to Rowan API docs
214
+ update_params = {'uuid': workflow_uuid}
215
+ updates_made = []
216
+
217
+ if name is not None:
218
+ update_params['name'] = name
219
+ updates_made.append(f"name: {name}")
220
+ if parent_uuid is not None:
221
+ update_params['parent_uuid'] = parent_uuid
222
+ updates_made.append(f"parent_uuid: {parent_uuid}")
223
+ if notes is not None:
224
+ update_params['notes'] = notes
225
+ updates_made.append(f"notes: {notes}")
226
+ if starred is not None:
227
+ update_params['starred'] = starred
228
+ updates_made.append(f"starred: {starred}")
229
+ if public is not None:
230
+ update_params['public'] = public
231
+ updates_made.append(f"public: {public}")
232
+ if email_when_complete is not None:
233
+ update_params['email_when_complete'] = email_when_complete
234
+ updates_made.append(f"email_when_complete: {email_when_complete}")
235
+
236
+ if len(update_params) == 1: # Only UUID provided
237
+ return " Error: At least one field must be provided for updating (name, parent_uuid, notes, starred, public, email_when_complete)"
238
+
239
+ # Call Rowan API with correct parameter structure
240
+ workflow = rowan.Workflow.update(**update_params)
241
+
242
+ # Format response using the returned workflow object
243
+ formatted = f" Workflow updated successfully!\n\n"
244
+ formatted += f" UUID: {safe_get_attr(workflow, 'uuid', workflow_uuid)}\n"
245
+ formatted += f" Name: {safe_get_attr(workflow, 'name', 'N/A')}\n"
246
+ formatted += f" Type: {safe_get_attr(workflow, 'object_type', 'N/A')}\n"
247
+ formatted += f" Parent: {safe_get_attr(workflow, 'parent_uuid', 'Root')}\n"
248
+ formatted += f" Starred: {'Yes' if safe_get_attr(workflow, 'starred', False) else 'No'}\n"
249
+ formatted += f" Public: {'Yes' if safe_get_attr(workflow, 'public', False) else 'No'}\n"
250
+ formatted += f" Email on Complete: {'Yes' if safe_get_attr(workflow, 'email_when_complete', False) else 'No'}\n"
251
+ formatted += f" Notes: {safe_get_attr(workflow, 'notes', 'None')}\n\n"
252
+
253
+ formatted += f" **Updates Applied:**\n"
254
+ for update in updates_made:
255
+ formatted += f"• {update}\n"
256
+
257
+ return formatted
258
+
259
+ elif action in ["stop", "status", "is_finished"]:
260
+ if not workflow_uuid:
261
+ return f" Error: 'workflow_uuid' is required for {action} action"
262
+
263
+ if action == "stop":
264
+ result = rowan.Workflow.stop(uuid=workflow_uuid)
265
+ return f" Workflow stop request submitted. Result: {result}"
266
+ elif action == "status":
267
+ workflow = rowan.Workflow.retrieve(uuid=workflow_uuid)
268
+
269
+ # Handle workflow as dictionary
270
+ def safe_get_dict_value(data, key, default='N/A'):
271
+ if isinstance(data, dict):
272
+ return data.get(key, default)
273
+ return safe_get_attr(data, key, default)
274
+
275
+ status = safe_get_dict_value(workflow, 'object_status', 'Unknown')
276
+ status_names = {
277
+ 0: "Queued",
278
+ 1: "Running",
279
+ 2: "Completed",
280
+ 3: "Failed",
281
+ 4: "Stopped",
282
+ 5: "Awaiting Queue"
283
+ }
284
+ status_name = status_names.get(status, f"Unknown ({status})")
285
+
286
+ formatted = f" **Workflow Status**: {status_name} ({status})\n"
287
+ formatted += f" UUID: {workflow_uuid}\n"
288
+ formatted += f" Name: {safe_get_dict_value(workflow, 'name', 'N/A')}\n"
289
+ formatted += f" Elapsed: {safe_get_dict_value(workflow, 'elapsed', 0):.2f}s\n"
290
+
291
+ if status == 2:
292
+ formatted += f" **Completed successfully!** Use 'retrieve' action to get results.\n"
293
+ elif status == 1:
294
+ formatted += f" **Currently running...** Check back later for results.\n"
295
+ elif status == 0:
296
+ formatted += f" **Queued and waiting to start**\n"
297
+ elif status == 3:
298
+ formatted += f" **Failed** - Check workflow details for error information.\n"
299
+ elif status == 4:
300
+ formatted += f" **Stopped**\n"
301
+
302
+ return formatted
303
+ elif action == "is_finished":
304
+ workflow = rowan.Workflow.retrieve(uuid=workflow_uuid)
305
+
306
+ # Handle workflow as dictionary
307
+ def safe_get_dict_value(data, key, default='N/A'):
308
+ if isinstance(data, dict):
309
+ return data.get(key, default)
310
+ return safe_get_attr(data, key, default)
311
+
312
+ status = safe_get_dict_value(workflow, 'object_status', 'Unknown')
313
+ is_finished = status in [2, 3, 4] # Completed, Failed, or Stopped
314
+
315
+ formatted = f" **Workflow Finished Check**\n"
316
+ formatted += f" UUID: {workflow_uuid}\n"
317
+ formatted += f" Status: {status}\n"
318
+ formatted += f" Finished: {'Yes' if is_finished else 'No'}\n"
319
+
320
+ if is_finished:
321
+ if status == 2:
322
+ formatted += f" Use 'retrieve' action to get results\n"
323
+ elif status == 3:
324
+ formatted += f" Workflow failed - check details for error info\n"
325
+ elif status == 4:
326
+ formatted += f" Workflow was stopped\n"
327
+ else:
328
+ formatted += f" Workflow is still {['queued', 'running'][status] if status in [0, 1] else 'in progress'}\n"
329
+
330
+ return formatted
331
+
332
+ elif action == "delete":
333
+ if not workflow_uuid:
334
+ return " Error: 'workflow_uuid' is required for deleting a workflow"
335
+
336
+ result = rowan.Workflow.delete(uuid=workflow_uuid)
337
+ return f" Workflow deletion request submitted. Result: {result}"
338
+
339
+ elif action == "list":
340
+ # Build filters
341
+ filters = {
342
+ 'page': page,
343
+ 'size': min(size * 5, 250) # Get more workflows to sort properly, cap at 250
344
+ }
345
+
346
+ if name_contains:
347
+ filters['name_contains'] = name_contains
348
+ if parent_uuid:
349
+ filters['parent_uuid'] = parent_uuid
350
+ if starred is not None:
351
+ filters['starred'] = starred
352
+ if public is not None:
353
+ filters['public'] = public
354
+ if object_status is not None:
355
+ filters['object_status'] = object_status
356
+ if object_type:
357
+ filters['object_type'] = object_type
358
+
359
+ workflows = rowan.Workflow.list(**filters)
360
+
361
+ # Sort workflows by created_at in descending order (most recent first)
362
+ if 'workflows' in workflows and workflows['workflows']:
363
+ from datetime import datetime
364
+
365
+ def parse_date(date_str):
366
+ try:
367
+ return datetime.fromisoformat(date_str.replace('Z', '+00:00'))
368
+ except:
369
+ return datetime.min
370
+
371
+ sorted_workflows = sorted(
372
+ workflows['workflows'],
373
+ key=lambda w: parse_date(w.get('created_at', '')),
374
+ reverse=True
375
+ )
376
+
377
+ # Remove object_logfile from each workflow and return only the requested number
378
+ cleaned_workflows = []
379
+ for workflow in sorted_workflows[:size]:
380
+ cleaned_workflow = {k: v for k, v in workflow.items() if k != 'object_logfile'}
381
+ cleaned_workflows.append(cleaned_workflow)
382
+
383
+ workflows['workflows'] = cleaned_workflows
384
+
385
+ return workflows
386
+
387
+ else:
388
+ return f" Error: Unknown action '{action}'. Available actions: create, retrieve, update, stop, status, is_finished, delete, list"
389
+
390
+ except Exception as e:
391
+ return f" Error in workflow management: {str(e)}"
392
+
393
+ def extract_workflow_results(workflow_type: str, object_data: Dict[str, Any]) -> str:
394
+ """Extract and format workflow results - simple raw data display."""
395
+
396
+ formatted = f" **{workflow_type.replace('_', ' ').title()} Results:**\n\n"
397
+
398
+ import json
399
+ try:
400
+ # Pretty print the object_data as JSON
401
+ formatted += f"```json\n{json.dumps(object_data, indent=2, default=str)}\n```\n"
402
+ except Exception as e:
403
+ # Fallback if JSON serialization fails
404
+ formatted += f"Raw object_data:\n{str(object_data)}\n"
405
+ formatted += f"(JSON serialization failed: {e})\n"
406
+
407
+ return formatted
408
+
409
+ def test_rowan_workflow_management():
410
+ """Test the workflow management function."""
411
+ try:
412
+ # Test list action
413
+ result = rowan_workflow_management("list", size=5)
414
+ print(" Workflow management test successful!")
415
+ print(f"Sample result: {result[:200]}...")
416
+ return True
417
+ except Exception as e:
418
+ print(f" Workflow management test failed: {e}")
419
+ return False
420
+
421
+ if __name__ == "__main__":
422
+ test_rowan_workflow_management()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rowan-mcp
3
- Version: 2.0.0
3
+ Version: 2.0.1
4
4
  Summary: Model Context Protocol server for Rowan computational chemistry platform
5
5
  Project-URL: Homepage, https://github.com/k-yenko/rowan-mcp
6
6
  Author-email: Katherine Yenko <katherineayenko@gmail.com>
@@ -130,7 +130,7 @@ Ask the LLM to:
130
130
 
131
131
  ## **System Requirements**
132
132
 
133
- - **Python 3.10+** (Python 3.11+ recommended)
133
+ - **Python 3.11+**
134
134
  - **Package manager**: [uv](https://docs.astral.sh/uv/) (recommended) or pip
135
135
  - **Rowan API key** (free at [labs.rowansci.com](https://labs.rowansci.com))
136
136
  - **MCP-compatible client** (Claude Desktop, etc.)
@@ -173,7 +173,7 @@ uv run python -m rowan_mcp
173
173
 
174
174
  ## **Requirements**
175
175
 
176
- - Python 3.10+
176
+ - Python 3.11+
177
177
  - Rowan API key
178
178
  - MCP-compatible AI assistant (Claude Desktop, etc.)
179
179
 
@@ -184,21 +184,6 @@ uv run python -m rowan_mcp
184
184
 
185
185
  ---
186
186
 
187
- ## **Todo**
188
-
189
- - [X] Remove unnecessary AI spaghetti formatting
190
- - [X] Remove no longer necessary API config lines
191
- - [ ] Some complex conformer searches hang on "running"
192
- - [X] Edit MCP one-liner context
193
- - [ ] Transition state finding and IRC
194
- - [X] `rowan_scan` - Potential energy surfaces
195
- - [ ] `rowan_docking` - Protein-ligand docking
196
- - [X] add in h-bond, BDE and macroscopic pka, logD, BBB
197
- - [ ] Folder listing API bug (returns 500 error) - Rowan side?
198
- - [ ] Multistage optimization sometimes shows unexpected imaginary frequencies
199
- - [X] Some calculations show as finished in logs but not in Rowan UI
200
- - [ ] Can you hook up Rowan's visual capabilites?
201
-
202
187
  ## **Citation**
203
188
 
204
189
  If you use this MCP tool in your research, please cite the underlying Rowan platform:
@@ -1,6 +1,33 @@
1
1
  rowan_mcp/__init__.py,sha256=1YLhdt94grBE3m3twzj4dpxSrYngceJIP_WG90ItmZo,376
2
2
  rowan_mcp/__main__.py,sha256=a4ojXN6YhHy7tTSqwCznYP6luVP6OrbVqwnkPfPFBbU,264
3
3
  rowan_mcp/server.py,sha256=w8194y9pSXKSxyOy4ADjuqAVrHxbMhUKloTUmWvTcJI,4286
4
+ rowan_mcp/functions/admet.py,sha256=d2IPIDlGpZasK0vR7cc4SiFsc1JCZjtv0oKs69u_W5M,2334
5
+ rowan_mcp/functions/bde.py,sha256=7BCC4XHiCRfk5BNxl9-AcLz3j8NVIlk7FQsI55RhQaA,3340
6
+ rowan_mcp/functions/calculation_retrieve.py,sha256=jL28RKvZX3QUeZ_qDlBfLdGG_Lsk6LyA1xjsHvPM-mg,3376
7
+ rowan_mcp/functions/conformers.py,sha256=DkeL3UMpKRmJY6IAqMs_Zd1LV77QbtQJARWkIlFSMRY,2450
8
+ rowan_mcp/functions/descriptors.py,sha256=SWvW8Zio_CAoCuYgTvEz9ZsEy7UPd9nDzOniY6vsvdA,2645
9
+ rowan_mcp/functions/docking.py,sha256=DLGkr3eGJBAJm4IG0cTkPyLn6nQUNEaMO7PlgV566Ko,11433
10
+ rowan_mcp/functions/docking_enhanced.py,sha256=lwviIWg_26VW8VlITIlHd-Lbw8E25tnvNrLfzK6fLJs,5528
11
+ rowan_mcp/functions/electronic_properties.py,sha256=VNeNkXfw5oH7tvin_GXzcxywitbmBtkTEQFG6aDD6bI,8052
12
+ rowan_mcp/functions/folder_management.py,sha256=T-uX0YEZzTOCn6l3rXBJ7lGJIqN5UEhKakTcdYkpKBs,4585
13
+ rowan_mcp/functions/fukui.py,sha256=Zgty-RhmEyNQ1jygTfPo59RvFO--xjRsUyRz_OvEseE,7648
14
+ rowan_mcp/functions/hydrogen_bond_basicity.py,sha256=I1V9okLWoF1fqoH2ZcKvviGLvs2oLV7bhCs8-gkxjSM,2787
15
+ rowan_mcp/functions/irc.py,sha256=ulMfkpVTXoQDwFVzAJRcEbyZpFxM2LucO7Mq-XkgNTs,4096
16
+ rowan_mcp/functions/macropka.py,sha256=ze0D8R5-1-FLfOLXNe06I-BUAUKF9CHeWuTYw30sT1s,3985
17
+ rowan_mcp/functions/molecular_converter.py,sha256=j9YeCnaHZahwkhG2EZNPu5VVmGy2sqIHPB_qf1ojoec,17349
18
+ rowan_mcp/functions/molecular_dynamics.py,sha256=yzA03LeFv8K59Cg1SAnavWwmodl4_KW667pRHJQTXNw,6990
19
+ rowan_mcp/functions/molecule_lookup.py,sha256=Ff3ARljNbLlGgSinREl6OFxoJ-HVXtbXPxy-r_6CMKs,1467
20
+ rowan_mcp/functions/multistage_opt.py,sha256=cey19EUiic5hs99EITYgwuYw2pS-oyqjspTw6X0OKdw,6083
21
+ rowan_mcp/functions/pdb_handler.py,sha256=EnhRqxStnke5kiSnDaWOzcJT8fAHW6VVIhTaH6ODkWE,6241
22
+ rowan_mcp/functions/pka.py,sha256=sAWBP_4x5otKMnR3LiSRx6a6SPA611LHEZuxspwBnww,2046
23
+ rowan_mcp/functions/redox_potential.py,sha256=YUxa-MdZ77D5blVq4VCpmWxHiaDU4kVU_Kn3caswdFA,13007
24
+ rowan_mcp/functions/scan.py,sha256=LWr6YKwCG3gv0ZtyCbPaJ81NlQyFTHFZ4WnXvSRxmVg,24235
25
+ rowan_mcp/functions/scan_analyzer.py,sha256=vnAokCFOxYbv3Px3p9dEIxQXL7ot6UMjeUfMb_ZF6Ug,13479
26
+ rowan_mcp/functions/solubility.py,sha256=-duTeVLEhnhNtNkA0L_cBk8Xjqw5bbAXrrEFqtElg4I,11112
27
+ rowan_mcp/functions/spin_states.py,sha256=NG7uJjTi_Fx-E4Qr7RzjNhfFmKlHfIGMD0Uhyo7sKT4,31895
28
+ rowan_mcp/functions/system_management.py,sha256=NroBqfG5KyaSEC8wCUgaBdoF8S26sxHWVqligVlZn58,19800
29
+ rowan_mcp/functions/tautomers.py,sha256=Jpwu3aQhGquWV4iFKlEyV790696MV8eCYOulkTcLx3M,2592
30
+ rowan_mcp/functions/workflow_management.py,sha256=EqXRqj0EuJz7h2igqOHBpq23Qyo-KT9geWp39URacxw,21130
4
31
  rowan_mcp/functions_v2/BENCHMARK.md,sha256=QzSrpJyKYo3FlnuiTxxGYcalMFTobury9G0PvXgVVM0,3026
5
32
  rowan_mcp/functions_v2/molecule_lookup.py,sha256=Ox7PKhjtQYMChaHbqo68IHhuxt5vXU_pOfytyJ3T09c,7795
6
33
  rowan_mcp/functions_v2/protein_management.py,sha256=6f53SOiRkYGSho4rkqm8rKxTDEmyNPcXzBbULRvLb0k,3429
@@ -36,7 +63,7 @@ rowan_mcp/tests/phenol_pka.py,sha256=XoCl__rXAlUcbvqZAEssvFVnos74dBI2iS_znwL91_8
36
63
  rowan_mcp/tests/pka.py,sha256=XoCl__rXAlUcbvqZAEssvFVnos74dBI2iS_znwL91_8,1030
37
64
  rowan_mcp/tests/protein_cofolding.py,sha256=5EC5P4yKb6M1NvZehydadkJbHC_w7Mo7qjIE1pmZp3c,854
38
65
  rowan_mcp/tests/scan.py,sha256=DFAOMp4itWNFn5BGMhIUD7fyvxBVP9uMkuxMx3otOSM,574
39
- rowan_mcp-2.0.0.dist-info/METADATA,sha256=2RgfjiMWN6BBSEy4blJ3Z7iDMM_RPBwKf-gzFBhAK2Q,6597
40
- rowan_mcp-2.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
41
- rowan_mcp-2.0.0.dist-info/entry_points.txt,sha256=QkmK3GHkTNA6gqyTIFrl2V2eVBm-VBdRAlDNsvi4Rl0,52
42
- rowan_mcp-2.0.0.dist-info/RECORD,,
66
+ rowan_mcp-2.0.1.dist-info/METADATA,sha256=PbBX6CIMK-ZZvPCm7A36gTy5xCscWHNG3C9q7-p3-Rk,5918
67
+ rowan_mcp-2.0.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
68
+ rowan_mcp-2.0.1.dist-info/entry_points.txt,sha256=QkmK3GHkTNA6gqyTIFrl2V2eVBm-VBdRAlDNsvi4Rl0,52
69
+ rowan_mcp-2.0.1.dist-info/RECORD,,