Skip to content

Commit 062fa4d

Browse files
authored
feat: add @opentiny/next-sdk (#165)
* feat: add @opentiny/next-sdk * fix: remove useless log
1 parent 08da3ec commit 062fa4d

9 files changed

Lines changed: 812 additions & 363 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"homepage": "https://opentiny.design/pro/",
3434
"pnpm": {
3535
"overrides": {
36+
"@modelcontextprotocol/sdk": "1.16.0",
3637
"@opentiny/tiny-engine-common>@opentiny/vue-renderless": "~3.20.0",
3738
"@opentiny/tiny-engine-plugin-datasource>@opentiny/vue-renderless": "~3.20.0",
3839
"@opentiny/tiny-engine-plugin-block>@opentiny/vue-renderless": "~3.20.0",

pnpm-lock.yaml

Lines changed: 668 additions & 360 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

template/tinyvue/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
"@babel/core": "^7.25.2",
4040
"@gaonengwww/mock-server": "^1.0.5",
4141
"@opentiny/icons": "^0.1.3",
42+
"@opentiny/next-remoter": "^0.0.10",
43+
"@opentiny/next-sdk": "^0.1.15",
4244
"@opentiny/vue": "^3.28.0",
4345
"@opentiny/vue-huicharts": "~3.28.0",
4446
"@opentiny/vue-icon": "~3.28.0",

template/tinyvue/src/App.vue

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,35 @@
66

77
<global-setting />
88
</div>
9+
<tiny-remoter
10+
agent-root="https://agent.opentiny.design/api/v1/webmcp-trial/"
11+
:session-id="sessionId"
12+
:menuItems="[
13+
{
14+
action: 'qr-code',
15+
show: false
16+
},
17+
{
18+
action: 'remote-control',
19+
show: false
20+
},
21+
{
22+
action: 'remote-url',
23+
show: false
24+
}
25+
]"
26+
/>
927
</template>
1028

1129
<script lang="ts" setup>
12-
import { provide } from 'vue';
30+
import { onMounted, provide, ref } from 'vue';
1331
import { TinyConfigProvider } from '@opentiny/vue';
1432
import GlobalSetting from '@/components/global-setting/index.vue';
33+
import { createMessageChannelPairTransport, WebMcpClient } from '@opentiny/next-sdk'
34+
import { TinyRemoter } from '@opentiny/next-remoter'
1535
import TinyThemeTool from '@opentiny/vue-theme/theme-tool';
1636
import { useTheme } from './hooks/useTheme';
37+
import '@opentiny/next-remoter/dist/style.css'
1738
1839
const theme = new TinyThemeTool();
1940
useTheme(theme);
@@ -30,6 +51,26 @@
3051
},
3152
},
3253
};
54+
55+
const sessionId = ref('')
56+
const [serverTransport, clientTransport] = createMessageChannelPairTransport()
57+
provide('serverTransport', serverTransport)
58+
59+
onMounted(async () => {
60+
61+
// 创建 WebMcpClient ,并与 WebAgent 连接
62+
const client = new WebMcpClient()
63+
await client.connect(clientTransport)
64+
const { sessionId: sessionID } = await client.connect({
65+
agent: true,
66+
67+
// sessionId 为可选参数。若传入该参数,系统将使用指定值作为会话标识;若未传入,WebAgent 服务将自动生成一个随机的字符串作为 sessionId。为便于通过 MCP Inspector 工具进行调试,此处采用了固定的 sessionId。用户亦可通过浏览器原生提供的 crypto.randomUUID() 方法生成随机字符串作为会话标识。
68+
sessionId: 'd299a869-c674-4125-a84b-bb4e24079b99',
69+
70+
url: 'https://agent.opentiny.design/api/v1/webmcp-trial/mcp'
71+
})
72+
sessionId.value = sessionID
73+
})
3374
</script>
3475

3576
<style lang="less" scoped>

template/tinyvue/src/assets/style/global.less

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,8 @@ body {
3838

3939
.tiny-button {
4040
--tv-Button-min-width: unset;
41+
}
42+
43+
.tr-container {
44+
z-index: 999 !important;
4145
}

template/tinyvue/src/utils/base-utils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ export function isCommonError(error: unknown): error is CommonError {
99
);
1010
}
1111

