scmcp-shared 0.3.7__tar.gz → 0.5.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.
Files changed (186) hide show
  1. scmcp_shared-0.5.0/.pre-commit-config.yaml +29 -0
  2. {scmcp_shared-0.3.7 → scmcp_shared-0.5.0}/PKG-INFO +3 -1
  3. {scmcp_shared-0.3.7 → scmcp_shared-0.5.0}/pyproject.toml +6 -1
  4. scmcp_shared-0.5.0/src/scmcp_shared/__init__.py +1 -0
  5. scmcp_shared-0.5.0/src/scmcp_shared/agent.py +80 -0
  6. scmcp_shared-0.5.0/src/scmcp_shared/backend.py +44 -0
  7. {scmcp_shared-0.3.7 → scmcp_shared-0.5.0}/src/scmcp_shared/cli.py +76 -37
  8. scmcp_shared-0.5.0/src/scmcp_shared/kb.py +38 -0
  9. {scmcp_shared-0.3.7 → scmcp_shared-0.5.0}/src/scmcp_shared/logging_config.py +6 -8
  10. scmcp_shared-0.5.0/src/scmcp_shared/mcp_base.py +184 -0
  11. scmcp_shared-0.5.0/src/scmcp_shared/schema/io.py +155 -0
  12. {scmcp_shared-0.3.7 → scmcp_shared-0.5.0}/src/scmcp_shared/schema/pl.py +386 -490
  13. scmcp_shared-0.5.0/src/scmcp_shared/schema/pp.py +900 -0
  14. scmcp_shared-0.5.0/src/scmcp_shared/schema/preset/__init__.py +15 -0
  15. {scmcp_shared-0.3.7/src/scmcp_shared/schema → scmcp_shared-0.5.0/src/scmcp_shared/schema/preset}/io.py +40 -50
  16. scmcp_shared-0.5.0/src/scmcp_shared/schema/preset/pl.py +843 -0
  17. {scmcp_shared-0.3.7/src/scmcp_shared/schema → scmcp_shared-0.5.0/src/scmcp_shared/schema/preset}/pp.py +227 -262
  18. {scmcp_shared-0.3.7/src/scmcp_shared/schema → scmcp_shared-0.5.0/src/scmcp_shared/schema/preset}/tl.py +368 -403
  19. {scmcp_shared-0.3.7/src/scmcp_shared/schema → scmcp_shared-0.5.0/src/scmcp_shared/schema/preset}/util.py +57 -72
  20. scmcp_shared-0.5.0/src/scmcp_shared/schema/tl.py +900 -0
  21. scmcp_shared-0.5.0/src/scmcp_shared/schema/tool.py +11 -0
  22. scmcp_shared-0.5.0/src/scmcp_shared/schema/util.py +123 -0
  23. scmcp_shared-0.5.0/src/scmcp_shared/server/__init__.py +1 -0
  24. scmcp_shared-0.5.0/src/scmcp_shared/server/auto.py +54 -0
  25. scmcp_shared-0.5.0/src/scmcp_shared/server/code.py +3 -0
  26. scmcp_shared-0.5.0/src/scmcp_shared/server/preset/__init__.py +14 -0
  27. {scmcp_shared-0.3.7/src/scmcp_shared/server → scmcp_shared-0.5.0/src/scmcp_shared/server/preset}/io.py +26 -22
  28. {scmcp_shared-0.3.7/src/scmcp_shared/server → scmcp_shared-0.5.0/src/scmcp_shared/server/preset}/pl.py +162 -78
  29. {scmcp_shared-0.3.7/src/scmcp_shared/server → scmcp_shared-0.5.0/src/scmcp_shared/server/preset}/pp.py +127 -65
  30. {scmcp_shared-0.3.7/src/scmcp_shared/server → scmcp_shared-0.5.0/src/scmcp_shared/server/preset}/tl.py +142 -79
  31. {scmcp_shared-0.3.7/src/scmcp_shared/server → scmcp_shared-0.5.0/src/scmcp_shared/server/preset}/util.py +123 -66
  32. {scmcp_shared-0.3.7 → scmcp_shared-0.5.0}/src/scmcp_shared/util.py +109 -38
  33. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/0-9499b1b5-85d4-44c2-8f05-1bcce87fe4ef.txn +3 -0
  34. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/1-1632e7a3-4427-4077-8d03-57437144443d.txn +0 -0
  35. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/10-dcf66479-eafb-4193-9358-198154aea1c1.txn +0 -0
  36. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/11-2c6ddc17-49f5-47b5-8764-753297cf9e1b.txn +0 -0
  37. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/12-f079d0a2-9c1c-4e7a-abf6-3e3c4cddac11.txn +0 -0
  38. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/13-5bda9382-a06e-493d-85cb-e066172778ce.txn +0 -0
  39. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/14-6f9c669f-25e2-4096-b7ea-9b421a37e110.txn +0 -0
  40. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/15-2068cca9-31e8-45a3-86d1-bcb8924c72b9.txn +0 -0
  41. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/16-72356bb2-5c98-424e-97aa-a92fa3453da6.txn +0 -0
  42. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/17-9baa67ce-f6d3-478c-9511-20d01988d4d5.txn +0 -0
  43. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/18-b9f2e28e-c4c5-4ce7-bf58-402d53a39558.txn +0 -0
  44. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/19-d199d9f9-7990-4ec0-adde-e98c1c927202.txn +0 -0
  45. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/2-3827747c-095e-41cb-af69-3814bab3a588.txn +0 -0
  46. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/20-31c197a3-7a23-472f-a7e3-056c0ff11e3a.txn +0 -0
  47. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/21-066e3024-36f8-4557-83ae-d2f338ec045c.txn +0 -0
  48. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/22-1d1ccbe5-7c4c-4882-986a-c61ec2f7925e.txn +0 -0
  49. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/23-b2c41628-b2d6-49f1-81b5-3cd9100894b4.txn +0 -0
  50. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/24-e43e2e8d-9dd8-479f-b63c-d147e737c49f.txn +0 -0
  51. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/25-188708e9-5ee1-4b5c-8e13-45dec8efd60d.txn +0 -0
  52. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/26-4f4b6316-5680-4b31-8dd6-215cce66d7b5.txn +0 -0
  53. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/27-c1855f86-abab-44ef-9e61-1312dffc9b06.txn +0 -0
  54. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/28-650d2be0-e977-4177-b457-b733eea8b6ea.txn +0 -0
  55. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/29-e47751d8-0e44-46da-82bd-4836b00a8431.txn +0 -0
  56. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/3-518fc9b8-67de-48f7-94c8-f2398c63dbc5.txn +0 -0
  57. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/30-0db3ea0f-deca-4b5b-9d70-9fb5f741f90c.txn +0 -0
  58. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/31-ef76f0bb-bcfa-4031-b3ad-94b51ee818a9.txn +0 -0
  59. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/32-a763e0f1-5c8d-460f-a455-0c6f6c4f1450.txn +0 -0
  60. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/33-30bce2aa-8e6f-4f42-a323-c420133aa20f.txn +0 -0
  61. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/34-f91ec2f9-d34b-4fcf-8bc7-564c28e50538.txn +0 -0
  62. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/35-7f36a75c-96a7-4868-a4db-5d5f78ecf850.txn +0 -0
  63. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/36-f2df7f99-a37c-458a-958d-7eb5e81eee18.txn +0 -0
  64. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/37-e0af7415-955b-4167-8705-fa9ddb643e9a.txn +0 -0
  65. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/38-6c24a9f0-ce71-4dda-8193-479aa23c5456.txn +0 -0
  66. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/39-72b8a20c-3112-4cf1-8b05-b74a2c02c3f2.txn +0 -0
  67. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/4-42904942-5a79-4f28-90ce-35a4ae8c40d1.txn +0 -0
  68. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/40-95ac7526-6a2e-4914-9654-288c41bff370.txn +0 -0
  69. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/41-13430464-5e62-4aa8-9709-513693a75095.txn +0 -0
  70. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/42-cbb1ad83-a906-4540-bc37-4db158eac618.txn +0 -0
  71. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/43-3c246401-d742-49a9-b24d-cfc457685461.txn +0 -0
  72. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/44-11315c14-ecf4-4e3d-8690-46d57cb3e8c0.txn +0 -0
  73. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/45-7bbf4bc6-96d4-425a-afa2-34aec7121c85.txn +0 -0
  74. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/5-82dda0b4-7838-4f04-90f3-4b1e932c6891.txn +0 -0
  75. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/6-c78352d8-16ba-4814-bd7f-74b8c0c5efe7.txn +0 -0
  76. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/7-bb882b35-63f3-4c52-870f-6a64e3ac7f3c.txn +0 -0
  77. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/8-fc84ad1b-1b59-4822-8fc7-70f4eb18f6d9.txn +0 -0
  78. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_transactions/9-3404dcf1-bb17-40e6-9e5d-d9942dff90b2.txn +0 -0
  79. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/1.manifest +0 -0
  80. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/10.manifest +0 -0
  81. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/11.manifest +0 -0
  82. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/12.manifest +0 -0
  83. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/13.manifest +0 -0
  84. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/14.manifest +0 -0
  85. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/15.manifest +0 -0
  86. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/16.manifest +0 -0
  87. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/17.manifest +0 -0
  88. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/18.manifest +0 -0
  89. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/19.manifest +0 -0
  90. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/2.manifest +0 -0
  91. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/20.manifest +0 -0
  92. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/21.manifest +0 -0
  93. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/22.manifest +0 -0
  94. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/23.manifest +0 -0
  95. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/24.manifest +0 -0
  96. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/25.manifest +0 -0
  97. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/26.manifest +0 -0
  98. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/27.manifest +0 -0
  99. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/28.manifest +0 -0
  100. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/29.manifest +0 -0
  101. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/3.manifest +0 -0
  102. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/30.manifest +0 -0
  103. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/31.manifest +0 -0
  104. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/32.manifest +0 -0
  105. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/33.manifest +0 -0
  106. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/34.manifest +0 -0
  107. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/35.manifest +0 -0
  108. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/36.manifest +0 -0
  109. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/37.manifest +0 -0
  110. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/38.manifest +0 -0
  111. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/39.manifest +0 -0
  112. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/4.manifest +0 -0
  113. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/40.manifest +0 -0
  114. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/41.manifest +0 -0
  115. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/42.manifest +0 -0
  116. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/43.manifest +0 -0
  117. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/44.manifest +0 -0
  118. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/45.manifest +0 -0
  119. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/46.manifest +0 -0
  120. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/5.manifest +0 -0
  121. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/6.manifest +0 -0
  122. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/7.manifest +0 -0
  123. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/8.manifest +0 -0
  124. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/_versions/9.manifest +0 -0
  125. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/0cc94dc3-2ca3-403c-a9df-59753f24b3f2.lance +0 -0
  126. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/254a1a71-cdd2-4cca-b7ec-a50540fed62c.lance +0 -0
  127. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/2574d216-5f05-4794-9e1c-986fafde6ab4.lance +0 -0
  128. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/26c7bbe7-5f97-4453-b065-123b764448b3.lance +0 -0
  129. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/286595a6-88f6-4b05-861e-e4f607a6fcdb.lance +0 -0
  130. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/33219b1a-7575-46ef-bb64-2bdbde1e692b.lance +0 -0
  131. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/344ad7dd-98bb-41de-b347-2f17bf5735cb.lance +0 -0
  132. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/395b1ecb-68fe-4dd3-a770-4b3ed393ae0c.lance +0 -0
  133. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/3db97f4c-9c35-44b7-9042-82b4006c5a22.lance +0 -0
  134. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/4540246e-b0bb-4f4d-8f98-60a64f1c42ed.lance +0 -0
  135. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/4643dbc0-9e45-4b63-81e1-e06cf9bddcec.lance +0 -0
  136. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/4f28eb52-d409-43f2-ae17-d1826f209006.lance +0 -0
  137. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/4fda4a22-35f0-4897-b4c5-6cedefba66e6.lance +0 -0
  138. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/51ee77b6-ab24-44c0-916e-b81ff3912731.lance +0 -0
  139. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/539d15c9-e88b-400c-9ab7-08f5e8dca10d.lance +0 -0
  140. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/54007132-91f5-4909-8678-c471ef4d100f.lance +0 -0
  141. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/546d88ec-0d5c-42bb-bccc-3271f3f183a4.lance +0 -0
  142. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/6912ad8a-343c-4ca1-b1b7-4300125e688e.lance +0 -0
  143. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/6b72e4f7-3051-40a4-a492-6d9eff1c647d.lance +0 -0
  144. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/6d7ee320-ae1c-4e49-8ce5-c85971b11ce6.lance +0 -0
  145. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/6ea47b70-69d4-43e9-b2c6-442fe50e4dd1.lance +0 -0
  146. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/72adb5ed-bf11-4d06-b146-0272d333db08.lance +0 -0
  147. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/745907df-e261-4f4b-a757-2c01c058bf27.lance +0 -0
  148. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/78988307-4c66-4ad7-b295-7463ecd53609.lance +0 -0
  149. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/798ce305-6d60-4f28-b82a-e1f647907abf.lance +0 -0
  150. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/7c159cce-2741-442b-9c63-f44bf2996718.lance +0 -0
  151. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/7cd7a818-e68b-4fa8-bad3-23b60c386670.lance +0 -0
  152. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/7ec553ed-0c7a-4bac-99b2-2ad976b40466.lance +0 -0
  153. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/83efdc85-d990-4762-b69a-fb1c3938f60f.lance +0 -0
  154. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/8c1613b3-8d69-49c5-bfd0-a021bb516faf.lance +0 -0
  155. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/95cd1b22-1f93-4133-9841-de21549ad8c5.lance +0 -0
  156. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/9a2ea5d4-087e-4d3a-b64b-6c568b8a0010.lance +0 -0
  157. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/b1c47dc6-450a-4dca-b465-badb6a3619c2.lance +0 -0
  158. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/b21d183a-c51d-463b-932e-9beb2e0da9aa.lance +0 -0
  159. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/b2598625-8683-4c33-be40-6f1a7555dc2c.lance +0 -0
  160. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/b7297b39-1fc5-4886-be21-708b369bef59.lance +0 -0
  161. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/b7d90085-5bd6-48f9-8a73-17eb7cf556fa.lance +0 -0
  162. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/b988ceee-57fc-423b-8e67-5729086c6946.lance +0 -0
  163. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/bfd68ea6-ddf4-44bb-aa87-2669f0462f7d.lance +0 -0
  164. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/cb8ac58e-0e59-4391-95ab-6195e71b9625.lance +0 -0
  165. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/d0f10434-afb5-49c5-9e77-d39a4f8cca94.lance +0 -0
  166. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/d3f89fec-5795-4e11-8c15-bc2206a7fae2.lance +0 -0
  167. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/e063c8a4-5ef1-4933-a049-b41049d9be5f.lance +0 -0
  168. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/e2348b24-f290-4fe2-a6c8-e98009ae4c1b.lance +0 -0
  169. scmcp_shared-0.5.0/src/scmcp_shared/vector_db/decoupler.lance/data/e5d3f893-6763-40dc-9d02-04e2f56a4883.lance +0 -0
  170. scmcp_shared-0.5.0/tests/conftest.py +27 -0
  171. {scmcp_shared-0.3.7 → scmcp_shared-0.5.0}/tests/test_io.py +7 -5
  172. scmcp_shared-0.5.0/tests/test_pp.py +108 -0
  173. scmcp_shared-0.3.7/src/scmcp_shared/__init__.py +0 -3
  174. scmcp_shared-0.3.7/src/scmcp_shared/server/__init__.py +0 -13
  175. scmcp_shared-0.3.7/src/scmcp_shared/server/base.py +0 -148
  176. scmcp_shared-0.3.7/tests/conftest.py +0 -31
  177. scmcp_shared-0.3.7/tests/test_pp.py +0 -119
  178. {scmcp_shared-0.3.7 → scmcp_shared-0.5.0}/.github/release.yml +0 -0
  179. {scmcp_shared-0.3.7 → scmcp_shared-0.5.0}/.github/workflows/publish.yml +0 -0
  180. {scmcp_shared-0.3.7 → scmcp_shared-0.5.0}/.github/workflows/test.yml +0 -0
  181. {scmcp_shared-0.3.7 → scmcp_shared-0.5.0}/LICENSE +0 -0
  182. {scmcp_shared-0.3.7 → scmcp_shared-0.5.0}/README.md +0 -0
  183. {scmcp_shared-0.3.7 → scmcp_shared-0.5.0}/src/scmcp_shared/schema/__init__.py +0 -0
  184. {scmcp_shared-0.3.7 → scmcp_shared-0.5.0}/tests/data/hg19/barcodes.tsv +0 -0
  185. {scmcp_shared-0.3.7 → scmcp_shared-0.5.0}/tests/data/hg19/genes.tsv +0 -0
  186. {scmcp_shared-0.3.7 → scmcp_shared-0.5.0}/tests/data/hg19/matrix.mtx +0 -0
