erpnext-mcp 0.2.0__tar.gz → 0.3.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: erpnext-mcp
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: MCP Server for ERPNext REST API
5
5
  Project-URL: Homepage, https://github.com/ching-tech/erpnext-mcp
6
6
  Project-URL: Repository, https://github.com/ching-tech/erpnext-mcp
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "erpnext-mcp"
3
- version = "0.2.0"
3
+ version = "0.3.0"
4
4
  description = "MCP Server for ERPNext REST API"
5
5
  readme = "README.md"
6
6
  license = {file = "LICENSE"}
@@ -430,6 +430,164 @@ async def download_file(file_name: str) -> dict:
430
430
  }
431
431
 
432
432
 
433
+ # ── Supplier/Customer Details ──────────────────────────
434
+
435
+
436
+ @mcp.tool()
437
+ async def get_supplier_details(name: str | None = None, keyword: str | None = None) -> dict:
438
+ """Get complete supplier details including address, phone, and contacts.
439
+
440
+ Args:
441
+ name: Exact supplier name (e.g. "SF0009-2 - 永心企業社")
442
+ keyword: Search keyword to find supplier (e.g. "永心")
443
+
444
+ Returns:
445
+ Dict with supplier info, address (phone/fax), and contacts (our purchaser + their contacts)
446
+ """
447
+ client = get_client()
448
+
449
+ # Find supplier
450
+ if name:
451
+ supplier = await client.get_doc("Supplier", name)
452
+ elif keyword:
453
+ suppliers = await client.get_list(
454
+ "Supplier",
455
+ fields=["name", "supplier_name", "supplier_group", "country"],
456
+ filters={"name": ["like", f"%{keyword}%"]},
457
+ limit_page_length=1,
458
+ )
459
+ if not suppliers:
460
+ return {"error": f"找不到關鍵字「{keyword}」的供應商"}
461
+ supplier = await client.get_doc("Supplier", suppliers[0]["name"])
462
+ else:
463
+ return {"error": "請提供 name 或 keyword"}
464
+
465
+ supplier_name = supplier.get("name")
466
+
467
+ # Get address (phone/fax)
468
+ # Address title format: "代碼 地址", e.g. "SF0009-2 地址"
469
+ code = supplier_name.split(" - ")[0] if " - " in supplier_name else supplier_name
470
+ addresses = await client.get_list(
471
+ "Address",
472
+ fields=["address_title", "address_line1", "city", "pincode", "phone", "fax"],
473
+ filters={"address_title": ["like", f"%{code}%"]},
474
+ limit_page_length=5,
475
+ )
476
+
477
+ # Get contacts via Dynamic Link
478
+ contacts = await client.get_list(
479
+ "Contact",
480
+ fields=["name", "first_name", "designation", "phone", "mobile_no", "email_id"],
481
+ filters=[["Dynamic Link", "link_name", "=", supplier_name]],
482
+ limit_page_length=50,
483
+ )
484
+
485
+ # Categorize contacts
486
+ # 有 designation 的是我們的人(採購人員/業務人員),沒有的是對方的聯絡人
487
+ our_contacts = []
488
+ their_contacts = []
489
+ for c in contacts:
490
+ contact_info = {
491
+ "name": c.get("first_name") or c.get("name"),
492
+ "designation": c.get("designation") or "",
493
+ "phone": c.get("phone") or c.get("mobile_no") or "",
494
+ "email": c.get("email_id") or "",
495
+ }
496
+ if c.get("designation"):
497
+ our_contacts.append(contact_info)
498
+ else:
499
+ their_contacts.append(contact_info)
500
+
501
+ return {
502
+ "supplier": {
503
+ "name": supplier_name,
504
+ "group": supplier.get("supplier_group"),
505
+ "country": supplier.get("country"),
506
+ "currency": supplier.get("default_currency"),
507
+ },
508
+ "address": addresses[0] if addresses else None,
509
+ "our_contacts": our_contacts,
510
+ "their_contacts": their_contacts,
511
+ }
512
+
513
+
514
+ @mcp.tool()
515
+ async def get_customer_details(name: str | None = None, keyword: str | None = None) -> dict:
516
+ """Get complete customer details including address, phone, and contacts.
517
+
518
+ Args:
519
+ name: Exact customer name (e.g. "CM0001 - 正達工程股份有限公司")
520
+ keyword: Search keyword to find customer (e.g. "正達")
521
+
522
+ Returns:
523
+ Dict with customer info, address (phone/fax), and contacts (our sales + their contacts)
524
+ """
525
+ client = get_client()
526
+
527
+ # Find customer
528
+ if name:
529
+ customer = await client.get_doc("Customer", name)
530
+ elif keyword:
531
+ customers = await client.get_list(
532
+ "Customer",
533
+ fields=["name", "customer_name", "customer_group", "territory"],
534
+ filters={"name": ["like", f"%{keyword}%"]},
535
+ limit_page_length=1,
536
+ )
537
+ if not customers:
538
+ return {"error": f"找不到關鍵字「{keyword}」的客戶"}
539
+ customer = await client.get_doc("Customer", customers[0]["name"])
540
+ else:
541
+ return {"error": "請提供 name 或 keyword"}
542
+
543
+ customer_name = customer.get("name")
544
+
545
+ # Get address (phone/fax)
546
+ code = customer_name.split(" - ")[0] if " - " in customer_name else customer_name
547
+ addresses = await client.get_list(
548
+ "Address",
549
+ fields=["address_title", "address_line1", "city", "pincode", "phone", "fax"],
550
+ filters={"address_title": ["like", f"%{code}%"]},
551
+ limit_page_length=5,
552
+ )
553
+
554
+ # Get contacts via Dynamic Link
555
+ contacts = await client.get_list(
556
+ "Contact",
557
+ fields=["name", "first_name", "designation", "phone", "mobile_no", "email_id"],
558
+ filters=[["Dynamic Link", "link_name", "=", customer_name]],
559
+ limit_page_length=50,
560
+ )
561
+
562
+ # Categorize contacts
563
+ # 有 designation 的是我們的人(採購人員/業務人員),沒有的是對方的聯絡人
564
+ our_contacts = []
565
+ their_contacts = []
566
+ for c in contacts:
567
+ contact_info = {
568
+ "name": c.get("first_name") or c.get("name"),
569
+ "designation": c.get("designation") or "",
570
+ "phone": c.get("phone") or c.get("mobile_no") or "",
571
+ "email": c.get("email_id") or "",
572
+ }
573
+ if c.get("designation"):
574
+ our_contacts.append(contact_info)
575
+ else:
576
+ their_contacts.append(contact_info)
577
+
578
+ return {
579
+ "customer": {
580
+ "name": customer_name,
581
+ "group": customer.get("customer_group"),
582
+ "territory": customer.get("territory"),
583
+ "currency": customer.get("default_currency"),
584
+ },
585
+ "address": addresses[0] if addresses else None,
586
+ "our_contacts": our_contacts,
587
+ "their_contacts": their_contacts,
588
+ }
589
+
590
+
433
591
  def main():
434
592
  mcp.run()
435
593
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes