Spaces:
Sleeping
Sleeping
Update biopython/mcp_output/mcp_plugin/mcp_service.py
Browse files
biopython/mcp_output/mcp_plugin/mcp_service.py
CHANGED
|
@@ -359,38 +359,37 @@ def blast_get_result(payload: dict):
|
|
| 359 |
@mcp.tool(name="blast_search", description="Search NCBI database using BLAST to find similar sequences")
|
| 360 |
def blast_search(payload: dict):
|
| 361 |
"""
|
| 362 |
-
使用 BLAST 搜索相似序列
|
| 363 |
Required fields: sequence
|
| 364 |
-
Optional fields:
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
- nr: For protein sequences
|
| 370 |
"""
|
| 371 |
try:
|
| 372 |
sequence = str(payload.get("sequence", "")).strip()
|
| 373 |
if not sequence:
|
| 374 |
return {"success": False, "result": None, "error": "sequence is required"}
|
| 375 |
-
|
| 376 |
-
#
|
| 377 |
sequence = "".join(sequence.split()).upper()
|
| 378 |
-
|
| 379 |
-
#
|
| 380 |
database = payload.get("database", "refseq_rna")
|
| 381 |
program = payload.get("program", "blastn")
|
| 382 |
hitlist_size = int(payload.get("hitlist_size", 20))
|
| 383 |
expect = float(payload.get("expect", 1e-10))
|
| 384 |
-
|
| 385 |
-
#
|
| 386 |
if database == "nt":
|
| 387 |
-
print("⚠️
|
| 388 |
-
print("💡 Consider using 'refseq_rna' for faster results
|
| 389 |
-
|
| 390 |
print(f"🔍 Submitting BLAST search: {len(sequence)} bp, database={database}, hits={hitlist_size}")
|
| 391 |
-
print(
|
| 392 |
-
|
| 393 |
-
#
|
| 394 |
try:
|
| 395 |
handle = NCBIWWW.qblast(
|
| 396 |
program=program,
|
|
@@ -400,47 +399,45 @@ def blast_search(payload: dict):
|
|
| 400 |
expect=expect,
|
| 401 |
format_type="XML"
|
| 402 |
)
|
| 403 |
-
|
| 404 |
-
blast_record = NCBIXML.read(handle)
|
| 405 |
-
handle.close()
|
| 406 |
-
|
| 407 |
-
print(f"✅ BLAST search completed: {len(blast_record.alignments)} alignments found")
|
| 408 |
except Exception as blast_error:
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
else:
|
| 417 |
-
raise blast_error
|
| 418 |
-
|
| 419 |
hits = []
|
| 420 |
-
for
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 444 |
result = {
|
| 445 |
"query_length": int(len(sequence)),
|
| 446 |
"database": str(database),
|
|
@@ -448,8 +445,9 @@ def blast_search(payload: dict):
|
|
| 448 |
"total_hits": int(len(hits)),
|
| 449 |
"hits": hits
|
| 450 |
}
|
| 451 |
-
|
| 452 |
return {"success": True, "result": result, "error": None}
|
|
|
|
| 453 |
except Exception as e:
|
| 454 |
return {"success": False, "result": None, "error": str(e)}
|
| 455 |
|
|
|
|
| 359 |
@mcp.tool(name="blast_search", description="Search NCBI database using BLAST to find similar sequences")
|
| 360 |
def blast_search(payload: dict):
|
| 361 |
"""
|
| 362 |
+
使用 BLAST 搜索相似序列(稳定版)
|
| 363 |
Required fields: sequence
|
| 364 |
+
Optional fields:
|
| 365 |
+
- database (nt/nr/refseq_rna, default refseq_rna)
|
| 366 |
+
- program (blastn/blastp, default blastn)
|
| 367 |
+
- hitlist_size (default 20)
|
| 368 |
+
- expect (default 1e-10)
|
|
|
|
| 369 |
"""
|
| 370 |
try:
|
| 371 |
sequence = str(payload.get("sequence", "")).strip()
|
| 372 |
if not sequence:
|
| 373 |
return {"success": False, "result": None, "error": "sequence is required"}
|
| 374 |
+
|
| 375 |
+
# 清理序列
|
| 376 |
sequence = "".join(sequence.split()).upper()
|
| 377 |
+
|
| 378 |
+
# 参数
|
| 379 |
database = payload.get("database", "refseq_rna")
|
| 380 |
program = payload.get("program", "blastn")
|
| 381 |
hitlist_size = int(payload.get("hitlist_size", 20))
|
| 382 |
expect = float(payload.get("expect", 1e-10))
|
| 383 |
+
|
| 384 |
+
# 提示信息
|
| 385 |
if database == "nt":
|
| 386 |
+
print("⚠️ Warning: 'nt' database is very large and may take 5-15 minutes!")
|
| 387 |
+
print("💡 Consider using 'refseq_rna' for faster results")
|
| 388 |
+
|
| 389 |
print(f"🔍 Submitting BLAST search: {len(sequence)} bp, database={database}, hits={hitlist_size}")
|
| 390 |
+
print("⏳ Please wait...")
|
| 391 |
+
|
| 392 |
+
# 提交 BLAST
|
| 393 |
try:
|
| 394 |
handle = NCBIWWW.qblast(
|
| 395 |
program=program,
|
|
|
|
| 399 |
expect=expect,
|
| 400 |
format_type="XML"
|
| 401 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 402 |
except Exception as blast_error:
|
| 403 |
+
return {
|
| 404 |
+
"success": False,
|
| 405 |
+
"result": None,
|
| 406 |
+
"error": f"BLAST submission failed: {str(blast_error)}"
|
| 407 |
+
}
|
| 408 |
+
|
| 409 |
+
# 迭代解析 XML
|
|
|
|
|
|
|
|
|
|
| 410 |
hits = []
|
| 411 |
+
for blast_record in NCBIXML.parse(handle):
|
| 412 |
+
for idx, alignment in enumerate(blast_record.alignments[:hitlist_size]):
|
| 413 |
+
if not alignment.hsps:
|
| 414 |
+
continue
|
| 415 |
+
|
| 416 |
+
hsp = alignment.hsps[0]
|
| 417 |
+
align_len = int(hsp.align_length)
|
| 418 |
+
identities = int(hsp.identities)
|
| 419 |
+
identity_pct = round((identities / align_len) * 100, 2) if align_len else 0
|
| 420 |
+
|
| 421 |
+
hits.append({
|
| 422 |
+
"rank": idx + 1,
|
| 423 |
+
"accession": str(alignment.accession),
|
| 424 |
+
"hit_id": str(alignment.hit_id),
|
| 425 |
+
"hit_def": str(alignment.hit_def),
|
| 426 |
+
"length": int(alignment.length),
|
| 427 |
+
"bit_score": float(hsp.bits),
|
| 428 |
+
"evalue": float(hsp.expect),
|
| 429 |
+
"identity_pct": float(identity_pct),
|
| 430 |
+
"identities": int(identities),
|
| 431 |
+
"align_length": int(align_len)
|
| 432 |
+
})
|
| 433 |
+
|
| 434 |
+
# 输出进度,保持 SSE 活跃
|
| 435 |
+
print(f"Processing alignment {idx+1}/{min(hitlist_size, len(blast_record.alignments))}")
|
| 436 |
+
|
| 437 |
+
handle.close()
|
| 438 |
+
print(f"✅ BLAST search completed: {len(hits)} alignments found")
|
| 439 |
+
|
| 440 |
+
# 构建返回结果
|
| 441 |
result = {
|
| 442 |
"query_length": int(len(sequence)),
|
| 443 |
"database": str(database),
|
|
|
|
| 445 |
"total_hits": int(len(hits)),
|
| 446 |
"hits": hits
|
| 447 |
}
|
| 448 |
+
|
| 449 |
return {"success": True, "result": result, "error": None}
|
| 450 |
+
|
| 451 |
except Exception as e:
|
| 452 |
return {"success": False, "result": None, "error": str(e)}
|
| 453 |
|