@@ -0,0 +1,29 @@
1
+ fail_fast: true
2
+
3
+ repos:
4
+ - repo: https://github.com/abravalheri/validate-pyproject
5
+ rev: v0.23
6
+ hooks:
7
+ - id: validate-pyproject
8
+
9
+ - repo: https://github.com/pre-commit/mirrors-prettier
10
+ rev: v3.1.0
11
+ hooks:
12
+ - id: prettier
13
+ types_or: [yaml, json5]
14
+
15
+ - repo: https://github.com/astral-sh/ruff-pre-commit
16
+ # Ruff version.
17
+ rev: v0.12.1
18
+ hooks:
19
+ # Run the linter.
20
+ - id: ruff-check
21
+ args: [--fix]
22
+ # Run the formatter.
23
+ - id: ruff-format
24
+
25
+ # - repo: https://github.com/northisup/pyright-pretty
26
+ # rev: v0.1.0
27
+ # hooks:
28
+ # - id: pyright-pretty
29
+ # files: ^src/|^tests/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scmcp_shared
3
- Version: 0.3.7
3
+ Version: 0.5.0
4
4
  Summary: A shared function libray for scmcphub
5
5
  Project-URL: Homepage, http://scmcphub.org/
6
6
  Project-URL: Repository, https://github.com/scmcphub/scmcp-shared
