@eyejack-creator/mcp 0.1.0 → 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.
Files changed (3) hide show
  1. package/README.md +6 -4
  2. package/dist/index.js +313 -173
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @eyejack-creator/mcp
2
2
 
3
- EyeJack Creator MCP server — connect **Claude Code / Codex / Cursor** to a live EyeJack editing session. Phase 3 of the agentic roadmap: **read-only** (scene inspection); writes, screenshots, and live sync arrive in later phases.
3
+ EyeJack Creator MCP server — connect **Claude Code / Codex / Cursor** to a live EyeJack editing session. Your AI reads the scene **and edits it**: revision-checked writes land in the open editor live, with a human-readable action log. Screenshots arrive in a later phase.
4
4
 
5
5
  ```
6
6
  Claude Code ──stdio──► @eyejack/mcp ──► @eyejack/creator-api ──► AppSync (agent session token)
@@ -19,16 +19,18 @@ claude mcp add eyejack \
19
19
 
20
20
  Other MCP clients consume the same `{command, args, env}` triple — Cursor (`.cursor/mcp.json` or Settings → MCP), Claude Desktop (`claude_desktop_config.json`), Codex (`~/.codex/config.toml` `mcp_servers`).
21
21
 
22
- 2. Ask away: *"What's in my scene?" · "Which assets aren't placed yet?" · "How is the layout arranged?"*
22
+ 2. Ask away: *"What's in my scene?" · "Arrange the panels in a row" · "Duplicate the logo panel and place it on the left"* — edits appear in your open editor as they happen.
23
23
 
24
24
  The token lives as long as your editor tab (the tab heartbeats it; closing the tab lets it lapse within minutes). Pressing Connect AI anywhere supersedes the previous session — one active AI session per account.
25
25
 
26
- ## Tools (v0)
26
+ ## Tools
27
27
 
28
- | Tool | Returns |
28
+ | Tool | Does |
29
29
  |---|---|
30
30
  | `get_scene` | Full structured snapshot: metadata, parsed scene document (unity-space, degrees), `sceneRevision`, referenced files |
31
31
  | `list_assets` | The artwork's asset files with `usedInScene` resolution |
32
+ | `update_scene` | Whole-document, revision-checked scene write — applied **live** in the open editor with your `ops` action log; conflicts return the current scene to rebase on |
33
+ | `publish_artwork` | Publish the artwork to its public launch URL (draft/disabled → published) |
32
34
  | `get_session_status` | Session scope, active state, expiry |
33
35
 
34
36
  Tool descriptions embed the scene-format contract so models handle coordinates/conventions correctly without external docs.
package/dist/index.js CHANGED
@@ -282,100 +282,6 @@ var STAGES = {
282
282
  }
283
283
  };
284
284
 
285
- // ../creator-api/dist/domains/agent.js
286
- var CREATE_AGENT_SESSION = `
287
- mutation CreateAgentSession($artworkId: ID!) {
288
- createAgentSession(artworkId: $artworkId) {
289
- sessionId
290
- token
291
- artworkId
292
- expiresAt
293
- }
294
- }
295
- `;
296
- var AGENT_SESSION_HEARTBEAT = `
297
- mutation AgentSessionHeartbeat($sessionId: ID!) {
298
- agentSessionHeartbeat(sessionId: $sessionId) {
299
- sessionId
300
- artworkId
301
- status
302
- expiresAt
303
- lastHeartbeat
304
- }
305
- }
306
- `;
307
- var END_AGENT_SESSION = `
308
- mutation EndAgentSession($sessionId: ID!) {
309
- endAgentSession(sessionId: $sessionId) {
310
- sessionId
311
- artworkId
312
- status
313
- expiresAt
314
- }
315
- }
316
- `;
317
- var AGENT_GET_SESSION = `
318
- query AgentGetSession {
319
- agentGetSession {
320
- sessionId
321
- artworkId
322
- userid
323
- status
324
- expiresAt
325
- lastHeartbeat
326
- }
327
- }
328
- `;
329
- var AGENT_GET_SCENE = `
330
- query AgentGetScene {
331
- agentGetScene {
332
- artworkId
333
- name
334
- status
335
- sceneConfig
336
- sceneRevision
337
- modified
338
- files {
339
- type
340
- path
341
- checksum
342
- fileID
343
- size
344
- width
345
- height
346
- pz
347
- }
348
- }
349
- }
350
- `;
351
- var AgentApi = class {
352
- constructor(client2) {
353
- this.client = client2;
354
- }
355
- // --- Browser-side (Cognito) session lifecycle ---
356
- async createSession(artworkId) {
357
- const data = await this.client.execute(CREATE_AGENT_SESSION, { artworkId });
358
- return data.createAgentSession;
359
- }
360
- async heartbeat(sessionId) {
361
- const data = await this.client.execute(AGENT_SESSION_HEARTBEAT, { sessionId });
362
- return data.agentSessionHeartbeat;
363
- }
364
- async endSession(sessionId) {
365
- const data = await this.client.execute(END_AGENT_SESSION, { sessionId });
366
- return data.endAgentSession;
367
- }
368
- // --- Agent-side (session token) scoped reads ---
369
- async getSession() {
370
- const data = await this.client.execute(AGENT_GET_SESSION);
371
- return data.agentGetSession;
372
- }
373
- async getScene() {
374
- const data = await this.client.execute(AGENT_GET_SCENE);
375
- return data.agentGetScene;
376
- }
377
- };
378
-
379
285
  // ../creator-api/dist/documents/artworks.js
380
286
  var ARTWORK_FIELDS = `
