tushare-mcp-server 1.0.0

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 (184) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +148 -0
  3. package/README_CN.md +146 -0
  4. package/bin/cli.js +88 -0
  5. package/build_index.py +159 -0
  6. package/crawl_docs.py +321 -0
  7. package/docs/0019_/345/237/272/351/207/221/345/210/227/350/241/250.md +75 -0
  8. package/docs/0025_/350/202/241/347/245/250/345/210/227/350/241/250.md +95 -0
  9. package/docs/0026_/344/272/244/346/230/223/346/227/245/345/216/206.md +73 -0
  10. package/docs/0027_/345/216/206/345/217/262/346/227/245/347/272/277.md +81 -0
  11. package/docs/0028_/345/244/215/346/235/203/345/233/240/345/255/220.md +79 -0
  12. package/docs/0032_/346/257/217/346/227/245/346/214/207/346/240/207.md +89 -0
  13. package/docs/0033_/345/210/251/346/266/246/350/241/250.md +168 -0
  14. package/docs/0036_/350/265/204/344/272/247/350/264/237/345/200/272/350/241/250.md +234 -0
  15. package/docs/0044_/347/216/260/351/207/221/346/265/201/351/207/217/350/241/250.md +174 -0
  16. package/docs/0045_/344/270/232/347/273/251/351/242/204/345/221/212.md +85 -0
  17. package/docs/0046_/344/270/232/347/273/251/345/277/253/346/212/245.md +86 -0
  18. package/docs/0047_/346/262/252/346/267/261/346/270/257/351/200/232/350/265/204/351/207/221/346/265/201/345/220/221.md +75 -0
  19. package/docs/0048_/346/262/252/346/267/261/350/202/241/351/200/232/345/215/201/345/244/247/346/210/220/344/272/244/350/202/241.md +81 -0
  20. package/docs/0049_/346/270/257/350/202/241/351/200/232/345/215/201/345/244/247/346/210/220/344/272/244/350/202/241.md +129 -0
  21. package/docs/0058_/350/236/215/350/265/204/350/236/215/345/210/270/344/272/244/346/230/223/346/261/207/346/200/273.md +85 -0
  22. package/docs/0059_/350/236/215/350/265/204/350/236/215/345/210/270/344/272/244/346/230/223/346/230/216/347/273/206.md +128 -0
  23. package/docs/0061_/345/211/215/345/215/201/345/244/247/350/202/241/344/270/234.md +68 -0
  24. package/docs/0062_/345/211/215/345/215/201/345/244/247/346/265/201/351/200/232/350/202/241/344/270/234.md +68 -0
  25. package/docs/0079_/350/264/242/345/212/241/346/214/207/346/240/207/346/225/260/346/215/256.md +227 -0
  26. package/docs/0080_/350/264/242/345/212/241/345/256/241/350/256/241/346/204/217/350/247/201.md +60 -0
  27. package/docs/0081_/344/270/273/350/220/245/344/270/232/345/212/241/346/236/204/346/210/220.md +64 -0
  28. package/docs/0094_/346/214/207/346/225/260/345/237/272/346/234/254/344/277/241/346/201/257.md +107 -0
  29. package/docs/0095_/346/214/207/346/225/260/346/227/245/347/272/277/350/241/214/346/203/205.md +78 -0
  30. package/docs/0096_/346/214/207/346/225/260/346/210/220/345/210/206/345/222/214/346/235/203/351/207/215.md +59 -0
  31. package/docs/0100_/350/202/241/347/245/250/346/233/276/347/224/250/345/220/215.md +52 -0
  32. package/docs/0103_/345/210/206/347/272/242/351/200/201/350/202/241/346/225/260/346/215/256.md +72 -0
  33. package/docs/0106_/351/276/231/350/231/216/346/246/234/346/257/217/346/227/245/347/273/237/350/256/241.md +143 -0
  34. package/docs/0107_/351/276/231/350/231/216/346/246/234/346/234/272/346/236/204/344/272/244/346/230/223.md +75 -0
  35. package/docs/0109_/351/200/232/347/224/250/350/241/214/346/203/205/346/216/245/345/217/243.md +118 -0
  36. package/docs/0110_/350/202/241/346/235/203/350/264/250/346/212/274/347/273/237/350/256/241.md +72 -0
  37. package/docs/0111_/350/202/241/346/235/203/350/264/250/346/212/274/346/230/216/347/273/206.md +69 -0
  38. package/docs/0112_/344/270/212/345/270/202/345/205/254/345/217/270/345/237/272/346/234/254/344/277/241/346/201/257.md +77 -0
  39. package/docs/0118_/345/237/272/351/207/221/347/256/241/347/220/206/344/272/272.md +67 -0
  40. package/docs/0119_/345/237/272/351/207/221/345/207/200/345/200/274.md +63 -0
  41. package/docs/0120_/345/237/272/351/207/221/345/210/206/347/272/242.md +76 -0
  42. package/docs/0121_/345/237/272/351/207/221/346/214/201/344/273/223.md +76 -0
  43. package/docs/0123_IPO/346/226/260/350/202/241/344/270/212/345/270/202.md +81 -0
  44. package/docs/0124_/350/202/241/347/245/250/345/233/236/350/264/255.md +79 -0
  45. package/docs/0127_ETF/346/227/245/347/272/277/350/241/214/346/203/205.md +66 -0
  46. package/docs/0128_/345/244/247/347/233/230/346/214/207/346/225/260/346/257/217/346/227/245/346/214/207/346/240/207.md +66 -0
  47. package/docs/0135_/346/234/237/350/264/247/345/220/210/347/272/246/344/277/241/346/201/257.md +80 -0
  48. package/docs/0137_/346/234/237/350/264/247/344/272/244/346/230/223/346/227/245/345/216/206.md +113 -0
  49. package/docs/0138_/346/234/237/350/264/247/346/227/245/347/272/277/350/241/214/346/203/205.md +131 -0
  50. package/docs/0139_/346/234/237/350/264/247/346/257/217/346/227/245/346/214/201/344/273/223/346/216/222/345/220/215.md +98 -0
  51. package/docs/0140_/346/234/237/350/264/247/344/273/223/345/215/225/346/227/245/346/212/245.md +83 -0
  52. package/docs/0141_/346/234/237/350/264/247/346/257/217/346/227/245/347/273/223/347/256/227/345/217/202/346/225/260.md +123 -0
  53. package/docs/0143_/346/226/260/351/227/273/345/277/253/350/256/257.md +71 -0
  54. package/docs/0144_/345/221/250/347/272/277/350/241/214/346/203/205.md +108 -0
  55. package/docs/0145_/346/234/210/347/272/277/350/241/214/346/203/205.md +106 -0
  56. package/docs/0146_/345/244/215/346/235/203/350/241/214/346/203/205.md +68 -0
  57. package/docs/0149_Shibor/345/210/251/347/216/207.md +92 -0
  58. package/docs/0150_Shibor/346/212/245/344/273/267/346/225/260/346/215/256.md +112 -0
  59. package/docs/0151_LPR/350/264/267/346/254/276/345/237/272/347/241/200/345/210/251/347/216/207.md +84 -0
  60. package/docs/0152_Libor/345/210/251/347/216/207.md +108 -0
  61. package/docs/0153_Hibor/345/210/251/347/216/207.md +107 -0
  62. package/docs/0154_/346/226/260/351/227/273/350/201/224/346/222/255/346/226/207/345/255/227/347/250/277.md +61 -0
  63. package/docs/0155_/345/215/227/345/215/216/346/234/237/350/264/247/346/214/207/346/225/260/350/241/214/346/203/205.md +146 -0
  64. package/docs/0158_/346/234/237/346/235/203/345/220/210/347/272/246/344/277/241/346/201/257.md +82 -0
  65. package/docs/0159_/346/234/237/346/235/203/346/227/245/347/272/277/350/241/214/346/203/205.md +80 -0
  66. package/docs/0160_/351/231/220/345/224/256/350/202/241/350/247/243/347/246/201.md +107 -0
  67. package/docs/0161_/345/244/247/345/256/227/344/272/244/346/230/223.md +102 -0
  68. package/docs/0162_/350/264/242/346/212/245/346/212/253/351/234/262/346/227/245/346/234/237/350/241/250.md +66 -0
  69. package/docs/0166_/350/202/241/344/270/234/344/272/272/346/225/260.md +73 -0
  70. package/docs/0170_/344/270/252/350/202/241/350/265/204/351/207/221/346/265/201/345/220/221.md +130 -0
  71. package/docs/0171_/346/214/207/346/225/260/345/221/250/347/272/277/350/241/214/346/203/205.md +100 -0
  72. package/docs/0172_/346/214/207/346/225/260/346/234/210/347/272/277/350/241/214/346/203/205.md +104 -0
  73. package/docs/0173_/346/270/251/345/267/236/346/260/221/351/227/264/345/200/237/350/264/267/345/210/251/347/216/207.md +114 -0
  74. package/docs/0175_/350/202/241/344/270/234/345/242/236/345/207/217/346/214/201.md +99 -0
  75. package/docs/0176_/344/270/212/345/270/202/345/205/254/345/217/270/345/205/254/345/221/212.md +70 -0
  76. package/docs/0178_/345/244/226/346/261/207/345/237/272/347/241/200/344/277/241/346/201/257.md +92 -0
  77. package/docs/0179_/345/244/226/346/261/207/346/227/245/347/272/277/350/241/214/346/203/205.md +113 -0
  78. package/docs/0181_/347/224/263/344/270/207/350/241/214/344/270/232/345/210/206/347/261/273.md +600 -0
  79. package/docs/0183_/346/257/217/346/227/245/346/266/250/350/267/214/345/201/234/344/273/267/346/240/274.md +85 -0
  80. package/docs/0185_/345/217/257/350/275/254/345/200/272/345/237/272/347/241/200/344/277/241/346/201/257.md +83 -0
  81. package/docs/0186_/345/217/257/350/275/254/345/200/272/345/217/221/350/241/214.md +105 -0
  82. package/docs/0187_/345/217/257/350/275/254/345/200/272/350/241/214/346/203/205.md +115 -0
  83. package/docs/0188_/346/262/252/346/267/261/350/202/241/351/200/232/346/214/201/350/202/241/346/230/216/347/273/206.md +77 -0
  84. package/docs/0189_/346/234/237/350/264/247/344/270/273/345/212/233/344/270/216/350/277/236/347/273/255/345/220/210/347/272/246.md +80 -0
  85. package/docs/0191_/346/270/257/350/202/241/345/237/272/347/241/200/344/277/241/346/201/257.md +90 -0
  86. package/docs/0192_/346/270/257/350/202/241/346/227/245/347/272/277/350/241/214/346/203/205.md +91 -0
  87. package/docs/0193_/344/270/212/345/270/202/345/205/254/345/217/270/347/256/241/347/220/206/345/261/202.md +95 -0
  88. package/docs/0194_/347/256/241/347/220/206/345/261/202/350/226/252/351/205/254/345/222/214/346/214/201/350/202/241.md +82 -0
  89. package/docs/0195_/346/226/260/351/227/273/351/200/232/350/256/257/351/225/277/347/257/207.md +70 -0
  90. package/docs/0196_/346/270/257/350/202/241/351/200/232/346/257/217/346/227/245/346/210/220/344/272/244/347/273/237/350/256/241.md +89 -0
  91. package/docs/0197_/346/270/257/350/202/241/351/200/232/346/257/217/346/234/210/346/210/220/344/272/244/347/273/237/350/256/241.md +89 -0
  92. package/docs/0199_ETF/345/244/215/346/235/203/345/233/240/345/255/220.md +69 -0
  93. package/docs/0201_/345/233/275/345/200/272/346/224/266/347/233/212/347/216/207/346/233/262/347/272/277.md +74 -0
  94. package/docs/0207_/345/237/272/351/207/221/350/247/204/346/250/241.md +75 -0
  95. package/docs/0208_/345/237/272/351/207/221/347/273/217/347/220/206.md +74 -0
  96. package/docs/0211_/345/233/275/351/231/205/344/270/273/350/246/201/346/214/207/346/225/260.md +125 -0
  97. package/docs/0214_/346/257/217/346/227/245/345/201/234/345/244/215/347/211/214/344/277/241/346/201/257.md +74 -0
  98. package/docs/0215_/346/262/252/346/267/261/345/270/202/345/234/272/346/257/217/346/227/245/344/272/244/346/230/223/347/273/237/350/256/241.md +121 -0
  99. package/docs/0219_/347/276/216/345/233/275/345/233/275/345/200/272/346/224/266/347/233/212/347/216/207/346/233/262/347/272/277.md +82 -0
  100. package/docs/0227_/345/233/275/345/206/205/347/224/237/344/272/247/346/200/273/345/200/274GDP.md +77 -0
  101. package/docs/0228_/345/261/205/346/260/221/346/266/210/350/264/271/344/273/267/346/240/274/346/214/207/346/225/260CPI.md +84 -0
  102. package/docs/0242_/350/264/247/345/270/201/344/276/233/345/272/224/351/207/217/346/234/210.md +71 -0
  103. package/docs/0245_/345/267/245/344/270/232/347/224/237/344/272/247/350/200/205/345/207/272/345/216/202/344/273/267/346/240/274/346/214/207/346/225/260PPI.md +94 -0
  104. package/docs/0246_/345/217/257/350/275/254/345/200/272/350/275/254/350/202/241/344/273/267/345/217/230/345/212/250.md +57 -0
  105. package/docs/0247_/345/217/257/350/275/254/345/200/272/350/275/254/350/202/241/347/273/223/346/236/234.md +81 -0
  106. package/docs/0250_/346/270/257/350/202/241/344/272/244/346/230/223/346/227/245/345/216/206.md +65 -0
  107. package/docs/0252_/347/276/216/350/202/241/345/237/272/347/241/200/344/277/241/346/201/257.md +73 -0
  108. package/docs/0253_/347/276/216/350/202/241/344/272/244/346/230/223/346/227/245/345/216/206.md +67 -0
  109. package/docs/0254_/347/276/216/350/202/241/346/227/245/347/272/277/350/241/214/346/203/205.md +83 -0
  110. package/docs/0255_/345/244/207/347/224/250/350/241/214/346/203/205.md +86 -0
  111. package/docs/0256_/345/200/272/345/210/270/345/233/236/350/264/255/346/227/245/350/241/214/346/203/205.md +112 -0
  112. package/docs/0259_/345/220/214/350/212/261/351/241/272/350/241/214/344/270/232/346/246/202/345/277/265/346/235/277/345/235/227.md +70 -0
  113. package/docs/0260_/345/220/214/350/212/261/351/241/272/346/246/202/345/277/265/345/222/214/350/241/214/344/270/232/346/214/207/346/225/260/350/241/214/346/203/205.md +79 -0
  114. package/docs/0261_/345/220/214/350/212/261/351/241/272/350/241/214/344/270/232/346/246/202/345/277/265/346/210/220/345/210/206.md +60 -0
  115. package/docs/0262_/350/202/241/347/245/250/345/216/206/345/217/262/345/210/227/350/241/250.md +75 -0
  116. package/docs/0265_/345/220/204/346/270/240/351/201/223/345/205/254/345/213/237/345/237/272/351/207/221/351/224/200/345/224/256/344/277/235/346/234/211/350/247/204/346/250/241/345/215/240/346/257/224.md +62 -0
  117. package/docs/0266_/351/224/200/345/224/256/346/234/272/346/236/204/345/205/254/345/213/237/345/237/272/351/207/221/351/224/200/345/224/256/344/277/235/346/234/211/350/247/204/346/250/241.md +81 -0
  118. package/docs/0267_/345/210/270/345/225/206/346/234/210/345/272/246/351/207/221/350/202/241.md +54 -0
  119. package/docs/0269_/345/217/257/350/275/254/345/200/272/350/265/216/345/233/236/344/277/241/346/201/257.md +68 -0
  120. package/docs/0274_/344/270/255/345/244/256/347/273/223/347/256/227/347/263/273/347/273/237/346/214/201/350/202/241/346/230/216/347/273/206.md +71 -0
  121. package/docs/0275_/346/234/272/346/236/204/350/260/203/347/240/224/346/225/260/346/215/256.md +72 -0
  122. package/docs/0284_/344/270/212/346/265/267/351/273/204/351/207/221/345/237/272/347/241/200/344/277/241/346/201/257.md +73 -0
  123. package/docs/0285_/344/270/212/346/265/267/351/273/204/351/207/221/347/216/260/350/264/247/346/227/245/350/241/214/346/203/205.md +88 -0
  124. package/docs/0292_/345/210/270/345/225/206/347/233/210/345/210/251/351/242/204/346/265/213.md +89 -0
  125. package/docs/0293_/346/257/217/346/227/245/347/255/271/347/240/201/345/217/212/350/203/234/347/216/207.md +66 -0
  126. package/docs/0294_/346/257/217/346/227/245/347/255/271/347/240/201/345/210/206/345/270/203.md +59 -0
  127. package/docs/0295_/344/270/255/345/244/256/347/273/223/347/256/227/347/263/273/347/273/237/346/214/201/350/202/241/347/273/237/350/256/241.md +69 -0
  128. package/docs/0298_/346/266/250/350/267/214/345/201/234/345/222/214/347/202/270/346/235/277/346/225/260/346/215/256.md +83 -0
  129. package/docs/0304_/346/270/257/350/202/241/345/210/206/351/222/237/350/241/214/346/203/205.md +87 -0
  130. package/docs/0305_/345/217/257/350/275/254/345/200/272/347/245/250/351/235/242/345/210/251/347/216/207.md +63 -0
  131. package/docs/0308_/344/270/255/344/277/241/350/241/214/344/270/232/346/214/207/346/225/260/346/227/245/350/241/214/346/203/205.md +79 -0
  132. package/docs/0310_/347/244/276/350/236/215/345/242/236/351/207/217/346/234/210/345/272/246.md +82 -0
  133. package/docs/0311_/345/270/202/345/234/272/346/270/270/350/265/204/346/234/200/345/205/250/345/220/215/345/275/225.md +147 -0
  134. package/docs/0312_/346/270/270/350/265/204/344/272/244/346/230/223/346/257/217/346/227/245/346/230/216/347/273/206.md +59 -0
  135. package/docs/0325_/351/207/207/350/264/255/347/273/217/347/220/206/346/214/207/346/225/260PMI.md +120 -0
  136. package/docs/0326_/350/236/215/350/265/204/350/236/215/345/210/270/346/240/207/347/232/204/347/233/230/345/211/215.md +72 -0
  137. package/docs/0327_/347/224/263/344/270/207/350/241/214/344/270/232/346/214/207/346/225/260/346/227/245/350/241/214/346/203/205.md +78 -0
  138. package/docs/0328_/350/202/241/347/245/250/346/212/200/346/234/257/351/235/242/345/233/240/345/255/220.md +290 -0
  139. package/docs/0329_/346/257/217/346/227/245/350/202/241/346/234/254/347/233/230/345/211/215.md +62 -0
  140. package/docs/0331_/350/275/254/350/236/215/350/265/204/344/272/244/346/230/223/346/261/207/346/200/273.md +73 -0
  141. package/docs/0335_/347/224/263/344/270/207/350/241/214/344/270/232/346/210/220/345/210/206/345/210/206/347/272/247.md +82 -0
  142. package/docs/0336_/345/221/250/346/234/210/347/272/277/350/241/214/346/203/205/346/257/217/346/227/245/346/233/264/346/226/260.md +78 -0
  143. package/docs/0337_/346/234/237/350/264/247/345/221/250/346/234/210/347/272/277/350/241/214/346/203/205.md +48 -0
  144. package/docs/0338_/347/276/216/350/202/241/345/244/215/346/235/203/350/241/214/346/203/205.md +94 -0
  145. package/docs/0339_/346/270/257/350/202/241/345/244/215/346/235/203/350/241/214/346/203/205.md +92 -0
  146. package/docs/0341_/346/234/237/346/235/203/345/210/206/351/222/237/350/241/214/346/203/205.md +88 -0
  147. package/docs/0343_/350/241/214/344/270/232/350/265/204/351/207/221/346/265/201/345/220/221THS.md +67 -0
  148. package/docs/0344_/346/235/277/345/235/227/350/265/204/351/207/221/346/265/201/345/220/221DC.md +72 -0
  149. package/docs/0345_/345/244/247/347/233/230/350/265/204/351/207/221/346/265/201/345/220/221DC.md +75 -0
  150. package/docs/0348_/344/270/252/350/202/241/350/265/204/351/207/221/346/265/201/345/220/221THS.md +62 -0
  151. package/docs/0349_/344/270/252/350/202/241/350/265/204/351/207/221/346/265/201/345/220/221DC.md +82 -0
  152. package/docs/0362_/344/270/234/346/226/271/350/264/242/345/257/214/346/246/202/345/277/265/346/235/277/345/235/227.md +70 -0
  153. package/docs/0363_/344/270/234/346/226/271/350/264/242/345/257/214/346/246/202/345/277/265/346/210/220/345/210/206.md +72 -0
  154. package/docs/0365_/345/221/250/346/234/210/347/272/277/345/244/215/346/235/203/350/241/214/346/203/205/346/257/217/346/227/245/346/233/264/346/226/260.md +86 -0
  155. package/docs/0368_/346/234/237/350/264/247/345/220/210/347/272/246/346/266/250/350/267/214/345/201/234/344/273/267/346/240/274.md +80 -0
  156. package/docs/0370_/345/216/206/345/217/262/345/210/206/351/222/237.md +73 -0
  157. package/docs/0371_/346/235/277/345/235/227/350/265/204/351/207/221/346/265/201/345/220/221THS.md +65 -0
  158. package/docs/0372_/345/256/236/346/227/266/346/227/245/347/272/277.md +73 -0
  159. package/docs/0373_/344/270/255/344/277/241/350/241/214/344/270/232/346/210/220/345/210/206.md +124 -0
  160. package/docs/0374_/345/256/236/346/227/266/345/210/206/351/222/237.md +56 -0
  161. package/docs/0375_/345/214/227/344/272/244/346/211/200/346/226/260/346/227/247/344/273/243/347/240/201/345/257/271/347/205/247.md +60 -0
  162. package/docs/0385_ETF/345/237/272/346/234/254/344/277/241/346/201/257.md +110 -0
  163. package/docs/0386_ETF/345/237/272/345/207/206/346/214/207/346/225/260.md +70 -0
  164. package/docs/0387_ETF/345/216/206/345/217/262/345/210/206/351/222/237.md +73 -0
  165. package/docs/0388_/346/270/257/350/202/241/350/264/242/345/212/241/346/214/207/346/240/207/346/225/260/346/215/256.md +148 -0
  166. package/docs/0389_/346/270/257/350/202/241/345/210/251/346/266/246/350/241/250.md +92 -0
  167. package/docs/0390_/346/270/257/350/202/241/350/265/204/344/272/247/350/264/237/345/200/272/350/241/250.md +120 -0
  168. package/docs/0391_/346/270/257/350/202/241/347/216/260/351/207/221/346/265/201/351/207/217/350/241/250.md +115 -0
  169. package/docs/0393_/347/276/216/350/202/241/350/264/242/345/212/241/346/214/207/346/240/207/346/225/260/346/215/256.md +143 -0
  170. package/docs/0394_/347/276/216/350/202/241/345/210/251/346/266/246/350/241/250.md +79 -0
  171. package/docs/0395_/347/276/216/350/202/241/350/265/204/344/272/247/350/264/237/345/200/272/350/241/250.md +67 -0
  172. package/docs/0396_/347/276/216/350/202/241/347/216/260/351/207/221/346/265/201/351/207/217/350/241/250.md +79 -0
  173. package/docs/0397_ST/350/202/241/347/245/250/345/210/227/350/241/250.md +72 -0
  174. package/docs/0398_/346/262/252/346/267/261/346/270/257/351/200/232/350/202/241/347/245/250/345/210/227/350/241/250.md +82 -0
  175. package/docs/0399_AH/350/202/241/346/257/224/344/273/267.md +79 -0
  176. package/docs/0406_/345/233/275/345/256/266/346/224/277/347/255/226/345/272/223.md +70 -0
  177. package/docs/0408_ETF/344/273/275/351/242/235/350/247/204/346/250/241.md +65 -0
  178. package/docs/0415_/345/210/270/345/225/206/347/240/224/347/251/266/346/212/245/345/221/212.md +68 -0
  179. package/docs/0423_ST/351/243/216/351/231/251/350/255/246/347/244/272/346/235/277/350/202/241/347/245/250.md +65 -0
  180. package/docs/_api_index.json +1213 -0
  181. package/docs/_index.md +178 -0
  182. package/package.json +42 -0
  183. package/requirements.txt +2 -0
  184. package/server.py +736 -0