@@ -37,8 +37,10 @@ License: BSD 3-Clause License
37
37
  License-File: LICENSE
38
38
  Keywords: AI,agent,bioinformatics,llm,mcp,model context protocol,scRNA-seq,single cell
39
39
  Requires-Python: >=3.10
40
+ Requires-Dist: abcoder
40
41
  Requires-Dist: fastmcp>=2.7.0
41
42
  Requires-Dist: igraph
43
+ Requires-Dist: instructor>=1.8.3
42
44
  Requires-Dist: leidenalg
43
45
  Requires-Dist: mcp>=1.8.0
44
46
  Requires-Dist: nest-asyncio
@@ -27,6 +27,8 @@ dependencies = [
27
27
  "mcp>=1.8.0",
28
28
  "fastmcp>=2.7.0",
29
29
  "nest_asyncio",
30
+ "instructor>=1.8.3",
31
+ "abcoder"
30
32
  ]
31
33
 
32
34
  [build-system]
@@ -51,4 +53,7 @@ Documentation = "https://docs.scmcphub.org/"
51
53
 
52
54
  [tool.pytest.ini_options]
53
55
  asyncio_mode = "strict"
54
- asyncio_default_fixture_loop_scope = "function"
56
+ asyncio_default_fixture_loop_scope = "function"
57
+
58
+ [tool.ruff]
59
+ lint.ignore = ["F403", "F405"]
@@ -0,0 +1 @@
1
+ __version__ = "0.5.0"
@@ -0,0 +1,80 @@
1
+ import instructor
2
+ from openai import OpenAI
3
+ from scmcp_shared.schema.tool import ToolList
4
+ import os
5
+
6
+
7
+ from agno.agent import Agent
8
+ from agno.models.openai import OpenAILike
9
+ from scmcp_shared.kb import load_kb
10
+
11
+ model = OpenAILike(
12
+ id=os.getenv("MODEL"),
13
+ base_url=os.getenv("BASE_URL"),
14
+ api_key=os.getenv("API_KEY"),
15
+ )
16
+
17
+
18
+ def rag_agent(task, software=None):
19
+ knowledge_base = load_kb(software=software)
20
+ agent = Agent(
21
+ model=model,
22
+ knowledge=knowledge_base,
23
+ show_tool_calls=True,
24
+ search_knowledge=True,
25
+ )
26
+ query = f"""
27
+ <task>
28
+ {task}
29
+ </task>
30
+ 拆分任务,分别查询知识库,查找完成每个子任务所需要的函数以及参数, 给出一个总的用于解决任务的代码示例。返回结果格式为:
31
+ <function_list>
32
+ <function>
33
+ <package>.<module>.<function_name>
34
+ <package>.<module>.<function_name>(<param1>[, <param2>=<default>][, ...][, **kwargs])
35
+ <一句话描述函数的主要功能>。
36
+
37
+ <可选:补充说明/实现细节/依赖等>
38
+
39
+ Parameters
40
+ :
41
+ <param1> (<type>) – <param1 的说明>
42
+
43
+ <param2> (<type> (default: <default>)) – <param2 的说明>
44
+
45
+ <param3> (<type> (default: <default>)) – <param3 的说明>
46
+
47
+ ...(按需继续列出其他参数)
48
+
49
+ **kwargs – <对底层实现传递的其他关键字参数的说明;如无需说明可简写为“其他关键字参数”。>
50
+ </function>
51
+ ...
52
+ </function_list>
53
+ <code_example>
54
+ [code_example]
55
+ </code_example>
56
+ """
57
+ rep = agent.run(query)
58
+ return rep.content
59
+
60
+
61
+ def select_tool(query):
62
+ API_KEY = os.environ.get("API_KEY", None)
63
+ BASE_URL = os.environ.get("BASE_URL", None)
64
+ MODEL = os.environ.get("MODEL", None)
65
+
66
+ client = OpenAI(api_key=API_KEY, base_url=BASE_URL)
67
+ client = instructor.from_openai(client)
68
+
69
+ response = client.chat.completions.create(
70
+ model=MODEL,
71
+ messages=[
72
+ {
73
+ "role": "system",
74
+ "content": "you are a bioinformatician, you are given a task and a list of tools, you need to select the most directly relevant tools to use to solve the task",
75
+ },
76
+ {"role": "user", "content": query},
77
+ ],
78
+ response_model=ToolList,
79
+ )
80
+ return response.tools
@@ -0,0 +1,44 @@
1
+ from collections.abc import Iterable
2
+ from abcoder.backend import NotebookManager
3
+
4
+ __all__ = ["AdataManager", "NotebookManager"]
5
+
6
+
7
+ class AdataManager:
8
+ def __init__(self, add_adtypes=None):
9
+ self.adata_dic = {"exp": {}, "activity": {}, "cnv": {}, "splicing": {}}
10
+ if isinstance(add_adtypes, str):
11
+ self.adata_dic[add_adtypes] = {}
12
+ elif isinstance(add_adtypes, Iterable):
13
+ self.adata_dic.update({adtype: {} for adtype in add_adtypes})
14
+ self.active_id = None
15
+ self.metadatWa = {}
16
+ self.cr_kernel = {}
17
+ self.cr_estimator = {}
18
+
19
+ def get_adata(self, sampleid=None, adtype="exp", adinfo=None):
20
+ if adinfo is not None:
21
+ kwargs = adinfo.model_dump()
22
+ sampleid = kwargs.get("sampleid", None)
23
+ adtype = kwargs.get("adtype", "exp")
24
+ try:
25
+ if self.active_id is None:
26
+ return None
27
+ sampleid = sampleid or self.active_id
28
+ return self.adata_dic[adtype][sampleid]
29
+ except KeyError as e:
30
+ raise KeyError(
31
+ f"Key {e} not found in adata_dic[{adtype}].Please check the sampleid or adtype."
32
+ )
33
+ except Exception as e:
34
+ raise Exception(f"fuck {e} {type(e)}")
35
+
36
+ def set_adata(self, adata, sampleid=None, sdtype="exp", adinfo=None):
37
+ if adinfo is not None:
38
+ kwargs = adinfo.model_dump()
39
+ sampleid = kwargs.get("sampleid", None)
40
+ sdtype = kwargs.get("adtype", "exp")
41
+ sampleid = sampleid or self.active_id
42
+ if sdtype not in self.adata_dic:
43
+ self.adata_dic[sdtype] = {}
44
+ self.adata_dic[sdtype][sampleid] = adata
@@ -1,50 +1,69 @@
1
1
  import argparse