381
287
  id
@@ -546,79 +452,6 @@ var DELETE_ARTWORK = `
546
452
  }
547
453
  `;
548
454
 
549
- // ../creator-api/dist/domains/artworks.js
550
- var ArtworksApi = class {
551
- constructor(client2, realtime) {
552
- this.client = client2;
553
- this.realtime = realtime;
554
- }
555
- /**
556
- * Live artwork-record updates for a user (fattened onUpdateArtwork) over the
557
- * SDK's realtime client. Payload fields are null for writers whose mutation
558
- * selection didn't include them (e.g. creator-electron) — guard accordingly.
559
- */
560
- watchUser(userid, handlers) {
561
- if (!this.realtime)
562
- throw new Error("ArtworksApi.watchUser: realtime client not configured");
563
- return this.realtime().subscribe(ON_UPDATE_ARTWORK, { userid }, {
564
- next: (data) => {
565
- if (data?.onUpdateArtwork)
566
- handlers.next(data.onUpdateArtwork);
567
- },
568
- error: handlers.error
569
- });
570
- }
571
- async get(id) {
572
- const data = await this.client.execute(GET_ARTWORK, { id });
573
- return data.getArtwork;
574
- }
575
- /** Batch fetch by ids (DynamoDB BatchGetItem under the hood). Missing ids are dropped. */
576
- async getMany(ids) {
577
- if (ids.length === 0)
578
- return [];
579
- const data = await this.client.execute(GET_ARTWORKS, {
580
- ids
581
- });
582
- return (data.getArtworks || []).filter((a) => !!a);
583
- }
584
- async listByUser(userid, after) {
585
- const data = await this.client.execute(GET_ARTWORKS_BY_USERID, { userid, after: after ?? null });
586
- return data.getArtworksByUserid;
587
- }
588
- async create(input) {
589
- const data = await this.client.execute(CREATE_ARTWORK, { input });
590
- return data.createArtwork;
591
- }
592
- /**
593
- * Legacy whole-record update — the same mutation the editor has always used.
594
- * The server bumps sceneRevision whenever input contains a non-null
595
- * sceneConfig. Fields absent from the input are left untouched; fields
596
- * explicitly null are REMOVED from the record (VTL semantics) — mirror the
597
- * legacy payload shape when swapping call sites.
598
- */
599
- async update(input) {
600
- const data = await this.client.execute(UPDATE_ARTWORK, {
601
- input
602
- });
603
- return data.updateArtwork;
604
- }
605
- async publish(id) {
606
- const data = await this.client.execute(PUBLISH_ARTWORK, { id });
607
- return data.publishArtwork;
608
- }
609
- async unpublish(id) {
610
- const data = await this.client.execute(UNPUBLISH_ARTWORK, { id });
611
- return data.unpublishArtwork;
612
- }
613
- /** Soft delete (status='deleted') via the backend's delete pipeline. */
614
- async remove(id) {
615
- const data = await this.client.execute(DELETE_ARTWORK, {
616
- input: { id }
617
- });
618
- return data.deleteArtwork;
619
- }
620
- };
621
-
622
455
  // ../creator-api/dist/domains/scene.js
