rowan-mcp 1.0.2__tar.gz → 2.0.1__tar.gz
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.
- rowan_mcp-2.0.1/.gitignore +21 -0
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/PKG-INFO +38 -45
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/README.md +33 -41
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/pyproject.toml +15 -4
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/__init__.py +1 -1
- rowan_mcp-2.0.1/rowan_mcp/__main__.py +12 -0
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/admet.py +0 -5
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/bde.py +1 -8
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/conformers.py +1 -4
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/descriptors.py +1 -4
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/docking.py +6 -56
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/electronic_properties.py +1 -4
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/folder_management.py +1 -8
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/fukui.py +1 -4
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/hydrogen_bond_basicity.py +1 -8
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/multistage_opt.py +1 -4
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/pka.py +1 -8
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/redox_potential.py +2 -5
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/system_management.py +1 -8
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/tautomers.py +1 -4
- rowan_mcp-2.0.1/rowan_mcp/functions_v2/BENCHMARK.md +86 -0
- rowan_mcp-2.0.1/rowan_mcp/functions_v2/molecule_lookup.py +232 -0
- rowan_mcp-2.0.1/rowan_mcp/functions_v2/protein_management.py +141 -0
- rowan_mcp-2.0.1/rowan_mcp/functions_v2/submit_basic_calculation_workflow.py +195 -0
- rowan_mcp-2.0.1/rowan_mcp/functions_v2/submit_conformer_search_workflow.py +158 -0
- rowan_mcp-2.0.1/rowan_mcp/functions_v2/submit_descriptors_workflow.py +52 -0
- rowan_mcp-2.0.1/rowan_mcp/functions_v2/submit_docking_workflow.py +244 -0
- rowan_mcp-2.0.1/rowan_mcp/functions_v2/submit_fukui_workflow.py +114 -0
- rowan_mcp-2.0.1/rowan_mcp/functions_v2/submit_irc_workflow.py +58 -0
- rowan_mcp-2.0.1/rowan_mcp/functions_v2/submit_macropka_workflow.py +99 -0
- rowan_mcp-2.0.1/rowan_mcp/functions_v2/submit_pka_workflow.py +72 -0
- rowan_mcp-2.0.1/rowan_mcp/functions_v2/submit_protein_cofolding_workflow.py +88 -0
- rowan_mcp-2.0.1/rowan_mcp/functions_v2/submit_redox_potential_workflow.py +55 -0
- rowan_mcp-2.0.1/rowan_mcp/functions_v2/submit_scan_workflow.py +82 -0
- rowan_mcp-2.0.1/rowan_mcp/functions_v2/submit_solubility_workflow.py +157 -0
- rowan_mcp-2.0.1/rowan_mcp/functions_v2/submit_tautomer_search_workflow.py +51 -0
- rowan_mcp-2.0.1/rowan_mcp/functions_v2/workflow_management_v2.py +382 -0
- rowan_mcp-2.0.1/rowan_mcp/server.py +134 -0
- rowan_mcp-2.0.1/rowan_mcp/tests/basic_calculation_from_json.py +0 -0
- rowan_mcp-2.0.1/rowan_mcp/tests/basic_calculation_with_constraint.py +33 -0
- rowan_mcp-2.0.1/rowan_mcp/tests/basic_calculation_with_solvent.py +0 -0
- rowan_mcp-2.0.1/rowan_mcp/tests/bde.py +37 -0
- rowan_mcp-2.0.1/rowan_mcp/tests/benchmark_queries.md +120 -0
- rowan_mcp-2.0.1/rowan_mcp/tests/cofolding_screen.py +131 -0
- rowan_mcp-2.0.1/rowan_mcp/tests/conformer_dependent_redox.py +37 -0
- rowan_mcp-2.0.1/rowan_mcp/tests/conformers.py +31 -0
- rowan_mcp-2.0.1/rowan_mcp/tests/data.json +189 -0
- rowan_mcp-2.0.1/rowan_mcp/tests/docking_screen.py +157 -0
- rowan_mcp-2.0.1/rowan_mcp/tests/irc.py +24 -0
- rowan_mcp-2.0.1/rowan_mcp/tests/macropka.py +13 -0
- rowan_mcp-2.0.1/rowan_mcp/tests/multistage_opt.py +13 -0
- rowan_mcp-2.0.1/rowan_mcp/tests/optimization.py +21 -0
- rowan_mcp-2.0.1/rowan_mcp/tests/phenol_pka.py +36 -0
- rowan_mcp-2.0.1/rowan_mcp/tests/pka.py +36 -0
- rowan_mcp-2.0.1/rowan_mcp/tests/protein_cofolding.py +17 -0
- rowan_mcp-2.0.1/rowan_mcp/tests/scan.py +28 -0
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/uv.lock +1 -1
- rowan_mcp-1.0.2/.gitignore +0 -12
- rowan_mcp-1.0.2/ROWAN_MCP_TEST_QUERIES.md +0 -123
- rowan_mcp-1.0.2/ROWAN_MCP_TOOLS.md +0 -440
- rowan_mcp-1.0.2/rowan-dxt.dxt +0 -0
- rowan_mcp-1.0.2/rowan_mcp/__main__.py +0 -14
- rowan_mcp-1.0.2/rowan_mcp/server.py +0 -169
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/calculation_retrieve.py +0 -0
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/docking_enhanced.py +0 -0
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/irc.py +0 -0
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/macropka.py +0 -0
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/molecular_converter.py +0 -0
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/molecular_dynamics.py +0 -0
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/molecule_lookup.py +0 -0
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/pdb_handler.py +0 -0
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/scan.py +0 -0
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/scan_analyzer.py +0 -0
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/solubility.py +0 -0
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/spin_states.py +0 -0
- {rowan_mcp-1.0.2 → rowan_mcp-2.0.1}/rowan_mcp/functions/workflow_management.py +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/.venv
|
|
2
|
+
/rowan-dxt
|
|
3
|
+
.env
|
|
4
|
+
__pycache__/
|
|
5
|
+
*.py[cod]
|
|
6
|
+
*$py.class
|
|
7
|
+
*.so
|
|
8
|
+
.DS_Store
|
|
9
|
+
|
|
10
|
+
.claude
|
|
11
|
+
CLAUDE.md
|
|
12
|
+
/dist
|
|
13
|
+
|
|
14
|
+
# MCP configuration (may contain tokens)
|
|
15
|
+
.mcp.json
|
|
16
|
+
|
|
17
|
+
# Personal compatibility notes
|
|
18
|
+
GEMINI_MCP_COMPATIBILITY.md
|
|
19
|
+
MCP_CHATGPT_GEMINI_COMPATIBILITY_ANALYSIS.md
|
|
20
|
+
ROWAN_MCP_TEST_QUERIES.md
|
|
21
|
+
ROWAN_MCP_TOOLS.md
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rowan-mcp
|
|
3
|
-
Version:
|
|
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>
|
|
@@ -13,14 +13,15 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
15
|
Classifier: Topic :: Scientific/Engineering :: Chemistry
|
|
16
|
-
Requires-Python: >=3.
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Requires-Dist: aiohttp>=3.12.15
|
|
17
18
|
Requires-Dist: fastapi>=0.104.0
|
|
18
|
-
Requires-Dist: fastmcp>=
|
|
19
|
+
Requires-Dist: fastmcp>=2.11.3
|
|
19
20
|
Requires-Dist: pubchempy>=1.0.4
|
|
20
21
|
Requires-Dist: pydantic>=2.0.0
|
|
21
22
|
Requires-Dist: python-dotenv>=1.0.0
|
|
22
23
|
Requires-Dist: rdkit>=2025.3.2
|
|
23
|
-
Requires-Dist: rowan-python>=
|
|
24
|
+
Requires-Dist: rowan-python>=2.1.1
|
|
24
25
|
Requires-Dist: typing-extensions>=4.0.0
|
|
25
26
|
Requires-Dist: uvicorn>=0.24.0
|
|
26
27
|
Provides-Extra: dev
|
|
@@ -32,7 +33,7 @@ Description-Content-Type: text/markdown
|
|
|
32
33
|
|
|
33
34
|
# Rowan MCP Server
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
MCP server for making it easy to run Rowan's molecular design and simulation tools.
|
|
36
37
|
|
|
37
38
|
---
|
|
38
39
|
|
|
@@ -56,34 +57,25 @@ That's it - no command line setup needed!
|
|
|
56
57
|
|
|
57
58
|
Just add this to your MCP configuration and it will automatically install and run:
|
|
58
59
|
|
|
59
|
-
**
|
|
60
|
+
**HTTP/SSE configuration:**
|
|
60
61
|
```json
|
|
61
62
|
{
|
|
62
63
|
"mcpServers": {
|
|
63
64
|
"rowan": {
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"env": {
|
|
67
|
-
"ROWAN_API_KEY": "your_api_key_here"
|
|
68
|
-
}
|
|
65
|
+
"type": "http",
|
|
66
|
+
"url": "http://127.0.0.1:6276/sse"
|
|
69
67
|
}
|
|
70
68
|
}
|
|
71
69
|
}
|
|
72
70
|
```
|
|
73
71
|
|
|
74
|
-
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
"env": {
|
|
82
|
-
"ROWAN_API_KEY": "your_api_key_here"
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
72
|
+
Then start the server:
|
|
73
|
+
```bash
|
|
74
|
+
# Set your API key
|
|
75
|
+
export ROWAN_API_KEY="your_api_key_here"
|
|
76
|
+
|
|
77
|
+
# Start the HTTP server
|
|
78
|
+
uvx --from rowan-mcp rowan-mcp
|
|
87
79
|
```
|
|
88
80
|
|
|
89
81
|
### **Option 2: Manual Installation**
|
|
@@ -100,20 +92,24 @@ uv add rowan-mcp
|
|
|
100
92
|
pip install rowan-mcp
|
|
101
93
|
```
|
|
102
94
|
|
|
103
|
-
Then
|
|
95
|
+
Then configure and start:
|
|
104
96
|
```json
|
|
105
97
|
{
|
|
106
98
|
"mcpServers": {
|
|
107
99
|
"rowan": {
|
|
108
|
-
"
|
|
109
|
-
"
|
|
110
|
-
"ROWAN_API_KEY": "your_api_key_here"
|
|
111
|
-
}
|
|
100
|
+
"type": "http",
|
|
101
|
+
"url": "http://127.0.0.1:6276/sse"
|
|
112
102
|
}
|
|
113
103
|
}
|
|
114
104
|
}
|
|
115
105
|
```
|
|
116
106
|
|
|
107
|
+
```bash
|
|
108
|
+
# Set API key and start server
|
|
109
|
+
export ROWAN_API_KEY="your_api_key_here"
|
|
110
|
+
rowan-mcp
|
|
111
|
+
```
|
|
112
|
+
|
|
117
113
|
### **Get API Key**
|
|
118
114
|
|
|
119
115
|
Visit [labs.rowansci.com](https://labs.rowansci.com) → Create account → Generate API key
|
|
@@ -134,7 +130,7 @@ Ask the LLM to:
|
|
|
134
130
|
|
|
135
131
|
## **System Requirements**
|
|
136
132
|
|
|
137
|
-
- **Python 3.
|
|
133
|
+
- **Python 3.11+**
|
|
138
134
|
- **Package manager**: [uv](https://docs.astral.sh/uv/) (recommended) or pip
|
|
139
135
|
- **Rowan API key** (free at [labs.rowansci.com](https://labs.rowansci.com))
|
|
140
136
|
- **MCP-compatible client** (Claude Desktop, etc.)
|
|
@@ -142,7 +138,8 @@ Ask the LLM to:
|
|
|
142
138
|
**Development commands** (if you cloned the repo):
|
|
143
139
|
```bash
|
|
144
140
|
# Run from source
|
|
145
|
-
|
|
141
|
+
export ROWAN_API_KEY="your_api_key_here"
|
|
142
|
+
uv run python -m rowan_mcp
|
|
146
143
|
```
|
|
147
144
|
|
|
148
145
|
---
|
|
@@ -176,7 +173,7 @@ uv run python -m rowan_mcp --http
|
|
|
176
173
|
|
|
177
174
|
## **Requirements**
|
|
178
175
|
|
|
179
|
-
- Python 3.
|
|
176
|
+
- Python 3.11+
|
|
180
177
|
- Rowan API key
|
|
181
178
|
- MCP-compatible AI assistant (Claude Desktop, etc.)
|
|
182
179
|
|
|
@@ -187,19 +184,6 @@ uv run python -m rowan_mcp --http
|
|
|
187
184
|
|
|
188
185
|
---
|
|
189
186
|
|
|
190
|
-
## **Todo**
|
|
191
|
-
|
|
192
|
-
- [ ] Remove unnecessary AI spaghetti formatting 🙃
|
|
193
|
-
- [ ] Some complex conformer searches hang on "running"
|
|
194
|
-
- [ ] Edit MCP one-liner context
|
|
195
|
-
- [ ] Transition state finding and IRC
|
|
196
|
-
- [X] `rowan_scan` - Potential energy surfaces
|
|
197
|
-
- [ ] `rowan_docking` - Protein-ligand docking
|
|
198
|
-
- [X] add in h-bond, BDE and macroscopic pka, logD, BBB
|
|
199
|
-
- [ ] Folder listing API bug (returns 500 error) - Rowan side?
|
|
200
|
-
- [ ] Multistage optimization sometimes shows unexpected imaginary frequencies
|
|
201
|
-
- [ ] Some calculations show as finished in logs but not in Rowan UI
|
|
202
|
-
|
|
203
187
|
## **Citation**
|
|
204
188
|
|
|
205
189
|
If you use this MCP tool in your research, please cite the underlying Rowan platform:
|
|
@@ -233,3 +217,12 @@ To update the dxt file:
|
|
|
233
217
|
# Then update the desktop extension
|
|
234
218
|
dxt pack rowan-dxt
|
|
235
219
|
```
|
|
220
|
+
### MCP inspector
|
|
221
|
+
```bash
|
|
222
|
+
# Start the server first
|
|
223
|
+
export ROWAN_API_KEY="your_api_key_here"
|
|
224
|
+
uv run python -m rowan_mcp &
|
|
225
|
+
|
|
226
|
+
# Then inspect
|
|
227
|
+
npx @modelcontextprotocol/inspector http://127.0.0.1:6276/sse
|
|
228
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Rowan MCP Server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
MCP server for making it easy to run Rowan's molecular design and simulation tools.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -24,34 +24,25 @@ That's it - no command line setup needed!
|
|
|
24
24
|
|
|
25
25
|
Just add this to your MCP configuration and it will automatically install and run:
|
|
26
26
|
|
|
27
|
-
**
|
|
27
|
+
**HTTP/SSE configuration:**
|
|
28
28
|
```json
|
|
29
29
|
{
|
|
30
30
|
"mcpServers": {
|
|
31
31
|
"rowan": {
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"env": {
|
|
35
|
-
"ROWAN_API_KEY": "your_api_key_here"
|
|
36
|
-
}
|
|
32
|
+
"type": "http",
|
|
33
|
+
"url": "http://127.0.0.1:6276/sse"
|
|
37
34
|
}
|
|
38
35
|
}
|
|
39
36
|
}
|
|
40
37
|
```
|
|
41
38
|
|
|
42
|
-
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"env": {
|
|
50
|
-
"ROWAN_API_KEY": "your_api_key_here"
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
39
|
+
Then start the server:
|
|
40
|
+
```bash
|
|
41
|
+
# Set your API key
|
|
42
|
+
export ROWAN_API_KEY="your_api_key_here"
|
|
43
|
+
|
|
44
|
+
# Start the HTTP server
|
|
45
|
+
uvx --from rowan-mcp rowan-mcp
|
|
55
46
|
```
|
|
56
47
|
|
|
57
48
|
### **Option 2: Manual Installation**
|
|
@@ -68,20 +59,24 @@ uv add rowan-mcp
|
|
|
68
59
|
pip install rowan-mcp
|
|
69
60
|
```
|
|
70
61
|
|
|
71
|
-
Then
|
|
62
|
+
Then configure and start:
|
|
72
63
|
```json
|
|
73
64
|
{
|
|
74
65
|
"mcpServers": {
|
|
75
66
|
"rowan": {
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"ROWAN_API_KEY": "your_api_key_here"
|
|
79
|
-
}
|
|
67
|
+
"type": "http",
|
|
68
|
+
"url": "http://127.0.0.1:6276/sse"
|
|
80
69
|
}
|
|
81
70
|
}
|
|
82
71
|
}
|
|
83
72
|
```
|
|
84
73
|
|
|
74
|
+
```bash
|
|
75
|
+
# Set API key and start server
|
|
76
|
+
export ROWAN_API_KEY="your_api_key_here"
|
|
77
|
+
rowan-mcp
|
|
78
|
+
```
|
|
79
|
+
|
|
85
80
|
### **Get API Key**
|
|
86
81
|
|
|
87
82
|
Visit [labs.rowansci.com](https://labs.rowansci.com) → Create account → Generate API key
|
|
@@ -102,7 +97,7 @@ Ask the LLM to:
|
|
|
102
97
|
|
|
103
98
|
## **System Requirements**
|
|
104
99
|
|
|
105
|
-
- **Python 3.
|
|
100
|
+
- **Python 3.11+**
|
|
106
101
|
- **Package manager**: [uv](https://docs.astral.sh/uv/) (recommended) or pip
|
|
107
102
|
- **Rowan API key** (free at [labs.rowansci.com](https://labs.rowansci.com))
|
|
108
103
|
- **MCP-compatible client** (Claude Desktop, etc.)
|
|
@@ -110,7 +105,8 @@ Ask the LLM to:
|
|
|
110
105
|
**Development commands** (if you cloned the repo):
|
|
111
106
|
```bash
|
|
112
107
|
# Run from source
|
|
113
|
-
|
|
108
|
+
export ROWAN_API_KEY="your_api_key_here"
|
|
109
|
+
uv run python -m rowan_mcp
|
|
114
110
|
```
|
|
115
111
|
|
|
116
112
|
---
|
|
@@ -144,7 +140,7 @@ uv run python -m rowan_mcp --http
|
|
|
144
140
|
|
|
145
141
|
## **Requirements**
|
|
146
142
|
|
|
147
|
-
- Python 3.
|
|
143
|
+
- Python 3.11+
|
|
148
144
|
- Rowan API key
|
|
149
145
|
- MCP-compatible AI assistant (Claude Desktop, etc.)
|
|
150
146
|
|
|
@@ -155,19 +151,6 @@ uv run python -m rowan_mcp --http
|
|
|
155
151
|
|
|
156
152
|
---
|
|
157
153
|
|
|
158
|
-
## **Todo**
|
|
159
|
-
|
|
160
|
-
- [ ] Remove unnecessary AI spaghetti formatting 🙃
|
|
161
|
-
- [ ] Some complex conformer searches hang on "running"
|
|
162
|
-
- [ ] Edit MCP one-liner context
|
|
163
|
-
- [ ] Transition state finding and IRC
|
|
164
|
-
- [X] `rowan_scan` - Potential energy surfaces
|
|
165
|
-
- [ ] `rowan_docking` - Protein-ligand docking
|
|
166
|
-
- [X] add in h-bond, BDE and macroscopic pka, logD, BBB
|
|
167
|
-
- [ ] Folder listing API bug (returns 500 error) - Rowan side?
|
|
168
|
-
- [ ] Multistage optimization sometimes shows unexpected imaginary frequencies
|
|
169
|
-
- [ ] Some calculations show as finished in logs but not in Rowan UI
|
|
170
|
-
|
|
171
154
|
## **Citation**
|
|
172
155
|
|
|
173
156
|
If you use this MCP tool in your research, please cite the underlying Rowan platform:
|
|
@@ -201,3 +184,12 @@ To update the dxt file:
|
|
|
201
184
|
# Then update the desktop extension
|
|
202
185
|
dxt pack rowan-dxt
|
|
203
186
|
```
|
|
187
|
+
### MCP inspector
|
|
188
|
+
```bash
|
|
189
|
+
# Start the server first
|
|
190
|
+
export ROWAN_API_KEY="your_api_key_here"
|
|
191
|
+
uv run python -m rowan_mcp &
|
|
192
|
+
|
|
193
|
+
# Then inspect
|
|
194
|
+
npx @modelcontextprotocol/inspector http://127.0.0.1:6276/sse
|
|
195
|
+
```
|
|
@@ -4,14 +4,14 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "rowan-mcp"
|
|
7
|
-
version = "
|
|
7
|
+
version = "2.0.1"
|
|
8
8
|
description = "Model Context Protocol server for Rowan computational chemistry platform"
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "Katherine Yenko", email = "katherineayenko@gmail.com"}
|
|
11
11
|
]
|
|
12
12
|
license = {text = "MIT"}
|
|
13
13
|
readme = "README.md"
|
|
14
|
-
requires-python = ">=3.
|
|
14
|
+
requires-python = ">=3.11"
|
|
15
15
|
urls = {Homepage = "https://github.com/k-yenko/rowan-mcp"}
|
|
16
16
|
classifiers = [
|
|
17
17
|
"Development Status :: 3 - Alpha",
|
|
@@ -24,8 +24,8 @@ classifiers = [
|
|
|
24
24
|
"Topic :: Scientific/Engineering :: Chemistry",
|
|
25
25
|
]
|
|
26
26
|
dependencies = [
|
|
27
|
-
"fastmcp>=
|
|
28
|
-
"rowan-python>=
|
|
27
|
+
"fastmcp>=2.11.3",
|
|
28
|
+
"rowan-python>=2.1.1",
|
|
29
29
|
"pydantic>=2.0.0",
|
|
30
30
|
"typing-extensions>=4.0.0",
|
|
31
31
|
"uvicorn>=0.24.0",
|
|
@@ -33,6 +33,7 @@ dependencies = [
|
|
|
33
33
|
"python-dotenv>=1.0.0",
|
|
34
34
|
"pubchempy>=1.0.4",
|
|
35
35
|
"rdkit>=2025.3.2",
|
|
36
|
+
"aiohttp>=3.12.15",
|
|
36
37
|
]
|
|
37
38
|
|
|
38
39
|
[project.optional-dependencies]
|
|
@@ -49,6 +50,16 @@ rowan-mcp = "rowan_mcp.server:main"
|
|
|
49
50
|
[tool.hatch.build.targets.wheel]
|
|
50
51
|
packages = ["rowan_mcp"]
|
|
51
52
|
|
|
53
|
+
[tool.hatch.build.targets.sdist]
|
|
54
|
+
exclude = [
|
|
55
|
+
"rowan-dxt/",
|
|
56
|
+
"dxt/",
|
|
57
|
+
"*.log",
|
|
58
|
+
"test_*.py",
|
|
59
|
+
".venv/",
|
|
60
|
+
"*.dxt"
|
|
61
|
+
]
|
|
62
|
+
|
|
52
63
|
[tool.black]
|
|
53
64
|
line-length = 88
|
|
54
65
|
target-version = ['py38']
|
|
@@ -5,7 +5,7 @@ This package provides MCP (Model Context Protocol) server functionality
|
|
|
5
5
|
for integrating with Rowan's computational chemistry platform.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
__version__ = "1.0.
|
|
8
|
+
__version__ = "1.0.2"
|
|
9
9
|
__author__ = "Kat Yenko"
|
|
10
10
|
__description__ = "MCP server for Rowan computational chemistry platform"
|
|
11
11
|
|
|
@@ -16,11 +16,6 @@ except ImportError:
|
|
|
16
16
|
# Setup logging
|
|
17
17
|
logger = logging.getLogger(__name__)
|
|
18
18
|
|
|
19
|
-
# Setup API key
|
|
20
|
-
api_key = os.getenv("ROWAN_API_KEY")
|
|
21
|
-
if rowan and api_key:
|
|
22
|
-
rowan.api_key = api_key
|
|
23
|
-
|
|
24
19
|
|
|
25
20
|
def log_rowan_api_call(workflow_type: str, **kwargs):
|
|
26
21
|
"""Log Rowan API calls with detailed parameters."""
|
|
@@ -10,14 +10,7 @@ from typing import Optional, List, Union
|
|
|
10
10
|
import logging
|
|
11
11
|
logger = logging.getLogger(__name__)
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
if not hasattr(rowan, 'api_key') or not rowan.api_key:
|
|
15
|
-
api_key = os.getenv("ROWAN_API_KEY")
|
|
16
|
-
if api_key:
|
|
17
|
-
rowan.api_key = api_key
|
|
18
|
-
logger.info("🔑 Rowan API key configured")
|
|
19
|
-
else:
|
|
20
|
-
logger.error("No ROWAN_API_KEY found in environment")
|
|
13
|
+
|
|
21
14
|
|
|
22
15
|
def log_rowan_api_call(workflow_type: str, **kwargs):
|
|
23
16
|
"""Log Rowan API calls and let Rowan handle its own errors."""
|
|
@@ -15,10 +15,7 @@ except ImportError:
|
|
|
15
15
|
# Setup logging
|
|
16
16
|
logger = logging.getLogger(__name__)
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
api_key = os.getenv("ROWAN_API_KEY")
|
|
20
|
-
if rowan and api_key:
|
|
21
|
-
rowan.api_key = api_key
|
|
18
|
+
|
|
22
19
|
|
|
23
20
|
def log_rowan_api_call(workflow_type: str, **kwargs):
|
|
24
21
|
"""Log Rowan API calls and let Rowan handle its own errors."""
|
|
@@ -15,10 +15,7 @@ except ImportError:
|
|
|
15
15
|
# Setup logging
|
|
16
16
|
logger = logging.getLogger(__name__)
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
api_key = os.getenv("ROWAN_API_KEY")
|
|
20
|
-
if rowan and api_key:
|
|
21
|
-
rowan.api_key = api_key
|
|
18
|
+
|
|
22
19
|
|
|
23
20
|
def log_rowan_api_call(workflow_type: str, **kwargs):
|
|
24
21
|
"""Log Rowan API calls with detailed parameters."""
|
|
@@ -192,63 +192,13 @@ def rowan_docking(
|
|
|
192
192
|
if conformers is not None:
|
|
193
193
|
compute_params["conformers"] = conformers
|
|
194
194
|
|
|
195
|
-
# Submit docking calculation
|
|
195
|
+
# Submit docking calculation and return raw result
|
|
196
196
|
result = rowan.compute(**compute_params)
|
|
197
|
-
|
|
198
|
-
# Format results
|
|
199
|
-
uuid = result.get('uuid', 'N/A')
|
|
200
|
-
status = result.get('status', 'unknown')
|
|
201
|
-
|
|
202
|
-
if blocking:
|
|
203
|
-
# Blocking mode - check if successful
|
|
204
|
-
if status == "success":
|
|
205
|
-
formatted = f"✅ Docking calculation '{name}' completed successfully!\n"
|
|
206
|
-
formatted += f"🔖 Workflow UUID: {uuid}\n"
|
|
207
|
-
formatted += f"📊 Status: {status}\n\n"
|
|
208
|
-
|
|
209
|
-
# Extract docking results if available
|
|
210
|
-
object_data = result.get("object_data", {})
|
|
211
|
-
scores = object_data.get("scores", [])
|
|
212
|
-
|
|
213
|
-
if scores:
|
|
214
|
-
formatted += f"🎯 Docking Results: {len(scores)} poses generated\n"
|
|
215
|
-
formatted += f"📈 Best docking score: {scores[0] if scores else 'N/A'}\n"
|
|
216
|
-
|
|
217
|
-
# Show top poses
|
|
218
|
-
formatted += "\nTop poses:\n"
|
|
219
|
-
for i, score in enumerate(scores[:5]):
|
|
220
|
-
formatted += f" {i+1}. Score: {score}\n"
|
|
221
|
-
|
|
222
|
-
if len(scores) > 5:
|
|
223
|
-
formatted += f" ... and {len(scores) - 5} more poses\n"
|
|
224
|
-
else:
|
|
225
|
-
formatted += "📈 Results: Check workflow details for docking data\n"
|
|
226
|
-
|
|
227
|
-
return formatted
|
|
228
|
-
else:
|
|
229
|
-
# Failed calculation
|
|
230
|
-
return f"❌ Docking calculation failed\n🔖 UUID: {uuid}\n📋 Status: {status}\n💬 Check workflow details for more information"
|
|
231
|
-
else:
|
|
232
|
-
# Non-blocking mode
|
|
233
|
-
formatted = f"📋 Docking calculation '{name}' submitted!\n"
|
|
234
|
-
formatted += f"🔖 Workflow UUID: {uuid}\n"
|
|
235
|
-
formatted += f"⏳ Status: Running...\n"
|
|
236
|
-
formatted += f"💡 Use rowan_workflow_management to check status\n\n"
|
|
237
|
-
|
|
238
|
-
formatted += f"Docking Details:\n"
|
|
239
|
-
formatted += f"🧬 Ligand: {initial_molecule}\n"
|
|
240
|
-
formatted += f"🎯 Target: {target_uuid or target[:50] + '...' if target and len(target) > 50 else target}\n"
|
|
241
|
-
formatted += f"📍 Pocket: center={pocket[0]}, size={pocket[1]}\n"
|
|
242
|
-
formatted += f"⚙️ Settings: csearch={do_csearch}, optimize={do_optimization}, refine={do_pose_refinement}\n"
|
|
243
|
-
|
|
244
|
-
if conformers:
|
|
245
|
-
formatted += f"🔬 Pre-optimized conformers: {len(conformers)}\n"
|
|
246
|
-
|
|
247
|
-
return formatted
|
|
197
|
+
return result
|
|
248
198
|
|
|
249
199
|
except Exception as e:
|
|
250
200
|
logger.error(f"Error in rowan_docking: {str(e)}")
|
|
251
|
-
return f"
|
|
201
|
+
return f"Docking calculation failed: {str(e)}"
|
|
252
202
|
|
|
253
203
|
def rowan_docking_pdb_id(
|
|
254
204
|
name: str,
|
|
@@ -297,7 +247,7 @@ def rowan_docking_pdb_id(
|
|
|
297
247
|
else:
|
|
298
248
|
ligand_param = f" smiles={smiles},\n"
|
|
299
249
|
|
|
300
|
-
return (f"
|
|
250
|
+
return (f"PDB {pdb_id} found in RCSB database!\n\n"
|
|
301
251
|
f"To perform docking with this protein:\n\n"
|
|
302
252
|
f"1. Go to https://labs.rowansci.com\n"
|
|
303
253
|
f"2. Upload the PDB file for {pdb_id}\n"
|
|
@@ -329,11 +279,11 @@ def test_rowan_docking():
|
|
|
329
279
|
pocket=((0.0, 0.0, 0.0), (20.0, 20.0, 20.0)),
|
|
330
280
|
blocking=False
|
|
331
281
|
)
|
|
332
|
-
print("
|
|
282
|
+
print("Docking test result:")
|
|
333
283
|
print(result)
|
|
334
284
|
return True
|
|
335
285
|
except Exception as e:
|
|
336
|
-
print(f"
|
|
286
|
+
print(f"Docking test failed: {e}")
|
|
337
287
|
return False
|
|
338
288
|
|
|
339
289
|
if __name__ == "__main__":
|
|
@@ -22,10 +22,7 @@ except ImportError:
|
|
|
22
22
|
logging.basicConfig(level=logging.INFO)
|
|
23
23
|
logger = logging.getLogger(__name__)
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
api_key = os.getenv("ROWAN_API_KEY")
|
|
27
|
-
if api_key and rowan:
|
|
28
|
-
rowan.api_key = api_key
|
|
25
|
+
|
|
29
26
|
|
|
30
27
|
def log_rowan_api_call(workflow_type: str, **kwargs):
|
|
31
28
|
"""Log Rowan API calls with detailed parameters."""
|
|
@@ -10,14 +10,7 @@ from typing import Optional
|
|
|
10
10
|
import logging
|
|
11
11
|
logger = logging.getLogger(__name__)
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
if not hasattr(rowan, 'api_key') or not rowan.api_key:
|
|
15
|
-
api_key = os.getenv("ROWAN_API_KEY")
|
|
16
|
-
if api_key:
|
|
17
|
-
rowan.api_key = api_key
|
|
18
|
-
logger.info("Rowan API key configured")
|
|
19
|
-
else:
|
|
20
|
-
logger.error("No ROWAN_API_KEY found in environment")
|
|
13
|
+
|
|
21
14
|
|
|
22
15
|
def rowan_folder_management(
|
|
23
16
|
action: str,
|
|
@@ -22,10 +22,7 @@ except ImportError:
|
|
|
22
22
|
logging.basicConfig(level=logging.INFO)
|
|
23
23
|
logger = logging.getLogger(__name__)
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
api_key = os.getenv("ROWAN_API_KEY")
|
|
27
|
-
if api_key and rowan:
|
|
28
|
-
rowan.api_key = api_key
|
|
25
|
+
|
|
29
26
|
|
|
30
27
|
def log_rowan_api_call(workflow_type: str, **kwargs):
|
|
31
28
|
"""Log Rowan API calls with detailed parameters."""
|
|
@@ -10,14 +10,7 @@ from typing import Optional
|
|
|
10
10
|
import logging
|
|
11
11
|
logger = logging.getLogger(__name__)
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
if not hasattr(rowan, 'api_key') or not rowan.api_key:
|
|
15
|
-
api_key = os.getenv("ROWAN_API_KEY")
|
|
16
|
-
if api_key:
|
|
17
|
-
rowan.api_key = api_key
|
|
18
|
-
logger.info("🔑 Rowan API key configured")
|
|
19
|
-
else:
|
|
20
|
-
logger.error("No ROWAN_API_KEY found in environment")
|
|
13
|
+
|
|
21
14
|
|
|
22
15
|
def log_rowan_api_call(workflow_type: str, **kwargs):
|
|
23
16
|
"""Log Rowan API calls and let Rowan handle its own errors."""
|
|
@@ -15,10 +15,7 @@ except ImportError:
|
|
|
15
15
|
# Setup logging
|
|
16
16
|
logger = logging.getLogger(__name__)
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
api_key = os.getenv("ROWAN_API_KEY")
|
|
20
|
-
if rowan and api_key:
|
|
21
|
-
rowan.api_key = api_key
|
|
18
|
+
|
|
22
19
|
|
|
23
20
|
def log_rowan_api_call(workflow_type: str, **kwargs):
|
|
24
21
|
"""Log Rowan API calls with detailed parameters."""
|
|
@@ -10,14 +10,7 @@ from typing import Optional
|
|
|
10
10
|
import logging
|
|
11
11
|
logger = logging.getLogger(__name__)
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
if not hasattr(rowan, 'api_key') or not rowan.api_key:
|
|
15
|
-
api_key = os.getenv("ROWAN_API_KEY")
|
|
16
|
-
if api_key:
|
|
17
|
-
rowan.api_key = api_key
|
|
18
|
-
logger.info("Rowan API key configured")
|
|
19
|
-
else:
|
|
20
|
-
logger.error("No ROWAN_API_KEY found in environment")
|
|
13
|
+
|
|
21
14
|
|
|
22
15
|
def log_rowan_api_call(workflow_type: str, **kwargs):
|
|
23
16
|
"""Log Rowan API calls and let Rowan handle its own errors."""
|
|
@@ -15,10 +15,7 @@ except ImportError:
|
|
|
15
15
|
# Setup logging
|
|
16
16
|
logger = logging.getLogger(__name__)
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
api_key = os.getenv("ROWAN_API_KEY")
|
|
20
|
-
if rowan and api_key:
|
|
21
|
-
rowan.api_key = api_key
|
|
18
|
+
|
|
22
19
|
|
|
23
20
|
def lookup_molecule_smiles(molecule_name: str) -> str:
|
|
24
21
|
"""Look up canonical SMILES for common molecule names."""
|
|
@@ -287,7 +284,7 @@ def rowan_redox_potential(
|
|
|
287
284
|
formatted += f" Job UUID: {result.get('uuid', 'N/A')}\n"
|
|
288
285
|
formatted += f" Status: {status}\n"
|
|
289
286
|
formatted += f"⚙ Mode: {mode_lower.title()}\n"
|
|
290
|
-
formatted += f"
|
|
287
|
+
formatted += f"Solvent: Acetonitrile\n"
|
|
291
288
|
|
|
292
289
|
# Show which potentials were calculated
|
|
293
290
|
calc_types = []
|