package/server.py ADDED
@@ -0,0 +1,736 @@
1
+ """
2
+ Tushare MCP Server - 提供 Tushare 金融数据 API 接口
3
+ 通过 MCP 协议暴露 Tushare Pro API,支持沪深股票、指数、基金、期货、期权等数据查询。
4
+ """
5
+
6
+ import json
7
+ import os
8
+ import re
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+ import httpx
13
+ from mcp.server.fastmcp import FastMCP
14
+
15
+ # ── 配置 ──────────────────────────────────────────────────────────────
16
+ TUSHARE_API_URL = "http://api.tushare.pro"
17
+
18
+ # 支持 .env 文件加载 token
19
+ def _load_env_token() -> str:
20
+ """从 .env 文件或环境变量读取 TUSHARE_TOKEN"""
21
+ token = os.environ.get("TUSHARE_TOKEN", "")
22
+ if token:
23
+ return token
24
+ env_path = Path(__file__).parent / ".env"
25
+ if env_path.exists():
26
+ for line in env_path.read_text().splitlines():
27
+ line = line.strip()
28
+ if line.startswith("#") or "=" not in line:
29
+ continue
30
+ k, v = line.split("=", 1)
31
+ if k.strip() == "TUSHARE_TOKEN":
32
+ return v.strip().strip("'\"")
33
+ return ""
34
+
35
+ TUSHARE_TOKEN = _load_env_token()
36
+
37
+ mcp = FastMCP(
38
+ "Tushare",
39
+ instructions=(
40
+ "Tushare 金融数据 MCP 服务器。提供 A 股、指数、基金、期货、期权、债券、"
41
+ "外汇、港股、美股、宏观经济等全方位金融数据查询。\n"
42
+ "使用前请确保已设置 TUSHARE_TOKEN 环境变量。\n"
43
+ "所有日期参数格式为 YYYYMMDD,股票代码格式如 000001.SZ、600000.SH。\n"
44
+ "如需查看某个接口的详细参数说明,请使用 search_api_docs 工具搜索关键词。"
45
+ ),
46
+ )
47
+
48
+ # ── 加载爬取的文档与索引 ──────────────────────────────────────────────
49
+ DOCS_DIR = Path(__file__).parent / "docs"
50
+ _docs_cache: dict[str, str] = {} # filename_stem -> content
51
+ _api_index: dict[str, dict] = {} # api_name -> {title, doc_id, description, file}
52
+
53
+
54
+ def _load_docs() -> dict[str, str]:
55
+ """加载 docs/ 目录下所有 Markdown 文档到内存"""
56
+ if _docs_cache:
57
+ return _docs_cache
58
+ if not DOCS_DIR.exists():
59
+ return _docs_cache
60
+ for f in sorted(DOCS_DIR.glob("*.md")):
61
+ if f.name.startswith("_"):
62
+ continue
63
+ _docs_cache[f.stem] = f.read_text(encoding="utf-8")
64
+ return _docs_cache
65
+
66
+
67
+ def _load_api_index() -> dict[str, dict]:
68
+ """加载 api_name -> 文档信息 的结构化索引"""
69
+ if _api_index:
70
+ return _api_index
71
+ index_path = DOCS_DIR / "_api_index.json"
72
+ if index_path.exists():
73
+ import json as _json
74
+ data = _json.loads(index_path.read_text(encoding="utf-8"))
75
+ _api_index.update(data)
76
+ return _api_index
77
+
78
+
79
+ # ── 复用 httpx 客户端 ─────────────────────────────────────────────────
80
+ _http_client: httpx.AsyncClient | None = None
81
+
82
+
83
+ def _get_client() -> httpx.AsyncClient:
84
+ global _http_client
85
+ if _http_client is None or _http_client.is_closed:
86
+ _http_client = httpx.AsyncClient(timeout=30)
87
+ return _http_client
88
+
89
+
90
+ # ── 核心请求函数 ──────────────────────────────────────────────────────
91
+ async def _call_tushare(
92
+ api_name: str,
93
+ params: dict[str, Any] | None = None,
94
+ fields: str = "",
95
+ limit: int = 5000,
96
+ ) -> dict[str, Any]:
97
+ """调用 Tushare HTTP API 并返回结果。"""
98
+ if not TUSHARE_TOKEN:
99
+ return {"error": "未设置 TUSHARE_TOKEN 环境变量,请在启动 MCP 时配置,或在项目根目录创建 .env 文件。"}
100
+
101
+ payload: dict[str, Any] = {
102
+ "api_name": api_name,
103
+ "token": TUSHARE_TOKEN,
104
+ "params": params or {},
105
+ }
106
+ if fields:
107
+ payload["fields"] = fields
108
+
109
+ client = _get_client()
110
+ try:
111
+ resp = await client.post(TUSHARE_API_URL, json=payload)
112
+ resp.raise_for_status()
113
+ except httpx.TimeoutException:
114
+ return {"error": "请求超时,Tushare API 响应过慢。", "hint": "请稍后重试,或缩小查询范围(添加日期/代码过滤)。"}
115
+ except httpx.HTTPStatusError as e:
116
+ return {"error": f"HTTP {e.response.status_code}", "hint": "Tushare API 服务异常,请稍后重试。"}
117
+ except httpx.HTTPError as e:
118
+ return {"error": f"网络错误: {type(e).__name__}", "hint": "请检查网络连接。"}
119
+ data = resp.json()
120
+
121
+ if data.get("code") != 0:
122
+ msg = data.get("msg", "未知错误")
123
+ error_info: dict[str, Any] = {"error": msg, "code": data.get("code")}
124
+ # 为常见错误添加恢复建议
125
+ if "权限" in msg or "积分" in msg:
126
+ error_info["hint"] = "此接口需要更高积分权限。请在 tushare.pro 查看积分要求,或使用替代接口。"
127
+ elif "频率" in msg or "每分钟" in msg:
128
+ error_info["hint"] = "请求过于频繁,请稍后重试。建议:减少请求频率,或缩小查询范围。"
129
+ elif "参数" in msg:
130
+ error_info["hint"] = f"请用 get_api_doc('{api_name}') 查看正确的参数格式。"
131
+ return error_info
132
+
133
+ # 将 fields + items 转换为易读的 records 格式
134
+ result = data.get("data", {})
135
+ columns = result.get("fields", [])
136
+ items = result.get("items", [])
137
+
138
+ if not items:
139
+ return {"columns": columns, "records": [], "total": 0}
140
+
141
+ shown = min(len(items), limit)
142
+ records = [dict(zip(columns, row)) for row in items[:shown]]
143
+ out: dict[str, Any] = {
144
+ "columns": columns,
145
+ "records": records,
146
+ "total": len(items),
147
+ "shown": shown,
148
+ }
149
+ if len(items) > shown:
150
+ out["hint"] = f"共 {len(items)} 条记录,仅显示前 {shown} 条。可通过 start_date/end_date 缩小范围分批获取。"
151
+ return out
152
+
153
+
154
+ # ── 通用查询工具 ──────────────────────────────────────────────────────
155
+ @mcp.tool()
156
+ async def tushare_query(
157
+ api_name: str,
158
+ params: dict[str, Any] | None = None,
159
+ fields: str = "",
160
+ ) -> str:
161
+ """通用 Tushare API 查询工具,可调用任意 Tushare Pro 接口。
162
+
163
+ 使用流程:
164
+ 1. 先用 get_api_doc(api_name) 查看接口的详细参数说明
165
+ 2. 或用 search_api_docs(keyword) 按关键词搜索接口
166
+ 3. 然后用本工具传入 api_name 和 params 执行查询
167
+
168
+ 通用说明:
169
+ - 日期格式: YYYYMMDD (如 20241231)
170
+ - 股票代码: 000001.SZ(深) 600000.SH(沪) 830799.BJ(北)
171
+ - 指数代码: 000001.SH(上证综指) 399001.SZ(深证成指)
172
+ - 基金代码: 510050.SH(场内ETF) 000001.OF(场外)
173
+ - 期货代码: CU2401.SHF(沪铜) IF2401.CFX(股指)
174
+
175
+ Args:
176
+ api_name: Tushare 接口名称,如 'daily', 'stock_basic', 'income' 等
177
+ params: 接口参数字典,如 {"ts_code": "000001.SZ", "start_date": "20240101"}
178
+ fields: 指定返回字段(逗号分隔),为空则返回全部默认字段
179
+ """
180
+ result = await _call_tushare(api_name, params, fields)
181
+ return json.dumps(result, ensure_ascii=False, indent=2)
182
+
183
+
184
+ # ── 便捷工具:股票基础 ───────────────────────────────────────────────
185
+ @mcp.tool()
186
+ async def get_stock_list(
187
+ list_status: str = "L",
188
+ exchange: str = "",
189
+ ) -> str:
190
+ """获取 A 股股票列表。
191
+
192
+ Args:
193
+ list_status: 上市状态。L-上市 D-退市 P-暂停上市,默认 L
194
+ exchange: 交易所。SSE-上交所 SZSE-深交所 BSE-北交所,留空返回全部
195
+ """
196
+ result = await _call_tushare(
197
+ "stock_basic",
198
+ {"exchange": exchange, "list_status": list_status},
199
+ "ts_code,symbol,name,area,industry,market,list_date,list_status",
200
+ )
201
+ return json.dumps(result, ensure_ascii=False, indent=2)
202
+
203
+
204
+ @mcp.tool()
205
+ async def get_trade_calendar(
206
+ exchange: str = "SSE",
207
+ start_date: str = "",
208
+ end_date: str = "",
209
+ is_open: str = "",
210
+ ) -> str:
211
+ """获取交易日历。
212
+
213
+ Args:
214
+ exchange: 交易所代码。SSE-上交所 SZSE-深交所 CFFEX-中金所 SHFE-上期所
215
+ CZCE-郑商所 DCE-大商所 INE-上能源
216
+ start_date: 开始日期 YYYYMMDD
217
+ end_date: 结束日期 YYYYMMDD
218
+ is_open: 是否交易 '0'休市 '1'交易,留空返回全部
219
+ """
220
+ params: dict[str, Any] = {"exchange": exchange}
221
+ if start_date:
222
+ params["start_date"] = start_date
223
+ if end_date:
224
+ params["end_date"] = end_date
225
+ if is_open:
226
+ params["is_open"] = is_open
227
+ result = await _call_tushare("trade_cal", params)
228
+ return json.dumps(result, ensure_ascii=False, indent=2)
229
+
230
+
231
+ # ── 便捷工具:行情数据 ───────────────────────────────────────────────
232
+ @mcp.tool()
233
+ async def get_daily_bars(
234
+ ts_code: str = "",
235
+ trade_date: str = "",
236
+ start_date: str = "",
237
+ end_date: str = "",
238
+ ) -> str:
239
+ """获取 A 股日线行情(未复权),交易日 15 点后更新。
240
+
241
+ Args:
242
+ ts_code: 股票代码(如 000001.SZ),支持多个逗号分隔。与 trade_date 二选一
243
+ trade_date: 交易日期 YYYYMMDD,获取当天全市场行情。与 ts_code 二选一
244
+ start_date: 开始日期 YYYYMMDD
245
+ end_date: 结束日期 YYYYMMDD
246
+ """
247
+ params: dict[str, Any] = {}
248
+ if ts_code:
249
+ params["ts_code"] = ts_code
250
+ if trade_date:
251
+ params["trade_date"] = trade_date
252
+ if start_date:
253
+ params["start_date"] = start_date
254
+ if end_date:
255
+ params["end_date"] = end_date
256
+ result = await _call_tushare("daily", params)
257
+ return json.dumps(result, ensure_ascii=False, indent=2)
258
+
259
+
260
+ @mcp.tool()
261
+ async def get_daily_basic(
262
+ ts_code: str = "",
263
+ trade_date: str = "",
264
+ start_date: str = "",
265
+ end_date: str = "",
266
+ ) -> str:
267
+ """获取每日指标:PE/PB/PS/总市值/流通市值/换手率/股息率等。
268
+
269
+ Args:
270
+ ts_code: 股票代码(如 000001.SZ)。与 trade_date 二选一
271
+ trade_date: 交易日期 YYYYMMDD。与 ts_code 二选一
272
+ start_date: 开始日期 YYYYMMDD
273
+ end_date: 结束日期 YYYYMMDD
274
+ """
275
+ params: dict[str, Any] = {}
276
+ if ts_code:
277
+ params["ts_code"] = ts_code
278
+ if trade_date:
279
+ params["trade_date"] = trade_date
280
+ if start_date:
281
+ params["start_date"] = start_date
282
+ if end_date:
283
+ params["end_date"] = end_date
284
+ result = await _call_tushare("daily_basic", params)
285
+ return json.dumps(result, ensure_ascii=False, indent=2)
286
+
287
+
288
+ # ── 便捷工具:财务数据 ───────────────────────────────────────────────
289
+ @mcp.tool()
290
+ async def get_income(
291
+ ts_code: str,
292
+ period: str = "",
293
+ start_date: str = "",
294
+ end_date: str = "",
295
+ ) -> str:
296
+ """获取上市公司利润表。
297
+
298
+ Args:
299
+ ts_code: 股票代码(如 600000.SH)
300
+ period: 报告期 YYYYMMDD(如 20231231 表示2023年报)
301
+ start_date: 公告开始日期
302
+ end_date: 公告结束日期
303
+ """
304
+ params: dict[str, Any] = {"ts_code": ts_code}
305
+ if period:
306
+ params["period"] = period
307
+ if start_date:
308
+ params["start_date"] = start_date
309
+ if end_date:
310
+ params["end_date"] = end_date
311
+ result = await _call_tushare("income", params)
312
+ return json.dumps(result, ensure_ascii=False, indent=2)
313
+
314
+
315
+ @mcp.tool()
316
+ async def get_balancesheet(
317
+ ts_code: str,
318
+ period: str = "",
319
+ start_date: str = "",
320
+ end_date: str = "",
321
+ ) -> str:
322
+ """获取上市公司资产负债表。
323
+
324
+ Args:
325
+ ts_code: 股票代码
326
+ period: 报告期 YYYYMMDD
327
+ start_date: 公告开始日期
328
+ end_date: 公告结束日期
329
+ """
330
+ params: dict[str, Any] = {"ts_code": ts_code}
331
+ if period:
332
+ params["period"] = period
333
+ if start_date:
334
+ params["start_date"] = start_date
335
+ if end_date:
336
+ params["end_date"] = end_date
337
+ result = await _call_tushare("balancesheet", params)
338
+ return json.dumps(result, ensure_ascii=False, indent=2)
339
+
340
+
341
+ @mcp.tool()
342
+ async def get_cashflow(
343
+ ts_code: str,
344
+ period: str = "",
345
+ start_date: str = "",
346
+ end_date: str = "",
347
+ ) -> str:
348
+ """获取上市公司现金流量表。
349
+
350
+ Args:
351
+ ts_code: 股票代码
352
+ period: 报告期 YYYYMMDD
353
+ start_date: 公告开始日期
354
+ end_date: 公告结束日期
355
+ """
356
+ params: dict[str, Any] = {"ts_code": ts_code}
357
+ if period:
358
+ params["period"] = period
359
+ if start_date:
360
+ params["start_date"] = start_date
361
+ if end_date:
362
+ params["end_date"] = end_date
363
+ result = await _call_tushare("cashflow", params)
364
+ return json.dumps(result, ensure_ascii=False, indent=2)
365
+
366
+
367
+ @mcp.tool()
368
+ async def get_fina_indicator(
369
+ ts_code: str,
370
+ period: str = "",
371
+ start_date: str = "",
372
+ end_date: str = "",
373
+ ) -> str:
374
+ """获取上市公司财务指标(ROE/ROA/毛利率/净利率等综合指标)。
375
+
376
+ Args:
377
+ ts_code: 股票代码
378
+ period: 报告期 YYYYMMDD
379
+ start_date: 公告开始日期
380
+ end_date: 公告结束日期
381
+ """
382
+ params: dict[str, Any] = {"ts_code": ts_code}
383
+ if period:
384
+ params["period"] = period
385
+ if start_date:
386
+ params["start_date"] = start_date
387
+ if end_date:
388
+ params["end_date"] = end_date
389
+ result = await _call_tushare("fina_indicator", params)
390
+ return json.dumps(result, ensure_ascii=False, indent=2)
391
+
392
+
393
+ # ── 便捷工具:指数 ───────────────────────────────────────────────────
394
+ @mcp.tool()
395
+ async def get_index_daily(
396
+ ts_code: str,
397
+ start_date: str = "",
398
+ end_date: str = "",
399
+ ) -> str:
400
+ """获取指数日线行情。
401
+
402
+ 常用指数代码:
403
+ - 000001.SH 上证综指
404
+ - 399001.SZ 深证成指
405
+ - 399006.SZ 创业板指
406
+ - 000300.SH 沪深300
407
+ - 000905.SH 中证500
408
+ - 000688.SH 科创50
409
+
410
+ Args:
411
+ ts_code: 指数代码
412
+ start_date: 开始日期 YYYYMMDD
413
+ end_date: 结束日期 YYYYMMDD
414
+ """
415
+ params: dict[str, Any] = {"ts_code": ts_code}
416
+ if start_date:
417
+ params["start_date"] = start_date
418
+ if end_date:
419
+ params["end_date"] = end_date
420
+ result = await _call_tushare("index_daily", params)
421
+ return json.dumps(result, ensure_ascii=False, indent=2)
422
+
423
+
424
+ # ── 便捷工具:资金流向 ───────────────────────────────────────────────
425
+ @mcp.tool()
426
+ async def get_moneyflow(
427
+ ts_code: str = "",
428
+ trade_date: str = "",
429
+ start_date: str = "",
430
+ end_date: str = "",
431
+ ) -> str:
432
+ """获取个股资金流向数据。
433
+
434
+ Args:
435
+ ts_code: 股票代码。与 trade_date 二选一
436
+ trade_date: 交易日期 YYYYMMDD。与 ts_code 二选一
437
+ start_date: 开始日期
438
+ end_date: 结束日期
439
+ """
440
+ params: dict[str, Any] = {}
441
+ if ts_code:
442
+ params["ts_code"] = ts_code
443
+ if trade_date:
444
+ params["trade_date"] = trade_date
445
+ if start_date:
446
+ params["start_date"] = start_date
447
+ if end_date:
448
+ params["end_date"] = end_date
449
+ result = await _call_tushare("moneyflow", params)
450
+ return json.dumps(result, ensure_ascii=False, indent=2)
451
+
452
+
453
+ # ── 便捷工具:概念板块 ───────────────────────────────────────────────
454
+ @mcp.tool()
455
+ async def get_concept(src: str = "ts") -> str:
456
+ """获取概念股分类,分析市场热点主题。
457
+
458
+ Args:
459
+ src: 来源。ts-Tushare sina-新浪
460
+ """
461
+ result = await _call_tushare("concept", {"src": src})
462
+ return json.dumps(result, ensure_ascii=False, indent=2)
463
+
464
+
465
+ @mcp.tool()
466
+ async def get_concept_detail(
467
+ concept_id: str = "",
468
+ ts_code: str = "",
469
+ ) -> str:
470
+ """查询概念股成分列表,或查某只股票属于哪些概念。
471
+
472
+ Args:
473
+ concept_id: 概念ID (从 get_concept 获取)。与 ts_code 二选一
474
+ ts_code: 股票代码。与 concept_id 二选一
475
+ """
476
+ params: dict[str, Any] = {}
477
+ if concept_id:
478
+ params["id"] = concept_id
479
+ if ts_code:
480
+ params["ts_code"] = ts_code
481
+ result = await _call_tushare("concept_detail", params)
482
+ return json.dumps(result, ensure_ascii=False, indent=2)
483
+
484
+
485
+ # ── 便捷工具:基金 ───────────────────────────────────────────────────
486
+ @mcp.tool()
487
+ async def get_fund_basic(
488
+ market: str = "E",
489
+ status: str = "L",
490
+ ) -> str:
491
+ """获取公募基金列表。
492
+
493
+ Args:
494
+ market: E-场内(ETF等) O-场外
495
+ status: D-摘牌 I-发行中 L-已上市
496
+ """
497
+ result = await _call_tushare("fund_basic", {"market": market, "status": status})
498
+ return json.dumps(result, ensure_ascii=False, indent=2)
499
+
500
+
501
+ @mcp.tool()
502
+ async def get_fund_nav(
503
+ ts_code: str,
504
+ start_date: str = "",
505
+ end_date: str = "",
506
+ ) -> str:
507
+ """获取基金净值数据。
508
+
509
+ Args:
510
+ ts_code: 基金代码(如 510050.SH)
511
+ start_date: 开始日期
512
+ end_date: 结束日期
513
+ """
514
+ params: dict[str, Any] = {"ts_code": ts_code}
515
+ if start_date:
516
+ params["start_date"] = start_date
517
+ if end_date:
518
+ params["end_date"] = end_date
519
+ result = await _call_tushare("fund_nav", params)
520
+ return json.dumps(result, ensure_ascii=False, indent=2)
521
+
522
+
523
+ # ── 便捷工具:宏观经济 ───────────────────────────────────────────────
524
+ @mcp.tool()
525
+ async def get_shibor(
526
+ start_date: str = "",
527
+ end_date: str = "",
528
+ ) -> str:
529
+ """获取 Shibor 拆借利率数据。
530
+
531
+ Args:
532
+ start_date: 开始日期 YYYYMMDD
533
+ end_date: 结束日期 YYYYMMDD
534
+ """
535
+ params: dict[str, Any] = {}
536
+ if start_date:
537
+ params["start_date"] = start_date
538
+ if end_date:
539
+ params["end_date"] = end_date
540
+ result = await _call_tushare("shibor", params)
541
+ return json.dumps(result, ensure_ascii=False, indent=2)
542
+
543
+
544
+ # ── API 文档工具 ──────────────────────────────────────────────────────
545
+ @mcp.tool()
546
+ async def get_api_doc(api_name: str) -> str:
547
+ """按接口名获取 Tushare API 的完整文档(参数、字段、示例、限量说明等)。
548
+
549
+ 这是查询某个已知接口详细用法的最佳方式。
550
+ 如果不确定接口名,请先用 search_api_docs 搜索。
551
+
552
+ Args:
553
+ api_name: Tushare 接口名,如 'daily', 'stock_basic', 'income',
554
+ 'fund_nav', 'index_daily', 'cb_daily', 'margin' 等
555
+ """
556
+ index = _load_api_index()
557
+ docs = _load_docs()
558
+
559
+ # 检查是否有别名(同名冲突的接口)
560
+ aliases = [n for n in index if "__" in n and n.split("__")[0] == api_name]
561
+
562
+ # 精确匹配
563
+ if api_name in index:
564
+ file_stem = index[api_name]["file"].replace(".md", "")
565
+ if file_stem in docs:
566
+ result = docs[file_stem]
567
+ # 如果有别名,追加提示
568
+ for alias in aliases:
569
+ alias_info = index[alias]
570
+ result += f"\n\n---\n> 注意:还有一个同名接口 `{alias}`({alias_info['title']}),可用 get_api_doc('{alias}') 查看。"
571
+ return result
572
+
573
+ # 别名精确匹配(如 trade_cal__期货交易日历)
574
+ if api_name in index:
575
+ pass # already handled above
576
+ for name, info in index.items():
577
+ if name == api_name:
578
+ file_stem = info["file"].replace(".md", "")
579
+ if file_stem in docs:
580
+ return docs[file_stem]
581
+
582
+ # 模糊匹配:接口名包含关键词 (要求至少4字符,避免短词误匹配)
583
+ candidates = []
584
+ if len(api_name) >= 4:
585
+ for name, info in index.items():
586
+ if name.startswith("_"):
587
+ continue
588
+ # 双向包含,但要求被包含方至少4字符,防止 "st" in "not_exist" 类误匹配
589
+ if (api_name in name) or (name in api_name and len(name) >= 4):
590
+ candidates.append((name, info))
591
+
592
+ if len(candidates) == 1:
593
+ file_stem = candidates[0][1]["file"].replace(".md", "")
594
+ if file_stem in docs:
595
+ return docs[file_stem]
596
+
597
+ if candidates:
598
+ result = f"未找到精确匹配 '{api_name}',以下接口可能相关:\n\n"
599
+ for name, info in candidates:
600
+ result += f"- **{name}**: {info['title']} — {info['description']}\n"
601
+ result += "\n请使用精确的接口名重新查询。"
602
+ return result
603
+
604
+ return (
605
+ f"未找到接口 '{api_name}'。\n"
606
+ f"请使用 search_api_docs 按关键词搜索,或查看 tushare://api-reference 获取完整接口列表。"
607
+ )
608
+
609
+
610
+ @mcp.tool()
611
+ async def search_api_docs(keyword: str) -> str:
612
+ """按关键词搜索 Tushare API 文档,适用于不确定接口名时的模糊查找。
613
+
614
+ 搜索范围包括接口名、中文标题、参数名、字段描述等。
615
+ 找到接口后,请用 get_api_doc(api_name) 获取完整文档。
616
+
617
+ Args:
618
+ keyword: 搜索关键词,如 "融资融券" "利润" "PE" "ETF" "港股" 等
619
+ """
620
+ index = _load_api_index()
621
+ docs = _load_docs()
622
+ if not docs:
623
+ return "文档目录为空,请先运行 crawl_docs.py 爬取文档。"
624
+
625
+ # 短关键词用单词边界匹配减少噪音
626
+ if len(keyword) <= 3 and keyword.isascii():
627
+ pattern = re.compile(r"(?<![a-zA-Z])" + re.escape(keyword) + r"(?![a-zA-Z])", re.IGNORECASE)
628
+ else:
629
+ pattern = re.compile(re.escape(keyword), re.IGNORECASE)
630
+
631
+ scored_matches: list[tuple[int, str]] = [] # (score, result_text)
632
+
633
+ for name, content in docs.items():
634
+ hit_count = len(pattern.findall(content)) + len(pattern.findall(name))
635
+ if hit_count == 0:
636
+ continue
637
+
638
+ lines = content.split("\n")
639
+ title_line = lines[0] if lines else name
640
+
641
+ # 从索引中找到 api_name
642
+ api_name_str = ""
643
+ for api_n, info in index.items():
644
+ if info["file"].replace(".md", "") == name:
645
+ api_name_str = api_n
646
+ break
647
+
648
+ # 计算相关性分数
649
+ score = hit_count
650
+ # 标题匹配加权
651
+ if pattern.search(title_line):
652
+ score += 50
653
+ # 接口名匹配加权
654
+ if api_name_str and pattern.search(api_name_str):
655
+ score += 30
656
+
657
+ # 提取不重叠的上下文片段
658
+ relevant = []
659
+ covered = set()
660
+ for i, line in enumerate(lines):
661
+ if pattern.search(line) and i not in covered:
662
+ start = max(0, i - 1)
663
+ end = min(len(lines), i + 4)
664
+ snippet = "\n".join(lines[start:end])
665
+ relevant.append(snippet)
666
+ covered.update(range(start, end))
667
+
668
+ if relevant:
669
+ header = f"### {title_line}"
670
+ if api_name_str and not api_name_str.startswith("_"):
671
+ header += f" (接口: `{api_name_str}`)"
672
+ result = header + "\n\n"
673
+ result += "\n...\n".join(relevant[:2]) # 最多2个片段
674
+ scored_matches.append((score, result))
675
+
676
+ if not scored_matches:
677
+ return f"未找到与 '{keyword}' 相关的文档。请尝试其他关键词。"
678
+
679
+ # 按相关性排序,取前8
680
+ scored_matches.sort(key=lambda x: -x[0])
681
+ top = scored_matches[:8]
682
+
683
+ header = f"找到 {len(scored_matches)} 个相关文档(按相关度排序,显示前 {len(top)} 个):\n\n"
684
+ return header + "\n\n---\n\n".join(r for _, r in top)
685
+
686
+
687
+ @mcp.resource("tushare://api-reference")
688
+ def get_api_reference() -> str:
689
+ """Tushare Pro API 完整接口索引(按类别分组),列出所有可用接口及简要说明。
690
+ 使用 get_api_doc(api_name) 获取具体接口的完整文档。"""
691
+ index = _load_api_index()
692
+ if not index:
693
+ return "API 索引为空,请先运行 build_index.py。"
694
+
695
+ # 按 category 分组
696
+ cats: dict[str, list[tuple[str, dict]]] = {}
697
+ for api_name, info in sorted(index.items()):
698
+ if api_name.startswith("_"):
699
+ continue
700
+ cat = info.get("category", "其他")
701
+ cats.setdefault(cat, []).append((api_name, info))
702
+
703
+ # 固定分类顺序
704
+ CAT_ORDER = [
705
+ "沪深股票", "财务数据", "资金流向", "市场参考", "指数数据", "概念板块",
706
+ "基金数据", "期货数据", "期权数据", "债券/可转债", "港股数据", "美股数据",
707
+ "外汇数据", "黄金现货", "宏观经济", "交易日历", "新闻公告", "其他",
708
+ ]
709
+
710
+ total = sum(len(v) for v in cats.values())
711
+ lines = [
712
+ "# Tushare Pro API 接口索引",
713
+ "",
714
+ f"共 {total} 个接口,分 {len(cats)} 个类别。使用 `get_api_doc(api_name)` 获取完整文档。",
715
+ "",
716
+ ]
717
+
718
+ for cat in CAT_ORDER:
719
+ if cat not in cats:
720
+ continue
721
+ items = cats[cat]
722
+ lines.append(f"## {cat} ({len(items)})")
723
+ lines.append("")
724
+ lines.append("| 接口名 | 名称 | 说明 |")
725
+ lines.append("|---|---|---|")
726
+ for api_name, info in items:
727
+ desc = info["description"][:50] if info["description"] else ""
728
+ lines.append(f"| {api_name} | {info['title']} | {desc} |")
729
+ lines.append("")
730
+
731
+ return "\n".join(lines)
732
+
733
+
734
+ # ── 启动 ──────────────────────────────────────────────────────────────
735
+ if __name__ == "__main__":
736
+ mcp.run()