623
456
  var MAX_DOC_BYTES = 3e5;
624
457
  function parseSceneConfig(raw) {
@@ -781,6 +614,241 @@ var SceneApi = class {
781
614
  }
782
615
  };
783
616
 
617
+ // ../creator-api/dist/domains/agent.js
618
+ var CREATE_AGENT_SESSION = `
619
+ mutation CreateAgentSession($artworkId: ID!) {
620
+ createAgentSession(artworkId: $artworkId) {
621
+ sessionId
622
+ token
623
+ artworkId
624
+ expiresAt
625
+ }
626
+ }
627
+ `;
628
+ var AGENT_SESSION_HEARTBEAT = `
629
+ mutation AgentSessionHeartbeat($sessionId: ID!) {
630
+ agentSessionHeartbeat(sessionId: $sessionId) {
631
+ sessionId
632
+ artworkId
633
+ status
634
+ expiresAt
635
+ lastHeartbeat
636
+ }
637
+ }
638
+ `;
639
+ var END_AGENT_SESSION = `
640
+ mutation EndAgentSession($sessionId: ID!) {
641
+ endAgentSession(sessionId: $sessionId) {
642
+ sessionId
643
+ artworkId
644
+ status
645
+ expiresAt
646
+ }
647
+ }
648
+ `;
649
+ var AGENT_GET_SESSION = `
650
+ query AgentGetSession {
651
+ agentGetSession {
652
+ sessionId
653
+ artworkId
654
+ userid
655
+ status
656
+ expiresAt
657
+ lastHeartbeat
658
+ }
659
+ }
660
+ `;
661
+ var GET_ARTWORK_SCENE_BACKUP = `
662
+ query GetArtworkSceneBackup($artworkId: ID!) {
663
+ getArtworkSceneBackup(artworkId: $artworkId) {
664
+ artworkId
665
+ sessionId
666
+ sceneConfig
667
+ sceneRevision
668
+ createdAt
669
+ }
670
+ }
671
+ `;
672
+ var AGENT_GET_SCENE = `
673
+ query AgentGetScene {
674
+ agentGetScene {
675
+ artworkId
676
+ name
677
+ status
678
+ sceneConfig
679
+ sceneRevision
680
+ modified
681
+ files {
682
+ type
683
+ path
684
+ checksum
685
+ fileID
686
+ size
687
+ width
688
+ height
689
+ pz
690
+ }
691
+ }
692
+ }
693
+ `;
694
+ var AgentApi = class {
695
+ constructor(client2) {
696
+ this.client = client2;
697
+ }
698
+ // --- Browser-side (Cognito) session lifecycle ---
699
+ async createSession(artworkId) {
700
+ const data = await this.client.execute(CREATE_AGENT_SESSION, { artworkId });
701
+ return data.createAgentSession;
702
+ }
703
+ async heartbeat(sessionId) {
704
+ const data = await this.client.execute(AGENT_SESSION_HEARTBEAT, { sessionId });
705
+ return data.agentSessionHeartbeat;
706
+ }
707
+ async endSession(sessionId) {
708
+ const data = await this.client.execute(END_AGENT_SESSION, { sessionId });
709
+ return data.endAgentSession;
710
+ }
711
+ /**
712
+ * The scene snapshot createAgentSession took before the (most recent)
713
+ * session started — what "Revert AI changes" restores. Browser-side
714
+ * (Cognito, owner-checked); null when no session ever ran or the 7-day
715
+ * TTL reaped it.
716
+ */
717
+ async getSceneBackup(artworkId) {
718
+ const data = await this.client.execute(GET_ARTWORK_SCENE_BACKUP, { artworkId });
719
+ return data.getArtworkSceneBackup;
720
+ }
721
+ // --- Agent-side (session token) scoped reads ---
722
+ async getSession() {
723
+ const data = await this.client.execute(AGENT_GET_SESSION);
724
+ return data.agentGetSession;
725
+ }
726
+ async getScene() {
727
+ const data = await this.client.execute(AGENT_GET_SCENE);
728
+ return data.agentGetScene;
729
+ }
730
+ // --- Agent-side (session token) writes (P4) ---
731
+ /**
732
+ * Revision-checked scene write with a session token. Same mutation and
733
+ * fan-out as SceneApi.save, but the conflict re-read uses agentGetScene
734
+ * (a session token cannot call getArtwork) and hands back the fresh record
735
+ * so the agent can rebase in one round trip. The backend hard-scopes the
736
+ * write to the session's artwork regardless of artworkId.
737
+ */
738
+ async saveScene(params) {
739
+ const raw = typeof params.doc === "string" ? params.doc : stringifySceneDocument(params.doc);
740
+ if (typeof params.doc !== "string") {
741
+ const errors = validateSceneDocument(params.doc, params.validateAgainstFiles).filter((i) => i.level === "error");
742
+ if (errors.length > 0) {
743
+ throw new Error(`Refusing to save invalid scene document: ${errors.map((i) => i.message).join("; ")}`);
744
+ }
745
+ }
746
+ try {
747
+ const data = await this.client.execute(UPDATE_ARTWORK_SCENE, {
748
+ input: {
749
+ id: params.artworkId,
750
+ expectedRevision: params.expectedRevision,
751
+ sceneConfig: raw,
752
+ ops: params.ops ?? null,
753
+ originId: params.originId ?? null
754
+ }
755
+ });
756
+ const payload = data.updateArtworkScene;
757
+ return { conflict: false, revision: payload.sceneRevision, modified: payload.modified ?? null, payload };
758
+ } catch (err) {
759
+ if (err instanceof GraphQLRequestError && err.errorType === "SceneRevisionConflict") {
760
+ const current = await this.getScene().catch(() => null);
761
+ return { conflict: true, currentRevision: current?.sceneRevision ?? null, current };
762
+ }
763
+ throw err;
764
+ }
765
+ }
766
+ /**
767
+ * Publish the session's artwork (guarded server-side: a session token can
768
+ * publish ONLY its own artwork; publish transitions draft/disabled →
769
+ * published — an already-published artwork rejects with a condition error).
770
+ */
771
+ async publish(artworkId) {
772
+ const data = await this.client.execute(PUBLISH_ARTWORK, {
773
+ id: artworkId
774
+ });
775
+ return data.publishArtwork;
776
+ }
777
+ };
778
+
779
+ // ../creator-api/dist/domains/artworks.js
780
+ var ArtworksApi = class {
781
+ constructor(client2, realtime) {
782
+ this.client = client2;
783
+ this.realtime = realtime;
784
+ }
785
+ /**
786
+ * Live artwork-record updates for a user (fattened onUpdateArtwork) over the
787
+ * SDK's realtime client. Payload fields are null for writers whose mutation
788
+ * selection didn't include them (e.g. creator-electron) — guard accordingly.
789
+ */
790
+ watchUser(userid, handlers) {
791
+ if (!this.realtime)
792
+ throw new Error("ArtworksApi.watchUser: realtime client not configured");
793
+ return this.realtime().subscribe(ON_UPDATE_ARTWORK, { userid }, {
794
+ next: (data) => {
795
+ if (data?.onUpdateArtwork)
796
+ handlers.next(data.onUpdateArtwork);
797
+ },
798
+ error: handlers.error
799
+ });
800
+ }
801
+ async get(id) {
802
+ const data = await this.client.execute(GET_ARTWORK, { id });
803
+ return data.getArtwork;
804
+ }
805
+ /** Batch fetch by ids (DynamoDB BatchGetItem under the hood). Missing ids are dropped. */
806
+ async getMany(ids) {
807
+ if (ids.length === 0)
808
+ return [];
809
+ const data = await this.client.execute(GET_ARTWORKS, {
810
+ ids
811
+ });
812
+ return (data.getArtworks || []).filter((a) => !!a);
813
+ }
814
+ async listByUser(userid, after) {
815
+ const data = await this.client.execute(GET_ARTWORKS_BY_USERID, { userid, after: after ?? null });
816
+ return data.getArtworksByUserid;
817
+ }
818
+ async create(input) {
819
+ const data = await this.client.execute(CREATE_ARTWORK, { input });
820
+ return data.createArtwork;
821
+ }
822
+ /**
823
+ * Legacy whole-record update — the same mutation the editor has always used.
824
+ * The server bumps sceneRevision whenever input contains a non-null
825
+ * sceneConfig. Fields absent from the input are left untouched; fields
826
+ * explicitly null are REMOVED from the record (VTL semantics) — mirror the
827
+ * legacy payload shape when swapping call sites.
828
+ */
829
+ async update(input) {
830
+ const data = await this.client.execute(UPDATE_ARTWORK, {
831
+ input
832
+ });
833
+ return data.updateArtwork;
834
+ }
835
+ async publish(id) {
836
+ const data = await this.client.execute(PUBLISH_ARTWORK, { id });
837
+ return data.publishArtwork;
838
+ }
839
+ async unpublish(id) {
840
+ const data = await this.client.execute(UNPUBLISH_ARTWORK, { id });
841
+ return data.unpublishArtwork;
842
+ }
843
+ /** Soft delete (status='deleted') via the backend's delete pipeline. */
844
+ async remove(id) {
845
+ const data = await this.client.execute(DELETE_ARTWORK, {
846
+ input: { id }
847
+ });
848
+ return data.deleteArtwork;
849
+ }
850
+ };
851
+
784
852
  // ../creator-api/dist/client.js
