labmate-mcp 7.0.0__py3-none-any.whl → 7.2.0__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.
- labmate_mcp/bench.py +1322 -0
- labmate_mcp/server.py +132 -0
- {labmate_mcp-7.0.0.dist-info → labmate_mcp-7.2.0.dist-info}/METADATA +1 -1
- {labmate_mcp-7.0.0.dist-info → labmate_mcp-7.2.0.dist-info}/RECORD +8 -8
- {labmate_mcp-7.0.0.dist-info → labmate_mcp-7.2.0.dist-info}/WHEEL +0 -0
- {labmate_mcp-7.0.0.dist-info → labmate_mcp-7.2.0.dist-info}/entry_points.txt +0 -0
- {labmate_mcp-7.0.0.dist-info → labmate_mcp-7.2.0.dist-info}/licenses/LICENSE +0 -0
- {labmate_mcp-7.0.0.dist-info → labmate_mcp-7.2.0.dist-info}/top_level.txt +0 -0
labmate_mcp/server.py
CHANGED
|
@@ -43,6 +43,13 @@ from labmate_mcp.bench import (
|
|
|
43
43
|
lookup_amino_acid as _lookup_amino_acid,
|
|
44
44
|
lookup_nmr_solvent as _lookup_nmr_solvent,
|
|
45
45
|
AMINO_ACIDS,
|
|
46
|
+
# v7.1.0 additions
|
|
47
|
+
lookup_lab_tips as _lookup_lab_tips,
|
|
48
|
+
lookup_safety as _lookup_safety,
|
|
49
|
+
# v7.2.0 additions
|
|
50
|
+
lookup_rxn_dev_checklist as _lookup_rxn_dev_checklist,
|
|
51
|
+
get_all_checklist_sections as _get_all_checklist_sections,
|
|
52
|
+
REACTION_DEV_CHECKLIST,
|
|
46
53
|
)
|
|
47
54
|
from labmate_mcp.chemistry import (
|
|
48
55
|
validate_cas as _validate_cas,
|
|
@@ -5102,6 +5109,131 @@ async def get_thesis_guide(params: ThesisGuideInput) -> str:
|
|
|
5102
5109
|
return "\n".join(lines)
|
|
5103
5110
|
|
|
5104
5111
|
|
|
5112
|
+
# =============================================================================
|
|
5113
|
+
# v7.1.0 — Lab Tips & Safety Cards
|
|
5114
|
+
# =============================================================================
|
|
5115
|
+
|
|
5116
|
+
|
|
5117
|
+
class LookupLabTipsInput(BaseModel):
|
|
5118
|
+
"""Search practical lab tips."""
|
|
5119
|
+
query: str = Field(description="Keyword, category, or topic (e.g. 'grignard', 'emulsion', 'moisture', 'safety', 'workup', 'TLC')")
|
|
5120
|
+
|
|
5121
|
+
|
|
5122
|
+
@mcp.tool()
|
|
5123
|
+
async def lookup_lab_tips(params: LookupLabTipsInput) -> str:
|
|
5124
|
+
"""Search practical lab tips and 'Law of the Lab' wisdom. 35 tips across 9 categories: reaction_setup (Grignard initiation, degassing, syringe technique, scale-up, molecular sieves), temperature_control (cooling baths, cryogenic technique, kinetic vs thermodynamic), moisture_sensitive (Schlenk line, solvent drying guide), workup_tips (breaking emulsions, drying agents, rotovap), purification_tips, analytical_tips (HRMS ionization), safety (azides, peroxides, Swern, pyrophorics, ozone, H2), common_mistakes (reagent quality, concentration guidelines, TLC technique, base equivalents), time_savers (parallel TLC, crude NMR, overnight setup)."""
|
|
5125
|
+
results = _lookup_lab_tips(params.query)
|
|
5126
|
+
if not results:
|
|
5127
|
+
return f"No tips found for '{params.query}'. Try: grignard, emulsion, moisture, cooling, safety, azide, swern, TLC, concentration, syringe, schlenk, drying, column, rotovap, scale-up, or a category name."
|
|
5128
|
+
lines = [f"## Lab Tips — '{params.query}' ({len(results)} found)\n"]
|
|
5129
|
+
for tip in results:
|
|
5130
|
+
lines.append(f"### {tip['title']}")
|
|
5131
|
+
lines.append(tip['tip'])
|
|
5132
|
+
if tip.get('details'):
|
|
5133
|
+
if isinstance(tip['details'], list):
|
|
5134
|
+
for d in tip['details']:
|
|
5135
|
+
lines.append(f" • {d}")
|
|
5136
|
+
elif isinstance(tip['details'], dict):
|
|
5137
|
+
for k, v in tip['details'].items():
|
|
5138
|
+
if isinstance(v, list):
|
|
5139
|
+
lines.append(f" **{k}:**")
|
|
5140
|
+
for item in v:
|
|
5141
|
+
lines.append(f" • {item}")
|
|
5142
|
+
else:
|
|
5143
|
+
lines.append(f" **{k}:** {v}")
|
|
5144
|
+
else:
|
|
5145
|
+
lines.append(f" {tip['details']}")
|
|
5146
|
+
lines.append("")
|
|
5147
|
+
return "\n".join(lines)
|
|
5148
|
+
|
|
5149
|
+
|
|
5150
|
+
class LookupSafetyInput(BaseModel):
|
|
5151
|
+
"""Search hazardous reagent safety cards."""
|
|
5152
|
+
query: str = Field(description="Reagent name or keyword (e.g. 'LAH', 'n-BuLi', 'diazomethane', 'HF', 'azide', 'OsO4', 'Pd/C', 'NaH', 't-BuLi')")
|
|
5153
|
+
|
|
5154
|
+
|
|
5155
|
+
@mcp.tool()
|
|
5156
|
+
async def lookup_safety_card(params: LookupSafetyInput) -> str:
|
|
5157
|
+
"""Look up hazardous reagent safety cards. 9 cards covering: n-BuLi, t-BuLi, LAH (LiAlH4), NaH, diazomethane (CH2N2), OsO4, HF, NaN3, Pd/C. Each card: hazards, handling protocol, quench procedure, first aid, storage. Essential quick-reference before working with dangerous reagents."""
|
|
5158
|
+
results = _lookup_safety(params.query)
|
|
5159
|
+
if not results:
|
|
5160
|
+
return f"No safety card found for '{params.query}'. Available cards: n-BuLi, t-BuLi, LAH, NaH, diazomethane, OsO4, HF, NaN3, Pd/C."
|
|
5161
|
+
lines = []
|
|
5162
|
+
for card in results:
|
|
5163
|
+
lines.append(f"## ⚠️ Safety Card: {card['reagent']}\n")
|
|
5164
|
+
lines.append(f"**Hazards:** {card['hazards']}")
|
|
5165
|
+
lines.append(f"**Handling:** {card['handling']}")
|
|
5166
|
+
if card.get('quench'):
|
|
5167
|
+
lines.append(f"**Quench:** {card['quench']}")
|
|
5168
|
+
if card.get('first_aid'):
|
|
5169
|
+
lines.append(f"**First aid:** {card['first_aid']}")
|
|
5170
|
+
if card.get('storage'):
|
|
5171
|
+
lines.append(f"**Storage:** {card['storage']}")
|
|
5172
|
+
lines.append("")
|
|
5173
|
+
return "\n".join(lines)
|
|
5174
|
+
|
|
5175
|
+
|
|
5176
|
+
# =============================================================================
|
|
5177
|
+
# v7.2.0 — Reaction Development Checklist
|
|
5178
|
+
# =============================================================================
|
|
5179
|
+
|
|
5180
|
+
|
|
5181
|
+
class RxnDevChecklistInput(BaseModel):
|
|
5182
|
+
"""Search the reaction development checklist."""
|
|
5183
|
+
query: str = Field(description="Section name, keyword, or topic. Sections: 'take_stock', 'kinetics', 'mechanism', 'optimisation', 'catalysis', 'scope', 'applications'. Keywords: 'KIE', 'Hammett', 'reproducible', 'selectivity', 'TON', 'bioconjugation', etc. Use 'all' for overview.")
|
|
5184
|
+
|
|
5185
|
+
|
|
5186
|
+
@mcp.tool()
|
|
5187
|
+
async def lookup_rxn_dev_checklist(params: RxnDevChecklistInput) -> str:
|
|
5188
|
+
"""Search the Reaction Development Checklist — a comprehensive guide for methodology studies. Based on Kerr, Jenkinson, Sheridan & Sparr, Chem. Soc. Rev. 2025 (DOI: 10.1039/D4CS01046A). 7 sections with actionable questions and practical tips: Take Stock (product characterisation, mass balance, reproducibility, minimal retron), Kinetics & Thermodynamics (rate laws, driving forces, equilibrium), Mechanism (KIE, radical clocks, Hammett, Stern-Volmer, computation), Optimisation (yield, selectivity, DoE, efficiency, sustainability), Catalysis (active species, enantioselectivity, TON), Scope (substrate diversity, FG tolerance, robustness screen, Mayr scale), Applications (total synthesis, LSF, skeletal editing, bioconjugation, materials)."""
|
|
5189
|
+
if params.query.lower().strip() == "all":
|
|
5190
|
+
lines = ["## Reaction Development Checklist — Overview",
|
|
5191
|
+
"_Based on Kerr, Jenkinson, Sheridan & Sparr, Chem. Soc. Rev. 2025_\n"]
|
|
5192
|
+
for key, section in REACTION_DEV_CHECKLIST.items():
|
|
5193
|
+
lines.append(f"### {section['icon']} {section['name']}")
|
|
5194
|
+
lines.append(section["summary"])
|
|
5195
|
+
for q in section["questions"]:
|
|
5196
|
+
lines.append(f" • {q['header']}")
|
|
5197
|
+
lines.append("")
|
|
5198
|
+
lines.append("Search a specific section or keyword for detailed checks and tips.")
|
|
5199
|
+
return "\n".join(lines)
|
|
5200
|
+
|
|
5201
|
+
results = _lookup_rxn_dev_checklist(params.query)
|
|
5202
|
+
if not results:
|
|
5203
|
+
sections = ", ".join(_get_all_checklist_sections())
|
|
5204
|
+
return f"No matches for '{params.query}'. Sections: {sections}. Try keywords like: KIE, Hammett, reproducible, selectivity, scope, TON, bioconjugation, sustainability, DoE, robustness, late-stage."
|
|
5205
|
+
|
|
5206
|
+
lines = []
|
|
5207
|
+
for r in results:
|
|
5208
|
+
if r["match_type"] == "section":
|
|
5209
|
+
lines.append(f"## {r['icon']} {r['section']}")
|
|
5210
|
+
lines.append(f"_{r['summary']}_\n")
|
|
5211
|
+
for q in r["questions"]:
|
|
5212
|
+
lines.append(f"### {q['header']}")
|
|
5213
|
+
for check in q.get("checks", []):
|
|
5214
|
+
lines.append(f" ☐ {check}")
|
|
5215
|
+
for check in q.get("extra_checks", []):
|
|
5216
|
+
lines.append(f" ☐ {check}")
|
|
5217
|
+
if q.get("tips"):
|
|
5218
|
+
lines.append(" **Tips:**")
|
|
5219
|
+
for tip in q["tips"]:
|
|
5220
|
+
lines.append(f" 💡 {tip}")
|
|
5221
|
+
lines.append("")
|
|
5222
|
+
else:
|
|
5223
|
+
lines.append(f"### {r['icon']} {r['section']} → {r['header']}")
|
|
5224
|
+
for check in r.get("checks", []):
|
|
5225
|
+
lines.append(f" ☐ {check}")
|
|
5226
|
+
for check in r.get("extra_checks", []):
|
|
5227
|
+
lines.append(f" ☐ {check}")
|
|
5228
|
+
if r.get("tips"):
|
|
5229
|
+
lines.append(" **Tips:**")
|
|
5230
|
+
for tip in r["tips"]:
|
|
5231
|
+
lines.append(f" 💡 {tip}")
|
|
5232
|
+
lines.append("")
|
|
5233
|
+
|
|
5234
|
+
return "\n".join(lines)
|
|
5235
|
+
|
|
5236
|
+
|
|
5105
5237
|
# =============================================================================
|
|
5106
5238
|
# Entry point
|
|
5107
5239
|
# =============================================================================
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
labmate_mcp/__init__.py,sha256=XN03SO8_l0brKxemaWRduycpmnAywho7wpQJSCgOfKM,151
|
|
2
2
|
labmate_mcp/__main__.py,sha256=DEhydVGp643KaLZatSrKq7-qgqpDH8g7S-E7mj-1hRw,89
|
|
3
3
|
labmate_mcp/apis.py,sha256=UyvMKFObLSUim1lVgVx1LeJ7R_lOmoA4GeJF3Oj6n2M,57153
|
|
4
|
-
labmate_mcp/bench.py,sha256=
|
|
4
|
+
labmate_mcp/bench.py,sha256=BhFypFIC2YzowqsATeHg4gRDlVRFd3O7cX6VKKGq6_A,333963
|
|
5
5
|
labmate_mcp/chemistry.py,sha256=3tvSR48T9Lx3mj8OroMhhE-1Xg7mzwalSpAsLmb0hcM,31529
|
|
6
6
|
labmate_mcp/peptide.py,sha256=17mjKz8BJ8BUS__TZh6FQMr7EmiydLyYI7RrAI8Zxgg,12691
|
|
7
|
-
labmate_mcp/server.py,sha256=
|
|
7
|
+
labmate_mcp/server.py,sha256=qq3EFCyYeQazSAUfttZUdfTBGtomebixqLZ5MM4q41A,215728
|
|
8
8
|
labmate_mcp/writing.py,sha256=WZqgIzplqZgNuhwBHAjXwXQ9A5tz8rxEzktJEl-GVHQ,79511
|
|
9
|
-
labmate_mcp-7.
|
|
10
|
-
labmate_mcp-7.
|
|
11
|
-
labmate_mcp-7.
|
|
12
|
-
labmate_mcp-7.
|
|
13
|
-
labmate_mcp-7.
|
|
14
|
-
labmate_mcp-7.
|
|
9
|
+
labmate_mcp-7.2.0.dist-info/licenses/LICENSE,sha256=apOEJKkENojldUYm_yKVJ-7Bn4t0oPPkdUJtYxQUQO8,1068
|
|
10
|
+
labmate_mcp-7.2.0.dist-info/METADATA,sha256=XY2trAEZwS40M9Rwiq21A4cIOxrm-aW_tSviNcKtmss,23995
|
|
11
|
+
labmate_mcp-7.2.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
12
|
+
labmate_mcp-7.2.0.dist-info/entry_points.txt,sha256=NhDC0g-0iSDsd3RYfmNPBAdg_9C2npgVxw9e4AOXpIk,56
|
|
13
|
+
labmate_mcp-7.2.0.dist-info/top_level.txt,sha256=VWDn7cOlUMYcWXhs4Mjax0xHCzDsWH9dgwtO1JiBSbg,12
|
|
14
|
+
labmate_mcp-7.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|