@opentiny/tiny-robot-cli 0.4.2-alpha.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 (36) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +21 -0
  3. package/bin/cli.js +187 -0
  4. package/package.json +37 -0
  5. package/templates/basic/.env.example +2 -0
  6. package/templates/basic/README.md +45 -0
  7. package/templates/basic/index.html +13 -0
  8. package/templates/basic/package.json +29 -0
  9. package/templates/basic/public/favicon.ico +0 -0
  10. package/templates/basic/public/modelcontextprotocol.png +0 -0
  11. package/templates/basic/src/App.vue +130 -0
  12. package/templates/basic/src/components/ChatList.vue +82 -0
  13. package/templates/basic/src/components/ChatSender.vue +125 -0
  14. package/templates/basic/src/components/ConversationHistory.vue +136 -0
  15. package/templates/basic/src/components/HistoryDrawerButton.vue +43 -0
  16. package/templates/basic/src/components/McpServerPickerButton.vue +278 -0
  17. package/templates/basic/src/components/ThemeToggleButton.vue +44 -0
  18. package/templates/basic/src/components/icons/IconDeepThink.vue +29 -0
  19. package/templates/basic/src/components/icons/IconModelAliyunBailian.vue +51 -0
  20. package/templates/basic/src/components/icons/IconModelDeepseek.vue +29 -0
  21. package/templates/basic/src/components/icons/IconMoon.vue +29 -0
  22. package/templates/basic/src/components/icons/IconPlugin.vue +29 -0
  23. package/templates/basic/src/components/icons/IconSun.vue +35 -0
  24. package/templates/basic/src/components/icons/IconWebSearch.vue +36 -0
  25. package/templates/basic/src/components/icons/index.ts +7 -0
  26. package/templates/basic/src/composables/useChat.ts +129 -0
  27. package/templates/basic/src/composables/useMcp.ts +170 -0
  28. package/templates/basic/src/composables/useModel.ts +82 -0
  29. package/templates/basic/src/main.ts +7 -0
  30. package/templates/basic/src/mcpServers.ts +40 -0
  31. package/templates/basic/src/models.ts +81 -0
  32. package/templates/basic/src/style.css +21 -0
  33. package/templates/basic/tsconfig.app.json +16 -0
  34. package/templates/basic/tsconfig.json +7 -0
  35. package/templates/basic/tsconfig.node.json +26 -0
  36. package/templates/basic/vite.config.ts +16 -0
