@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.
Files changed (2) hide show
  1. package/dist/index.js +248 -76
  2. 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($artworkId: ID!) {
621
- createAgentSession(artworkId: $artworkId) {
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
- async createSession(artworkId) {
746
- const data = await this.client.execute(CREATE_AGENT_SESSION, { artworkId });
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
- async getScene() {
773
- const data = await this.client.execute(AGENT_GET_SCENE);
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: ${err.errorType || err.message}`;
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 cachedArtworkId = null;
1035
- const artworkId = async () => {
1036
- if (!cachedArtworkId) {
1037
- cachedArtworkId = (await client2.agent.getSession()).artworkId;
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
- return cachedArtworkId;
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
- "get_scene",
1043
- `Read the full structured snapshot of the connected EyeJack artwork: metadata, the parsed scene document, the current sceneRevision, and the asset files it references. This session's token is scoped to exactly one artwork \u2014 there is nothing to select. ${SCENE_CONTRACT}`,
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 record = await client2.agent.getScene();
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 the connected artwork \u2014 the palette the scene's panels can reference via assetID == fileID. Read-only.",
1072
- async () => {
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 record = await client2.agent.getScene();
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
- expectedRevision: z.number().int().min(0).describe("The sceneRevision your edit is based on, from your latest read."),
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 the connected artwork's scene (whole-document, revision-checked). The user's open 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 get_scene (or the revision returned by your last successful update_scene). On a revision conflict nothing is written \u2014 the response includes the current scene so you can rebase and retry. ${SCENE_CONTRACT}`,
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: await 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}). The user's editor has applied your change live.`
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 the connected 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). Ask the user before publishing unless they already told you to.`,
1136
- async () => {
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 the connected artwork exactly as the user's open editor shows it. 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.",
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 itself: which artwork it is scoped to, whether it is active, and when it expires (the editor tab heartbeats it while open; closing the tab lets it lapse).",
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.3.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 scene read/write + screenshot tools ready.`);
1445
+ console.error(`eyejack-mcp connected (stage: ${config.stage}) \u2014 account-scoped workspace tools ready.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eyejack-creator/mcp",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "EyeJack Creator MCP server — connect Claude Code / Codex / Cursor to a live EyeJack editing session.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",