2
- from typing import Optional, Union, Type, Dict, Callable
3
- from enum import Enum
2
+ from typing import Optional, Dict, Callable
4
3
  from .util import add_figure_route, set_env
5
4
  import os
6
5
 
7
6
 
8
7
  class MCPCLI:
9
8
  """Base class for CLI applications with support for dynamic modules and parameters."""
10
-
9
+
11
10
  def __init__(self, name: str, help_text: str, mcp=None, manager=None):
12
11
  self.name = name
13
12
  self.mcp = mcp
14
13
  self.manager = manager
15
- self.parser = argparse.ArgumentParser(
16
- description=help_text,
17
- prog=name
18
- )
14
+ self.parser = argparse.ArgumentParser(description=help_text, prog=name)
19
15
  self.subcommands: Dict[str, tuple[argparse.ArgumentParser, Callable]] = {}
20
16
  self._setup_commands()
21
-
17
+
22
18
  def _setup_commands(self):
23
19
  """Setup the main commands for the CLI."""
24
- subparsers = self.parser.add_subparsers(dest='command', help='Available commands')
25
- run_parser = subparsers.add_parser('run', help='Start the server with the specified configuration')
20
+ subparsers = self.parser.add_subparsers(
21
+ dest="command", help="Available commands"
22
+ )
23
+ run_parser = subparsers.add_parser(
24
+ "run", help="Start the server with the specified configuration"
25
+ )
26
26
  self._setup_run_command(run_parser)
