@eyejack-creator/mcp 0.3.0 → 0.4.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 +248 -76
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -617,11 +617,10 @@ var SceneApi = class {
|
|
|
617
617
|
|
|
618
618
|
// ../creator-api/dist/domains/agent.js
|
|
619
619
|
var CREATE_AGENT_SESSION = `
|
|
620
|
-
mutation CreateAgentSession
|
|
621
|
-
createAgentSession
|
|
620
|
+
mutation CreateAgentSession {
|
|
621
|
+
createAgentSession {
|
|
622
622
|
sessionId
|
|
623
623
|
token
|
|
624
|
-
artworkId
|
|
625
624
|
expiresAt
|
|
626
625
|
}
|
|
627
626
|
}
|
|
@@ -714,9 +713,34 @@ var ON_AGENT_ACTION_RESULT = `
|
|
|
714
713
|
}
|
|
715
714
|
}
|
|
716
715
|
`;
|
|
716
|
+
var AGENT_LIST_ARTWORKS = `
|
|
717
|
+
query AgentListArtworks {
|
|
718
|
+
agentListArtworks {
|
|
719
|
+
id
|
|
720
|
+
userid
|
|
721
|
+
name
|
|
722
|
+
description
|
|
723
|
+
status
|
|
724
|
+
artworkType
|
|
725
|
+
sceneRevision
|
|
726
|
+
modified
|
|
727
|
+
created
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
`;
|
|
731
|
+
var AGENT_ENSURE_SCENE_BACKUP = `
|
|
732
|
+
mutation AgentEnsureSceneBackup($artworkId: ID!) {
|
|
733
|
+
agentEnsureSceneBackup(artworkId: $artworkId) {
|
|
734
|
+
artworkId
|
|
735
|
+
sessionId
|
|
736
|
+
sceneRevision
|
|
737
|
+
createdAt
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
`;
|
|
717
741
|
var AGENT_GET_SCENE = `
|
|
718
|
-
query AgentGetScene {
|
|
719
|
-
agentGetScene {
|
|
742
|
+
query AgentGetScene($artworkId: ID) {
|
|
743
|
+
agentGetScene(artworkId: $artworkId) {
|
|
720
744
|
artworkId
|
|
721
745
|
name
|
|
722
746
|
status
|
|
@@ -740,10 +764,12 @@ var AgentApi = class {
|
|
|
740
764
|
constructor(client2, realtime) {
|
|
741
765
|
this.client = client2;
|
|
742
766
|
this.realtime = realtime;
|
|
767
|
+
this.backupEnsured = /* @__PURE__ */ new Set();
|
|
743
768
|
}
|
|
744
769
|
// --- Browser-side (Cognito) session lifecycle ---
|
|
745
|
-
|
|
746
|
-
|
|
770
|
+
/** Mint an ACCOUNT-scoped session (P6a) — authorizes everything the account owns. */
|
|
771
|
+
async createSession() {
|
|
772
|
+
const data = await this.client.execute(CREATE_AGENT_SESSION);
|
|
747
773
|
return data.createAgentSession;
|
|
748
774
|
}
|
|
749
775
|
async heartbeat(sessionId) {
|
|
@@ -769,10 +795,52 @@ var AgentApi = class {
|
|
|
769
795
|
const data = await this.client.execute(AGENT_GET_SESSION);
|
|
770
796
|
return data.agentGetSession;
|
|
771
797
|
}
|
|
772
|
-
|
|
773
|
-
|
|
798
|
+
/** Read any artwork the session's account owns (ownership checked server-side). */
|
|
799
|
+
async getScene(artworkId) {
|
|
800
|
+
const data = await this.client.execute(AGENT_GET_SCENE, {
|
|
801
|
+
artworkId
|
|
802
|
+
});
|
|
774
803
|
return data.agentGetScene;
|
|
775
804
|
}
|
|
805
|
+
/** The account's workspace: every non-deleted artwork it owns. */
|
|
806
|
+
async listArtworks() {
|
|
807
|
+
const data = await this.client.execute(AGENT_LIST_ARTWORKS);
|
|
808
|
+
return data.agentListArtworks ?? [];
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Create a new panels (worldTarget) artwork owned by the session's account
|
|
812
|
+
* (the server forces ownership regardless of input). Mirrors the editor's
|
|
813
|
+
* new-panels defaults; the artwork starts published like the web flow.
|
|
814
|
+
*/
|
|
815
|
+
async createArtwork(params) {
|
|
816
|
+
const id = `Artwork-${globalThis.crypto.randomUUID()}`;
|
|
817
|
+
const input = {
|
|
818
|
+
id,
|
|
819
|
+
files: [],
|
|
820
|
+
creatorVersion: "agent-mcp",
|
|
821
|
+
status: "published",
|
|
822
|
+
artworkType: "worldTarget",
|
|
823
|
+
name: params?.name ?? "Untitled panels",
|
|
824
|
+
description: params?.description ?? "",
|
|
825
|
+
downloadsCount: 0,
|
|
826
|
+
viewsCount: 0,
|
|
827
|
+
publishEnabled: true,
|
|
828
|
+
enabled: true,
|
|
829
|
+
animationHasAlpha: false,
|
|
830
|
+
animationVideoOrientation: "horizontal"
|
|
831
|
+
};
|
|
832
|
+
const data = await this.client.execute(CREATE_ARTWORK, { input });
|
|
833
|
+
return data.createArtwork;
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Lazy pre-write snapshot (P6a): idempotent per (session, artwork) —
|
|
837
|
+
* powers the editor's "Revert AI changes". saveScene calls this
|
|
838
|
+
* automatically before a session's first write to each artwork.
|
|
839
|
+
*/
|
|
840
|
+
async ensureSceneBackup(artworkId) {
|
|
841
|
+
const data = await this.client.execute(AGENT_ENSURE_SCENE_BACKUP, { artworkId });
|
|
842
|
+
return data.agentEnsureSceneBackup;
|
|
843
|
+
}
|
|
776
844
|
// --- Agent-side (session token) writes (P4) ---
|
|
777
845
|
/**
|
|
778
846
|
* Revision-checked scene write with a session token. Same mutation and
|
|
@@ -789,6 +857,13 @@ var AgentApi = class {
|
|
|
789
857
|
throw new Error(`Refusing to save invalid scene document: ${errors.map((i) => i.message).join("; ")}`);
|
|
790
858
|
}
|
|
791
859
|
}
|
|
860
|
+
if (!this.backupEnsured.has(params.artworkId)) {
|
|
861
|
+
try {
|
|
862
|
+
await this.ensureSceneBackup(params.artworkId);
|
|
863
|
+
this.backupEnsured.add(params.artworkId);
|
|
864
|
+
} catch {
|
|
865
|
+
}
|
|
866
|
+
}
|
|
792
867
|
try {
|
|
793
868
|
const data = await this.client.execute(UPDATE_ARTWORK_SCENE, {
|
|
794
869
|
input: {
|
|
@@ -803,7 +878,7 @@ var AgentApi = class {
|
|
|
803
878
|
return { conflict: false, revision: payload.sceneRevision, modified: payload.modified ?? null, payload };
|
|
804
879
|
} catch (err) {
|
|
805
880
|
if (err instanceof GraphQLRequestError && err.errorType === "SceneRevisionConflict") {
|
|
806
|
-
const current = await this.getScene().catch(() => null);
|
|
881
|
+
const current = await this.getScene(params.artworkId).catch(() => null);
|
|
807
882
|
return { conflict: true, currentRevision: current?.sceneRevision ?? null, current };
|
|
808
883
|
}
|
|
809
884
|
throw err;
|
|
@@ -1019,7 +1094,7 @@ function errorText(err) {
|
|
|
1019
1094
|
if ((err.errorType || "").includes("Unauthorized")) {
|
|
1020
1095
|
return REJECTED_HINT;
|
|
1021
1096
|
}
|
|
1022
|
-
return `EyeJack API error
|
|
1097
|
+
return `EyeJack API error (${err.errorType || "unknown"}): ${err.message}`;
|
|
1023
1098
|
}
|
|
1024
1099
|
return `Error: ${err.message}`;
|
|
1025
1100
|
}
|
|
@@ -1031,19 +1106,92 @@ var asError = (err) => ({
|
|
|
1031
1106
|
isError: true
|
|
1032
1107
|
});
|
|
1033
1108
|
function registerTools(server2, client2, options) {
|
|
1034
|
-
let
|
|
1035
|
-
const
|
|
1036
|
-
|
|
1037
|
-
|
|
1109
|
+
let resultSub = null;
|
|
1110
|
+
const pendingResults = /* @__PURE__ */ new Map();
|
|
1111
|
+
const ensureResultListener = async () => {
|
|
1112
|
+
if (resultSub) return;
|
|
1113
|
+
resultSub = client2.agent.watchActionResults(options.sessionId, {
|
|
1114
|
+
next: (result) => {
|
|
1115
|
+
const resolve = pendingResults.get(result.requestId);
|
|
1116
|
+
if (resolve) {
|
|
1117
|
+
pendingResults.delete(result.requestId);
|
|
1118
|
+
resolve(result);
|
|
1119
|
+
}
|
|
1120
|
+
},
|
|
1121
|
+
error: (err) => {
|
|
1122
|
+
console.error("[eyejack-mcp] action result channel error:", err);
|
|
1123
|
+
}
|
|
1124
|
+
});
|
|
1125
|
+
await new Promise((resolve) => setTimeout(resolve, 800));
|
|
1126
|
+
};
|
|
1127
|
+
const requestOnce = async (actionType, payload, timeoutMs) => {
|
|
1128
|
+
const requestId = randomUUID();
|
|
1129
|
+
const waiter = new Promise((resolve) => {
|
|
1130
|
+
const timer = setTimeout(() => {
|
|
1131
|
+
pendingResults.delete(requestId);
|
|
1132
|
+
resolve(null);
|
|
1133
|
+
}, timeoutMs);
|
|
1134
|
+
pendingResults.set(requestId, (result) => {
|
|
1135
|
+
clearTimeout(timer);
|
|
1136
|
+
resolve(result);
|
|
1137
|
+
});
|
|
1138
|
+
});
|
|
1139
|
+
await client2.agent.requestAction({
|
|
1140
|
+
sessionId: options.sessionId,
|
|
1141
|
+
requestId,
|
|
1142
|
+
actionType,
|
|
1143
|
+
payload
|
|
1144
|
+
});
|
|
1145
|
+
return waiter;
|
|
1146
|
+
};
|
|
1147
|
+
const NO_ARTWORK_HINT = "No artworkId given and no artwork is open in the editor tab. Pass artworkId explicitly (see list_artworks) or use open_artwork first.";
|
|
1148
|
+
const resolveArtworkId = async (explicit) => {
|
|
1149
|
+
if (explicit) return explicit;
|
|
1150
|
+
await ensureResultListener();
|
|
1151
|
+
const pong = await requestOnce("PING", null, 6e3);
|
|
1152
|
+
if (pong?.ok && pong.payload) {
|
|
1153
|
+
try {
|
|
1154
|
+
const parsed = JSON.parse(pong.payload);
|
|
1155
|
+
if (parsed.activeArtworkId) return parsed.activeArtworkId;
|
|
1156
|
+
} catch {
|
|
1157
|
+
}
|
|
1038
1158
|
}
|
|
1039
|
-
|
|
1159
|
+
throw new Error(NO_ARTWORK_HINT);
|
|
1160
|
+
};
|
|
1161
|
+
const artworkIdParam = {
|
|
1162
|
+
artworkId: z.string().optional().describe(
|
|
1163
|
+
'Target artwork id ("Artwork-\u2026"), any panels project the account owns. Omit to target the one open in the editor tab.'
|
|
1164
|
+
)
|
|
1040
1165
|
};
|
|
1041
1166
|
server2.tool(
|
|
1042
|
-
"
|
|
1043
|
-
|
|
1167
|
+
"list_artworks",
|
|
1168
|
+
"List every artwork in the connected EyeJack account \u2014 the whole workspace this session can read and edit. Only worldTarget (panels) artworks are editable through this server; other types are read-only context.",
|
|
1044
1169
|
async () => {
|
|
1045
1170
|
try {
|
|
1046
|
-
const
|
|
1171
|
+
const artworks = await client2.agent.listArtworks();
|
|
1172
|
+
return asText(
|
|
1173
|
+
artworks.map((a) => ({
|
|
1174
|
+
id: a.id,
|
|
1175
|
+
name: a.name,
|
|
1176
|
+
artworkType: a.artworkType,
|
|
1177
|
+
status: a.status,
|
|
1178
|
+
sceneRevision: a.sceneRevision ?? 0,
|
|
1179
|
+
modified: a.modified
|
|
1180
|
+
}))
|
|
1181
|
+
);
|
|
1182
|
+
} catch (err) {
|
|
1183
|
+
return asError(err);
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
);
|
|
1187
|
+
server2.tool(
|
|
1188
|
+
"get_scene",
|
|
1189
|
+
`Read the full structured snapshot of an artwork: metadata, the parsed scene document, the current sceneRevision, and the asset files it references. The session covers the whole account (P6a) \u2014 omit artworkId to read whatever is open in the editor tab. ${SCENE_CONTRACT}`,
|
|
1190
|
+
artworkIdParam,
|
|
1191
|
+
async (args) => {
|
|
1192
|
+
try {
|
|
1193
|
+
const id = await resolveArtworkId(args.artworkId);
|
|
1194
|
+
const record = await client2.agent.getScene(id);
|
|
1047
1195
|
const doc = parseSceneConfig(record.sceneConfig);
|
|
1048
1196
|
return asText({
|
|
1049
1197
|
artworkId: record.artworkId,
|
|
@@ -1068,10 +1216,12 @@ function registerTools(server2, client2, options) {
|
|
|
1068
1216
|
);
|
|
1069
1217
|
server2.tool(
|
|
1070
1218
|
"list_assets",
|
|
1071
|
-
"List the asset files (images, videos, 3D models) available on
|
|
1072
|
-
|
|
1219
|
+
"List the asset files (images, videos, 3D models) available on an artwork \u2014 the palette its panels can reference via assetID == fileID. Omit artworkId to target the open artwork. Read-only.",
|
|
1220
|
+
artworkIdParam,
|
|
1221
|
+
async (args) => {
|
|
1073
1222
|
try {
|
|
1074
|
-
const
|
|
1223
|
+
const id = await resolveArtworkId(args.artworkId);
|
|
1224
|
+
const record = await client2.agent.getScene(id);
|
|
1075
1225
|
const doc = parseSceneConfig(record.sceneConfig);
|
|
1076
1226
|
const usedIds = new Set((doc?.scenes?.[0]?.children || []).map((c) => c.assetID));
|
|
1077
1227
|
return asText(
|
|
@@ -1089,8 +1239,55 @@ function registerTools(server2, client2, options) {
|
|
|
1089
1239
|
}
|
|
1090
1240
|
}
|
|
1091
1241
|
);
|
|
1242
|
+
const createArtworkParams = {
|
|
1243
|
+
name: z.string().min(1).max(120).optional().describe('Project name (default "Untitled panels").'),
|
|
1244
|
+
description: z.string().max(2e3).optional().describe("Optional description.")
|
|
1245
|
+
};
|
|
1246
|
+
server2.tool(
|
|
1247
|
+
"create_artwork",
|
|
1248
|
+
"Create a new, empty panels (worldTarget) project in the connected account. It starts with no assets \u2014 the human adds media in the browser; you can arrange the scene once assets exist. Follow up with open_artwork to bring it up in their editor.",
|
|
1249
|
+
createArtworkParams,
|
|
1250
|
+
async (args) => {
|
|
1251
|
+
const { name, description } = args;
|
|
1252
|
+
try {
|
|
1253
|
+
const created = await client2.agent.createArtwork({ name, description });
|
|
1254
|
+
return asText({
|
|
1255
|
+
id: created.id,
|
|
1256
|
+
status: created.status,
|
|
1257
|
+
message: `Created "${name ?? "Untitled panels"}" (${created.id}). Use open_artwork to show it in the user's editor.`
|
|
1258
|
+
});
|
|
1259
|
+
} catch (err) {
|
|
1260
|
+
return asError(err);
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
);
|
|
1264
|
+
const openArtworkParams = {
|
|
1265
|
+
artworkId: z.string().describe('The panels artwork to open ("Artwork-\u2026").')
|
|
1266
|
+
};
|
|
1267
|
+
server2.tool(
|
|
1268
|
+
"open_artwork",
|
|
1269
|
+
"Navigate the user's open editor tab to a panels artwork \u2014 steering the viewport. Viewport-bound tools (screenshot) always target whatever is open. Requires the editor tab to be open; the user sees the navigation happen, so tell them why you are switching.",
|
|
1270
|
+
openArtworkParams,
|
|
1271
|
+
async (args) => {
|
|
1272
|
+
const { artworkId } = args;
|
|
1273
|
+
try {
|
|
1274
|
+
await ensureResultListener();
|
|
1275
|
+
const result = await requestOnce("OPEN_ARTWORK", JSON.stringify({ artworkId }), 15e3);
|
|
1276
|
+
if (!result) {
|
|
1277
|
+
return asText("No response from the editor tab \u2014 is it open and visible?");
|
|
1278
|
+
}
|
|
1279
|
+
if (!result.ok) {
|
|
1280
|
+
return asText(`Could not open the artwork: ${result.error || "unknown error"}`);
|
|
1281
|
+
}
|
|
1282
|
+
return asText({ opened: artworkId });
|
|
1283
|
+
} catch (err) {
|
|
1284
|
+
return asError(err);
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
);
|
|
1092
1288
|
const updateSceneParams = {
|
|
1093
|
-
|
|
1289
|
+
artworkId: z.string().optional().describe("Target artwork id. Omit to write the one open in the editor tab."),
|
|
1290
|
+
expectedRevision: z.number().int().min(0).describe("The sceneRevision your edit is based on, from your latest read of THIS artwork."),
|
|
1094
1291
|
scene: z.record(z.any()).describe(
|
|
1095
1292
|
"The complete scene document to store: { version, settings?, assets, scenes }. Top-level keys are frozen to these; per-child additive keys are allowed."
|
|
1096
1293
|
),
|
|
@@ -1100,13 +1297,14 @@ function registerTools(server2, client2, options) {
|
|
|
1100
1297
|
};
|
|
1101
1298
|
server2.tool(
|
|
1102
1299
|
"update_scene",
|
|
1103
|
-
`Write
|
|
1300
|
+
`Write an artwork's scene (whole-document, revision-checked; account-scoped \u2014 any panels project you own, open in the editor or not). If the artwork is open, the user's editor applies the change LIVE and shows your ops descriptions, so keep ops honest and human-readable. Rules: send the COMPLETE document (this is not a patch \u2014 anything you omit is deleted); preserve any unknown keys you read; base expectedRevision on your latest read of the SAME artwork. On a revision conflict nothing is written \u2014 the response includes the current scene so you can rebase and retry. ${SCENE_CONTRACT}`,
|
|
1104
1301
|
updateSceneParams,
|
|
1105
1302
|
async (args) => {
|
|
1106
|
-
const { expectedRevision, scene, ops } = args;
|
|
1303
|
+
const { artworkId, expectedRevision, scene, ops } = args;
|
|
1107
1304
|
try {
|
|
1305
|
+
const id = await resolveArtworkId(artworkId);
|
|
1108
1306
|
const result = await client2.agent.saveScene({
|
|
1109
|
-
artworkId:
|
|
1307
|
+
artworkId: id,
|
|
1110
1308
|
doc: scene,
|
|
1111
1309
|
expectedRevision,
|
|
1112
1310
|
ops,
|
|
@@ -1115,6 +1313,7 @@ function registerTools(server2, client2, options) {
|
|
|
1115
1313
|
if (result.conflict) {
|
|
1116
1314
|
return asText({
|
|
1117
1315
|
conflict: true,
|
|
1316
|
+
artworkId: id,
|
|
1118
1317
|
message: "Not written: the scene changed since your read (the human may have edited it). Rebase your change onto the current scene below and retry with its revision.",
|
|
1119
1318
|
currentRevision: result.currentRevision,
|
|
1120
1319
|
currentScene: result.current ? parseSceneConfig(result.current.sceneConfig) : null
|
|
@@ -1122,8 +1321,9 @@ function registerTools(server2, client2, options) {
|
|
|
1122
1321
|
}
|
|
1123
1322
|
return asText({
|
|
1124
1323
|
conflict: false,
|
|
1324
|
+
artworkId: id,
|
|
1125
1325
|
revision: result.revision,
|
|
1126
|
-
message: `Saved (revision ${result.revision})
|
|
1326
|
+
message: `Saved (revision ${result.revision}).`
|
|
1127
1327
|
});
|
|
1128
1328
|
} catch (err) {
|
|
1129
1329
|
return asError(err);
|
|
@@ -1132,10 +1332,11 @@ function registerTools(server2, client2, options) {
|
|
|
1132
1332
|
);
|
|
1133
1333
|
server2.tool(
|
|
1134
1334
|
"publish_artwork",
|
|
1135
|
-
`Publish
|
|
1136
|
-
|
|
1335
|
+
`Publish an artwork so it is publicly viewable at its launch URL (${options.launchBaseUrl}/<artworkId>). Only works when the artwork is in draft or disabled state \u2014 already-published artworks reject (their live page already reflects saved scene changes). Omit artworkId to target the open artwork. Ask the user before publishing unless they already told you to.`,
|
|
1336
|
+
artworkIdParam,
|
|
1337
|
+
async (args) => {
|
|
1137
1338
|
try {
|
|
1138
|
-
const id = await artworkId
|
|
1339
|
+
const id = await resolveArtworkId(args.artworkId);
|
|
1139
1340
|
const artwork = await client2.agent.publish(id);
|
|
1140
1341
|
return asText({
|
|
1141
1342
|
id: artwork.id,
|
|
@@ -1145,67 +1346,29 @@ function registerTools(server2, client2, options) {
|
|
|
1145
1346
|
} catch (err) {
|
|
1146
1347
|
if (err instanceof GraphQLRequestError && `${err.errorType} ${err.message}`.includes("ConditionalCheckFailed")) {
|
|
1147
1348
|
return asText(
|
|
1148
|
-
"Nothing to do: the artwork is already published (publish only transitions draft/disabled \u2192 published). Saved scene changes are already live on its launch URL."
|
|
1349
|
+
"Nothing to do: the artwork is already published (publish only transitions draft/disabled \u2192 published), or the account does not own it. Saved scene changes are already live on its launch URL."
|
|
1149
1350
|
);
|
|
1150
1351
|
}
|
|
1151
1352
|
return asError(err);
|
|
1152
1353
|
}
|
|
1153
1354
|
}
|
|
1154
1355
|
);
|
|
1155
|
-
let resultSub = null;
|
|
1156
|
-
const pendingResults = /* @__PURE__ */ new Map();
|
|
1157
|
-
const ensureResultListener = async () => {
|
|
1158
|
-
if (resultSub) return;
|
|
1159
|
-
resultSub = client2.agent.watchActionResults(options.sessionId, {
|
|
1160
|
-
next: (result) => {
|
|
1161
|
-
const resolve = pendingResults.get(result.requestId);
|
|
1162
|
-
if (resolve) {
|
|
1163
|
-
pendingResults.delete(result.requestId);
|
|
1164
|
-
resolve(result);
|
|
1165
|
-
}
|
|
1166
|
-
},
|
|
1167
|
-
error: (err) => {
|
|
1168
|
-
console.error("[eyejack-mcp] action result channel error:", err);
|
|
1169
|
-
}
|
|
1170
|
-
});
|
|
1171
|
-
await new Promise((resolve) => setTimeout(resolve, 800));
|
|
1172
|
-
};
|
|
1173
|
-
const requestOnce = async (payload, timeoutMs) => {
|
|
1174
|
-
const requestId = randomUUID();
|
|
1175
|
-
const waiter = new Promise((resolve) => {
|
|
1176
|
-
const timer = setTimeout(() => {
|
|
1177
|
-
pendingResults.delete(requestId);
|
|
1178
|
-
resolve(null);
|
|
1179
|
-
}, timeoutMs);
|
|
1180
|
-
pendingResults.set(requestId, (result) => {
|
|
1181
|
-
clearTimeout(timer);
|
|
1182
|
-
resolve(result);
|
|
1183
|
-
});
|
|
1184
|
-
});
|
|
1185
|
-
await client2.agent.requestAction({
|
|
1186
|
-
sessionId: options.sessionId,
|
|
1187
|
-
requestId,
|
|
1188
|
-
actionType: "SCREENSHOT",
|
|
1189
|
-
payload
|
|
1190
|
-
});
|
|
1191
|
-
return waiter;
|
|
1192
|
-
};
|
|
1193
1356
|
const screenshotParams = {
|
|
1194
1357
|
width: z.number().int().min(64).max(2048).optional().describe("Image width in px (default 1280; height follows the editor aspect)."),
|
|
1195
1358
|
quality: z.number().min(0.1).max(1).optional().describe("JPEG quality 0.1\u20131 (default 0.8; auto-degraded if the image exceeds the transport cap).")
|
|
1196
1359
|
};
|
|
1197
1360
|
server2.tool(
|
|
1198
1361
|
"screenshot",
|
|
1199
|
-
"See the scene: capture a rendered JPEG of
|
|
1362
|
+
"See the scene: capture a rendered JPEG of whatever artwork the user's editor tab currently shows (use open_artwork to switch it first). Use it to visually verify your update_scene edits (read \u2192 edit \u2192 screenshot \u2192 correct). Requires the editor tab open in a desktop browser; if nobody answers, the tab is closed, hidden, or on a touch device.",
|
|
1200
1363
|
screenshotParams,
|
|
1201
1364
|
async (args) => {
|
|
1202
1365
|
const { width, quality } = args;
|
|
1203
1366
|
try {
|
|
1204
1367
|
await ensureResultListener();
|
|
1205
1368
|
const payload = JSON.stringify({ width: width ?? 1280, quality: quality ?? 0.8 });
|
|
1206
|
-
let result = await requestOnce(payload, 2e4);
|
|
1369
|
+
let result = await requestOnce("SCREENSHOT", payload, 2e4);
|
|
1207
1370
|
if (!result) {
|
|
1208
|
-
result = await requestOnce(payload, 2e4);
|
|
1371
|
+
result = await requestOnce("SCREENSHOT", payload, 2e4);
|
|
1209
1372
|
}
|
|
1210
1373
|
if (!result) {
|
|
1211
1374
|
return asText(
|
|
@@ -1231,14 +1394,23 @@ function registerTools(server2, client2, options) {
|
|
|
1231
1394
|
);
|
|
1232
1395
|
server2.tool(
|
|
1233
1396
|
"get_session_status",
|
|
1234
|
-
"Inspect the agent session
|
|
1397
|
+
"Inspect the agent session: whether it is active, when it expires (the editor tab heartbeats it while open; closing the tab lets it lapse), and which artwork the tab currently shows (activeArtworkId; null when no panels page is open).",
|
|
1235
1398
|
async () => {
|
|
1236
1399
|
try {
|
|
1237
1400
|
const session = await client2.agent.getSession();
|
|
1238
1401
|
const now = Math.floor(Date.now() / 1e3);
|
|
1402
|
+
let activeArtworkId = null;
|
|
1403
|
+
try {
|
|
1404
|
+
await ensureResultListener();
|
|
1405
|
+
const pong = await requestOnce("PING", null, 5e3);
|
|
1406
|
+
if (pong?.ok && pong.payload) {
|
|
1407
|
+
activeArtworkId = JSON.parse(pong.payload).activeArtworkId ?? null;
|
|
1408
|
+
}
|
|
1409
|
+
} catch {
|
|
1410
|
+
}
|
|
1239
1411
|
return asText({
|
|
1240
|
-
artworkId: session.artworkId,
|
|
1241
1412
|
status: session.status,
|
|
1413
|
+
activeArtworkId,
|
|
1242
1414
|
expiresAt: session.expiresAt,
|
|
1243
1415
|
expiresInSeconds: session.expiresAt ? session.expiresAt - now : null,
|
|
1244
1416
|
lastHeartbeat: session.lastHeartbeat
|
|
@@ -1261,7 +1433,7 @@ var client = createCreatorClient({
|
|
|
1261
1433
|
});
|
|
1262
1434
|
var server = new McpServer({
|
|
1263
1435
|
name: "eyejack-creator",
|
|
1264
|
-
version: "0.
|
|
1436
|
+
version: "0.4.0"
|
|
1265
1437
|
});
|
|
1266
1438
|
registerTools(server, client, {
|
|
1267
1439
|
launchBaseUrl: config.launchBaseUrl,
|
|
@@ -1270,4 +1442,4 @@ registerTools(server, client, {
|
|
|
1270
1442
|
});
|
|
1271
1443
|
var transport = new StdioServerTransport();
|
|
1272
1444
|
await server.connect(transport);
|
|
1273
|
-
console.error(`eyejack-mcp connected (stage: ${config.stage}) \u2014
|
|
1445
|
+
console.error(`eyejack-mcp connected (stage: ${config.stage}) \u2014 account-scoped workspace tools ready.`);
|
package/package.json
CHANGED