785
853
  function createCreatorClient(options) {
786
854
  const endpoint = options.endpoint ?? (options.stage ? STAGES[options.stage].graphqlEndpoint : void 0);
@@ -841,10 +909,12 @@ function loadConfig() {
841
909
  console.error(`Unknown EYEJACK_STAGE "${stage}" and no EYEJACK_GRAPHQL_ENDPOINT set.`);
842
910
  process.exit(1);
843
911
  }
844
- return { token, endpoint, stage };
912
+ const launchBaseUrl = stage === "prd" ? "https://launch.eyejack.io" : "https://launch.eyejack.dev";
913
+ return { token, endpoint, stage, launchBaseUrl };
845
914
  }
846
915
 
847
916
  // src/tools.ts
917
+ import { z } from "zod";
848
918
  var SCENE_CONTRACT = `Scene document conventions (the stored EyeJack format):
849
919
  - Coordinate space is unity-style left-handed; rotations are in DEGREES.
850
920
  - Only scenes[0] is rendered ("root"); its children[] are the panels/models.
@@ -852,7 +922,7 @@ var SCENE_CONTRACT = `Scene document conventions (the stored EyeJack format):
852
922
  - A child's assetID must equal a files[].fileID (the asset it displays).
853
923
  - files[] entries carry type (image | video | video-alpha | glb | image-target | cover), path (CDN url), width, height.
