@vibevibes/mcp 0.7.0 → 0.8.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/dist/index.js +29 -0
- package/dist/server.js +61 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -640,6 +640,35 @@ Use this to orient yourself before deciding whether to act.`, {
|
|
|
640
640
|
return { content: [{ type: "text", text: `Look failed: ${toErrorMessage(err)}` }] };
|
|
641
641
|
}
|
|
642
642
|
});
|
|
643
|
+
// ── Tool: screenshot ───────────────────────────────────────
|
|
644
|
+
server.tool("screenshot", `Capture a screenshot of the experience as seen in the browser.
|
|
645
|
+
|
|
646
|
+
Returns the current visual state as a PNG image. Use this to:
|
|
647
|
+
- See what the UI looks like
|
|
648
|
+
- Debug visual/layout issues
|
|
649
|
+
- Verify your changes rendered correctly
|
|
650
|
+
|
|
651
|
+
Requires at least one browser viewer to be connected.`, {}, async () => {
|
|
652
|
+
try {
|
|
653
|
+
const res = await fetchJSON("/screenshot", { timeoutMs: 15000 });
|
|
654
|
+
if (res.error) {
|
|
655
|
+
return { content: [{ type: "text", text: `Screenshot failed: ${res.error}` }] };
|
|
656
|
+
}
|
|
657
|
+
if (!res.dataUrl) {
|
|
658
|
+
return { content: [{ type: "text", text: "Screenshot returned empty" }] };
|
|
659
|
+
}
|
|
660
|
+
// dataUrl is "data:image/png;base64,..."
|
|
661
|
+
const base64 = res.dataUrl.replace(/^data:image\/\w+;base64,/, "");
|
|
662
|
+
return {
|
|
663
|
+
content: [
|
|
664
|
+
{ type: "image", data: base64, mimeType: "image/png" },
|
|
665
|
+
],
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
catch (err) {
|
|
669
|
+
return { content: [{ type: "text", text: `Screenshot failed: ${toErrorMessage(err)}` }] };
|
|
670
|
+
}
|
|
671
|
+
});
|
|
643
672
|
// ── Tool: disconnect ────────────────────────────────────────
|
|
644
673
|
server.tool("disconnect", `Disconnect from the current experience.
|
|
645
674
|
|
package/dist/server.js
CHANGED
|
@@ -903,6 +903,64 @@ app.post("/browser-error", (req, res) => {
|
|
|
903
903
|
}
|
|
904
904
|
res.json({ ok: true });
|
|
905
905
|
});
|
|
906
|
+
// ── Screenshot ─────────────────────────────────────────────
|
|
907
|
+
const screenshotCallbacks = new Map();
|
|
908
|
+
app.get("/screenshot", async (_req, res) => {
|
|
909
|
+
// Find a browser WebSocket connection to request a screenshot from
|
|
910
|
+
let browserWs = null;
|
|
911
|
+
for (const [ws, actorId] of room.wsConnections.entries()) {
|
|
912
|
+
if (ws.readyState === WebSocket.OPEN && actorId.startsWith("viewer-")) {
|
|
913
|
+
browserWs = ws;
|
|
914
|
+
break;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
// Fallback: any open connection
|
|
918
|
+
if (!browserWs) {
|
|
919
|
+
for (const [ws] of room.wsConnections.entries()) {
|
|
920
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
921
|
+
browserWs = ws;
|
|
922
|
+
break;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
if (!browserWs) {
|
|
927
|
+
return res.status(503).json({ error: "No browser connected to capture screenshot" });
|
|
928
|
+
}
|
|
929
|
+
const id = `ss-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
930
|
+
try {
|
|
931
|
+
const dataUrl = await new Promise((resolve, reject) => {
|
|
932
|
+
const timer = setTimeout(() => {
|
|
933
|
+
screenshotCallbacks.delete(id);
|
|
934
|
+
reject(new Error("Screenshot timeout"));
|
|
935
|
+
}, 10000);
|
|
936
|
+
screenshotCallbacks.set(id, {
|
|
937
|
+
resolve: (url) => { clearTimeout(timer); resolve(url); },
|
|
938
|
+
reject: (err) => { clearTimeout(timer); reject(err); },
|
|
939
|
+
});
|
|
940
|
+
browserWs.send(JSON.stringify({ type: "screenshot_request", id }));
|
|
941
|
+
});
|
|
942
|
+
res.json({ dataUrl });
|
|
943
|
+
}
|
|
944
|
+
catch (err) {
|
|
945
|
+
res.status(500).json({ error: toErrorMessage(err) });
|
|
946
|
+
}
|
|
947
|
+
});
|
|
948
|
+
// Called by WebSocket handler when browser sends screenshot_response
|
|
949
|
+
function handleScreenshotResponse(msg) {
|
|
950
|
+
const cb = screenshotCallbacks.get(msg.id);
|
|
951
|
+
if (!cb)
|
|
952
|
+
return;
|
|
953
|
+
screenshotCallbacks.delete(msg.id);
|
|
954
|
+
if (msg.error) {
|
|
955
|
+
cb.reject(new Error(msg.error));
|
|
956
|
+
}
|
|
957
|
+
else if (msg.dataUrl) {
|
|
958
|
+
cb.resolve(msg.dataUrl);
|
|
959
|
+
}
|
|
960
|
+
else {
|
|
961
|
+
cb.reject(new Error("Empty screenshot response"));
|
|
962
|
+
}
|
|
963
|
+
}
|
|
906
964
|
// ── Agent context ──────────────────────────────────────────
|
|
907
965
|
app.get("/agent-context", (req, res) => {
|
|
908
966
|
const rawSince = queryInt(req.query.since);
|
|
@@ -1219,6 +1277,9 @@ export async function startServer(config) {
|
|
|
1219
1277
|
}
|
|
1220
1278
|
}
|
|
1221
1279
|
}
|
|
1280
|
+
if (msg.type === "screenshot_response") {
|
|
1281
|
+
handleScreenshotResponse(msg);
|
|
1282
|
+
}
|
|
1222
1283
|
}
|
|
1223
1284
|
catch (err) {
|
|
1224
1285
|
if (!(err instanceof SyntaxError)) {
|