27
- self.subcommands['run'] = (run_parser, self._run_command)
28
-
27
+ self.subcommands["run"] = (run_parser, self._run_command)
28
+
29
29
  def _setup_run_command(self, parser: argparse.ArgumentParser):
30
30
  """Setup run command arguments."""
31
- parser.add_argument('-t', '--transport', default="stdio",
32
- choices=["stdio", "shttp", "sse"],
33
- help='specify transport type')
34
- parser.add_argument('-p', '--port', type=int, default=8000, help='transport port')
35
- parser.add_argument('--host', default='127.0.0.1', help='transport host')
36
- parser.add_argument('-f', '--forward', help='forward request to another server')
37
- parser.add_argument('-wd', '--working-dir', default=".", help='working directory')
38
- parser.add_argument('--log-file', help='log file path, use stdout if None')
39
-
40
- def add_command(self, name: str, help_text: str, handler: Callable) -> argparse.ArgumentParser:
31
+ parser.add_argument(
32
+ "-t",
33
+ "--transport",
34
+ default="stdio",
35
+ choices=["stdio", "shttp", "sse"],
36
+ help="specify transport type",
37
+ )
38
+ parser.add_argument(
39
+ "-p", "--port", type=int, default=8000, help="transport port"
40
+ )
41
+ parser.add_argument("--host", default="127.0.0.1", help="transport host")
42
+ parser.add_argument("-f", "--forward", help="forward request to another server")
43
+ parser.add_argument(
44
+ "-wd", "--working-dir", default=".", help="working directory"
45
+ )
46
+ parser.add_argument(
47
+ "--run-mode", choices=["tool", "code"], default="code", help="run mode"
48
+ )
49
+ parser.add_argument(
50
+ "--tool-mode",
51
+ choices=["auto", "normal"],
52
+ default="normal",
53
+ help="tool selection mode",
54
+ )
55
+ parser.add_argument("--log-file", help="log file path, use stdout if None")
56
+
57
+ def add_command(
58
+ self, name: str, help_text: str, handler: Callable
59
+ ) -> argparse.ArgumentParser:
41
60
  """add new subcommand
42
-
61
+
43
62
  Args:
44
63
  name: subcommand name
45
64
  help_text: help text
46
65
  handler: handler function
47
-
66
+
48
67
  Returns:
49
68
  ArgumentParser: parser for the subcommand
50
69
  """