@@ -0,0 +1,51 @@
1
+ <script setup lang="ts">
2
+ withDefaults(
3
+ defineProps<{
4
+ size?: number
5
+ }>(),
6
+ {
7
+ size: 24,
8
+ },
9
+ )
10
+ </script>
11
+
12
+ <template>
13
+ <svg
14
+ viewBox="0 0 48 48"
15
+ xmlns="http://www.w3.org/2000/svg"
16
+ xmlns:xlink="http://www.w3.org/1999/xlink"
17
+ :width="size"
18
+ :height="size"
19
+ fill="none"
20
+ >
21
+ <rect id="阿里云百炼" width="48.000000" height="48.000000" x="0.000000" y="0.000000" />
22
+ <g id="组合 5451">
23
+ <path id="矢量 141" d="M7 14L7 34L15.6608 29L15.6608 19L7 14Z" fill="rgb(4,35,219)" fill-rule="nonzero" />
24
+ <path id="矢量 142" d="M15.6606 19L15.6606 29L24.3206 24L15.6606 19Z" fill="rgb(28,84,228)" fill-rule="nonzero" />
25
+ <path
26
+ id="矢量 143"
27
+ d="M24.3206 24L15.6606 19L32.9773 8.99414L41.6415 14L24.3206 24Z"
28
+ fill="rgb(171,155,255)"
29
+ fill-rule="nonzero"
30
+ />
31
+ <path
32
+ id="矢量 144"
33
+ d="M15.6608 19L7 14L24.3208 4L32.9775 8.99417L15.6608 19Z"
34
+ fill="rgb(115,71,255)"
35
+ fill-rule="nonzero"
36
+ />
37
+ <path
38
+ id="矢量 145"
39
+ d="M41.6415 34L24.3206 44L15.6606 39L32.9806 29L41.6415 34Z"
40
+ fill="rgb(0,207,202)"
41
+ fill-rule="nonzero"
42
+ />
43
+ <path
44
+ id="矢量 146"
45
+ d="M32.9808 29L41.6417 24L41.6417 34L32.9808 29ZM15.6608 39L7 34L24.3208 24L32.9808 29L15.6608 39Z"
46
+ fill="rgb(0,235,210)"
47
+ fill-rule="nonzero"
48
+ />
49
+ </g>
50
+ </svg>
51
+ </template>
@@ -0,0 +1,29 @@
1
+ <script setup lang="ts">
2
+ withDefaults(
3
+ defineProps<{
4
+ size?: number
5
+ }>(),
6
+ {
7
+ size: 24,
8
+ },
9
+ )
10
+ </script>
11
+
12
+ <template>
13
+ <svg
14
+ viewBox="0 0 48 48"
15
+ xmlns="http://www.w3.org/2000/svg"
16
+ xmlns:xlink="http://www.w3.org/1999/xlink"
17
+ :width="size"
18
+ :height="size"
19
+ fill="none"
20
+ >
21
+ <rect id="deepseek" width="48.000000" height="48.000000" x="0.000000" y="0.000000" />
22
+ <path
23
+ id="矢量 122"
24
+ d="M47.4955 9.02617C46.9872 8.77197 46.7678 9.2565 46.4708 9.50273C46.3696 9.58218 46.2833 9.68546 46.1969 9.78076C45.4541 10.5909 44.5864 11.1232 43.4515 11.0596C41.7933 10.9643 40.3774 11.4965 39.1263 12.7912C38.8603 11.1946 37.9766 10.2415 36.6312 9.62983C35.9262 9.31213 35.2153 8.99437 34.7218 8.30328C34.3783 7.81082 34.2849 7.26274 34.1122 6.72263C34.003 6.39695 33.8937 6.06335 33.5264 6.00772C33.1263 5.94416 32.9704 6.28575 32.8135 6.5717C32.188 7.73935 31.9447 9.02617 31.9695 10.3288C32.0231 13.2599 33.2365 15.5952 35.6453 17.2554C35.9193 17.446 35.9898 17.6366 35.9034 17.9146C35.7396 18.4865 35.543 19.0426 35.3712 19.6145C35.2619 19.9799 35.0982 20.0593 34.7139 19.9004C33.3923 19.3365 32.2505 18.5024 31.2407 17.4937C29.528 15.8017 27.979 13.9351 26.0478 12.4735C25.6005 12.1363 25.1415 11.8159 24.6707 11.5124C22.6997 9.55836 24.9288 7.95383 25.4451 7.76317C25.9843 7.5646 25.6328 6.88148 23.8883 6.88941C22.1437 6.89733 20.5481 7.49312 18.5146 8.28743C18.2168 8.40656 17.904 8.49393 17.5843 8.56547C15.7375 8.20798 13.8212 8.12858 11.8185 8.35891C8.04843 8.78787 5.0369 10.6069 2.82276 13.7127C0.163756 17.446 -0.462801 21.6877 0.304738 26.1121C1.109 30.7748 3.44128 34.6352 7.02373 37.6536C10.7392 40.7832 15.0176 42.3163 19.8988 42.0224C22.8636 41.8476 26.164 41.4426 29.8874 38.2255C30.8266 38.7021 31.8126 38.8928 33.447 39.0357C34.707 39.1549 35.9193 38.9722 36.8576 38.7736C38.3281 38.4559 38.2258 37.0658 37.6946 36.8116C33.3844 34.7622 34.3306 35.5963 33.4708 34.9212C35.6611 32.276 38.9615 29.5276 40.2523 20.6233C40.3546 19.9164 40.2682 19.4715 40.2523 18.8996C40.2444 18.5501 40.3218 18.415 40.714 18.3754C41.7933 18.2483 42.8418 17.9464 43.804 17.4063C46.596 15.8494 47.7229 13.2917 47.989 10.2256C48.0288 9.75694 47.9811 9.2724 47.4955 9.02618M23.1604 36.621C18.9843 33.2689 16.9577 32.1648 16.1207 32.2125C15.3383 32.2601 15.4793 33.1736 15.6511 33.7694C15.8308 34.3572 16.0661 34.7622 16.3947 35.2786C16.6212 35.6202 16.7781 36.1285 16.1674 36.5098C14.822 37.3597 12.4837 36.2238 12.3745 36.1683C9.65197 34.5319 7.3752 32.3713 5.77166 29.4165C4.22276 26.5728 3.32418 23.5226 3.17522 20.2659C3.13549 19.4795 3.36287 19.2014 4.12844 19.0584C5.1437 18.8606 6.1634 18.8342 7.18755 18.979C11.4511 19.6146 15.0812 21.5606 18.1234 24.6426C19.86 26.398 21.1736 28.4951 22.5279 30.5445C23.9667 32.7208 25.5156 34.7941 27.4865 36.4939C28.1826 37.0896 28.7376 37.5424 29.2698 37.876C27.6662 38.0587 24.9904 38.0984 23.1604 36.621M25.1631 23.4749C25.1631 23.1254 25.4372 22.8474 25.7817 22.8474C25.8598 22.8487 25.93 22.862 25.9922 22.8871C26.0515 22.9099 26.106 22.941 26.1557 22.9805C26.2055 23.0199 26.2482 23.0659 26.2839 23.1185C26.3195 23.171 26.3465 23.2277 26.3649 23.2885C26.3832 23.3493 26.392 23.4114 26.3914 23.4749C26.3919 23.5159 26.3884 23.5564 26.3809 23.5967C26.3734 23.6369 26.362 23.676 26.3467 23.714C26.3314 23.752 26.3126 23.7881 26.2901 23.8223C26.2677 23.8566 26.2421 23.8883 26.2134 23.9175C26.1847 23.9467 26.1534 23.9727 26.1195 23.9957C26.0857 24.0187 26.0499 24.0381 26.0121 24.054C25.9744 24.0699 25.9355 24.0819 25.8953 24.09C25.8552 24.0982 25.8147 24.1023 25.7738 24.1025C25.733 24.1023 25.6926 24.0982 25.6527 24.09C25.6127 24.0818 25.574 24.0698 25.5364 24.0539C25.4989 24.038 25.4633 24.0185 25.4296 23.9955C25.396 23.9724 25.3649 23.9463 25.3365 23.9171C25.3081 23.8879 25.2828 23.8561 25.2607 23.8218C25.2386 23.7876 25.2201 23.7514 25.2052 23.7135C25.1903 23.6755 25.1793 23.6365 25.1722 23.5963C25.1652 23.5561 25.1621 23.5157 25.1631 23.4749ZM31.3817 26.7316C30.9835 26.8985 30.5844 27.0415 30.2001 27.0573C29.6064 27.0891 28.957 26.8429 28.6056 26.541C28.0575 26.0723 27.6662 25.8102 27.5024 24.9921C27.4319 24.6426 27.4706 24.1025 27.5332 23.7927C27.6742 23.1254 27.5173 22.6965 27.0566 22.3073C26.6813 21.9895 26.2037 21.9021 25.6795 21.9021C25.4838 21.9021 25.3042 21.8148 25.1711 21.7433C25.1387 21.7278 25.1082 21.7091 25.0797 21.6873C25.0511 21.6655 25.0251 21.641 25.0017 21.6138C24.9782 21.5867 24.9577 21.5574 24.9403 21.5259C24.9229 21.4945 24.9089 21.4617 24.8982 21.4274C24.8876 21.3931 24.8806 21.358 24.8772 21.3223C24.8738 21.2865 24.8741 21.2508 24.8781 21.2151C24.8821 21.1794 24.8897 21.1445 24.9009 21.1104C24.9122 21.0762 24.9267 21.0436 24.9447 21.0125C24.9993 20.9013 25.2654 20.6312 25.328 20.5836C26.0398 20.1705 26.861 20.3056 27.6196 20.6154C28.3235 20.9092 28.8547 21.4494 29.6223 22.212C30.4047 23.1334 30.5457 23.3876 30.9905 24.0786C31.343 24.6187 31.6637 25.1748 31.8822 25.8102C32.0161 26.2074 31.8434 26.533 31.3817 26.7316L31.3817 26.7316Z"
25
+ fill="rgb(77,107,254)"
26
+ fill-rule="nonzero"
27
+ />
28
+ </svg>
29
+ </template>
@@ -0,0 +1,29 @@
1
+ <script setup lang="ts">
2
+ withDefaults(
3
+ defineProps<{
4
+ size?: number
5
+ }>(),
6
+ {
7
+ size: 24,
8
+ },
9
+ )
10
+ </script>
11
+
12
+ <template>
13
+ <svg
14
+ xmlns="http://www.w3.org/2000/svg"
15
+ :width="size"
16
+ :height="size"
17
+ viewBox="0 0 24 24"
18
+ fill="none"
19
+ stroke="currentColor"
20
+ stroke-width="2"
21
+ stroke-linecap="round"
22
+ stroke-linejoin="round"
23
+ class="lucide lucide-moon-icon lucide-moon"
24
+ >
25
+ <path
26
+ d="M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401"
27
+ />
28
+ </svg>
29
+ </template>
@@ -0,0 +1,29 @@
1
+ <script setup lang="ts">
2
+ withDefaults(
3
+ defineProps<{
4
+ size?: number
5
+ }>(),
6
+ {
7
+ size: 24,
8
+ },
9
+ )
10
+ </script>
11
+
12
+ <template>
13
+ <svg
14
+ xmlns="http://www.w3.org/2000/svg"
15
+ xmlns:xlink="http://www.w3.org/1999/xlink"
16
+ :width="size"
17
+ :height="size"
18
+ viewBox="0 0 16 16"
19
+ fill="none"
20
+ >
21
+ <rect id="MCP" width="16.000000" height="16.000000" x="0.000000" y="0.000000" />
22
+ <path
23
+ id="矢量 665"
24
+ d="M6.40942 2.00781C6.78857 1.9375 7.17603 1.89844 7.57178 1.89844L11.8848 1.89844C11.9937 1.89844 12.0945 1.91406 12.1875 1.95312C12.2803 1.99219 12.3652 2.05469 12.4424 2.13281C12.5193 2.20312 12.5769 2.28906 12.6155 2.38281C12.6541 2.47656 12.6733 2.57812 12.6733 2.6875L12.6733 5.40625L14.7217 5.40625C15.0015 5.40625 15.2217 5.625 15.2217 5.90625C15.2217 6.1875 15.0015 6.40625 14.7217 6.40625L12.6733 6.40625L12.6733 9.50781L14.7217 9.50781C15.0015 9.50781 15.2217 9.72656 15.2217 10.0078C15.2217 10.2891 15.0015 10.5078 14.7217 10.5078L12.6733 10.5078L12.6733 13.25C12.6733 13.3594 12.6541 13.4609 12.6155 13.5547C12.5769 13.6484 12.5193 13.7344 12.4424 13.8125C12.3652 13.8906 12.2803 13.9453 12.1875 13.9844C12.0945 14.0234 11.9937 14.0391 11.8848 14.0391L7.57178 14.0391C7.11963 14.0391 6.67798 13.9922 6.24731 13.8984L6.24731 13.8984L6.24731 13.8984C5.8938 13.8203 5.54736 13.7109 5.20825 13.5625C4.86572 13.4219 4.5415 13.2422 4.23584 13.0469C3.89355 12.8203 3.57446 12.5625 3.27856 12.2656C2.98267 11.9688 2.72192 11.6484 2.49658 11.3047L2.49658 11.3047C2.47144 11.2656 2.44678 11.2266 2.42261 11.1875C2.25269 10.9219 2.10449 10.6328 1.97754 10.3359C1.83423 9.99219 1.7229 9.64844 1.64429 9.29688L1.64429 9.29688C1.63232 9.24219 1.62109 9.1875 1.6106 9.13281C1.53711 8.75 1.50024 8.36719 1.50024 7.96875C1.50024 7.51562 1.54834 7.07812 1.64429 6.64844L1.64429 6.64844C1.7229 6.28906 1.83423 5.94531 1.97754 5.60938C2.12256 5.26562 2.29565 4.9375 2.49683 4.63281C2.51074 4.60938 2.52515 4.59375 2.53931 4.57031C2.75464 4.25 3.00122 3.95312 3.27856 3.67969C3.57446 3.38281 3.89355 3.11719 4.23584 2.89844L4.23584 2.89844C4.27393 2.86719 4.3125 2.84375 4.35132 2.82031C4.6228 2.64844 4.90845 2.5 5.20825 2.375C5.54736 2.23438 5.8938 2.125 6.24731 2.03906L6.24731 2.03906C6.30103 2.03125 6.35522 2.01562 6.40942 2.00781ZM6.44604 3.02344L6.44604 3.02344C6.81201 2.9375 7.18701 2.89844 7.57178 2.89844L11.6733 2.89844L11.6733 13.0391L7.57178 13.0391C7.18701 13.0391 6.81201 13 6.44629 12.9141L6.44604 12.9141C6.15747 12.8516 5.87476 12.7578 5.5979 12.6406C5.31494 12.5234 5.04736 12.3828 4.79468 12.2188L4.79468 12.2188C4.77661 12.2031 4.75854 12.1953 4.74072 12.1797C4.47168 12 4.21997 11.7891 3.9856 11.5547C3.7356 11.3047 3.51587 11.0391 3.32617 10.75L3.32617 10.75C3.30566 10.7188 3.28516 10.6797 3.26538 10.6484C3.12549 10.4297 3.00317 10.1875 2.89868 9.94531C2.78149 9.66406 2.69019 9.38281 2.62476 9.09375L2.62476 9.09375C2.54175 8.72656 2.50024 8.35156 2.50024 7.96875C2.50024 7.58594 2.54175 7.21094 2.62476 6.84375L2.62476 6.84375L2.62476 6.84375C2.62891 6.82812 2.63306 6.80469 2.63721 6.78906C2.70166 6.52344 2.78882 6.25781 2.89868 5.99219C3.01807 5.71094 3.16089 5.44531 3.32642 5.19531L3.32642 5.19531C3.33813 5.17188 3.3501 5.15625 3.3623 5.14062C3.54346 4.86719 3.75146 4.61719 3.9856 4.38281C4.2356 4.13281 4.50513 3.91406 4.79468 3.72656L4.79468 3.72656L4.79468 3.72656C5.04736 3.5625 5.31494 3.41406 5.5979 3.29688C5.87476 3.17969 6.15747 3.08594 6.44604 3.02344ZM6.64185 6.60938C6.64185 6.32812 6.42188 6.10938 6.14185 6.10938C5.86182 6.10938 5.64185 6.32812 5.64185 6.60938L5.64185 9.67188C5.64185 9.94531 5.86182 10.1719 6.14185 10.1719C6.42188 10.1719 6.64185 9.94531 6.64185 9.67188L6.64185 6.60938Z"
25
+ fill="currentColor"
26
+ fill-rule="evenodd"
27
+ />
28
+ </svg>
29
+ </template>
@@ -0,0 +1,35 @@
1
+ <script setup lang="ts">
2
+ withDefaults(
3
+ defineProps<{
4
+ size?: number
5
+ }>(),
6
+ {
7
+ size: 24,
8
+ },
9
+ )
10
+ </script>
11
+
12
+ <template>
13
+ <svg
14
+ xmlns="http://www.w3.org/2000/svg"
15
+ :width="size"
16
+ :height="size"
17
+ viewBox="0 0 24 24"
18
+ fill="none"
19
+ stroke="currentColor"
20
+ stroke-width="2"
21
+ stroke-linecap="round"
22
+ stroke-linejoin="round"
23
+ class="lucide lucide-sun-icon lucide-sun"
24
+ >
25
+ <circle cx="12" cy="12" r="4" />
26
+ <path d="M12 2v2" />
27
+ <path d="M12 20v2" />
28
+ <path d="m4.93 4.93 1.41 1.41" />
29
+ <path d="m17.66 17.66 1.41 1.41" />
30
+ <path d="M2 12h2" />
31
+ <path d="M20 12h2" />
32
+ <path d="m6.34 17.66-1.41 1.41" />
33
+ <path d="m19.07 4.93-1.41 1.41" />
34
+ </svg>
35
+ </template>
@@ -0,0 +1,36 @@
1
+ <script setup lang="ts">
2
+ withDefaults(
3
+ defineProps<{
4
+ size?: number
5
+ }>(),
6
+ {
7
+ size: 24,
8
+ },
9
+ )
10
+ </script>
11
+
12
+ <template>
13
+ <svg
14
+ xmlns="http://www.w3.org/2000/svg"
15
+ xmlns:xlink="http://www.w3.org/1999/xlink"
16
+ :width="size"
17
+ :height="size"
18
+ viewBox="0 0 16 16"
19
+ fill="none"
20
+ >
21
+ <defs>
22
+ <clipPath id="clip98_50741">
23
+ <rect id="联网搜索" width="16.000000" height="16.000000" fill="white" fill-opacity="0" />
24
+ </clipPath>
25
+ </defs>
26
+ <g clip-path="url(#clip98_50741)">
27
+ <path
28
+ id="形状"
29
+ d="M8 2C4.67 2 2 4.68 2 8C2 11.31 4.67 14 8 14C11.31 14 14 11.31 14 8C14 4.68 11.31 2 8 2ZM8 1C11.86 1 15 4.13 15 8C15 11.86 11.86 15 8 15C4.12 15 1 11.86 1 8C1 4.13 4.12 1 8 1ZM8 2C6.71 2 5.5 4.65 5.5 8C5.5 11.34 6.71 14 8 14C9.27 14 10.5 11.34 10.5 8C10.5 4.65 9.27 2 8 2ZM8 1C10.03 1 11.5 4.16 11.5 8C11.5 11.83 10.03 15 8 15C5.95 15 4.5 11.83 4.5 8C4.5 4.16 5.95 1 8 1ZM14.5 7.5C14.77 7.5 15 7.71 15 8C15 8.27 14.77 8.5 14.5 8.5L1.5 8.5C1.21 8.5 1 8.27 1 8C1 7.71 1.21 7.5 1.5 7.5L14.5 7.5Z"
30
+ fill="currentColor"
31
+ fill-opacity="1.000000"
32
+ fill-rule="nonzero"
33
+ />
34
+ </g>
35
+ </svg>
36
+ </template>
@@ -0,0 +1,7 @@
1
+ export { default as IconDeepThink } from './IconDeepThink.vue'
2
+ export { default as IconModelAliyunBailian } from './IconModelAliyunBailian.vue'
3
+ export { default as IconModelDeepseek } from './IconModelDeepseek.vue'
4
+ export { default as IconMoon } from './IconMoon.vue'
5
+ export { default as IconPlugin } from './IconPlugin.vue'
6
+ export { default as IconSun } from './IconSun.vue'
7
+ export { default as IconWebSearch } from './IconWebSearch.vue'
@@ -0,0 +1,129 @@
1
+ import { sseStreamToGenerator, toolPlugin, useConversation } from '@opentiny/tiny-robot-kit'
2
+ import { computed, ref } from 'vue'
3
+ import type { McpServerKey } from '../mcpServers'
4
+ import { useMcp } from './useMcp'
5
+ import { useModel } from './useModel'
6
+
7
+ function createChatStore() {
8
+ const inputMessage = ref('')
9
+ const modelStore = useModel()
10
+ const mcpStore = useMcp()
11
+ const { listTools, callTool } = mcpStore
12
+ const { selectedModel, getSelectedModelParams } = modelStore
13
+
14
+ const useConversationReturn = useConversation({
15
+ useMessageOptions: {
16
+ responseProvider: async (requestBody, abortSignal) => {
17
+ const currentModel = selectedModel.value
18
+ if (!currentModel) {
19
+ throw new Error('No model selected.')
20
+ }
21
+ if (!currentModel.apiKey) {
22
+ throw new Error(`Missing API key for provider "${currentModel.provider}".`)
23
+ }
24
+
25
+ const response = await fetch(currentModel.apiUrl, {
26
+ method: 'POST',
27
+ headers: {
28
+ 'Content-Type': 'application/json',
29
+ Authorization: `Bearer ${currentModel.apiKey}`,
30
+ },
31
+ body: JSON.stringify({
32
+ ...requestBody,
33
+ ...getSelectedModelParams(),
34
+ stream: true,
35
+ }),
36
+ signal: abortSignal,
37
+ })
38
+
39
+ if (!response.ok) {
40
+ const detail = await response.text().catch(() => '')
41
+ throw new Error(`HTTP ${response.status}: ${response.statusText}${detail ? ` - ${detail}` : ''}`)
42
+ }
43
+
44
+ return sseStreamToGenerator(response, { signal: abortSignal })
45
+ },
46
+ initialMessages: [
47
+ {
48
+ role: 'system',
49
+ content: 'You are a helpful assistant.',
50
+ },
51
+ ],
52
+ plugins: [
53
+ toolPlugin({
54
+ getTools: async () => {
55
+ const mcpTools = await listTools()
56
+ return mcpTools.map((tool) => ({
57
+ type: 'function',
58
+ function: {
59
+ name: tool.name,
60
+ description: tool.description || '',
61
+ parameters: (tool.inputSchema as Record<string, unknown> | undefined) || {
62
+ type: 'object',
63
+ properties: {},
64
+ },
65
+ },
66
+ }))
67
+ },
68
+ callTool: async (toolCall) => {
69
+ const toolName = toolCall.function?.name
70
+ if (!toolName || !toolName.includes('__')) {
71
+ throw new Error(`Unknown MCP tool name: ${toolName || '(empty)'}`)
72
+ }
73
+ const [serverKeyPart, ...nameParts] = toolName.split('__')
74
+ const originalToolName = nameParts.join('__')
75
+ const serverKey = serverKeyPart as McpServerKey
76
+ if (!originalToolName) {
77
+ throw new Error(`Unknown MCP tool name: ${toolName}`)
78
+ }
79
+ const args = JSON.parse(toolCall.function?.arguments || '{}') as Record<string, unknown>
80
+ return await callTool(serverKey, originalToolName, args)
81
+ },
82
+ }),
83
+ {
84
+ onError({ currentTurn, error }) {
85
+ console.error(error)
86
+ currentTurn[currentTurn.length - 1].content = String(error)
87
+ },
88
+ },
89
+ ],
90
+ },
91
+ autoSaveMessages: true,
92
+ })
93
+
94
+ const messages = computed(() => useConversationReturn.activeConversation.value?.engine.messages.value || [])
95
+ const isProcessing = computed(
96
+ () => useConversationReturn.activeConversation.value?.engine.isProcessing.value ?? false,
97
+ )
98
+
99
+ function sendMessage(content: string) {
100
+ const value = content?.trim()
101
+ if (!value || isProcessing.value) {
102
+ return
103
+ }
104
+ if (!useConversationReturn.activeConversationId.value) {
105
+ useConversationReturn.createConversation({ title: value.slice(0, 24) })
106
+ }
107
+ useConversationReturn.sendMessage(value)
108
+ inputMessage.value = ''
109
+ }
110
+
111
+ return {
112
+ ...useConversationReturn,
113
+ ...modelStore,
114
+ messages,
115
+ inputMessage,
116
+ isProcessing,
117
+ sendMessage,
118
+ }
119
+ }
120
+
121
+ type ChatStore = ReturnType<typeof createChatStore>
122
+ let chatStore: ChatStore | null = null
123
+
124
+ export function useChat() {
125
+ if (!chatStore) {
126
+ chatStore = createChatStore()
127
+ }
128
+ return chatStore
129
+ }
@@ -0,0 +1,170 @@
1
+ import { computed, ref } from 'vue'
2
+ import { Client } from '@modelcontextprotocol/sdk/client'
3
+ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse'
4
+ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp'
5
+ import type { CallToolRequest } from '@modelcontextprotocol/sdk/types'
6
+ import { McpServers, type McpServerConfig, type McpServerKey } from '../mcpServers'
7
+
8
+ type McpTool = Awaited<ReturnType<Client['listTools']>>['tools'][number]
9
+ type McpToolCallResult = Awaited<ReturnType<Client['callTool']>>
10
+
11
+ function createMcpClient(serverKey: McpServerKey, server: McpServerConfig) {
12
+ const client = new Client({
13
+ name: serverKey,
14
+ version: '0.1.0',
15
+ })
16
+ const requestInit = {
17
+ headers: server.headers,
18
+ }
19
+ const transport =
20
+ server.type === 'streamableHttp'
21
+ ? new StreamableHTTPClientTransport(new URL(server.baseUrl), { requestInit })
22
+ : new SSEClientTransport(new URL(server.baseUrl), { requestInit })
23
+ return { client, transport }
24
+ }
25
+
26
+ function getServerByKey(serverKey: McpServerKey): McpServerConfig {
27
+ const server = McpServers[serverKey]
28
+ if (!server) {
29
+ throw new Error(`Unknown MCP server key: ${serverKey}`)
30
+ }
31
+ return server
32
+ }
33
+
34
+ async function listMcpToolsFromServer(serverKey: McpServerKey): Promise<McpTool[]> {
35
+ const server = getServerByKey(serverKey)
36
+ const { client, transport } = createMcpClient(serverKey, server)
37
+ try {
38
+ await client.connect(transport)
39
+ const response = await client.listTools()
40
+ return response.tools
41
+ } finally {
42
+ await client.close().catch(() => undefined)
43
+ }
44
+ }
45
+
46
+ async function callMcpToolFromServer(
47
+ serverKey: McpServerKey,
48
+ toolName: string,
49
+ args: CallToolRequest['params']['arguments'] = {},
50
+ ): Promise<McpToolCallResult> {
51
+ const server = getServerByKey(serverKey)
52
+ const { client, transport } = createMcpClient(serverKey, server)
53
+ try {
54
+ await client.connect(transport)
55
+ return await client.callTool({
56
+ name: toolName,
57
+ arguments: args,
58
+ })
59
+ } finally {
60
+ await client.close().catch(() => undefined)
61
+ }
62
+ }
63
+
64
+ function createMcpStore() {
65
+ // Added servers are selectable from McpServers and start empty by default.
66
+ const addedMcpServers = ref<McpServerKey[]>([])
67
+ const inUseMcpServers = ref<McpServerKey[]>([])
68
+
69
+ const availableMcpServers = computed(() => Object.keys(McpServers).map((serverKey) => serverKey as McpServerKey))
70
+
71
+ function addMcpServer(serverKey: McpServerKey) {
72
+ if (!McpServers[serverKey]) {
73
+ throw new Error(`Unknown MCP server key: ${serverKey}`)
74
+ }
75
+ if (!addedMcpServers.value.includes(serverKey)) {
76
+ addedMcpServers.value = [...addedMcpServers.value, serverKey]
77
+ }
78
+ // First add should enable the server immediately.
79
+ if (!inUseMcpServers.value.includes(serverKey)) {
80
+ inUseMcpServers.value = [...inUseMcpServers.value, serverKey]
81
+ }
82
+ }
83
+
84
+ function toggleMcpServer(serverKey: McpServerKey) {
85
+ if (!addedMcpServers.value.includes(serverKey)) {
86
+ throw new Error(`MCP server "${serverKey}" is not added.`)
87
+ }
88
+ if (inUseMcpServers.value.includes(serverKey)) {
89
+ inUseMcpServers.value = inUseMcpServers.value.filter((item) => item !== serverKey)
90
+ return
91
+ }
92
+ inUseMcpServers.value = [...inUseMcpServers.value, serverKey]
93
+ }
94
+
95
+ function removeMcpServer(serverKey: McpServerKey) {
96
+ if (!addedMcpServers.value.includes(serverKey)) {
97
+ return
98
+ }
99
+ addedMcpServers.value = addedMcpServers.value.filter((item) => item !== serverKey)
100
+ inUseMcpServers.value = inUseMcpServers.value.filter((item) => item !== serverKey)
101
+ }
102
+
103
+ function assertServerInUse(serverKey: McpServerKey) {
104
+ if (!inUseMcpServers.value.includes(serverKey)) {
105
+ throw new Error(`MCP server "${serverKey}" is not in use.`)
106
+ }
107
+ }
108
+
109
+ async function listTools(serverKey?: McpServerKey): Promise<McpTool[]> {
110
+ if (serverKey) {
111
+ return listMcpToolsFromServer(serverKey)
112
+ }
113
+
114
+ const resultEntries = await Promise.allSettled(
115
+ inUseMcpServers.value.map(async (key) => await listMcpToolsFromServer(key)),
116
+ )
117
+
118
+ const allTools: McpTool[] = []
119
+ resultEntries.forEach((item, index) => {
120
+ const serverKey = inUseMcpServers.value[index]
121
+ if (!serverKey) {
122
+ return
123
+ }
124
+ if (item.status === 'fulfilled') {
125
+ item.value.forEach((tool, toolIndex) => {
126
+ const originalName = typeof tool.name === 'string' ? tool.name : `tool_${toolIndex + 1}`
127
+ allTools.push({
128
+ ...tool,
129
+ name: `${serverKey}__${originalName}`,
130
+ })
131
+ })
132
+ return
133
+ }
134
+ console.error(`[MCP] Failed to list tools from "${serverKey}":`, item.reason)
135
+ })
136
+
137
+ return allTools
138
+ }
139
+
140
+ async function callTool(
141
+ serverKey: McpServerKey,
142
+ toolName: string,
143
+ args: Record<string, unknown> = {},
144
+ ): Promise<McpToolCallResult> {
145
+ assertServerInUse(serverKey)
146
+ return callMcpToolFromServer(serverKey, toolName, args)
147
+ }
148
+
149
+ return {
150
+ McpServers,
151
+ availableMcpServers,
152
+ addedMcpServers,
153
+ addMcpServer,
154
+ inUseMcpServers,
155
+ toggleMcpServer,
156
+ removeMcpServer,
157
+ listTools,
158
+ callTool,
159
+ }
160
+ }
161
+
162
+ type McpStore = ReturnType<typeof createMcpStore>
163
+ let mcpStore: McpStore | null = null
164
+
165
+ export function useMcp() {
166
+ if (!mcpStore) {
167
+ mcpStore = createMcpStore()
168
+ }
169
+ return mcpStore
170
+ }
@@ -0,0 +1,82 @@
1
+ import type { Component } from 'vue'
2
+ import { computed, ref } from 'vue'
3
+ import { ModelConfigs, type ModelConfigItem } from '../models'
4
+
5
+ export interface ModelOption {
6
+ id: string
7
+ provider: string
8
+ name: string
9
+ model: string
10
+ icon: Component
11
+ apiUrl: string
12
+ apiKey?: string
13
+ capabilities?: ModelConfigItem['capabilities']
14
+ }
15
+
16
+ function createModelStore() {
17
+ const modelOptions: ModelOption[] = Object.entries(ModelConfigs).flatMap(([provider, config]) =>
18
+ config.models.map((modelItem) => ({
19
+ id: `${provider}:${modelItem.model}`,
20
+ provider,
21
+ name: modelItem.name,
22
+ model: modelItem.model,
23
+ icon: config.icon,
24
+ apiUrl: config.apiUrl,
25
+ apiKey: config.apiKey?.trim(),
26
+ capabilities: modelItem.capabilities,
27
+ })),
28
+ )
29
+
30
+ const selectedModelId = ref(modelOptions[0]?.id || '')
31
+ const selectedModel = computed<ModelOption | undefined>(() =>
32
+ modelOptions.find((item) => item.id === selectedModelId.value),
33
+ )
34
+
35
+ const thinkingEnabled = ref(false)
36
+ const searchEnabled = ref(false)
37
+
38
+ const supportsThinking = computed(() => Boolean(selectedModel.value?.capabilities?.thinking))
39
+ const supportsSearch = computed(() => Boolean(selectedModel.value?.capabilities?.search))
40
+
41
+ const hasApiConfig = computed(() => {
42
+ const current = selectedModel.value
43
+ return Boolean(current?.apiUrl && current?.apiKey)
44
+ })
45
+
46
+ function getSelectedModelParams(): Record<string, unknown> {
47
+ const current = selectedModel.value
48
+ if (!current) {
49
+ return {}
50
+ }
51
+
52
+ const { thinking, search } = current.capabilities ?? {}
53
+
54
+ return {
55
+ model: current.model,
56
+ ...(thinkingEnabled.value && thinking ? thinking : {}),
57
+ ...(searchEnabled.value && search ? search : {}),
58
+ }
59
+ }
60
+
61
+ return {
62
+ modelOptions,
63
+ selectedModelId,
64
+ selectedModel,
65
+ thinkingEnabled,
66
+ searchEnabled,
67
+ supportsThinking,
68
+ supportsSearch,
69
+ hasApiConfig,
70
+ getSelectedModelParams,
71
+ }
72
+ }
73
+
74
+ type ModelStore = ReturnType<typeof createModelStore>
75
+ let modelStore: ModelStore | null = null
76
+
77
+ export function useModel() {
78
+ if (!modelStore) {
79
+ modelStore = createModelStore()
80
+ }
81
+ return modelStore
82
+ }
@@ -0,0 +1,7 @@
1
+ import { createApp } from 'vue'
2
+ import App from './App.vue'
3
+
4
+ import '@opentiny/tiny-robot/dist/style.css'
5
+ import './style.css'
6
+
7
+ createApp(App).mount('#app')
@@ -0,0 +1,40 @@
1
+ export type McpServerConfig = {
2
+ type: 'sse' | 'streamableHttp'
3
+ name: string
4
+ description?: string
5
+ baseUrl: string
6
+ headers?: Record<string, string>
7
+ logoUrl?: string
8
+ }
9
+
10
+ export const McpServers = {
11
+ 'china-railway': {
12
+ type: 'sse',
13
+ description: '开源社区开发者封装,提供 12306购票信息查询等服务',
14
+ name: '12306 车票查询',
15
+ baseUrl: 'https://dashscope.aliyuncs.com/api/v1/mcps/china-railway/sse',
16
+ logoUrl: 'https://img.alicdn.com/imgextra/i3/O1CN01yUKR7l1FrpKqvxNjt_!!6000000000541-2-tps-512-512.png',
17
+ headers: {
18
+ Authorization: `Bearer ${import.meta.env.VITE_ALIYUN_DASHSCOPE_KEY}`,
19
+ },
20
+ },
21
+ 'amap-maps': {
22
+ type: 'sse',
23
+ description:
24
+ '高德地图MCP Server现已覆盖15大核心接口,提供全场景覆盖的地理信息服务,包括生成专属地图、导航到目的地、打车、地理编码、逆地理编码、IP定位、天气查询、骑行路径规划、步行路径规划、驾车路径规划、公交路径规划、距离测量、关键词搜索、周边搜索、详情搜索等。',
25
+ name: '高德地图',
26
+ baseUrl: 'https://dashscope.aliyuncs.com/api/v1/mcps/amap-maps/sse',
27
+ logoUrl: 'https://img.alicdn.com/imgextra/i4/O1CN01iPPabT1EGRN6uatHP_!!6000000000324-0-tps-512-512.jpg',
28
+ headers: {
29
+ Authorization: `Bearer ${import.meta.env.VITE_ALIYUN_DASHSCOPE_KEY}`,
30
+ },
31
+ },
32
+ 'model-context-protocol-mcp': {
33
+ type: 'streamableHttp',
34
+ name: 'Model Context Protocol MCP',
35
+ baseUrl: window.location.origin + '/modelcontextprotocol-mcp',
36
+ logoUrl: window.location.origin + '/modelcontextprotocol.png',
37
+ },
38
+ } satisfies Record<string, McpServerConfig>
39
+
40
+ export type McpServerKey = keyof typeof McpServers