mikser-io-mcp 0.2.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/LICENSE +21 -0
- package/README.md +46 -0
- package/index.js +1065 -0
- package/package.json +47 -0
- package/preview.js +532 -0
- package/public/preview-ui-shell.html +147 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>mikser · MCP-UI</title>
|
|
6
|
+
<style>
|
|
7
|
+
body { margin: 0; font-family: system-ui, -apple-system, sans-serif; color: #1f2937; background: #ffffff; }
|
|
8
|
+
#mikser-ui-root { padding: 0; }
|
|
9
|
+
#mikser-debug {
|
|
10
|
+
display: none;
|
|
11
|
+
position: fixed;
|
|
12
|
+
bottom: 0; left: 0; right: 0;
|
|
13
|
+
max-height: 30vh;
|
|
14
|
+
overflow: auto;
|
|
15
|
+
padding: 0.5em 1em;
|
|
16
|
+
border-top: 1px solid #d1d5db;
|
|
17
|
+
background: #f9fafb;
|
|
18
|
+
font: 12px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
19
|
+
color: #4b5563;
|
|
20
|
+
z-index: 9999;
|
|
21
|
+
}
|
|
22
|
+
#mikser-debug.shown { display: block; }
|
|
23
|
+
#mikser-debug strong { display: block; margin-bottom: 0.3em; color: #374151; font: 600 11px ui-sans-serif, system-ui; text-transform: uppercase; letter-spacing: 0.04em; }
|
|
24
|
+
#mikser-debug ol { margin: 0; padding: 0; list-style: none; }
|
|
25
|
+
#mikser-debug li { padding: 1px 0; }
|
|
26
|
+
#mikser-debug li.ok { color: #047857; }
|
|
27
|
+
#mikser-debug li.fail { color: #b91c1c; }
|
|
28
|
+
#mikser-debug .t { color: #9ca3af; margin-right: 0.4em; }
|
|
29
|
+
</style>
|
|
30
|
+
</head>
|
|
31
|
+
<body>
|
|
32
|
+
<div id="mikser-ui-root"></div>
|
|
33
|
+
<div id="mikser-debug">
|
|
34
|
+
<strong>mikser · MCP Apps protocol</strong>
|
|
35
|
+
<ol id="mikser-debug-log"></ol>
|
|
36
|
+
</div>
|
|
37
|
+
<script>
|
|
38
|
+
(function () {
|
|
39
|
+
var nextId = 1, hostOrigin = '*';
|
|
40
|
+
var pending = new Map();
|
|
41
|
+
var debugLog = document.getElementById('mikser-debug-log');
|
|
42
|
+
var debugPanel = document.getElementById('mikser-debug');
|
|
43
|
+
var t0 = Date.now();
|
|
44
|
+
var ctx = { entityId: null, layoutId: null };
|
|
45
|
+
|
|
46
|
+
function log(msg, cls) {
|
|
47
|
+
debugPanel.classList.add('shown');
|
|
48
|
+
var li = document.createElement('li');
|
|
49
|
+
if (cls) li.className = cls;
|
|
50
|
+
var dt = ((Date.now() - t0) / 1000).toFixed(2);
|
|
51
|
+
li.innerHTML = '<span class="t">+' + dt + 's</span>' + msg;
|
|
52
|
+
debugLog.appendChild(li);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function rpc(method, params, timeoutMs) {
|
|
56
|
+
return new Promise(function (resolve, reject) {
|
|
57
|
+
var id = nextId++;
|
|
58
|
+
pending.set(id, { resolve: resolve, reject: reject });
|
|
59
|
+
log('→ ' + method + ' (id=' + id + ')');
|
|
60
|
+
window.parent.postMessage({ jsonrpc: '2.0', method: method, params: params, id: id }, hostOrigin);
|
|
61
|
+
if (timeoutMs) {
|
|
62
|
+
setTimeout(function () {
|
|
63
|
+
if (!pending.has(id)) return;
|
|
64
|
+
pending.delete(id);
|
|
65
|
+
log('✗ ' + method + ' — no reply after ' + timeoutMs + 'ms', 'fail');
|
|
66
|
+
reject(new Error(method + ' timeout (' + timeoutMs + 'ms — host did not reply)'));
|
|
67
|
+
}, timeoutMs);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function injectContent(structured) {
|
|
73
|
+
if (!structured) { log('tool-result had no structuredContent', 'fail'); return; }
|
|
74
|
+
ctx.entityId = structured.entityId || null;
|
|
75
|
+
ctx.layoutId = structured.layoutId || null;
|
|
76
|
+
var root = document.getElementById('mikser-ui-root');
|
|
77
|
+
root.innerHTML = structured.html || '';
|
|
78
|
+
// innerHTML doesn't execute embedded <script> tags — re-create them.
|
|
79
|
+
Array.prototype.forEach.call(root.querySelectorAll('script'), function (oldScript) {
|
|
80
|
+
var newScript = document.createElement('script');
|
|
81
|
+
Array.prototype.forEach.call(oldScript.attributes, function (a) {
|
|
82
|
+
newScript.setAttribute(a.name, a.value);
|
|
83
|
+
});
|
|
84
|
+
newScript.textContent = oldScript.textContent;
|
|
85
|
+
oldScript.parentNode.replaceChild(newScript, oldScript);
|
|
86
|
+
});
|
|
87
|
+
log('✓ content injected (' + (structured.html || '').length + ' bytes, entityId=' + JSON.stringify(ctx.entityId) + ')', 'ok');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
window.addEventListener('message', function (e) {
|
|
91
|
+
var data = e.data;
|
|
92
|
+
if (data && typeof data.id !== 'undefined' && pending.has(data.id)) {
|
|
93
|
+
var entry = pending.get(data.id);
|
|
94
|
+
pending.delete(data.id);
|
|
95
|
+
if (data.error) entry.reject(data.error);
|
|
96
|
+
else entry.resolve(data.result);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (data && data.method === 'ui/notifications/tool-result') {
|
|
100
|
+
log('← ui/notifications/tool-result');
|
|
101
|
+
injectContent(data.params && data.params.structuredContent);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Public API. Layouts call this from button click handlers to deliver
|
|
107
|
+
// an action back to mikser. Returns a Promise that resolves with
|
|
108
|
+
// mikser_ui_action's tool result (pure relay or handler-forwarded).
|
|
109
|
+
window.sendAction = function (action, payload) {
|
|
110
|
+
payload = payload || {};
|
|
111
|
+
log('sendAction: ' + action);
|
|
112
|
+
return rpc('tools/call', {
|
|
113
|
+
name: 'mikser_ui_action',
|
|
114
|
+
arguments: {
|
|
115
|
+
entityId: ctx.entityId,
|
|
116
|
+
layoutId: ctx.layoutId,
|
|
117
|
+
action: action,
|
|
118
|
+
payload: payload,
|
|
119
|
+
},
|
|
120
|
+
}, 5000).then(function (r) {
|
|
121
|
+
log('✓ mikser_ui_action returned', 'ok');
|
|
122
|
+
return r;
|
|
123
|
+
}).catch(function (err) {
|
|
124
|
+
log('✗ mikser_ui_action failed: ' + err.message, 'fail');
|
|
125
|
+
throw err;
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Handshake — sent immediately on load. If the host doesn't reply,
|
|
130
|
+
// content will still inject when tool-result arrives, but clicks
|
|
131
|
+
// won't deliver (no AppBridge translating tools/call frames).
|
|
132
|
+
log('shell loaded');
|
|
133
|
+
rpc('ui/initialize', {
|
|
134
|
+
appCapabilities: { availableDisplayModes: ['inline'] },
|
|
135
|
+
}, 2000).then(function (init) {
|
|
136
|
+
var hostName = (init && init.hostInfo && init.hostInfo.name) || 'unknown';
|
|
137
|
+
log('✓ ui/initialize replied — host=' + hostName, 'ok');
|
|
138
|
+
if (init && init.hostInfo && init.hostInfo.origin) {
|
|
139
|
+
hostOrigin = init.hostInfo.origin;
|
|
140
|
+
}
|
|
141
|
+
}).catch(function () {
|
|
142
|
+
log('Diagnosis: host did NOT respond to ui/initialize. The MCP Apps AppBridge is not implemented (or disabled) on this host. Click actions will not reach mikser. See ADR-0008.', 'fail');
|
|
143
|
+
});
|
|
144
|
+
})();
|
|
145
|
+
</script>
|
|
146
|
+
</body>
|
|
147
|
+
</html>
|