@@ -52,24 +71,24 @@ class MCPCLI:
52
71
  parser = subparsers.add_parser(name, help=help_text)
53
72
  self.subcommands[name] = (parser, handler)
54
73
  return parser
55
-
74
+
56
75
  def get_command_parser(self, name: str) -> Optional[argparse.ArgumentParser]:
57
76
  """get the parser for the subcommand
58
-
77
+
59
78
  Args:
60
79
  name: subcommand name
61
-
80
+
62
81
  Returns:
63
82
  ArgumentParser: parser for the subcommand, return None if the subcommand does not exist
64
83
  """
65
84
  if name in self.subcommands:
66
85
  return self.subcommands[name][0]
67
86
  return None
68
-
87
+
69
88
  def _run_command(self, args):
70
89
  """Start the server with the specified configuration."""
71
90
  os.chdir(args.working_dir)
72
- if hasattr(args, 'module'):
91
+ if hasattr(args, "module"):
73
92
  if "all" in args.module:
74
93
  modules = None
75
94
  elif isinstance(args.module, list) and bool(args.module):
@@ -77,7 +96,31 @@ class MCPCLI:
77
96
  else:
78
97
  modules = None
79
98
  if self.manager is not None:
80
- self.mcp = self.manager(self.name, include_modules=modules).mcp
99
+ from .backend import NotebookManager, AdataManager
100
+
101
+ if args.run_mode == "code":
102
+ backend = NotebookManager
103
+ self.mcp = self.manager(
104
+ self.name, include_tags=["nb", "rag"], backend=backend
105
+ ).mcp
106
+ else:
107
+ backend = AdataManager
108
+ self.mcp = self.manager(
109
+ self.name, include_modules=modules, backend=backend
110
+ ).mcp
111
+ all_tools = self.mcp._tool_manager._tools
112
+ auto_tools = {
113
+ tool: all_tools[tool]
114
+ for tool in all_tools
115
+ if "auto" in all_tools[tool].tags
116
+ }
117
+ if args.tool_mode == "auto":
118
+ all_tools = self.mcp._tool_manager._tools
119
+ self.mcp._tool_manager._all_tools = all_tools
120
+ self.mcp._tool_manager._tools = auto_tools
121
+ else:
122
+ for name in auto_tools:
123
+ self.mcp._tool_manager.remove_tool(name)
81
124
  elif self.mcp is not None:
82
125
  pass
83
126
  else:
@@ -88,19 +131,15 @@ class MCPCLI:
88
131
  def run_mcp(self, log_file, forward, transport, host, port):
89
132
  set_env(log_file, forward, transport, host, port)
90
133
  from .logging_config import setup_logger
134
+
91
135
  setup_logger(log_file)
92
136
  if transport == "stdio":
93
137
  self.mcp.run()
94
138
  elif transport in ["sse", "shttp"]:
95
139
  transport = "streamable-http" if transport == "shttp" else transport
96
140
  add_figure_route(self.mcp)
97
- self.mcp.run(
98
- transport=transport,
99
- host=host,
100
- port=port,
101
- log_level="info"
102
- )
103
-
141
+ self.mcp.run(transport=transport, host=host, port=port, log_level="info")
142
+
104
143
  def run(self):
105
144
  """Run the CLI application."""
106
145
  args = self.parser.parse_args()
