@tleblancureta/proto 0.1.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.
- package/core-web/src/ProtoApp.tsx +163 -0
- package/core-web/src/components/Shell.tsx +276 -0
- package/core-web/src/components/shell/EmptyState.tsx +33 -0
- package/core-web/src/components/shell/FocusView.tsx +55 -0
- package/core-web/src/components/shell/Toolbar.tsx +233 -0
- package/core-web/src/components/shell/persistence.ts +20 -0
- package/core-web/src/components/shell/types.ts +14 -0
- package/core-web/src/components/ui/avatar.tsx +18 -0
- package/core-web/src/components/ui/badge.tsx +28 -0
- package/core-web/src/components/ui/button.tsx +40 -0
- package/core-web/src/components/ui/card.tsx +32 -0
- package/core-web/src/components/ui/inline-edit.tsx +120 -0
- package/core-web/src/components/ui/input.tsx +18 -0
- package/core-web/src/components/ui/scroll-area.tsx +12 -0
- package/core-web/src/components/ui/separator.tsx +23 -0
- package/core-web/src/components/ui/shell-dialog.tsx +79 -0
- package/core-web/src/components/ui/skeleton.tsx +9 -0
- package/core-web/src/components/ui/textarea.tsx +17 -0
- package/core-web/src/components/widgets/agent/Generative.tsx +74 -0
- package/core-web/src/components/widgets/agent/Primitives.tsx +225 -0
- package/core-web/src/components/widgets/agent/actions.ts +52 -0
- package/core-web/src/hooks/useAuth.ts +80 -0
- package/core-web/src/hooks/useData.ts +44 -0
- package/core-web/src/hooks/useMountEffect.ts +10 -0
- package/core-web/src/hooks/useTheme.ts +37 -0
- package/core-web/src/index.ts +52 -0
- package/core-web/src/lib/api.ts +231 -0
- package/core-web/src/lib/config.ts +14 -0
- package/core-web/src/lib/define-widget.ts +71 -0
- package/core-web/src/lib/drag.ts +45 -0
- package/core-web/src/lib/supabase.ts +6 -0
- package/core-web/src/lib/utils.ts +6 -0
- package/core-web/src/lib/widgetCache.ts +29 -0
- package/core-web/src/vite-env.d.ts +1 -0
- package/dist/core-mcp/src/app.d.ts +40 -0
- package/dist/core-mcp/src/app.d.ts.map +1 -0
- package/dist/core-mcp/src/app.js +141 -0
- package/dist/core-mcp/src/app.js.map +1 -0
- package/dist/core-mcp/src/define-tool.d.ts +70 -0
- package/dist/core-mcp/src/define-tool.d.ts.map +1 -0
- package/dist/core-mcp/src/define-tool.js +38 -0
- package/dist/core-mcp/src/define-tool.js.map +1 -0
- package/dist/core-mcp/src/entity-tools.d.ts +27 -0
- package/dist/core-mcp/src/entity-tools.d.ts.map +1 -0
- package/dist/core-mcp/src/entity-tools.js +99 -0
- package/dist/core-mcp/src/entity-tools.js.map +1 -0
- package/dist/core-mcp/src/index.d.ts +36 -0
- package/dist/core-mcp/src/index.d.ts.map +1 -0
- package/dist/core-mcp/src/index.js +116 -0
- package/dist/core-mcp/src/index.js.map +1 -0
- package/dist/core-mcp/src/supabase.d.ts +7 -0
- package/dist/core-mcp/src/supabase.d.ts.map +1 -0
- package/dist/core-mcp/src/supabase.js +18 -0
- package/dist/core-mcp/src/supabase.js.map +1 -0
- package/dist/core-mcp/src/tools/_helpers.d.ts +44 -0
- package/dist/core-mcp/src/tools/_helpers.d.ts.map +1 -0
- package/dist/core-mcp/src/tools/_helpers.js +23 -0
- package/dist/core-mcp/src/tools/_helpers.js.map +1 -0
- package/dist/core-mcp/src/tools/ui.d.ts +9 -0
- package/dist/core-mcp/src/tools/ui.d.ts.map +1 -0
- package/dist/core-mcp/src/tools/ui.js +100 -0
- package/dist/core-mcp/src/tools/ui.js.map +1 -0
- package/dist/core-mcp/src/workflow-tools.d.ts +41 -0
- package/dist/core-mcp/src/workflow-tools.d.ts.map +1 -0
- package/dist/core-mcp/src/workflow-tools.js +382 -0
- package/dist/core-mcp/src/workflow-tools.js.map +1 -0
- package/dist/core-shared/src/define-entity.d.ts +73 -0
- package/dist/core-shared/src/define-entity.d.ts.map +1 -0
- package/dist/core-shared/src/define-entity.js +47 -0
- package/dist/core-shared/src/define-entity.js.map +1 -0
- package/dist/core-shared/src/define-workflow.d.ts +111 -0
- package/dist/core-shared/src/define-workflow.d.ts.map +1 -0
- package/dist/core-shared/src/define-workflow.js +92 -0
- package/dist/core-shared/src/define-workflow.js.map +1 -0
- package/dist/core-shared/src/index.d.ts +5 -0
- package/dist/core-shared/src/index.d.ts.map +1 -0
- package/dist/core-shared/src/index.js +7 -0
- package/dist/core-shared/src/index.js.map +1 -0
- package/dist/core-shared/src/scheduling.d.ts +69 -0
- package/dist/core-shared/src/scheduling.d.ts.map +1 -0
- package/dist/core-shared/src/scheduling.js +39 -0
- package/dist/core-shared/src/scheduling.js.map +1 -0
- package/dist/core-shared/src/schemas.d.ts +51 -0
- package/dist/core-shared/src/schemas.d.ts.map +1 -0
- package/dist/core-shared/src/schemas.js +18 -0
- package/dist/core-shared/src/schemas.js.map +1 -0
- package/dist/core-web/src/ProtoApp.d.ts +19 -0
- package/dist/core-web/src/ProtoApp.d.ts.map +1 -0
- package/dist/core-web/src/ProtoApp.js +92 -0
- package/dist/core-web/src/ProtoApp.js.map +1 -0
- package/dist/core-web/src/components/Shell.d.ts +46 -0
- package/dist/core-web/src/components/Shell.d.ts.map +1 -0
- package/dist/core-web/src/components/Shell.js +104 -0
- package/dist/core-web/src/components/Shell.js.map +1 -0
- package/dist/core-web/src/components/shell/EmptyState.d.ts +13 -0
- package/dist/core-web/src/components/shell/EmptyState.d.ts.map +1 -0
- package/dist/core-web/src/components/shell/EmptyState.js +7 -0
- package/dist/core-web/src/components/shell/EmptyState.js.map +1 -0
- package/dist/core-web/src/components/shell/FocusView.d.ts +16 -0
- package/dist/core-web/src/components/shell/FocusView.d.ts.map +1 -0
- package/dist/core-web/src/components/shell/FocusView.js +12 -0
- package/dist/core-web/src/components/shell/FocusView.js.map +1 -0
- package/dist/core-web/src/components/shell/Toolbar.d.ts +35 -0
- package/dist/core-web/src/components/shell/Toolbar.d.ts.map +1 -0
- package/dist/core-web/src/components/shell/Toolbar.js +42 -0
- package/dist/core-web/src/components/shell/Toolbar.js.map +1 -0
- package/dist/core-web/src/components/shell/persistence.d.ts +8 -0
- package/dist/core-web/src/components/shell/persistence.d.ts.map +1 -0
- package/dist/core-web/src/components/shell/persistence.js +20 -0
- package/dist/core-web/src/components/shell/persistence.js.map +1 -0
- package/dist/core-web/src/components/shell/types.d.ts +13 -0
- package/dist/core-web/src/components/shell/types.d.ts.map +1 -0
- package/dist/core-web/src/components/shell/types.js +2 -0
- package/dist/core-web/src/components/shell/types.js.map +1 -0
- package/dist/core-web/src/components/ui/avatar.d.ts +5 -0
- package/dist/core-web/src/components/ui/avatar.d.ts.map +1 -0
- package/dist/core-web/src/components/ui/avatar.js +9 -0
- package/dist/core-web/src/components/ui/avatar.js.map +1 -0
- package/dist/core-web/src/components/ui/badge.d.ts +13 -0
- package/dist/core-web/src/components/ui/badge.d.ts.map +1 -0
- package/dist/core-web/src/components/ui/badge.js +13 -0
- package/dist/core-web/src/components/ui/badge.js.map +1 -0
- package/dist/core-web/src/components/ui/button.d.ts +22 -0
- package/dist/core-web/src/components/ui/button.d.ts.map +1 -0
- package/dist/core-web/src/components/ui/button.js +21 -0
- package/dist/core-web/src/components/ui/button.js.map +1 -0
- package/dist/core-web/src/components/ui/card.d.ts +7 -0
- package/dist/core-web/src/components/ui/card.d.ts.map +1 -0
- package/dist/core-web/src/components/ui/card.js +13 -0
- package/dist/core-web/src/components/ui/card.js.map +1 -0
- package/dist/core-web/src/components/ui/inline-edit.d.ts +20 -0
- package/dist/core-web/src/components/ui/inline-edit.d.ts.map +1 -0
- package/dist/core-web/src/components/ui/inline-edit.js +63 -0
- package/dist/core-web/src/components/ui/inline-edit.js.map +1 -0
- package/dist/core-web/src/components/ui/input.d.ts +4 -0
- package/dist/core-web/src/components/ui/input.d.ts.map +1 -0
- package/dist/core-web/src/components/ui/input.js +7 -0
- package/dist/core-web/src/components/ui/input.js.map +1 -0
- package/dist/core-web/src/components/ui/scroll-area.d.ts +4 -0
- package/dist/core-web/src/components/ui/scroll-area.d.ts.map +1 -0
- package/dist/core-web/src/components/ui/scroll-area.js +7 -0
- package/dist/core-web/src/components/ui/scroll-area.js.map +1 -0
- package/dist/core-web/src/components/ui/separator.d.ts +7 -0
- package/dist/core-web/src/components/ui/separator.d.ts.map +1 -0
- package/dist/core-web/src/components/ui/separator.js +7 -0
- package/dist/core-web/src/components/ui/separator.js.map +1 -0
- package/dist/core-web/src/components/ui/shell-dialog.d.ts +16 -0
- package/dist/core-web/src/components/ui/shell-dialog.d.ts.map +1 -0
- package/dist/core-web/src/components/ui/shell-dialog.js +36 -0
- package/dist/core-web/src/components/ui/shell-dialog.js.map +1 -0
- package/dist/core-web/src/components/ui/skeleton.d.ts +3 -0
- package/dist/core-web/src/components/ui/skeleton.d.ts.map +1 -0
- package/dist/core-web/src/components/ui/skeleton.js +7 -0
- package/dist/core-web/src/components/ui/skeleton.js.map +1 -0
- package/dist/core-web/src/components/ui/textarea.d.ts +4 -0
- package/dist/core-web/src/components/ui/textarea.d.ts.map +1 -0
- package/dist/core-web/src/components/ui/textarea.js +7 -0
- package/dist/core-web/src/components/ui/textarea.js.map +1 -0
- package/dist/core-web/src/components/widgets/agent/Generative.d.ts +13 -0
- package/dist/core-web/src/components/widgets/agent/Generative.d.ts.map +1 -0
- package/dist/core-web/src/components/widgets/agent/Generative.js +42 -0
- package/dist/core-web/src/components/widgets/agent/Generative.js.map +1 -0
- package/dist/core-web/src/components/widgets/agent/Primitives.d.ts +79 -0
- package/dist/core-web/src/components/widgets/agent/Primitives.d.ts.map +1 -0
- package/dist/core-web/src/components/widgets/agent/Primitives.js +116 -0
- package/dist/core-web/src/components/widgets/agent/Primitives.js.map +1 -0
- package/dist/core-web/src/components/widgets/agent/actions.d.ts +3 -0
- package/dist/core-web/src/components/widgets/agent/actions.d.ts.map +1 -0
- package/dist/core-web/src/components/widgets/agent/actions.js +33 -0
- package/dist/core-web/src/components/widgets/agent/actions.js.map +1 -0
- package/dist/core-web/src/hooks/useAuth.d.ts +25 -0
- package/dist/core-web/src/hooks/useAuth.d.ts.map +1 -0
- package/dist/core-web/src/hooks/useAuth.js +53 -0
- package/dist/core-web/src/hooks/useAuth.js.map +1 -0
- package/dist/core-web/src/hooks/useData.d.ts +10 -0
- package/dist/core-web/src/hooks/useData.d.ts.map +1 -0
- package/dist/core-web/src/hooks/useData.js +37 -0
- package/dist/core-web/src/hooks/useData.js.map +1 -0
- package/dist/core-web/src/hooks/useMountEffect.d.ts +6 -0
- package/dist/core-web/src/hooks/useMountEffect.d.ts.map +1 -0
- package/dist/core-web/src/hooks/useMountEffect.js +10 -0
- package/dist/core-web/src/hooks/useMountEffect.js.map +1 -0
- package/dist/core-web/src/hooks/useTheme.d.ts +6 -0
- package/dist/core-web/src/hooks/useTheme.d.ts.map +1 -0
- package/dist/core-web/src/hooks/useTheme.js +31 -0
- package/dist/core-web/src/hooks/useTheme.js.map +1 -0
- package/dist/core-web/src/index.d.ts +33 -0
- package/dist/core-web/src/index.d.ts.map +1 -0
- package/dist/core-web/src/index.js +38 -0
- package/dist/core-web/src/index.js.map +1 -0
- package/dist/core-web/src/lib/api.d.ts +60 -0
- package/dist/core-web/src/lib/api.d.ts.map +1 -0
- package/dist/core-web/src/lib/api.js +204 -0
- package/dist/core-web/src/lib/api.js.map +1 -0
- package/dist/core-web/src/lib/config.d.ts +10 -0
- package/dist/core-web/src/lib/config.d.ts.map +1 -0
- package/dist/core-web/src/lib/config.js +10 -0
- package/dist/core-web/src/lib/config.js.map +1 -0
- package/dist/core-web/src/lib/define-widget.d.ts +52 -0
- package/dist/core-web/src/lib/define-widget.d.ts.map +1 -0
- package/dist/core-web/src/lib/define-widget.js +14 -0
- package/dist/core-web/src/lib/define-widget.js.map +1 -0
- package/dist/core-web/src/lib/drag.d.ts +20 -0
- package/dist/core-web/src/lib/drag.d.ts.map +1 -0
- package/dist/core-web/src/lib/drag.js +33 -0
- package/dist/core-web/src/lib/drag.js.map +1 -0
- package/dist/core-web/src/lib/supabase.d.ts +2 -0
- package/dist/core-web/src/lib/supabase.d.ts.map +1 -0
- package/dist/core-web/src/lib/supabase.js +5 -0
- package/dist/core-web/src/lib/supabase.js.map +1 -0
- package/dist/core-web/src/lib/utils.d.ts +3 -0
- package/dist/core-web/src/lib/utils.d.ts.map +1 -0
- package/dist/core-web/src/lib/utils.js +6 -0
- package/dist/core-web/src/lib/utils.js.map +1 -0
- package/dist/core-web/src/lib/widgetCache.d.ts +18 -0
- package/dist/core-web/src/lib/widgetCache.d.ts.map +1 -0
- package/dist/core-web/src/lib/widgetCache.js +28 -0
- package/dist/core-web/src/lib/widgetCache.js.map +1 -0
- package/dist/mcp.d.ts +2 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +2 -0
- package/dist/mcp.js.map +1 -0
- package/dist/shared.d.ts +2 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +2 -0
- package/dist/shared.js.map +1 -0
- package/dist/web.d.ts +2 -0
- package/dist/web.d.ts.map +1 -0
- package/dist/web.js +2 -0
- package/dist/web.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { GATEWAY_URL, INTERNAL_SECRET as SECRET, WS_URL } from './config'
|
|
2
|
+
|
|
3
|
+
export interface StreamEvent {
|
|
4
|
+
type: 'init' | 'text' | 'tool_use' | 'tool_result' | 'result' | 'error' | 'thinking' | 'auth' | 'pong' | 'shell_refresh'
|
|
5
|
+
text?: string
|
|
6
|
+
tool?: string
|
|
7
|
+
args?: Record<string, unknown>
|
|
8
|
+
content?: string
|
|
9
|
+
message?: string
|
|
10
|
+
session_id?: string
|
|
11
|
+
duration_ms?: number
|
|
12
|
+
cost_usd?: number
|
|
13
|
+
status?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type EventHandler = (event: StreamEvent) => void
|
|
17
|
+
|
|
18
|
+
class ProtoSocket {
|
|
19
|
+
private ws: WebSocket | null = null
|
|
20
|
+
private authenticated = false
|
|
21
|
+
private messageHandler: EventHandler | null = null
|
|
22
|
+
private shellRefreshHandler: (() => void) | null = null
|
|
23
|
+
private connectPromise: Promise<void> | null = null
|
|
24
|
+
private reconnectTimer: ReturnType<typeof setTimeout> | null = null
|
|
25
|
+
private pingTimer: ReturnType<typeof setInterval> | null = null
|
|
26
|
+
private reconnectDelay = 3000
|
|
27
|
+
private static readonly MAX_RECONNECT_DELAY = 60000
|
|
28
|
+
|
|
29
|
+
connect(): Promise<void> {
|
|
30
|
+
if (this.ws?.readyState === WebSocket.OPEN && this.authenticated) {
|
|
31
|
+
return Promise.resolve()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (this.connectPromise) return this.connectPromise
|
|
35
|
+
|
|
36
|
+
this.connectPromise = new Promise((resolve, reject) => {
|
|
37
|
+
this.ws = new WebSocket(WS_URL)
|
|
38
|
+
|
|
39
|
+
this.ws.onopen = () => {
|
|
40
|
+
// Authenticate
|
|
41
|
+
this.ws!.send(JSON.stringify({ type: 'auth', secret: SECRET }))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.ws.onmessage = (e) => {
|
|
45
|
+
try {
|
|
46
|
+
const event: StreamEvent = JSON.parse(e.data)
|
|
47
|
+
|
|
48
|
+
if (event.type === 'auth' && event.status === 'ok') {
|
|
49
|
+
this.authenticated = true
|
|
50
|
+
this.connectPromise = null
|
|
51
|
+
this.reconnectDelay = 3000 // reset on successful auth
|
|
52
|
+
// Start keepalive ping every 25s to prevent idle disconnects
|
|
53
|
+
if (this.pingTimer) clearInterval(this.pingTimer)
|
|
54
|
+
this.pingTimer = setInterval(() => {
|
|
55
|
+
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
56
|
+
try { this.ws.send(JSON.stringify({ type: 'ping' })) } catch {}
|
|
57
|
+
}
|
|
58
|
+
}, 25000)
|
|
59
|
+
resolve()
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (event.type === 'shell_refresh') {
|
|
64
|
+
this.shellRefreshHandler?.()
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (event.type === 'pong') return
|
|
69
|
+
|
|
70
|
+
this.messageHandler?.(event)
|
|
71
|
+
} catch {}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
this.ws.onclose = () => {
|
|
75
|
+
this.authenticated = false
|
|
76
|
+
this.connectPromise = null
|
|
77
|
+
if (this.pingTimer) { clearInterval(this.pingTimer); this.pingTimer = null }
|
|
78
|
+
// Synthesize an error event ONLY if a stream is in flight (handler set)
|
|
79
|
+
// so the chat turn can reset. Fire only once then clear.
|
|
80
|
+
if (this.messageHandler) {
|
|
81
|
+
try { this.messageHandler({ type: 'error', message: 'Conexion perdida. Reintenta.' }) } catch {}
|
|
82
|
+
this.messageHandler = null
|
|
83
|
+
}
|
|
84
|
+
// Auto-reconnect with exponential backoff (3s → 6s → 12s → ... → 60s max)
|
|
85
|
+
if (!this.reconnectTimer) {
|
|
86
|
+
this.reconnectTimer = setTimeout(() => {
|
|
87
|
+
this.reconnectTimer = null
|
|
88
|
+
this.connect().catch(() => {})
|
|
89
|
+
}, this.reconnectDelay)
|
|
90
|
+
this.reconnectDelay = Math.min(this.reconnectDelay * 2, ProtoSocket.MAX_RECONNECT_DELAY)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this.ws.onerror = () => {
|
|
95
|
+
this.connectPromise = null
|
|
96
|
+
try { this.messageHandler?.({ type: 'error', message: 'Error de conexion WebSocket' }) } catch {}
|
|
97
|
+
reject(new Error('WebSocket connection failed'))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Timeout
|
|
101
|
+
setTimeout(() => {
|
|
102
|
+
if (!this.authenticated) {
|
|
103
|
+
this.connectPromise = null
|
|
104
|
+
reject(new Error('WebSocket auth timeout'))
|
|
105
|
+
}
|
|
106
|
+
}, 10000)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
return this.connectPromise
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
onMessage(handler: EventHandler) {
|
|
113
|
+
this.messageHandler = handler
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
onShellRefresh(handler: () => void) {
|
|
117
|
+
this.shellRefreshHandler = handler
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async send(data: any) {
|
|
121
|
+
await this.connect()
|
|
122
|
+
this.ws?.send(JSON.stringify(data))
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
isConnected(): boolean {
|
|
126
|
+
return this.ws?.readyState === WebSocket.OPEN && this.authenticated
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
disconnect() {
|
|
130
|
+
if (this.reconnectTimer) clearTimeout(this.reconnectTimer)
|
|
131
|
+
this.ws?.close()
|
|
132
|
+
this.ws = null
|
|
133
|
+
this.authenticated = false
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Singleton
|
|
138
|
+
export const protoSocket = new ProtoSocket()
|
|
139
|
+
|
|
140
|
+
/** @deprecated Use protoSocket instead. */
|
|
141
|
+
export const hermesSocket = protoSocket
|
|
142
|
+
|
|
143
|
+
// Chat request params
|
|
144
|
+
interface ChatParams {
|
|
145
|
+
company_id: string
|
|
146
|
+
user_id: string
|
|
147
|
+
message: string
|
|
148
|
+
session_key?: string
|
|
149
|
+
enabled_skills?: string[]
|
|
150
|
+
company_context?: string
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Send a chat message via WebSocket. Events come through protoSocket.onMessage().
|
|
155
|
+
*/
|
|
156
|
+
export async function sendChatWs(params: ChatParams) {
|
|
157
|
+
await protoSocket.send({ type: 'chat', channel: 'web', ...params })
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Reset the Claude CLI session for a company so the next message starts
|
|
162
|
+
* a fresh conversation (no --resume history).
|
|
163
|
+
*/
|
|
164
|
+
export async function resetSession(companyId: string, sessionKey = 'web') {
|
|
165
|
+
try {
|
|
166
|
+
await fetch(`${GATEWAY_URL}/reset`, {
|
|
167
|
+
method: 'POST',
|
|
168
|
+
headers: { 'Content-Type': 'application/json', 'X-Internal-Secret': SECRET },
|
|
169
|
+
body: JSON.stringify({ company_id: companyId, session_key: sessionKey }),
|
|
170
|
+
})
|
|
171
|
+
} catch {}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Legacy: Send chat via REST (blocking).
|
|
176
|
+
*/
|
|
177
|
+
export async function sendChat(params: ChatParams) {
|
|
178
|
+
const res = await fetch(`${GATEWAY_URL}/chat`, {
|
|
179
|
+
method: 'POST',
|
|
180
|
+
headers: { 'Content-Type': 'application/json', 'X-Internal-Secret': SECRET },
|
|
181
|
+
body: JSON.stringify({ channel: 'web', ...params }),
|
|
182
|
+
})
|
|
183
|
+
if (!res.ok) throw new Error(`Error ${res.status}`)
|
|
184
|
+
return res.json()
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Legacy: SSE stream (kept for fallback).
|
|
189
|
+
*/
|
|
190
|
+
export function streamChat(
|
|
191
|
+
params: ChatParams,
|
|
192
|
+
onEvent: (event: StreamEvent) => void,
|
|
193
|
+
): AbortController {
|
|
194
|
+
const controller = new AbortController()
|
|
195
|
+
|
|
196
|
+
fetch(`${GATEWAY_URL}/chat/stream`, {
|
|
197
|
+
method: 'POST',
|
|
198
|
+
headers: { 'Content-Type': 'application/json', 'X-Internal-Secret': SECRET },
|
|
199
|
+
body: JSON.stringify({ channel: 'web', ...params }),
|
|
200
|
+
signal: controller.signal,
|
|
201
|
+
}).then(async (res) => {
|
|
202
|
+
if (!res.ok) { onEvent({ type: 'error', message: `Error ${res.status}` }); return }
|
|
203
|
+
const reader = res.body?.getReader()
|
|
204
|
+
if (!reader) return
|
|
205
|
+
const decoder = new TextDecoder()
|
|
206
|
+
let buffer = ''
|
|
207
|
+
let gotResult = false
|
|
208
|
+
|
|
209
|
+
while (true) {
|
|
210
|
+
const { done, value } = await reader.read()
|
|
211
|
+
if (done) break
|
|
212
|
+
buffer += decoder.decode(value, { stream: true })
|
|
213
|
+
const lines = buffer.split('\n')
|
|
214
|
+
buffer = lines.pop() || ''
|
|
215
|
+
for (const line of lines) {
|
|
216
|
+
if (line.startsWith('data: ')) {
|
|
217
|
+
try {
|
|
218
|
+
const event = JSON.parse(line.slice(6))
|
|
219
|
+
if (event.type === 'result' || event.type === 'error') gotResult = true
|
|
220
|
+
onEvent(event)
|
|
221
|
+
} catch {}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (!gotResult) onEvent({ type: 'result', text: '' })
|
|
226
|
+
}).catch((err) => {
|
|
227
|
+
if (err.name !== 'AbortError') onEvent({ type: 'error', message: err.message })
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
return controller
|
|
231
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized frontend config. Every module that needs the gateway URL or
|
|
3
|
+
* the internal secret should import from here — do NOT read
|
|
4
|
+
* `import.meta.env.VITE_*` directly, so the default values stay in sync.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export const GATEWAY_URL: string =
|
|
8
|
+
(import.meta.env.VITE_GATEWAY_URL as string) || 'http://localhost:8092'
|
|
9
|
+
|
|
10
|
+
export const INTERNAL_SECRET: string =
|
|
11
|
+
(import.meta.env.VITE_INTERNAL_SECRET as string) || ''
|
|
12
|
+
|
|
13
|
+
/** WebSocket endpoint, derived from GATEWAY_URL. */
|
|
14
|
+
export const WS_URL: string = GATEWAY_URL.replace(/^http/, 'ws') + '/ws'
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* defineWidget — declarative shell widget definition.
|
|
3
|
+
*
|
|
4
|
+
* App code declares widgets as data instead of hardcoding a switch in Shell.
|
|
5
|
+
* Each widget provides a `render(instance, ctx)` function that returns the
|
|
6
|
+
* React node, pulling whatever it needs from the shared ShellContext.
|
|
7
|
+
*
|
|
8
|
+
* The `ShellContext` interface below contains the framework-level fields every
|
|
9
|
+
* widget receives. Apps extend it via TypeScript module augmentation to add
|
|
10
|
+
* their own fields (cart state, app-specific callbacks, etc).
|
|
11
|
+
*
|
|
12
|
+
* Example augmentation in an app:
|
|
13
|
+
*
|
|
14
|
+
* declare module 'proto/web' {
|
|
15
|
+
* interface ShellContext {
|
|
16
|
+
* cartItems: CartItem[]
|
|
17
|
+
* addToCart: (item: CartItem) => void
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
*/
|
|
21
|
+
import type { ReactNode } from 'react'
|
|
22
|
+
import type { ActiveEntity, WidgetInstance } from '../components/shell/types'
|
|
23
|
+
|
|
24
|
+
export interface ShellContext {
|
|
25
|
+
companyId: string
|
|
26
|
+
refreshKey: number
|
|
27
|
+
activeEntity: ActiveEntity | null
|
|
28
|
+
|
|
29
|
+
onSendToChat: (message: string) => void
|
|
30
|
+
onActivateEntity?: (e: ActiveEntity) => void
|
|
31
|
+
onDeactivateEntity?: () => void
|
|
32
|
+
onCloseTab?: (e: ActiveEntity) => void
|
|
33
|
+
|
|
34
|
+
/** Force widgets to re-fetch without a full shell reload. */
|
|
35
|
+
triggerLocalRefresh: () => void
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type WidgetCategory = 'general' | 'cockpit'
|
|
39
|
+
|
|
40
|
+
export interface WidgetSize {
|
|
41
|
+
w: number
|
|
42
|
+
h: number
|
|
43
|
+
minW: number
|
|
44
|
+
minH: number
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface WidgetDefinition {
|
|
48
|
+
type: string
|
|
49
|
+
title: string
|
|
50
|
+
icon?: string
|
|
51
|
+
category: WidgetCategory
|
|
52
|
+
defaultSize?: WidgetSize
|
|
53
|
+
render: (instance: WidgetInstance, ctx: ShellContext) => ReactNode
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function defineWidget(def: WidgetDefinition): WidgetDefinition {
|
|
57
|
+
return def
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type WidgetRegistry = Map<string, WidgetDefinition>
|
|
61
|
+
|
|
62
|
+
export function buildWidgetRegistry(defs: readonly WidgetDefinition[]): WidgetRegistry {
|
|
63
|
+
const map: WidgetRegistry = new Map()
|
|
64
|
+
for (const def of defs) {
|
|
65
|
+
if (map.has(def.type)) {
|
|
66
|
+
console.warn(`[defineWidget] duplicate widget type: ${def.type}`)
|
|
67
|
+
}
|
|
68
|
+
map.set(def.type, def)
|
|
69
|
+
}
|
|
70
|
+
return map
|
|
71
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export interface DragContext {
|
|
2
|
+
type: string
|
|
3
|
+
id: string
|
|
4
|
+
label: string
|
|
5
|
+
meta?: string
|
|
6
|
+
agentContext: {
|
|
7
|
+
entity: string
|
|
8
|
+
id: string
|
|
9
|
+
summary: Record<string, any>
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function setDragData(e: React.DragEvent, ctx: DragContext) {
|
|
14
|
+
e.dataTransfer.setData('application/proto', JSON.stringify(ctx))
|
|
15
|
+
e.dataTransfer.effectAllowed = 'copy'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getDragData(e: React.DragEvent): DragContext | null {
|
|
19
|
+
try {
|
|
20
|
+
const raw = e.dataTransfer.getData('application/proto')
|
|
21
|
+
return raw ? JSON.parse(raw) : null
|
|
22
|
+
} catch { return null }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function hasDragData(e: React.DragEvent): boolean {
|
|
26
|
+
return e.dataTransfer.types.includes('application/proto')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Build structured context for the agent.
|
|
31
|
+
* Injected as context prefix, not as user message text.
|
|
32
|
+
*/
|
|
33
|
+
export function buildAgentPrompt(ctx: DragContext): string {
|
|
34
|
+
const { entity, id, summary } = ctx.agentContext
|
|
35
|
+
const lines = Object.entries(summary)
|
|
36
|
+
.filter(([_, v]) => v != null && v !== '')
|
|
37
|
+
.map(([k, v]) => ` ${k}: ${typeof v === 'object' ? JSON.stringify(v) : v}`)
|
|
38
|
+
|
|
39
|
+
return [
|
|
40
|
+
`[CONTEXTO: El usuario selecciono un ${entity}]`,
|
|
41
|
+
`ID: ${id}`,
|
|
42
|
+
...lines,
|
|
43
|
+
`[Consulta datos actualizados con los tools MCP si necesitas mas detalle. No repitas info que ya tienes.]`,
|
|
44
|
+
].join('\n')
|
|
45
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tiny in-memory cache for cockpit widget data.
|
|
3
|
+
*
|
|
4
|
+
* Keyed by `${widgetName}:${entityId}`. Lives for the page session.
|
|
5
|
+
*
|
|
6
|
+
* Usage pattern in widgets:
|
|
7
|
+
* const [data, setData] = useState(() => cacheGet(key))
|
|
8
|
+
* useEffect(() => {
|
|
9
|
+
* fetch().then(d => { cacheSet(key, d); setData(d) })
|
|
10
|
+
* }, [id, refreshKey])
|
|
11
|
+
*
|
|
12
|
+
* If cached, the widget renders instantly with stale data, then silently
|
|
13
|
+
* swaps to fresh data when the fetch resolves. No skeleton on re-visits.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const store = new Map<string, unknown>()
|
|
17
|
+
|
|
18
|
+
export function cacheGet<T>(key: string): T | undefined {
|
|
19
|
+
return store.get(key) as T | undefined
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function cacheSet<T>(key: string, value: T): void {
|
|
23
|
+
store.set(key, value)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function cacheClear(key?: string): void {
|
|
27
|
+
if (key) store.delete(key)
|
|
28
|
+
else store.clear()
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type ToolDefinition } from './define-tool.js';
|
|
2
|
+
import type { EntityDefinition } from '../../core-shared/src/index.js';
|
|
3
|
+
import type { WorkflowDefinition } from '../../core-shared/src/index.js';
|
|
4
|
+
export interface ProtoMcpOptions {
|
|
5
|
+
/** App name (used in MCP server metadata). */
|
|
6
|
+
name: string;
|
|
7
|
+
version?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Directory containing tool files. Each .ts/.js file should export
|
|
10
|
+
* default an array of ToolDefinition objects.
|
|
11
|
+
* Defaults to ./tools (relative to the calling file).
|
|
12
|
+
*/
|
|
13
|
+
toolsDir?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Path to entities barrel (e.g. ./entities/index.ts).
|
|
16
|
+
* Should export `ENTITIES` as an array of EntityDefinition.
|
|
17
|
+
* If omitted, auto-detected from ./entities/index.
|
|
18
|
+
*/
|
|
19
|
+
entitiesPath?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Path to workflow definition file, or an already-imported WorkflowDefinition.
|
|
22
|
+
* If omitted, auto-detected from ./workflows/*.
|
|
23
|
+
*/
|
|
24
|
+
workflow?: string | WorkflowDefinition;
|
|
25
|
+
/** Extra tools to register alongside auto-discovered ones. */
|
|
26
|
+
extraTools?: ToolDefinition[];
|
|
27
|
+
/** Extra entities to register alongside auto-discovered ones. */
|
|
28
|
+
extraEntities?: EntityDefinition[];
|
|
29
|
+
}
|
|
30
|
+
export declare function createProtoMcp(opts: ProtoMcpOptions): Promise<{
|
|
31
|
+
/** Run as stdio subprocess (for Claude Code CLI). */
|
|
32
|
+
stdio(): Promise<void>;
|
|
33
|
+
/** Run as HTTP server (for Docker / standalone). */
|
|
34
|
+
http(httpOpts?: {
|
|
35
|
+
port?: number;
|
|
36
|
+
}): Promise<void>;
|
|
37
|
+
/** Get tool count (for smoke tests). */
|
|
38
|
+
readonly toolCount: number;
|
|
39
|
+
}>;
|
|
40
|
+
//# sourceMappingURL=app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../../core-mcp/src/app.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAKrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AACtE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AAExE,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAEhB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IAErB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAAA;IAEtC,8DAA8D;IAC9D,UAAU,CAAC,EAAE,cAAc,EAAE,CAAA;IAE7B,iEAAiE;IACjE,aAAa,CAAC,EAAE,gBAAgB,EAAE,CAAA;CACnC;AAwED,wBAAsB,cAAc,CAAC,IAAI,EAAE,eAAe;IAyCtD,qDAAqD;;IAMrD,oDAAoD;oBAC9B;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE;IASvC,wCAAwC;;GAM3C"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createProtoMcp — single-call factory that builds a fully-wired MCP app.
|
|
3
|
+
*
|
|
4
|
+
* Auto-discovers tools, entities, and workflows from the app directory.
|
|
5
|
+
* The developer only writes tool files (export default [...]) and optionally
|
|
6
|
+
* entities/index + workflows — everything else is handled by the framework.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
*
|
|
10
|
+
* // app/mcp.ts (stdio)
|
|
11
|
+
* import { createProtoMcp } from 'proto/mcp'
|
|
12
|
+
* const app = await createProtoMcp({ name: 'my-app' })
|
|
13
|
+
* await app.stdio()
|
|
14
|
+
*
|
|
15
|
+
* // app/mcp-http.ts (HTTP)
|
|
16
|
+
* import { createProtoMcp } from 'proto/mcp'
|
|
17
|
+
* const app = await createProtoMcp({ name: 'my-app' })
|
|
18
|
+
* await app.http({ port: 8093 })
|
|
19
|
+
*/
|
|
20
|
+
import { readdirSync, existsSync } from 'node:fs';
|
|
21
|
+
import { resolve, dirname, join } from 'node:path';
|
|
22
|
+
import { pathToFileURL } from 'node:url';
|
|
23
|
+
import { createMcpServer, runStdio, runHttp } from './index.js';
|
|
24
|
+
import { registerTools } from './define-tool.js';
|
|
25
|
+
import { registerUiTools } from './tools/ui.js';
|
|
26
|
+
import { registerEntityTools } from './entity-tools.js';
|
|
27
|
+
import { registerWorkflowTools } from './workflow-tools.js';
|
|
28
|
+
async function loadToolsFromDir(dir) {
|
|
29
|
+
if (!existsSync(dir))
|
|
30
|
+
return [];
|
|
31
|
+
const files = readdirSync(dir).filter(f => (f.endsWith('.ts') || f.endsWith('.js')) && f !== 'index.ts' && f !== 'index.js');
|
|
32
|
+
const allTools = [];
|
|
33
|
+
for (const file of files) {
|
|
34
|
+
const fullPath = join(dir, file);
|
|
35
|
+
const mod = await import(pathToFileURL(fullPath).href);
|
|
36
|
+
const tools = mod.default;
|
|
37
|
+
if (Array.isArray(tools)) {
|
|
38
|
+
allTools.push(...tools);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return allTools;
|
|
42
|
+
}
|
|
43
|
+
async function loadEntities(dirOrBarrel) {
|
|
44
|
+
// Try barrel file first (index.ts/index.js)
|
|
45
|
+
for (const ext of ['.ts', '.js']) {
|
|
46
|
+
const p = dirOrBarrel + ext;
|
|
47
|
+
if (existsSync(p)) {
|
|
48
|
+
const mod = await import(pathToFileURL(p).href);
|
|
49
|
+
return mod.ENTITIES || mod.default || [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Auto-discover individual entity files from the directory
|
|
53
|
+
const dir = dirname(dirOrBarrel);
|
|
54
|
+
if (!existsSync(dir))
|
|
55
|
+
return [];
|
|
56
|
+
const files = readdirSync(dir).filter(f => (f.endsWith('.ts') || f.endsWith('.js')) && f !== 'index.ts' && f !== 'index.js');
|
|
57
|
+
const entities = [];
|
|
58
|
+
for (const file of files) {
|
|
59
|
+
const mod = await import(pathToFileURL(join(dir, file)).href);
|
|
60
|
+
if (mod.default && typeof mod.default === 'object' && 'name' in mod.default) {
|
|
61
|
+
entities.push(mod.default);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return entities;
|
|
65
|
+
}
|
|
66
|
+
async function loadWorkflow(basePath) {
|
|
67
|
+
if (!existsSync(basePath))
|
|
68
|
+
return null;
|
|
69
|
+
const files = readdirSync(basePath).filter(f => (f.endsWith('.ts') || f.endsWith('.js')) && !f.startsWith('_'));
|
|
70
|
+
if (files.length === 0)
|
|
71
|
+
return null;
|
|
72
|
+
// Load the first workflow file found
|
|
73
|
+
const mod = await import(pathToFileURL(join(basePath, files[0])).href);
|
|
74
|
+
return mod.default || null;
|
|
75
|
+
}
|
|
76
|
+
function resolveAppDir(callerUrl) {
|
|
77
|
+
if (callerUrl) {
|
|
78
|
+
return dirname(callerUrl.replace('file://', ''));
|
|
79
|
+
}
|
|
80
|
+
return process.cwd();
|
|
81
|
+
}
|
|
82
|
+
export async function createProtoMcp(opts) {
|
|
83
|
+
// Resolve paths relative to cwd (app root)
|
|
84
|
+
const appDir = process.cwd();
|
|
85
|
+
const toolsDir = opts.toolsDir
|
|
86
|
+
? resolve(appDir, opts.toolsDir)
|
|
87
|
+
: resolve(appDir, 'app', 'tools');
|
|
88
|
+
const entitiesPath = opts.entitiesPath
|
|
89
|
+
? resolve(appDir, opts.entitiesPath)
|
|
90
|
+
: resolve(appDir, 'app', 'entities', 'index');
|
|
91
|
+
const workflowsDir = resolve(appDir, 'app', 'workflows');
|
|
92
|
+
// Load everything
|
|
93
|
+
const tools = await loadToolsFromDir(toolsDir);
|
|
94
|
+
const entities = await loadEntities(entitiesPath);
|
|
95
|
+
const extraTools = opts.extraTools || [];
|
|
96
|
+
const extraEntities = opts.extraEntities || [];
|
|
97
|
+
const allTools = [...tools, ...extraTools];
|
|
98
|
+
const allEntities = [...entities, ...extraEntities];
|
|
99
|
+
let workflow = null;
|
|
100
|
+
if (opts.workflow) {
|
|
101
|
+
workflow = typeof opts.workflow === 'string'
|
|
102
|
+
? await loadWorkflow(resolve(appDir, opts.workflow)).then(w => w)
|
|
103
|
+
: opts.workflow;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
workflow = await loadWorkflow(workflowsDir);
|
|
107
|
+
}
|
|
108
|
+
function buildServer() {
|
|
109
|
+
const server = createMcpServer({ name: opts.name, version: opts.version });
|
|
110
|
+
registerUiTools(server);
|
|
111
|
+
if (allTools.length > 0)
|
|
112
|
+
registerTools(server, allTools);
|
|
113
|
+
if (allEntities.length > 0)
|
|
114
|
+
registerEntityTools(server, allEntities);
|
|
115
|
+
if (workflow)
|
|
116
|
+
registerWorkflowTools(server, workflow);
|
|
117
|
+
return server;
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
/** Run as stdio subprocess (for Claude Code CLI). */
|
|
121
|
+
async stdio() {
|
|
122
|
+
const server = buildServer();
|
|
123
|
+
await runStdio(server);
|
|
124
|
+
},
|
|
125
|
+
/** Run as HTTP server (for Docker / standalone). */
|
|
126
|
+
async http(httpOpts) {
|
|
127
|
+
const port = httpOpts?.port || parseInt(process.env.MCP_PORT || '8093', 10);
|
|
128
|
+
await runHttp({
|
|
129
|
+
port,
|
|
130
|
+
displayName: opts.name,
|
|
131
|
+
buildServer,
|
|
132
|
+
});
|
|
133
|
+
},
|
|
134
|
+
/** Get tool count (for smoke tests). */
|
|
135
|
+
get toolCount() {
|
|
136
|
+
const server = buildServer();
|
|
137
|
+
return Object.keys(server._registeredTools).length;
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../../core-mcp/src/app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACjD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAuB,MAAM,kBAAkB,CAAA;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAqC3D,KAAK,UAAU,gBAAgB,CAAC,GAAW;IACzC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAA;IAE/B,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CACnC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,UAAU,IAAI,CAAC,KAAK,UAAU,CACtF,CAAA;IAED,MAAM,QAAQ,GAAqB,EAAE,CAAA;IAErC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;QAChC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAA;QACtD,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAA;QACzB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAA;QACzB,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,WAAmB;IAC7C,4CAA4C;IAC5C,KAAK,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC;QACjC,MAAM,CAAC,GAAG,WAAW,GAAG,GAAG,CAAA;QAC3B,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;YAClB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;YAC/C,OAAO,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,OAAO,IAAI,EAAE,CAAA;QAC1C,CAAC;IACH,CAAC;IAED,2DAA2D;IAC3D,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IAChC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAA;IAE/B,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CACnC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,UAAU,IAAI,CAAC,KAAK,UAAU,CACtF,CAAA;IAED,MAAM,QAAQ,GAAuB,EAAE,CAAA;IACvC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;QAC7D,IAAI,GAAG,CAAC,OAAO,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,IAAI,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;YAC5E,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;QAC5B,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,QAAgB;IAC1C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAA;IAEtC,MAAM,KAAK,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,MAAM,CACxC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CACpE,CAAA;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAEnC,qCAAqC;IACrC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;IACtE,OAAO,GAAG,CAAC,OAAO,IAAI,IAAI,CAAA;AAC5B,CAAC;AAED,SAAS,aAAa,CAAC,SAAkB;IACvC,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAA;IAClD,CAAC;IACD,OAAO,OAAO,CAAC,GAAG,EAAE,CAAA;AACtB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAqB;IACxD,2CAA2C;IAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAA;IAE5B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ;QAC5B,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC;QAChC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAA;IAEnC,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY;QACpC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC;QACpC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,CAAA;IAE/C,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,CAAA;IAExD,kBAAkB;IAClB,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC,CAAA;IAC9C,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,YAAY,CAAC,CAAA;IACjD,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,EAAE,CAAA;IACxC,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,EAAE,CAAA;IAC9C,MAAM,QAAQ,GAAG,CAAC,GAAG,KAAK,EAAE,GAAG,UAAU,CAAC,CAAA;IAC1C,MAAM,WAAW,GAAG,CAAC,GAAG,QAAQ,EAAE,GAAG,aAAa,CAAC,CAAA;IAEnD,IAAI,QAAQ,GAA8B,IAAI,CAAA;IAC9C,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,QAAQ,GAAG,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ;YAC1C,CAAC,CAAC,MAAM,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACjE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAA;IACnB,CAAC;SAAM,CAAC;QACN,QAAQ,GAAG,MAAM,YAAY,CAAC,YAAY,CAAC,CAAA;IAC7C,CAAC;IAED,SAAS,WAAW;QAClB,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAA;QAC1E,eAAe,CAAC,MAAM,CAAC,CAAA;QACvB,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;YAAE,aAAa,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;QACxD,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC;YAAE,mBAAmB,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;QACpE,IAAI,QAAQ;YAAE,qBAAqB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;QACrD,OAAO,MAAM,CAAA;IACf,CAAC;IAED,OAAO;QACL,qDAAqD;QACrD,KAAK,CAAC,KAAK;YACT,MAAM,MAAM,GAAG,WAAW,EAAE,CAAA;YAC5B,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAA;QACxB,CAAC;QAED,oDAAoD;QACpD,KAAK,CAAC,IAAI,CAAC,QAA4B;YACrC,MAAM,IAAI,GAAG,QAAQ,EAAE,IAAI,IAAI,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,MAAM,EAAE,EAAE,CAAC,CAAA;YAC3E,MAAM,OAAO,CAAC;gBACZ,IAAI;gBACJ,WAAW,EAAE,IAAI,CAAC,IAAI;gBACtB,WAAW;aACZ,CAAC,CAAA;QACJ,CAAC;QAED,wCAAwC;QACxC,IAAI,SAAS;YACX,MAAM,MAAM,GAAG,WAAW,EAAE,CAAA;YAC5B,OAAO,MAAM,CAAC,IAAI,CAAE,MAAc,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAA;QAC7D,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* defineTool — declarative MCP tool definition.
|
|
3
|
+
*
|
|
4
|
+
* App code declares tools as data instead of calling server.tool() directly.
|
|
5
|
+
* The framework later registers them via registerTools(), giving us a
|
|
6
|
+
* single place to wrap handlers with context injection, error normalization,
|
|
7
|
+
* logging, metrics, etc.
|
|
8
|
+
*
|
|
9
|
+
* Example (app-space):
|
|
10
|
+
*
|
|
11
|
+
* import { defineTool } from 'proto/mcp'
|
|
12
|
+
* import { z } from 'zod'
|
|
13
|
+
*
|
|
14
|
+
* export default [
|
|
15
|
+
* defineTool({
|
|
16
|
+
* name: 'create_thing',
|
|
17
|
+
* description: 'Creates a thing.',
|
|
18
|
+
* schema: { name: z.string() },
|
|
19
|
+
* handler: async (args) => {
|
|
20
|
+
* // ... do work ...
|
|
21
|
+
* return { content: [{ type: 'text', text: 'ok' }] }
|
|
22
|
+
* },
|
|
23
|
+
* }),
|
|
24
|
+
* ]
|
|
25
|
+
*/
|
|
26
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
27
|
+
import type { z } from 'zod';
|
|
28
|
+
/**
|
|
29
|
+
* Minimal MCP tool response shape. Tools that need structured agent responses
|
|
30
|
+
* should use the `agent()` / `agentErr()` helpers from @proto/core-mcp and
|
|
31
|
+
* return their output. Index signature matches the MCP SDK's tool callback
|
|
32
|
+
* return type (allows fields like `_meta`, `isError`, etc.).
|
|
33
|
+
*/
|
|
34
|
+
export interface ToolResult {
|
|
35
|
+
content: Array<{
|
|
36
|
+
type: 'text';
|
|
37
|
+
text: string;
|
|
38
|
+
}>;
|
|
39
|
+
[key: string]: unknown;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Handler signature. Receives validated args (inferred from the Zod schema)
|
|
43
|
+
* and must return an MCP-shaped result. Throwing is allowed — the framework
|
|
44
|
+
* wraps the handler in try/catch and returns a normalized error response.
|
|
45
|
+
*/
|
|
46
|
+
export type ToolHandler<S extends z.ZodRawShape> = (args: z.infer<z.ZodObject<S>>) => Promise<ToolResult> | ToolResult;
|
|
47
|
+
/**
|
|
48
|
+
* Declarative tool definition. `schema` is a Zod raw shape (the same object
|
|
49
|
+
* you'd pass as the third argument to `server.tool()` today).
|
|
50
|
+
*/
|
|
51
|
+
export interface ToolDefinition<S extends z.ZodRawShape = z.ZodRawShape> {
|
|
52
|
+
name: string;
|
|
53
|
+
description: string;
|
|
54
|
+
schema: S;
|
|
55
|
+
handler: ToolHandler<S>;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Identity helper with type inference. Pass a definition object, get it back
|
|
59
|
+
* unchanged. Use in default exports of app/tools files:
|
|
60
|
+
*
|
|
61
|
+
* export default [defineTool({ ... }), defineTool({ ... })]
|
|
62
|
+
*/
|
|
63
|
+
export declare function defineTool<S extends z.ZodRawShape>(def: ToolDefinition<S>): ToolDefinition<S>;
|
|
64
|
+
/**
|
|
65
|
+
* Register an array of tool definitions on a server instance. Wraps each
|
|
66
|
+
* handler in a try/catch that emits a normalized error response if the tool
|
|
67
|
+
* throws.
|
|
68
|
+
*/
|
|
69
|
+
export declare function registerTools(server: McpServer, defs: readonly ToolDefinition[]): void;
|
|
70
|
+
//# sourceMappingURL=define-tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-tool.d.ts","sourceRoot":"","sources":["../../../core-mcp/src/define-tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAE5B;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC9C,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED;;;;GAIG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,CAAC,CAAC,WAAW,IAAI,CACjD,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAC1B,OAAO,CAAC,UAAU,CAAC,GAAG,UAAU,CAAA;AAErC;;;GAGG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW;IACrE,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,CAAC,CAAA;IACT,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,CAAC,CAAC,WAAW,EAChD,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,GACrB,cAAc,CAAC,CAAC,CAAC,CAEnB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,SAAS,cAAc,EAAE,GAC9B,IAAI,CA0BN"}
|