1313 <tr >
1414 <th >{{ $t('name') }}</th >
1515 <th >{{ $t('client_id') }}</th >
16+ <th >Redirect URIs</th >
1617 <th >{{ $t('scopes') }}</th >
1718 <th >Tạo lúc</th >
1819 <th >{{ $t('actions') }}</th >
2324 <td class =" font-weight-medium" >{{ client.name }}</td >
2425 <td class =" text-body-2 font-mono" >{{ client.client_id }}</td >
2526 <td >
26- <v-chip size =" x-small" variant =" tonal" color =" primary" >{{ client.scopes }}</v-chip >
27+ <template v-if =" parseJSON (client .redirect_uris ).length " >
28+ <v-chip v-for =" uri in parseJSON(client.redirect_uris)" :key =" uri" size =" x-small" variant =" tonal" class =" mr-1 mb-1" >{{ uri }}</v-chip >
29+ </template >
30+ <span v-else class =" text-grey text-caption" >Chưa cấu hình</span >
31+ </td >
32+ <td >
33+ <v-chip v-for =" scope in parseJSON(client.scopes)" :key =" scope" size =" x-small" variant =" tonal" color =" primary" class =" mr-1" >{{ scope }}</v-chip >
2734 </td >
2835 <td class =" text-caption" >{{ new Date(client.created_at).toLocaleString('vi-VN') }}</td >
2936 <td >
4249 </div >
4350
4451 <!-- Create Dialog -->
45- <v-dialog v-model =" createDialog" max-width =" 520 " >
52+ <v-dialog v-model =" createDialog" max-width =" 560 " >
4653 <v-card class =" pa-6" >
4754 <v-card-title >{{ $t('create_connection') }}</v-card-title >
48- <v-text-field v-model =" newName" :label =" $t('name')" class =" mt-4 mb-3" hint =" Tên hiển thị cho kết nối này" persistent-hint />
4955
50- <div v-if =" generatedSecret" class =" bg-grey-lighten-4 pa-4 rounded mb-3" >
56+ <v-text-field v-model =" newName" :label =" $t('name')" class =" mt-4" hint =" Tên hiển thị cho kết nối này" persistent-hint />
57+
58+ <v-combobox
59+ v-model =" newRedirectURIs"
60+ label =" Redirect URIs"
61+ multiple
62+ chips
63+ closable-chips
64+ class =" mt-4"
65+ hint =" Nhập URL callback rồi nhấn Enter (vd: https://claude.ai/oauth/callback)"
66+ persistent-hint
67+ />
68+
69+ <v-select
70+ v-model =" newScopes"
71+ :items =" scopeOptions"
72+ label =" Phân quyền (Scopes)"
73+ multiple
74+ chips
75+ class =" mt-4"
76+ hint =" Chọn quyền truy cập cho kết nối"
77+ persistent-hint
78+ />
79+
80+ <div v-if =" generatedSecret" class =" bg-grey-lighten-4 pa-4 rounded mt-4" >
5181 <div class =" text-caption text-grey mb-1" >{{ $t('client_id') }}</div >
5282 <div class =" font-mono text-body-2 mb-3" >{{ generatedClientId }}</div >
5383 <div class =" text-caption text-grey mb-1" >{{ $t('client_secret') }}</div >
6191 </v-btn >
6292 </div >
6393
64- <v-card-actions class =" px-0" >
94+ <v-card-actions class =" px-0 mt-4 " >
6595 <v-spacer />
6696 <v-btn variant =" text" @click =" closeDialog" >{{ generatedSecret ? 'Đóng' : $t('cancel') }}</v-btn >
6797 <v-btn v-if =" !generatedSecret" color =" primary" :loading =" creating" :disabled =" !newName" @click =" generateClient" >{{ $t('create') }}</v-btn >
@@ -80,6 +110,9 @@ import api from '../api'
80110const clients = ref <any []>([])
81111const createDialog = ref (false )
82112const newName = ref (' ' )
113+ const newRedirectURIs = ref <string []>([])
114+ const newScopes = ref <string []>([' read' , ' write' ])
115+ const scopeOptions = [' read' , ' write' ]
83116const generatedClientId = ref (' ' )
84117const generatedSecret = ref (' ' )
85118const creating = ref (false )
@@ -88,6 +121,14 @@ const snackText = ref('')
88121
89122onMounted (loadClients )
90123
124+ function parseJSON(val : string ): string [] {
125+ try {
126+ return JSON .parse (val ) || []
127+ } catch {
128+ return []
129+ }
130+ }
131+
91132async function loadClients() {
92133 try {
93134 const { data } = await api .get (' /mcp/clients' )
@@ -98,11 +139,17 @@ async function loadClients() {
98139async function generateClient() {
99140 creating .value = true
100141 try {
101- const { data } = await api .post (' /mcp/clients' , { name: newName .value })
142+ const { data } = await api .post (' /mcp/clients' , {
143+ name: newName .value ,
144+ redirect_uris: newRedirectURIs .value ,
145+ scopes: newScopes .value ,
146+ })
102147 generatedClientId .value = data .client_id
103148 generatedSecret .value = data .client_secret
104149 await loadClients ()
105150 newName .value = ' '
151+ newRedirectURIs .value = []
152+ newScopes .value = [' read' , ' write' ]
106153 } catch (err : any ) {
107154 snackText .value = err .response ?.data ?.error || ' Lỗi tạo kết nối'
108155 snackbar .value = true
0 commit comments