@@ -0,0 +1,38 @@
1
+ from agno.document.chunking.agentic import AgenticChunking
2
+ from agno.embedder.openai import OpenAIEmbedder
3
+ from agno.models.deepseek import DeepSeek
4
+ from agno.vectordb.lancedb import LanceDb
5
+ from agno.knowledge.agent import AgentKnowledge
6
+ import importlib.resources
7
+ import os
8
+
9
+
10
+ embedder_id = os.getenv("EMBEDDER_MODEL")
11
+ embedder_api_key = os.getenv("EMBEDDER_API_KEY")
12
+ embedder_base_url = os.getenv("EMBEDDER_BASE_URL")
13
+ model_id = os.getenv("MODEL")
14
+ model_api_key = os.getenv("API_KEY")
15
+ model_base_url = os.getenv("BASE_URL")
16
+
17
+
18
+ def load_kb(software=None):
19
+ vector_db = LanceDb(
20
+ table_name=software,
21
+ uri=importlib.resources.path("scmcp_shared", "vector_db"),
22
+ embedder=OpenAIEmbedder(
23
+ id=embedder_id,
24
+ base_url=embedder_base_url,
25
+ api_key=embedder_api_key,
26
+ ),
27
+ )
28
+ model = DeepSeek(
29
+ id=model_id,
30
+ base_url=model_base_url,
31
+ api_key=model_api_key,
32
+ )
33
+ knowledge_base = AgentKnowledge(
34
+ chunking_strategy=AgenticChunking(model=model),
35
+ vector_db=vector_db,
36
+ )
37
+
38
+ return knowledge_base
@@ -1,18 +1,17 @@
1
1
  import logging
2
2
  import sys
3
- import os
3
+
4
4
  from .util import get_env
5
5
 
6
- def setup_logger(name="sc-mcp-server", log_file=None):
7
6
 
7
+ def setup_logger(name="sc-mcp-server", log_file=None):
8
8
  logger = logging.getLogger(name)
9
9
  logger.setLevel(logging.INFO)
10
10
  if logger.handlers:
11
11
  return logger
12
-
12
+
13
13
  formatter = logging.Formatter(
14
- '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
15
- '%Y-%m-%d %H:%M:%S'
14
+ "%(asctime)s - %(name)s - %(levelname)s - %(message)s", "%Y-%m-%d %H:%M:%S"
16
15
  )
17
16
  if log_file is None:
18
17
  log_file = get_env("LOG_FILE")
@@ -20,12 +19,11 @@ def setup_logger(name="sc-mcp-server", log_file=None):
20
19
  log_handler = logging.FileHandler(log_file)
21
20
  log_handler.setFormatter(formatter)
22
21
  logger.addHandler(log_handler)
23
-
22
+
24
23
  logger.info(f"logging output: {log_file}")
25
24
  else:
26
25
  log_handler = logging.StreamHandler(sys.stdout)
27
26
  log_handler.setFormatter(formatter)
28
27
  logger.addHandler(log_handler)
29
- logger.info(f"loggin file output: stdout")
28
+ logger.info("loggin file output: stdout")
30
29
  return logger
