convex-mcp-visual 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.
- package/README.md +844 -0
- package/dist/apps/apps/realtime-dashboard/index.html +15 -0
- package/dist/apps/apps/schema-browser/index.html +15 -0
- package/dist/apps/assets/modulepreload-polyfill-B5Qt9EMX.js +1 -0
- package/dist/apps/assets/realtime-dashboard-BPA99DZn.js +140 -0
- package/dist/apps/assets/schema-browser-BEcF8hRP.js +499 -0
- package/dist/apps/assets/style-BTxSpbLq.css +1 -0
- package/dist/convex-client.d.ts +56 -0
- package/dist/convex-client.d.ts.map +1 -0
- package/dist/convex-client.js +206 -0
- package/dist/convex-client.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +93 -0
- package/dist/index.js.map +1 -0
- package/dist/resources/dashboard.d.ts +7 -0
- package/dist/resources/dashboard.d.ts.map +1 -0
- package/dist/resources/dashboard.js +255 -0
- package/dist/resources/dashboard.js.map +1 -0
- package/dist/resources/schema-browser.d.ts +7 -0
- package/dist/resources/schema-browser.d.ts.map +1 -0
- package/dist/resources/schema-browser.js +654 -0
- package/dist/resources/schema-browser.js.map +1 -0
- package/dist/server.d.ts +12 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +142 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/dashboard.d.ts +19 -0
- package/dist/tools/dashboard.d.ts.map +1 -0
- package/dist/tools/dashboard.js +309 -0
- package/dist/tools/dashboard.js.map +1 -0
- package/dist/tools/schema-browser.d.ts +19 -0
- package/dist/tools/schema-browser.d.ts.map +1 -0
- package/dist/tools/schema-browser.js +260 -0
- package/dist/tools/schema-browser.js.map +1 -0
- package/dist/ui-server.d.ts +26 -0
- package/dist/ui-server.d.ts.map +1 -0
- package/dist/ui-server.js +202 -0
- package/dist/ui-server.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,654 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema Browser Resource Handler
|
|
3
|
+
*
|
|
4
|
+
* Serves the bundled Schema Browser HTML UI.
|
|
5
|
+
*/
|
|
6
|
+
import { readFileSync, existsSync } from 'fs';
|
|
7
|
+
import { join, dirname } from 'path';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
export async function getSchemaResourceContent() {
|
|
11
|
+
// Try to load the bundled HTML file
|
|
12
|
+
const bundledPath = join(__dirname, '..', 'apps', 'schema-browser.html');
|
|
13
|
+
if (existsSync(bundledPath)) {
|
|
14
|
+
return readFileSync(bundledPath, 'utf-8');
|
|
15
|
+
}
|
|
16
|
+
// Fallback: return inline HTML for development/testing
|
|
17
|
+
return getInlineSchemaUI();
|
|
18
|
+
}
|
|
19
|
+
function getInlineSchemaUI() {
|
|
20
|
+
return `<!DOCTYPE html>
|
|
21
|
+
<html lang="en">
|
|
22
|
+
<head>
|
|
23
|
+
<meta charset="UTF-8">
|
|
24
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
25
|
+
<title>Schema Browser - Convex MCP Apps</title>
|
|
26
|
+
<style>
|
|
27
|
+
* {
|
|
28
|
+
box-sizing: border-box;
|
|
29
|
+
margin: 0;
|
|
30
|
+
padding: 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
:root {
|
|
34
|
+
--bg-primary: #1a1a2e;
|
|
35
|
+
--bg-secondary: #16213e;
|
|
36
|
+
--bg-tertiary: #0f3460;
|
|
37
|
+
--text-primary: #eee;
|
|
38
|
+
--text-secondary: #aaa;
|
|
39
|
+
--accent: #e94560;
|
|
40
|
+
--accent-hover: #ff6b6b;
|
|
41
|
+
--border: #333;
|
|
42
|
+
--success: #4caf50;
|
|
43
|
+
--warning: #ff9800;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
body {
|
|
47
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
|
48
|
+
background: var(--bg-primary);
|
|
49
|
+
color: var(--text-primary);
|
|
50
|
+
min-height: 100vh;
|
|
51
|
+
display: flex;
|
|
52
|
+
flex-direction: column;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.header {
|
|
56
|
+
background: var(--bg-secondary);
|
|
57
|
+
padding: 12px 20px;
|
|
58
|
+
border-bottom: 1px solid var(--border);
|
|
59
|
+
display: flex;
|
|
60
|
+
justify-content: space-between;
|
|
61
|
+
align-items: center;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.header h1 {
|
|
65
|
+
font-size: 18px;
|
|
66
|
+
font-weight: 600;
|
|
67
|
+
display: flex;
|
|
68
|
+
align-items: center;
|
|
69
|
+
gap: 8px;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.header-actions {
|
|
73
|
+
display: flex;
|
|
74
|
+
gap: 8px;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.btn {
|
|
78
|
+
background: var(--bg-tertiary);
|
|
79
|
+
color: var(--text-primary);
|
|
80
|
+
border: 1px solid var(--border);
|
|
81
|
+
padding: 6px 12px;
|
|
82
|
+
border-radius: 4px;
|
|
83
|
+
cursor: pointer;
|
|
84
|
+
font-size: 13px;
|
|
85
|
+
transition: all 0.2s;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.btn:hover {
|
|
89
|
+
background: var(--accent);
|
|
90
|
+
border-color: var(--accent);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.main {
|
|
94
|
+
display: flex;
|
|
95
|
+
flex: 1;
|
|
96
|
+
overflow: hidden;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.sidebar {
|
|
100
|
+
width: 240px;
|
|
101
|
+
background: var(--bg-secondary);
|
|
102
|
+
border-right: 1px solid var(--border);
|
|
103
|
+
overflow-y: auto;
|
|
104
|
+
flex-shrink: 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.sidebar-header {
|
|
108
|
+
padding: 12px 16px;
|
|
109
|
+
font-size: 12px;
|
|
110
|
+
text-transform: uppercase;
|
|
111
|
+
color: var(--text-secondary);
|
|
112
|
+
border-bottom: 1px solid var(--border);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.table-list {
|
|
116
|
+
list-style: none;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.table-item {
|
|
120
|
+
padding: 10px 16px;
|
|
121
|
+
cursor: pointer;
|
|
122
|
+
display: flex;
|
|
123
|
+
justify-content: space-between;
|
|
124
|
+
align-items: center;
|
|
125
|
+
border-bottom: 1px solid var(--border);
|
|
126
|
+
transition: background 0.2s;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.table-item:hover {
|
|
130
|
+
background: var(--bg-tertiary);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.table-item.active {
|
|
134
|
+
background: var(--accent);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.table-name {
|
|
138
|
+
font-size: 14px;
|
|
139
|
+
font-weight: 500;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.table-count {
|
|
143
|
+
font-size: 12px;
|
|
144
|
+
color: var(--text-secondary);
|
|
145
|
+
background: var(--bg-primary);
|
|
146
|
+
padding: 2px 8px;
|
|
147
|
+
border-radius: 10px;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.content {
|
|
151
|
+
flex: 1;
|
|
152
|
+
display: flex;
|
|
153
|
+
flex-direction: column;
|
|
154
|
+
overflow: hidden;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.schema-panel {
|
|
158
|
+
padding: 20px;
|
|
159
|
+
flex: 1;
|
|
160
|
+
overflow-y: auto;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.schema-title {
|
|
164
|
+
font-size: 20px;
|
|
165
|
+
margin-bottom: 16px;
|
|
166
|
+
display: flex;
|
|
167
|
+
align-items: center;
|
|
168
|
+
gap: 12px;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.schema-grid {
|
|
172
|
+
display: grid;
|
|
173
|
+
grid-template-columns: 1fr 1fr;
|
|
174
|
+
gap: 20px;
|
|
175
|
+
margin-bottom: 20px;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.schema-card {
|
|
179
|
+
background: var(--bg-secondary);
|
|
180
|
+
border: 1px solid var(--border);
|
|
181
|
+
border-radius: 8px;
|
|
182
|
+
overflow: hidden;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.schema-card-header {
|
|
186
|
+
background: var(--bg-tertiary);
|
|
187
|
+
padding: 10px 16px;
|
|
188
|
+
font-weight: 600;
|
|
189
|
+
font-size: 14px;
|
|
190
|
+
border-bottom: 1px solid var(--border);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.schema-card-body {
|
|
194
|
+
padding: 12px;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.field-row {
|
|
198
|
+
display: flex;
|
|
199
|
+
justify-content: space-between;
|
|
200
|
+
padding: 6px 8px;
|
|
201
|
+
border-radius: 4px;
|
|
202
|
+
font-size: 13px;
|
|
203
|
+
font-family: 'SF Mono', Monaco, 'Courier New', monospace;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.field-row:hover {
|
|
207
|
+
background: var(--bg-tertiary);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.field-name {
|
|
211
|
+
color: var(--accent-hover);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.field-type {
|
|
215
|
+
color: var(--text-secondary);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.field-optional {
|
|
219
|
+
color: var(--warning);
|
|
220
|
+
font-size: 11px;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.documents-panel {
|
|
224
|
+
background: var(--bg-secondary);
|
|
225
|
+
border-top: 1px solid var(--border);
|
|
226
|
+
max-height: 300px;
|
|
227
|
+
overflow: hidden;
|
|
228
|
+
display: flex;
|
|
229
|
+
flex-direction: column;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.documents-header {
|
|
233
|
+
padding: 10px 16px;
|
|
234
|
+
display: flex;
|
|
235
|
+
justify-content: space-between;
|
|
236
|
+
align-items: center;
|
|
237
|
+
border-bottom: 1px solid var(--border);
|
|
238
|
+
background: var(--bg-tertiary);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.documents-title {
|
|
242
|
+
font-weight: 600;
|
|
243
|
+
font-size: 14px;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.pagination {
|
|
247
|
+
display: flex;
|
|
248
|
+
align-items: center;
|
|
249
|
+
gap: 8px;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.pagination button {
|
|
253
|
+
background: var(--bg-primary);
|
|
254
|
+
color: var(--text-primary);
|
|
255
|
+
border: 1px solid var(--border);
|
|
256
|
+
width: 28px;
|
|
257
|
+
height: 28px;
|
|
258
|
+
border-radius: 4px;
|
|
259
|
+
cursor: pointer;
|
|
260
|
+
display: flex;
|
|
261
|
+
align-items: center;
|
|
262
|
+
justify-content: center;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.pagination button:hover:not(:disabled) {
|
|
266
|
+
background: var(--accent);
|
|
267
|
+
border-color: var(--accent);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.pagination button:disabled {
|
|
271
|
+
opacity: 0.5;
|
|
272
|
+
cursor: not-allowed;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.documents-table {
|
|
276
|
+
overflow-x: auto;
|
|
277
|
+
flex: 1;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
table {
|
|
281
|
+
width: 100%;
|
|
282
|
+
border-collapse: collapse;
|
|
283
|
+
font-size: 13px;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
th, td {
|
|
287
|
+
padding: 8px 12px;
|
|
288
|
+
text-align: left;
|
|
289
|
+
border-bottom: 1px solid var(--border);
|
|
290
|
+
white-space: nowrap;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
th {
|
|
294
|
+
background: var(--bg-primary);
|
|
295
|
+
font-weight: 600;
|
|
296
|
+
position: sticky;
|
|
297
|
+
top: 0;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
td {
|
|
301
|
+
font-family: 'SF Mono', Monaco, 'Courier New', monospace;
|
|
302
|
+
font-size: 12px;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
tr:hover td {
|
|
306
|
+
background: var(--bg-tertiary);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.empty-state {
|
|
310
|
+
text-align: center;
|
|
311
|
+
padding: 60px 20px;
|
|
312
|
+
color: var(--text-secondary);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.empty-state h2 {
|
|
316
|
+
margin-bottom: 8px;
|
|
317
|
+
color: var(--text-primary);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.warning-badge {
|
|
321
|
+
background: var(--warning);
|
|
322
|
+
color: #000;
|
|
323
|
+
padding: 4px 8px;
|
|
324
|
+
border-radius: 4px;
|
|
325
|
+
font-size: 12px;
|
|
326
|
+
margin-top: 12px;
|
|
327
|
+
display: inline-block;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.loading {
|
|
331
|
+
display: flex;
|
|
332
|
+
align-items: center;
|
|
333
|
+
justify-content: center;
|
|
334
|
+
padding: 40px;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.spinner {
|
|
338
|
+
width: 30px;
|
|
339
|
+
height: 30px;
|
|
340
|
+
border: 3px solid var(--border);
|
|
341
|
+
border-top-color: var(--accent);
|
|
342
|
+
border-radius: 50%;
|
|
343
|
+
animation: spin 1s linear infinite;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
@keyframes spin {
|
|
347
|
+
to { transform: rotate(360deg); }
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.status-dot {
|
|
351
|
+
width: 8px;
|
|
352
|
+
height: 8px;
|
|
353
|
+
border-radius: 50%;
|
|
354
|
+
background: var(--success);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.status-dot.error {
|
|
358
|
+
background: var(--accent);
|
|
359
|
+
}
|
|
360
|
+
</style>
|
|
361
|
+
</head>
|
|
362
|
+
<body>
|
|
363
|
+
<div class="header">
|
|
364
|
+
<h1>
|
|
365
|
+
<span class="status-dot" id="statusDot"></span>
|
|
366
|
+
Schema Browser
|
|
367
|
+
</h1>
|
|
368
|
+
<div class="header-actions">
|
|
369
|
+
<button class="btn" id="refreshBtn">Refresh</button>
|
|
370
|
+
<button class="btn" id="queryBtn">Query</button>
|
|
371
|
+
</div>
|
|
372
|
+
</div>
|
|
373
|
+
|
|
374
|
+
<div class="main">
|
|
375
|
+
<div class="sidebar">
|
|
376
|
+
<div class="sidebar-header">Tables</div>
|
|
377
|
+
<ul class="table-list" id="tableList">
|
|
378
|
+
<li class="loading"><div class="spinner"></div></li>
|
|
379
|
+
</ul>
|
|
380
|
+
</div>
|
|
381
|
+
|
|
382
|
+
<div class="content">
|
|
383
|
+
<div class="schema-panel" id="schemaPanel">
|
|
384
|
+
<div class="empty-state">
|
|
385
|
+
<h2>Select a Table</h2>
|
|
386
|
+
<p>Choose a table from the sidebar to view its schema and documents.</p>
|
|
387
|
+
</div>
|
|
388
|
+
</div>
|
|
389
|
+
|
|
390
|
+
<div class="documents-panel" id="documentsPanel" style="display: none;">
|
|
391
|
+
<div class="documents-header">
|
|
392
|
+
<span class="documents-title">Documents</span>
|
|
393
|
+
<div class="pagination">
|
|
394
|
+
<button id="prevPage" disabled><</button>
|
|
395
|
+
<span id="pageInfo">Page 1</span>
|
|
396
|
+
<button id="nextPage">></button>
|
|
397
|
+
</div>
|
|
398
|
+
</div>
|
|
399
|
+
<div class="documents-table">
|
|
400
|
+
<table>
|
|
401
|
+
<thead id="docTableHead"></thead>
|
|
402
|
+
<tbody id="docTableBody"></tbody>
|
|
403
|
+
</table>
|
|
404
|
+
</div>
|
|
405
|
+
</div>
|
|
406
|
+
</div>
|
|
407
|
+
</div>
|
|
408
|
+
|
|
409
|
+
<script>
|
|
410
|
+
// Schema Browser Application
|
|
411
|
+
const app = {
|
|
412
|
+
config: null,
|
|
413
|
+
selectedTable: null,
|
|
414
|
+
currentPage: 1,
|
|
415
|
+
pageSize: 50,
|
|
416
|
+
|
|
417
|
+
init() {
|
|
418
|
+
// Parse config from URL
|
|
419
|
+
const params = new URLSearchParams(window.location.search);
|
|
420
|
+
const configParam = params.get('config');
|
|
421
|
+
if (configParam) {
|
|
422
|
+
try {
|
|
423
|
+
this.config = JSON.parse(decodeURIComponent(configParam));
|
|
424
|
+
this.pageSize = this.config.pageSize || 50;
|
|
425
|
+
} catch (e) {
|
|
426
|
+
console.error('Failed to parse config:', e);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Set up event listeners
|
|
431
|
+
document.getElementById('refreshBtn').addEventListener('click', () => this.refresh());
|
|
432
|
+
document.getElementById('queryBtn').addEventListener('click', () => this.openQueryBuilder());
|
|
433
|
+
document.getElementById('prevPage').addEventListener('click', () => this.changePage(-1));
|
|
434
|
+
document.getElementById('nextPage').addEventListener('click', () => this.changePage(1));
|
|
435
|
+
|
|
436
|
+
// Initialize communication with host
|
|
437
|
+
this.setupHostCommunication();
|
|
438
|
+
|
|
439
|
+
// Load initial data
|
|
440
|
+
this.loadTables();
|
|
441
|
+
|
|
442
|
+
// Select initial table if specified
|
|
443
|
+
if (this.config?.selectedTable) {
|
|
444
|
+
this.selectTable(this.config.selectedTable);
|
|
445
|
+
}
|
|
446
|
+
},
|
|
447
|
+
|
|
448
|
+
setupHostCommunication() {
|
|
449
|
+
// Listen for messages from MCP host
|
|
450
|
+
window.addEventListener('message', (event) => {
|
|
451
|
+
if (event.data.type === 'mcp-response') {
|
|
452
|
+
this.handleMcpResponse(event.data);
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
},
|
|
456
|
+
|
|
457
|
+
sendToHost(method, params) {
|
|
458
|
+
// Send JSON-RPC request to MCP host
|
|
459
|
+
const message = {
|
|
460
|
+
jsonrpc: '2.0',
|
|
461
|
+
id: Date.now(),
|
|
462
|
+
method,
|
|
463
|
+
params
|
|
464
|
+
};
|
|
465
|
+
window.parent.postMessage({ type: 'mcp-request', ...message }, '*');
|
|
466
|
+
return message.id;
|
|
467
|
+
},
|
|
468
|
+
|
|
469
|
+
handleMcpResponse(response) {
|
|
470
|
+
// Handle responses from MCP server via host
|
|
471
|
+
console.log('MCP Response:', response);
|
|
472
|
+
},
|
|
473
|
+
|
|
474
|
+
loadTables() {
|
|
475
|
+
const tableList = document.getElementById('tableList');
|
|
476
|
+
|
|
477
|
+
// Use config data if available
|
|
478
|
+
if (this.config?.tables && this.config.tables.length > 0) {
|
|
479
|
+
tableList.innerHTML = this.config.tables.map(t => \`
|
|
480
|
+
<li class="table-item" data-table="\${t.name}">
|
|
481
|
+
<span class="table-name">\${t.name}</span>
|
|
482
|
+
<span class="table-count">\${this.formatCount(t.documentCount)}</span>
|
|
483
|
+
</li>
|
|
484
|
+
\`).join('');
|
|
485
|
+
|
|
486
|
+
// Add click handlers
|
|
487
|
+
tableList.querySelectorAll('.table-item').forEach(item => {
|
|
488
|
+
item.addEventListener('click', () => {
|
|
489
|
+
this.selectTable(item.dataset.table);
|
|
490
|
+
});
|
|
491
|
+
});
|
|
492
|
+
} else {
|
|
493
|
+
tableList.innerHTML = \`
|
|
494
|
+
<li class="empty-state" style="padding: 20px;">
|
|
495
|
+
<p>No tables found</p>
|
|
496
|
+
</li>
|
|
497
|
+
\`;
|
|
498
|
+
}
|
|
499
|
+
},
|
|
500
|
+
|
|
501
|
+
formatCount(count) {
|
|
502
|
+
if (count >= 1000000) return (count / 1000000).toFixed(1) + 'M';
|
|
503
|
+
if (count >= 1000) return (count / 1000).toFixed(1) + 'k';
|
|
504
|
+
return count?.toString() || '0';
|
|
505
|
+
},
|
|
506
|
+
|
|
507
|
+
selectTable(tableName) {
|
|
508
|
+
this.selectedTable = tableName;
|
|
509
|
+
this.currentPage = 1;
|
|
510
|
+
|
|
511
|
+
// Update active state in sidebar
|
|
512
|
+
document.querySelectorAll('.table-item').forEach(item => {
|
|
513
|
+
item.classList.toggle('active', item.dataset.table === tableName);
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
// Load schema
|
|
517
|
+
this.loadSchema(tableName);
|
|
518
|
+
|
|
519
|
+
// Load documents
|
|
520
|
+
this.loadDocuments(tableName);
|
|
521
|
+
},
|
|
522
|
+
|
|
523
|
+
loadSchema(tableName) {
|
|
524
|
+
const panel = document.getElementById('schemaPanel');
|
|
525
|
+
const schema = this.config?.selectedSchema;
|
|
526
|
+
|
|
527
|
+
// Show schema UI
|
|
528
|
+
panel.innerHTML = \`
|
|
529
|
+
<div class="schema-title">
|
|
530
|
+
<span>\${tableName}</span>
|
|
531
|
+
</div>
|
|
532
|
+
<div class="schema-grid">
|
|
533
|
+
<div class="schema-card">
|
|
534
|
+
<div class="schema-card-header">Declared Schema</div>
|
|
535
|
+
<div class="schema-card-body" id="declaredSchema">
|
|
536
|
+
\${this.renderFields(schema?.declaredFields || [])}
|
|
537
|
+
</div>
|
|
538
|
+
</div>
|
|
539
|
+
<div class="schema-card">
|
|
540
|
+
<div class="schema-card-header">Inferred Schema</div>
|
|
541
|
+
<div class="schema-card-body" id="inferredSchema">
|
|
542
|
+
\${this.renderFields(schema?.inferredFields || [])}
|
|
543
|
+
</div>
|
|
544
|
+
</div>
|
|
545
|
+
</div>
|
|
546
|
+
\${this.checkSchemaMismatches(schema)}
|
|
547
|
+
\`;
|
|
548
|
+
},
|
|
549
|
+
|
|
550
|
+
renderFields(fields) {
|
|
551
|
+
if (!fields || fields.length === 0) {
|
|
552
|
+
return '<p style="color: var(--text-secondary); padding: 8px;">No schema defined</p>';
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
return fields.map(f => \`
|
|
556
|
+
<div class="field-row">
|
|
557
|
+
<span>
|
|
558
|
+
<span class="field-name">\${f.name}</span>
|
|
559
|
+
\${f.optional ? '<span class="field-optional">?</span>' : ''}
|
|
560
|
+
</span>
|
|
561
|
+
<span class="field-type">\${f.type}</span>
|
|
562
|
+
</div>
|
|
563
|
+
\`).join('');
|
|
564
|
+
},
|
|
565
|
+
|
|
566
|
+
checkSchemaMismatches(schema) {
|
|
567
|
+
if (!schema) return '';
|
|
568
|
+
|
|
569
|
+
const declared = new Set(schema.declaredFields?.map(f => f.name) || []);
|
|
570
|
+
const inferred = new Set(schema.inferredFields?.map(f => f.name) || []);
|
|
571
|
+
|
|
572
|
+
const missingInSchema = [...inferred].filter(f => !declared.has(f) && !f.startsWith('_'));
|
|
573
|
+
|
|
574
|
+
if (missingInSchema.length > 0) {
|
|
575
|
+
return \`
|
|
576
|
+
<div class="warning-badge">
|
|
577
|
+
Fields in data but not in schema: \${missingInSchema.join(', ')}
|
|
578
|
+
</div>
|
|
579
|
+
\`;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
return '';
|
|
583
|
+
},
|
|
584
|
+
|
|
585
|
+
loadDocuments(tableName) {
|
|
586
|
+
const panel = document.getElementById('documentsPanel');
|
|
587
|
+
panel.style.display = 'flex';
|
|
588
|
+
|
|
589
|
+
// Placeholder documents for demo
|
|
590
|
+
const docs = [
|
|
591
|
+
{ _id: 'abc123...', _creationTime: Date.now(), status: 'active' },
|
|
592
|
+
{ _id: 'def456...', _creationTime: Date.now() - 86400000, status: 'pending' },
|
|
593
|
+
];
|
|
594
|
+
|
|
595
|
+
this.renderDocuments(docs);
|
|
596
|
+
},
|
|
597
|
+
|
|
598
|
+
renderDocuments(docs) {
|
|
599
|
+
const thead = document.getElementById('docTableHead');
|
|
600
|
+
const tbody = document.getElementById('docTableBody');
|
|
601
|
+
|
|
602
|
+
if (!docs || docs.length === 0) {
|
|
603
|
+
thead.innerHTML = '';
|
|
604
|
+
tbody.innerHTML = '<tr><td colspan="100" style="text-align: center;">No documents</td></tr>';
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// Get all unique keys
|
|
609
|
+
const keys = [...new Set(docs.flatMap(d => Object.keys(d)))];
|
|
610
|
+
|
|
611
|
+
thead.innerHTML = \`<tr>\${keys.map(k => \`<th>\${k}</th>\`).join('')}</tr>\`;
|
|
612
|
+
tbody.innerHTML = docs.map(doc => \`
|
|
613
|
+
<tr>
|
|
614
|
+
\${keys.map(k => \`<td>\${this.formatValue(doc[k])}</td>\`).join('')}
|
|
615
|
+
</tr>
|
|
616
|
+
\`).join('');
|
|
617
|
+
},
|
|
618
|
+
|
|
619
|
+
formatValue(value) {
|
|
620
|
+
if (value === null || value === undefined) return '<span style="opacity:0.5">null</span>';
|
|
621
|
+
if (typeof value === 'object') return JSON.stringify(value);
|
|
622
|
+
if (typeof value === 'string' && value.length > 50) return value.slice(0, 50) + '...';
|
|
623
|
+
return String(value);
|
|
624
|
+
},
|
|
625
|
+
|
|
626
|
+
changePage(delta) {
|
|
627
|
+
this.currentPage += delta;
|
|
628
|
+
document.getElementById('pageInfo').textContent = \`Page \${this.currentPage}\`;
|
|
629
|
+
document.getElementById('prevPage').disabled = this.currentPage <= 1;
|
|
630
|
+
|
|
631
|
+
if (this.selectedTable) {
|
|
632
|
+
this.loadDocuments(this.selectedTable);
|
|
633
|
+
}
|
|
634
|
+
},
|
|
635
|
+
|
|
636
|
+
refresh() {
|
|
637
|
+
if (this.selectedTable) {
|
|
638
|
+
this.loadSchema(this.selectedTable);
|
|
639
|
+
this.loadDocuments(this.selectedTable);
|
|
640
|
+
}
|
|
641
|
+
},
|
|
642
|
+
|
|
643
|
+
openQueryBuilder() {
|
|
644
|
+
alert('Query builder coming soon!');
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
// Initialize app when DOM is ready
|
|
649
|
+
document.addEventListener('DOMContentLoaded', () => app.init());
|
|
650
|
+
</script>
|
|
651
|
+
</body>
|
|
652
|
+
</html>`;
|
|
653
|
+
}
|
|
654
|
+
//# sourceMappingURL=schema-browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-browser.js","sourceRoot":"","sources":["../../src/resources/schema-browser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE1D,MAAM,CAAC,KAAK,UAAU,wBAAwB;IAC5C,oCAAoC;IACpC,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,qBAAqB,CAAC,CAAC;IAEzE,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC5B,OAAO,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,uDAAuD;IACvD,OAAO,iBAAiB,EAAE,CAAC;AAC7B,CAAC;AAED,SAAS,iBAAiB;IACxB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAwnBD,CAAC;AACT,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Server Setup
|
|
3
|
+
*
|
|
4
|
+
* Creates and configures the MCP server with tools and resources
|
|
5
|
+
* for Convex database exploration.
|
|
6
|
+
*/
|
|
7
|
+
export interface ConvexMcpServer {
|
|
8
|
+
startStdio: () => Promise<void>;
|
|
9
|
+
startHttp: (port: number) => Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export declare function createServer(): Promise<ConvexMcpServer>;
|
|
12
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAmBH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5C;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC,eAAe,CAAC,CA0I7D"}
|