12+
export const sleep = (ms: number): Promise<void> => {
13+
return new Promise((resolve) => setTimeout(resolve, ms))
14+
}
15+
1216
export default class BaseUtils {
1317
public static getErrorMessage(error: unknown): string {
1418
return this.convertToCommonError(error).message;

template/tinyvue/src/views/locale/components/add-locale.vue

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,10 @@
101101
Option as TinyOption,
102102
Popover as TinyPopover,
103103
} from '@opentiny/vue';
104-
import { computed, reactive, ref, watch } from 'vue';
104+
import { WebMcpServer, z } from '@opentiny/next-sdk'
105+
import { computed, reactive, ref, watch, onMounted, inject } from 'vue';
105106
import { useI18n } from 'vue-i18n';
107+
import { sleep } from '@/utils/base-utils';
106108
import langTable from './lang-table.vue';
107109
108110
const emits = defineEmits<{
@@ -216,6 +218,39 @@
216218
setLangTableClose()
217219
}
218220
})
221+
222+
onMounted(async () => {
223+
const server = new WebMcpServer({
224+
name: 'i18n-management-mcp-server',
225+
version: '1.0.0'
226+
})
227+
const serverTransport = inject<any>('serverTransport')
228+
229+
server.registerTool(
230+
'add-i18n-entry',
231+
{
232+
title: '添加国际化词条',
233+
description: '添加国际化词条',
234+
inputSchema: {
235+
key: z.string().describe('词条关键字'),
236+
content: z.string().describe('词条内容'),
237+
lang: z.union([z.literal(1), z.literal(2)]).describe('词条语言ID,英文 enUS 为:1,中文 zhCN 为:2'),
238+
}
239+
},
240+
async ({ key, content, lang }) => {
241+
onOpen()
242+
await sleep(1000)
243+
locale.key = key
244+
locale.content = content
245+
locale.lang = lang
246+
await sleep(1000)
247+
addLocale()
248+
return { content: [{ type: 'text', text: `收到: ${key}` }] }
249+
}
250+
)
251+
252+
await server.connect(serverTransport)
253+
});
219254
</script>
220255

221256
<style scoped lang="less">

template/tinyvue/src/views/menu/info/components/add-menu.vue

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,16 @@
171171
: Number(menuInfo.parentId),
172172
};
173173
},
174+
setMenuInfo: (data: Omit<CreateMenuDto, 'id'>) => {
175+
menuInfo.name = data.name;
176+
menuInfo.path = data.path;
177+
menuInfo.component = data.component;
178+
menuInfo.icon = data.icon;
179+
menuInfo.menuType = data.menuType;
180+
menuInfo.parentId = data.parentId;
181+
menuInfo.order = data.order;
182+
menuInfo.locale = data.locale;
183+
},
174184
valid: async () => {
175185
return menuForm.value.validate();
176186
},

template/tinyvue/src/views/menu/info/components/info-tab.vue

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Modal as TinyModal,
88
Loading,
99
} from '@opentiny/vue';
10+
import { WebMcpServer, z } from '@opentiny/next-sdk'
1011
import { getAllLocalItems } from '@/api/local';
1112
import useLoading from '@/hooks/loading';
1213
import {
@@ -26,6 +27,7 @@
2627
import menuTree, { Node } from './menu-tree.vue';
2728
import UpdateForm from './update-form.vue';
2829
import AddMenu from './add-menu.vue';
30+
import { sleep } from '@/utils/base-utils';
2931
3032
const { modalSize } = useResponsiveSize()
3133
@@ -241,10 +243,52 @@
241243
fetchLocalItems();
242244
});
243245
244-
onMounted(() => {
246+
onMounted(async () => {
245247
Promise.all([fetchMenu(), fetchLocalItems()]).finally(() => {
246248
treeLoading.value = false;
247249
});
250+
251+
const server = new WebMcpServer({
252+
name: 'menu-management-mcp-server',
253+
version: '1.0.0'
254+
})
255+
const serverTransport = inject<any>('serverTransport')
256+
257+
server.registerTool(
258+
'add-menu',
259+
{
260+
title: '添加菜单',
261+
description: '添加菜单',
262+
inputSchema: {
263+
name: z.string().describe('名称'),
264+
order: z.number().describe('优先级').default(0),
265+
parentId: z.number().describe('父菜单ID').optional(),
266+
icon: z.string().describe('图标').optional().default(''),
267+
component: z.string().describe('组件'),
268+
path: z.string().describe('路径'),
269+
locale: z.string().describe('国际化'),
270+
}
271+
},
272+
async ({ name, order, parentId, icon, component, path, locale }) => {
273+
handleAddMenu()
274+
await sleep(1000)
275+
addMenu.value.setMenuInfo({
276+
name,
277+
order,
278+
parentId,
279+
icon,
280+
component,
281+
menuType: "/",
282+
path,
283+
locale,
284+
})
285+
await sleep(1000)
286+
onClickAdd()
287+
return { content: [{ type: 'text', text: `收到: ${name}` }] }
288+
}
289+
)
290+
291+
await server.connect(serverTransport)
248292
});
249293
</script>
250294

0 commit comments

Comments
 (0)