31
-
@@ -0,0 +1,184 @@
1
+ import inspect
2
+ from fastmcp import FastMCP
3
+ from .schema import AdataInfo
4
+ from .util import filter_tools
5
+ from collections.abc import AsyncIterator
6
+ from contextlib import asynccontextmanager
7
+ import asyncio
8
+ from typing import Optional, List, Any
9
+ from abcoder.backend import NotebookManager
10
+
11
+
12
+ # class BaseMCP:
13
+ # """Base class for all Scanpy MCP classes."""
14
+
15
+ # def __init__(self, name: str, instructions=None, lifespan=None, include_tools: list = None, exclude_tools: list = None, AdataInfo = AdataInfo):
16
+ # """
17
+ # Initialize BaseMCP with optional tool filtering.
18
+
19
+ # Args:
20
+ # name (str): Name of the MCP server
21
+ # include_tools (list, optional): List of tool names to include. If None, all tools are included.
22
+ # exclude_tools (list, optional): List of tool names to exclude. If None, no tools are excluded.
23
+ # AdataInfo: The AdataInfo class to use for type annotations.
24
+ # """
25
+ # self.mcp = FastMCP(name, instructions=instructions, lifespan=lifespan)
26
+ # self.include_tools = include_tools
27
+ # self.exclude_tools = exclude_tools
28
+ # self.AdataInfo = AdataInfo
29
+ # self._register_tools()
30
+
31
+ # def _register_tools(self):
32
+ # """Register all tool methods with the FastMCP instance based on include/exclude filters"""
33
+ # # Get all methods of the class
34
+ # methods = inspect.getmembers(self, predicate=inspect.ismethod)
35
+
36
+ # # Filter methods that start with _tool_
37
+ # tool_methods = [tl_method() for name, tl_method in methods if name.startswith('_tool_')]
38
+
39
+ # # Filter tools based on include/exclude lists
40
+ # if self.include_tools is not None:
41
+ # tool_methods = [tl for tl in tool_methods if tl.name in self.include_tools]
42
+
43
+ # if self.exclude_tools is not None:
44
+ # tool_methods = [tl for tl in tool_methods if tl.name not in self.exclude_tools]
45
+
46
+ # # Register filtered tools
47
+ # for tool in tool_methods:
48
+ # # Get the function returned by the tool method
49
+ # if tool is not None:
50
+ # self.mcp.add_tool(tool)
51
+
52
+
53
+ class BaseMCP:
54
+ """Base class for all Scanpy MCP classes."""
55
+
56
+ def __init__(
57
+ self,
58
+ name: str,
59
+ include_tools: list = None,
60
+ exclude_tools: list = None,
61
+ AdataInfo=AdataInfo,
62
+ ):
63
+ """
64
+ Initialize BaseMCP with optional tool filtering.
65
+
66
+ Args:
67
+ name (str): Name of the MCP server
68
+ include_tools (list, optional): List of tool names to include. If None, all tools are included.
69
+ exclude_tools (list, optional): List of tool names to exclude. If None, no tools are excluded.
70
+ AdataInfo: The AdataInfo class to use for type annotations.
71
+ """
72
+ self.mcp = FastMCP(name)
73
+ self.include_tools = include_tools
74
+ self.exclude_tools = exclude_tools
75
+ self.AdataInfo = AdataInfo
76
+ self._register_tools()
77
+
78
+ def _register_tools(self):
79
+ """Register all tool methods with the FastMCP instance based on include/exclude filters"""
80
+ # Get all methods of the class
81
+ methods = inspect.getmembers(self, predicate=inspect.ismethod)
82
+
83
+ # Filter methods that start with _tool_
84
+ tool_methods = [
85
+ tl_method() for name, tl_method in methods if name.startswith("_tool_")
86
+ ]
87
+
88
+ # Filter tools based on include/exclude lists
89
+ if self.include_tools is not None:
90
+ tool_methods = [tl for tl in tool_methods if tl.name in self.include_tools]
91
+
92
+ if self.exclude_tools is not None:
93
+ tool_methods = [
94
+ tl for tl in tool_methods if tl.name not in self.exclude_tools
95
+ ]
96
+
97
+ # Register filtered tools
98
+ for tool in tool_methods:
99
+ # Get the function returned by the tool method
100
+ if tool is not None:
101
+ self.mcp.add_tool(tool)
102
+
103
+
104
+ class BaseMCPManager:
105
+ """Base class for MCP module management."""
106
+
107
+ def __init__(
108
+ self,
109
+ name: str,
110
+ instructions: Optional[str] = None,
111
+ include_modules: Optional[List[str]] = None,
112
+ exclude_modules: Optional[List[str]] = None,
113
+ include_tools: Optional[List[str]] = None,
114
+ exclude_tools: Optional[List[str]] = None,
115
+ include_tags: Optional[List[str]] = None,
116
+ exclude_tags: Optional[List[str]] = None,
117
+ backend: Optional[NotebookManager] = None,
118
+ ):
119
+ """
120
+ Initialize BaseMCPManager with optional module filtering.
121
+
122
+ Args:
123
+ name (str): Name of the MCP server
124
+ include_modules (List[str], optional): List of module names to include. If None, all modules are included.
125
+ exclude_modules (List[str], optional): List of module names to exclude. If None, no modules are excluded.
126
+ include_tools (List[str], optional): List of tool names to include. If None, all tools are included.
127
+ exclude_tools (List[str], optional): List of tool names to exclude. If None, no tools are excluded.
128
+ """
129
+ self.exec_backend = backend()
130
+ self.mcp = FastMCP(
131
+ name,
132
+ instructions=instructions,
133
+ lifespan=self.exec_lifespan,
134
+ include_tags=include_tags,
135
+ exclude_tags=exclude_tags,
136
+ )
137
+ self.include_modules = include_modules
138
+ self.exclude_modules = exclude_modules
139
+ self.include_tools = include_tools
140
+ self.exclude_tools = exclude_tools
141
+ self.available_modules = {}
142
+ self.init_mcp()
143
+ self.register_mcp()
144
+
145
+ def init_mcp(self):
146
+ """Initialize available modules. To be implemented by subclasses."""
147
+ raise NotImplementedError("Subclasses must implement init_mcp")
148
+
149
+ def register_mcp(self):
150
+ """Register modules based on include/exclude filters."""
151
+ # Filter modules based on include/exclude lists
152
+ if self.include_modules is not None:
153
+ self.available_modules = {
154
+ k: v
155
+ for k, v in self.available_modules.items()
156
+ if k in self.include_modules
157
+ }
158
+
159
+ if self.exclude_modules is not None:
160
+ self.available_modules = {
161
+ k: v
162
+ for k, v in self.available_modules.items()
163
+ if k not in self.exclude_modules
164
+ }
165
+
166
+ # Register each module
167
+ for module_name, mcpi in self.available_modules.items():
168
+ if isinstance(mcpi, FastMCP):
169
+ if self.include_tools is not None and module_name in self.include_tools:
170
+ mcpi = filter_tools(
171
+ mcpi, include_tools=self.include_tools[module_name]
172
+ )
173
+ if self.exclude_tools is not None and module_name in self.exclude_tools:
174
+ mcpi = filter_tools(
175
+ mcpi, exclude_tools=self.exclude_tools[module_name]
176
+ )
177
+
178
+ asyncio.run(self.mcp.import_server(module_name, mcpi))
179
+ else:
180
+ asyncio.run(self.mcp.import_server(module_name, mcpi().mcp))
181
+
182
+ @asynccontextmanager
183
+ async def exec_lifespan(self, server: FastMCP) -> AsyncIterator[Any]:
184
+ yield self.exec_backend