854
924
  - Assets are provisioned by the human in the browser for now \u2014 there is no upload tool yet.
855
- - sceneRevision is the concurrency counter; every write bumps it (writes arrive in a later phase).`;
925
+ - sceneRevision is the optimistic-concurrency counter: pass your latest read's revision as expectedRevision when writing; every successful write bumps it by 1.`;
856
926
  var REJECTED_HINT = 'Session token rejected (expired, ended, or superseded). Ask the user to press "Connect AI" in the EyeJack editor again and update EYEJACK_SESSION_TOKEN.';
857
927
  function errorText(err) {
858
928
  if (err instanceof TransportError && (err.status === 401 || err.status === 403)) {
@@ -873,7 +943,14 @@ var asError = (err) => ({
873
943
  content: [{ type: "text", text: errorText(err) }],
874
944
  isError: true
875
945
  });
876
- function registerTools(server2, client2) {
946
+ function registerTools(server2, client2, options) {
947
+ let cachedArtworkId = null;
948
+ const artworkId = async () => {
949
+ if (!cachedArtworkId) {
950
+ cachedArtworkId = (await client2.agent.getSession()).artworkId;
951
+ }
952
+ return cachedArtworkId;
953
+ };
877
954
  server2.tool(
878
955
  "get_scene",
879
956
  `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}`,
@@ -925,6 +1002,69 @@ function registerTools(server2, client2) {
925
1002
  }
926
1003
  }
927
1004
  );
1005
+ const updateSceneParams = {
1006
+ expectedRevision: z.number().int().min(0).describe("The sceneRevision your edit is based on, from your latest read."),
1007
+ scene: z.record(z.any()).describe(
1008
+ "The complete scene document to store: { version, settings?, assets, scenes }. Top-level keys are frozen to these; per-child additive keys are allowed."
1009
+ ),
1010
+ ops: z.array(z.string().min(1).max(200)).min(1).max(10).describe(
1011
+ 'Short human-readable summaries of what changed (e.g. "Moved the logo panel 0.5 left"). Shown live to the user in their editor.'
1012
+ )
1013
+ };
1014
+ server2.tool(
1015
+ "update_scene",
1016
+ `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}`,
1017
+ updateSceneParams,
1018
+ async (args) => {
1019
+ const { expectedRevision, scene, ops } = args;
1020
+ try {
1021
+ const result = await client2.agent.saveScene({
1022
+ artworkId: await artworkId(),
1023
+ doc: scene,
1024
+ expectedRevision,
1025
+ ops,
1026
+ originId: "agent:mcp"
1027
+ });
1028
+ if (result.conflict) {
1029
+ return asText({
1030
+ conflict: true,
1031
+ 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.",
1032
+ currentRevision: result.currentRevision,
1033
+ currentScene: result.current ? parseSceneConfig(result.current.sceneConfig) : null
1034
+ });
1035
+ }
1036
+ return asText({
1037
+ conflict: false,
1038
+ revision: result.revision,
1039
+ message: `Saved (revision ${result.revision}). The user's editor has applied your change live.`
1040
+ });
1041
+ } catch (err) {
1042
+ return asError(err);
1043
+ }
1044
+ }
1045
+ );
1046
+ server2.tool(
1047
+ "publish_artwork",
1048
+ `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.`,
1049
+ async () => {
1050
+ try {
1051
+ const id = await artworkId();
1052
+ const artwork = await client2.agent.publish(id);
1053
+ return asText({
1054
+ id: artwork.id,
1055
+ status: artwork.status,
1056
+ url: `${options.launchBaseUrl}/${artwork.id}`
1057
+ });
1058
+ } catch (err) {
1059
+ if (err instanceof GraphQLRequestError && `${err.errorType} ${err.message}`.includes("ConditionalCheckFailed")) {
1060
+ return asText(
1061
+ "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."
1062
+ );
1063
+ }
1064
+ return asError(err);
1065
+ }
1066
+ }
1067
+ );
928
1068
  server2.tool(
929
1069
  "get_session_status",
930
1070
  "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).",
@@ -954,9 +1094,9 @@ var client = createCreatorClient({
954
1094
  });
955
1095
  var server = new McpServer({
956
1096
  name: "eyejack-creator",
957
- version: "0.1.0"
1097
+ version: "0.2.0"
958
1098
  });
959
- registerTools(server, client);
1099
+ registerTools(server, client, { launchBaseUrl: config.launchBaseUrl });
960
1100
  var transport = new StdioServerTransport();
961
1101
  await server.connect(transport);
962
- console.error(`eyejack-mcp connected (stage: ${config.stage}) \u2014 read-only tools ready.`);
1102
+ console.error(`eyejack-mcp connected (stage: ${config.stage}) \u2014 scene read/write tools ready.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eyejack-creator/mcp",
3
- "version": "0.1.0",
3
+ "version": "0.2.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",
@@ -20,7 +20,7 @@
20
20
  },
21
21
  "dependencies": {
22
22
  "@modelcontextprotocol/sdk": "^1.0.0",
23
- "zod": "^3.23.0"
23
+ "zod": "3.24.2"
24
24
  },
25
25
  "devDependencies": {
26
26
  "esbuild": "^0.24.0",