@agent-deck/backend 1.2.8 → 1.2.10

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 (46) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/lib/agent-deck-context.d.ts +1 -1
  3. package/dist/lib/agent-deck-context.d.ts.map +1 -1
  4. package/dist/lib/agent-deck-context.js +7 -26
  5. package/dist/lib/agent-deck-context.js.map +1 -1
  6. package/dist/lib/client-scope.d.ts +4 -0
  7. package/dist/lib/client-scope.d.ts.map +1 -1
  8. package/dist/lib/client-scope.js +15 -1
  9. package/dist/lib/client-scope.js.map +1 -1
  10. package/dist/mcp-server.d.ts +8 -1
  11. package/dist/mcp-server.d.ts.map +1 -1
  12. package/dist/mcp-server.js +82 -216
  13. package/dist/mcp-server.js.map +1 -1
  14. package/dist/mcp-session-binding.d.ts +3 -4
  15. package/dist/mcp-session-binding.d.ts.map +1 -1
  16. package/dist/mcp-session-binding.js +3 -10
  17. package/dist/mcp-session-binding.js.map +1 -1
  18. package/dist/routes/scope.d.ts.map +1 -1
  19. package/dist/routes/scope.js +78 -58
  20. package/dist/routes/scope.js.map +1 -1
  21. package/dist/scope/badge.d.ts +3 -0
  22. package/dist/scope/badge.d.ts.map +1 -0
  23. package/dist/scope/badge.js +23 -0
  24. package/dist/scope/badge.js.map +1 -0
  25. package/dist/scope/bindings-sidecar.d.ts +2 -5
  26. package/dist/scope/bindings-sidecar.d.ts.map +1 -1
  27. package/dist/scope/bindings-sidecar.js +13 -17
  28. package/dist/scope/bindings-sidecar.js.map +1 -1
  29. package/dist/scope/display.d.ts +3 -3
  30. package/dist/scope/display.d.ts.map +1 -1
  31. package/dist/scope/display.js +41 -31
  32. package/dist/scope/display.js.map +1 -1
  33. package/dist/scope/live-display-registry.d.ts +25 -0
  34. package/dist/scope/live-display-registry.d.ts.map +1 -0
  35. package/dist/scope/live-display-registry.js +64 -0
  36. package/dist/scope/live-display-registry.js.map +1 -0
  37. package/dist/server/index.d.ts +2 -0
  38. package/dist/server/index.d.ts.map +1 -1
  39. package/dist/server/index.js +3 -0
  40. package/dist/server/index.js.map +1 -1
  41. package/package.json +2 -2
  42. package/static-ui/assets/index-CQoBRW1p.js +409 -0
  43. package/static-ui/assets/index-wleyGzod.css +1 -0
  44. package/static-ui/index.html +2 -2
  45. package/static-ui/assets/index-BnvBkGNu.js +0 -404
  46. package/static-ui/assets/index-D2upmaZj.css +0 -1
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AgentDeckContextError = void 0;
4
4
  exports.resolveAgentDeckId = resolveAgentDeckId;
5
5
  const shared_1 = require("@agent-deck/shared");
6
- const repo_deck_1 = require("../scope/repo-deck");
7
6
  class AgentDeckContextError extends Error {
8
7
  constructor(message) {
9
8
  super(message);
@@ -15,34 +14,16 @@ function headerValue(request, name) {
15
14
  const value = request.headers[name];
16
15
  return typeof value === 'string' && value.trim() ? value.trim() : undefined;
17
16
  }
18
- /** Resolve which deck an agent request is scoped to (workspace manifest or explicit deck id). */
17
+ /** Resolve which deck an agent request is scoped to (session deck id header only). */
19
18
  async function resolveAgentDeckId(request, db) {
20
19
  const deckIdHeader = headerValue(request, shared_1.AGENT_DECK_DECK_ID_HEADER);
21
- if (deckIdHeader) {
22
- const deck = await db.getDeck(deckIdHeader);
23
- if (!deck) {
24
- throw new AgentDeckContextError(`Deck not found: ${deckIdHeader}`);
25
- }
26
- return deckIdHeader;
20
+ if (!deckIdHeader) {
21
+ throw new AgentDeckContextError('No bound deck call bind_workspace with deckId (use get_decks to list decks) or switch_bound_deck');
27
22
  }
28
- const workspaceRoot = headerValue(request, shared_1.AGENT_DECK_WORKSPACE_HEADER);
29
- if (workspaceRoot) {
30
- let manifest;
31
- try {
32
- manifest = await (0, repo_deck_1.loadRepoDeckManifest)(workspaceRoot);
33
- }
34
- catch (error) {
35
- throw new AgentDeckContextError(error instanceof repo_deck_1.RepoDeckManifestError ? error.message : 'Failed to load repo deck manifest');
36
- }
37
- if (!manifest) {
38
- throw new AgentDeckContextError(`No ${'.agent-deck/deck.yaml'} found in workspace: ${workspaceRoot}`);
39
- }
40
- const deck = await db.getDeck(manifest.deck_id);
41
- if (!deck) {
42
- throw new AgentDeckContextError(`deck_id in repo manifest not found in Agent Deck: ${manifest.deck_id}`);
43
- }
44
- return manifest.deck_id;
23
+ const deck = await db.getDeck(deckIdHeader);
24
+ if (!deck) {
25
+ throw new AgentDeckContextError(`Deck not found: ${deckIdHeader}`);
45
26
  }
46
- throw new AgentDeckContextError('Agent deck context required: set x-agent-deck-workspace (with .agent-deck/deck.yaml) or x-agent-deck-deck-id');
27
+ return deckIdHeader;
47
28
  }
48
29
  //# sourceMappingURL=agent-deck-context.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-deck-context.js","sourceRoot":"","sources":["../../src/lib/agent-deck-context.ts"],"names":[],"mappings":";;;AAqBA,gDA2CC;AA/DD,+CAG4B;AAE5B,kDAAiF;AAEjF,MAAa,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AALD,sDAKC;AAED,SAAS,WAAW,CAAC,OAAuB,EAAE,IAAY;IACxD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9E,CAAC;AAED,iGAAiG;AAC1F,KAAK,UAAU,kBAAkB,CACtC,OAAuB,EACvB,EAAmB;IAEnB,MAAM,YAAY,GAAG,WAAW,CAAC,OAAO,EAAE,kCAAyB,CAAC,CAAC;IACrE,IAAI,YAAY,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,qBAAqB,CAAC,mBAAmB,YAAY,EAAE,CAAC,CAAC;QACrE,CAAC;QACD,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,MAAM,aAAa,GAAG,WAAW,CAAC,OAAO,EAAE,oCAA2B,CAAC,CAAC;IACxE,IAAI,aAAa,EAAE,CAAC;QAClB,IAAI,QAAQ,CAAC;QACb,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,IAAA,gCAAoB,EAAC,aAAa,CAAC,CAAC;QACvD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,qBAAqB,CAC7B,KAAK,YAAY,iCAAqB,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,mCAAmC,CAC7F,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,qBAAqB,CAC7B,MAAM,uBAAuB,wBAAwB,aAAa,EAAE,CACrE,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAChD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,qBAAqB,CAC7B,qDAAqD,QAAQ,CAAC,OAAO,EAAE,CACxE,CAAC;QACJ,CAAC;QAED,OAAO,QAAQ,CAAC,OAAO,CAAC;IAC1B,CAAC;IAED,MAAM,IAAI,qBAAqB,CAC7B,8GAA8G,CAC/G,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"agent-deck-context.js","sourceRoot":"","sources":["../../src/lib/agent-deck-context.ts"],"names":[],"mappings":";;;AAiBA,gDAgBC;AAhCD,+CAA+D;AAG/D,MAAa,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AALD,sDAKC;AAED,SAAS,WAAW,CAAC,OAAuB,EAAE,IAAY;IACxD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9E,CAAC;AAED,sFAAsF;AAC/E,KAAK,UAAU,kBAAkB,CACtC,OAAuB,EACvB,EAAmB;IAEnB,MAAM,YAAY,GAAG,WAAW,CAAC,OAAO,EAAE,kCAAyB,CAAC,CAAC;IACrE,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,IAAI,qBAAqB,CAC7B,oGAAoG,CACrG,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC5C,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,qBAAqB,CAAC,mBAAmB,YAAY,EAAE,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC"}
@@ -6,6 +6,10 @@ export declare function sanitizeServiceForAgent(service: Service): Service;
6
6
  export declare function getClientScope(request: FastifyRequest): ClientScope;
7
7
  export declare function isDashboardClient(request: FastifyRequest): boolean;
8
8
  export declare function requireDashboardClient(request: FastifyRequest): void;
9
+ export declare function requireAgentClient(request: FastifyRequest): void;
10
+ export declare class AgentClientOnlyError extends Error {
11
+ constructor(message?: string);
12
+ }
9
13
  export declare class DashboardOnlyError extends Error {
10
14
  constructor(message?: string);
11
15
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client-scope.d.ts","sourceRoot":"","sources":["../../src/lib/client-scope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAGL,IAAI,EACJ,OAAO,EACR,MAAM,oBAAoB,CAAC;AAE5B,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,OAAO,CAAC;AAchD,sEAAsE;AACtE,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAUjE;AASD,wBAAgB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,WAAW,CAMnE;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAElE;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAIpE;AAED,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,SAA4D;CAIhF;AAED,oFAAoF;AACpF,wBAAgB,cAAc,CAC5B,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,WAAW,EAClB,aAAa,CAAC,EAAE,MAAM,GACrB,IAAI,CASN;AAED,qCAAqC;AACrC,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,WAAW,EAClB,aAAa,CAAC,EAAE,MAAM,GACrB,IAAI,CAEN"}
1
+ {"version":3,"file":"client-scope.d.ts","sourceRoot":"","sources":["../../src/lib/client-scope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAIL,IAAI,EACJ,OAAO,EACR,MAAM,oBAAoB,CAAC;AAE5B,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,OAAO,CAAC;AAchD,sEAAsE;AACtE,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAUjE;AASD,wBAAgB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,WAAW,CAMnE;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAElE;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAIpE;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAKhE;AAED,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,SAAuD;CAI3E;AAED,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,SAA4D;CAIhF;AAED,oFAAoF;AACpF,wBAAgB,cAAc,CAC5B,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,WAAW,EAClB,aAAa,CAAC,EAAE,MAAM,GACrB,IAAI,CASN;AAED,qCAAqC;AACrC,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,WAAW,EAClB,aAAa,CAAC,EAAE,MAAM,GACrB,IAAI,CAEN"}
@@ -1,10 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DashboardOnlyError = void 0;
3
+ exports.DashboardOnlyError = exports.AgentClientOnlyError = void 0;
4
4
  exports.sanitizeServiceForAgent = sanitizeServiceForAgent;
5
5
  exports.getClientScope = getClientScope;
6
6
  exports.isDashboardClient = isDashboardClient;
7
7
  exports.requireDashboardClient = requireDashboardClient;
8
+ exports.requireAgentClient = requireAgentClient;
8
9
  exports.applyDeckScope = applyDeckScope;
9
10
  exports.applyDeckCredentialScope = applyDeckCredentialScope;
10
11
  const shared_1 = require("@agent-deck/shared");
@@ -50,6 +51,19 @@ function requireDashboardClient(request) {
50
51
  throw new DashboardOnlyError();
51
52
  }
52
53
  }
54
+ function requireAgentClient(request) {
55
+ const value = request.headers[shared_1.AGENT_DECK_CLIENT_HEADER];
56
+ if (typeof value !== 'string' || value.toLowerCase() !== shared_1.AGENT_DECK_AGENT_CLIENT) {
57
+ throw new AgentClientOnlyError();
58
+ }
59
+ }
60
+ class AgentClientOnlyError extends Error {
61
+ constructor(message = 'This operation requires an Agent Deck agent client') {
62
+ super(message);
63
+ this.name = 'AgentClientOnlyError';
64
+ }
65
+ }
66
+ exports.AgentClientOnlyError = AgentClientOnlyError;
53
67
  class DashboardOnlyError extends Error {
54
68
  constructor(message = 'This operation requires the Agent Deck dashboard client') {
55
69
  super(message);
@@ -1 +1 @@
1
- {"version":3,"file":"client-scope.js","sourceRoot":"","sources":["../../src/lib/client-scope.ts"],"names":[],"mappings":";;;AAuBA,0DAUC;AASD,wCAMC;AAED,8CAEC;AAED,wDAIC;AAUD,wCAaC;AAGD,4DAMC;AAzFD,+CAK4B;AAI5B,SAAS,wBAAwB,CAC/B,OAAuC;IAEvC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,IAAI,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;IAC5B,OAAO,IAAI,CAAC,aAAa,CAAC;IAC1B,OAAO,IAAI,CAAC,aAAa,CAAC;IAC1B,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AACzD,CAAC;AAED,sEAAsE;AACtE,SAAgB,uBAAuB,CAAC,OAAgB;IACtD,OAAO;QACL,GAAG,OAAO;QACV,iBAAiB,EAAE,SAAS;QAC5B,gBAAgB,EAAE,SAAS;QAC3B,iBAAiB,EAAE,SAAS;QAC5B,UAAU,EAAE,SAAS;QACrB,OAAO,EAAE,wBAAwB,CAAC,OAAO,CAAC,OAAO,CAAC;QAClD,QAAQ,EAAE,SAAS;KACpB,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAU;IACtC,OAAO;QACL,GAAG,IAAI;QACP,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,uBAAuB,CAAC,IAAI,EAAE;KAC5D,CAAC;AACJ,CAAC;AAED,SAAgB,cAAc,CAAC,OAAuB;IACpD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,iCAAwB,CAAC,CAAC;IACxD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,WAAW,EAAE,KAAK,oCAA2B,EAAE,CAAC;QACrF,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAgB,iBAAiB,CAAC,OAAuB;IACvD,OAAO,cAAc,CAAC,OAAO,CAAC,KAAK,WAAW,CAAC;AACjD,CAAC;AAED,SAAgB,sBAAsB,CAAC,OAAuB;IAC5D,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,kBAAkB,EAAE,CAAC;IACjC,CAAC;AACH,CAAC;AAED,MAAa,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,OAAO,GAAG,yDAAyD;QAC7E,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AALD,gDAKC;AAED,oFAAoF;AACpF,SAAgB,cAAc,CAC5B,IAAU,EACV,KAAkB,EAClB,aAAsB;IAEtB,IAAI,KAAK,KAAK,WAAW,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,SAAS,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,aAAa,IAAI,IAAI,CAAC,EAAE,KAAK,aAAa,EAAE,CAAC;QAC/C,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,EAAE,GAAG,SAAS,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;AAC1D,CAAC;AAED,qCAAqC;AACrC,SAAgB,wBAAwB,CACtC,IAAU,EACV,KAAkB,EAClB,aAAsB;IAEtB,OAAO,cAAc,CAAC,IAAI,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC;AACpD,CAAC"}
1
+ {"version":3,"file":"client-scope.js","sourceRoot":"","sources":["../../src/lib/client-scope.ts"],"names":[],"mappings":";;;AAwBA,0DAUC;AASD,wCAMC;AAED,8CAEC;AAED,wDAIC;AAED,gDAKC;AAiBD,wCAaC;AAGD,4DAMC;AAxGD,+CAM4B;AAI5B,SAAS,wBAAwB,CAC/B,OAAuC;IAEvC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,IAAI,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;IAC5B,OAAO,IAAI,CAAC,aAAa,CAAC;IAC1B,OAAO,IAAI,CAAC,aAAa,CAAC;IAC1B,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AACzD,CAAC;AAED,sEAAsE;AACtE,SAAgB,uBAAuB,CAAC,OAAgB;IACtD,OAAO;QACL,GAAG,OAAO;QACV,iBAAiB,EAAE,SAAS;QAC5B,gBAAgB,EAAE,SAAS;QAC3B,iBAAiB,EAAE,SAAS;QAC5B,UAAU,EAAE,SAAS;QACrB,OAAO,EAAE,wBAAwB,CAAC,OAAO,CAAC,OAAO,CAAC;QAClD,QAAQ,EAAE,SAAS;KACpB,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAU;IACtC,OAAO;QACL,GAAG,IAAI;QACP,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,uBAAuB,CAAC,IAAI,EAAE;KAC5D,CAAC;AACJ,CAAC;AAED,SAAgB,cAAc,CAAC,OAAuB;IACpD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,iCAAwB,CAAC,CAAC;IACxD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,WAAW,EAAE,KAAK,oCAA2B,EAAE,CAAC;QACrF,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAgB,iBAAiB,CAAC,OAAuB;IACvD,OAAO,cAAc,CAAC,OAAO,CAAC,KAAK,WAAW,CAAC;AACjD,CAAC;AAED,SAAgB,sBAAsB,CAAC,OAAuB;IAC5D,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,kBAAkB,EAAE,CAAC;IACjC,CAAC;AACH,CAAC;AAED,SAAgB,kBAAkB,CAAC,OAAuB;IACxD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,iCAAwB,CAAC,CAAC;IACxD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,WAAW,EAAE,KAAK,gCAAuB,EAAE,CAAC;QACjF,MAAM,IAAI,oBAAoB,EAAE,CAAC;IACnC,CAAC;AACH,CAAC;AAED,MAAa,oBAAqB,SAAQ,KAAK;IAC7C,YAAY,OAAO,GAAG,oDAAoD;QACxE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AALD,oDAKC;AAED,MAAa,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,OAAO,GAAG,yDAAyD;QAC7E,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AALD,gDAKC;AAED,oFAAoF;AACpF,SAAgB,cAAc,CAC5B,IAAU,EACV,KAAkB,EAClB,aAAsB;IAEtB,IAAI,KAAK,KAAK,WAAW,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,SAAS,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,aAAa,IAAI,IAAI,CAAC,EAAE,KAAK,aAAa,EAAE,CAAC;QAC/C,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,EAAE,GAAG,SAAS,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;AAC1D,CAAC;AAED,qCAAqC;AACrC,SAAgB,wBAAwB,CACtC,IAAU,EACV,KAAkB,EAClB,aAAsB;IAEtB,OAAO,cAAc,CAAC,IAAI,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC;AACpD,CAAC"}
@@ -9,6 +9,9 @@ export declare class AgentDeckMCPServer {
9
9
  /** Per-session workspace + optional deck override (see mcp-session-binding.ts). */
10
10
  private sessionBinding;
11
11
  private activeSessionId;
12
+ /** Session badge from the backend registry (POST /api/scope/live-display response). */
13
+ private badgeBySession;
14
+ private lastTouchAtMs;
12
15
  private get server();
13
16
  constructor(port?: number, backendUrl?: string);
14
17
  private createMcpServer;
@@ -16,7 +19,11 @@ export declare class AgentDeckMCPServer {
16
19
  private getAgentHeaders;
17
20
  private fetchDeck;
18
21
  private buildBindingPayload;
19
- private writeBindingSidecar;
22
+ private registerLiveDisplay;
23
+ private static readonly TOUCH_DEBOUNCE_MS;
24
+ /** Fire-and-forget lastActivityAt bump; only for sessions the registry knows. */
25
+ private touchLiveDisplay;
26
+ private unregisterLiveDisplay;
20
27
  private callBackendAPI;
21
28
  private getBoundDeckId;
22
29
  private toolResult;
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"AAUA,OAAO,OAA8B,MAAM,SAAS,CAAC;AA2BrD,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,GAAG,CAAsB;IACjC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAiC;IACjD,mFAAmF;IACnF,OAAO,CAAC,wBAAwB,CAAwB;IACxD,mFAAmF;IACnF,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,eAAe,CAAqB;IAE5C,OAAO,KAAK,MAAM,GAKjB;gBAEW,IAAI,GAAE,MAAa,EAAE,UAAU,GAAE,MAAgC;IAc7E,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,eAAe;YAIT,SAAS;YAkBT,mBAAmB;YAYnB,mBAAmB;YAqBnB,cAAc;YAwCd,cAAc;IAQ5B,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,SAAS;IAIjB,6EAA6E;IAC7E,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,UAAU;IAy2BlB,OAAO,CAAC,cAAc;IA8MtB,OAAO,CAAC,WAAW;IA2CnB,OAAO,CAAC,kBAAkB;YAKZ,aAAa;YAuDb,uBAAuB;IAiB/B,KAAK;IAmCL,IAAI;CAeX"}
1
+ {"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"AASA,OAAO,OAA8B,MAAM,SAAS,CAAC;AAmBrD,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,GAAG,CAAsB;IACjC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAiC;IACjD,mFAAmF;IACnF,OAAO,CAAC,wBAAwB,CAAwB;IACxD,mFAAmF;IACnF,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,eAAe,CAAqB;IAC5C,uFAAuF;IACvF,OAAO,CAAC,cAAc,CAA6B;IACnD,OAAO,CAAC,aAAa,CAA6B;IAElD,OAAO,KAAK,MAAM,GAKjB;gBAEW,IAAI,GAAE,MAAa,EAAE,UAAU,GAAE,MAAgC;IAc7E,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,eAAe;YAIT,SAAS;YAkBT,mBAAmB;YAgBnB,mBAAmB;IA+BjC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAElD,iFAAiF;IACjF,OAAO,CAAC,gBAAgB;YAmBV,qBAAqB;YAUrB,cAAc;YAwCd,cAAc;IAQ5B,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,SAAS;IAIjB,6EAA6E;IAC7E,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,UAAU;IAkpBlB,OAAO,CAAC,cAAc;IA8MtB,OAAO,CAAC,WAAW;IA2CnB,OAAO,CAAC,kBAAkB;YAKZ,aAAa;YA2Db,uBAAuB;IAkB/B,KAAK;IAiCL,IAAI;CAeX"}
@@ -10,13 +10,9 @@ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
10
10
  const shared_1 = require("@agent-deck/shared");
11
11
  const express_1 = __importDefault(require("express"));
12
12
  const node_crypto_1 = require("node:crypto");
13
- const promises_1 = __importDefault(require("node:fs/promises"));
14
- const node_path_1 = __importDefault(require("node:path"));
15
13
  const zod_1 = require("zod");
16
- const repo_deck_1 = require("./scope/repo-deck");
17
14
  const version_1 = require("./lib/version");
18
15
  const mcp_session_binding_1 = require("./mcp-session-binding");
19
- const bindings_sidecar_1 = require("./scope/bindings-sidecar");
20
16
  const agentClientHeaders = {
21
17
  [shared_1.AGENT_DECK_CLIENT_HEADER]: shared_1.AGENT_DECK_AGENT_CLIENT,
22
18
  Accept: 'application/json',
@@ -31,6 +27,9 @@ class AgentDeckMCPServer {
31
27
  /** Per-session workspace + optional deck override (see mcp-session-binding.ts). */
32
28
  sessionBinding;
33
29
  activeSessionId;
30
+ /** Session badge from the backend registry (POST /api/scope/live-display response). */
31
+ badgeBySession = new Map();
32
+ lastTouchAtMs = new Map();
34
33
  get server() {
35
34
  if (!this.mcpServerForRegistration) {
36
35
  throw new Error('Internal: MCP server not in registration context');
@@ -78,18 +77,22 @@ class AgentDeckMCPServer {
78
77
  }
79
78
  return body.data;
80
79
  }
81
- async buildBindingPayload(sessionId, manifestDeckId) {
80
+ async buildBindingPayload(sessionId) {
82
81
  const snapshot = this.sessionBinding.getBinding(sessionId);
83
82
  const deck = await this.callBackendAPI('/api/scope/deck');
83
+ const badge = this.badgeBySession.get(sessionId);
84
+ const cardCounts = deck ? (0, shared_1.countDeckCards)(deck) : { mcp: 0, credentials: 0, playbooks: 0 };
84
85
  return {
85
86
  workspaceRoot: snapshot.workspaceRoot,
86
87
  deck_id: deck?.id,
87
88
  deck_name: deck?.name,
88
- deck_source: (0, mcp_session_binding_1.resolveDeckBindingSource)(snapshot, manifestDeckId),
89
+ deck_source: (0, mcp_session_binding_1.resolveDeckBindingSource)(snapshot),
89
90
  session_deck_override: this.sessionBinding.hasSessionDeckOverride(sessionId),
91
+ badge,
92
+ display_summary: (0, shared_1.formatDisplayLine)(deck?.name ?? null, cardCounts, { badge }),
90
93
  };
91
94
  }
92
- async writeBindingSidecar(sessionId, manifestDeckId) {
95
+ async registerLiveDisplay(sessionId) {
93
96
  const snapshot = this.sessionBinding.getBinding(sessionId);
94
97
  if (!snapshot.workspaceRoot) {
95
98
  return;
@@ -98,14 +101,51 @@ class AgentDeckMCPServer {
98
101
  if (!deck?.id || !deck?.name) {
99
102
  return;
100
103
  }
101
- await (0, bindings_sidecar_1.upsertBindingForSession)(sessionId, {
102
- deckId: deck.id,
103
- deckName: deck.name,
104
- source: (0, mcp_session_binding_1.resolveDeckBindingSource)(snapshot, manifestDeckId),
105
- updatedAt: new Date().toISOString(),
106
- cardCounts: (0, shared_1.countDeckCards)(deck),
107
- workspaceRoot: snapshot.workspaceRoot,
104
+ const clientName = this.sessions.get(sessionId)?.server.server.getClientVersion()?.name;
105
+ const result = await this.callBackendAPI('/api/scope/live-display', {
106
+ method: 'POST',
107
+ headers: { 'Content-Type': 'application/json' },
108
+ body: JSON.stringify({
109
+ mcpSessionId: sessionId,
110
+ workspaceRoot: snapshot.workspaceRoot,
111
+ deckId: deck.id,
112
+ deckName: deck.name,
113
+ source: (0, mcp_session_binding_1.resolveDeckBindingSource)(snapshot),
114
+ clientName,
115
+ cardCounts: (0, shared_1.countDeckCards)(deck),
116
+ updatedAt: new Date().toISOString(),
117
+ }),
108
118
  });
119
+ if (result && typeof result.badge === 'string') {
120
+ this.badgeBySession.set(sessionId, result.badge);
121
+ }
122
+ }
123
+ static TOUCH_DEBOUNCE_MS = 5_000;
124
+ /** Fire-and-forget lastActivityAt bump; only for sessions the registry knows. */
125
+ touchLiveDisplay(sessionId) {
126
+ if (!this.badgeBySession.has(sessionId)) {
127
+ return;
128
+ }
129
+ const now = Date.now();
130
+ if (now - (this.lastTouchAtMs.get(sessionId) ?? 0) < AgentDeckMCPServer.TOUCH_DEBOUNCE_MS) {
131
+ return;
132
+ }
133
+ this.lastTouchAtMs.set(sessionId, now);
134
+ void this.callBackendAPI(`/api/scope/live-display/${encodeURIComponent(sessionId)}/touch`, {
135
+ method: 'POST',
136
+ headers: { 'Content-Type': 'application/json' },
137
+ body: JSON.stringify({ at: new Date().toISOString() }),
138
+ }).catch(() => { });
139
+ }
140
+ async unregisterLiveDisplay(sessionId) {
141
+ try {
142
+ await this.callBackendAPI(`/api/scope/live-display/${encodeURIComponent(sessionId)}`, {
143
+ method: 'DELETE',
144
+ });
145
+ }
146
+ catch {
147
+ // Best effort when MCP session closes.
148
+ }
109
149
  }
110
150
  async callBackendAPI(endpoint, init = {}) {
111
151
  try {
@@ -168,84 +208,52 @@ class AgentDeckMCPServer {
168
208
  setupTools() {
169
209
  this.registerTool("bind_workspace", {
170
210
  title: "Bind Workspace",
171
- description: "Bind this MCP session to a workspace root. Pass deckId for a session-only deck override (same path, different concurrent agents). Without deckId, uses .agent-deck/deck.yaml when present.",
211
+ description: "Bind this MCP session to a workspace root and deck. Use get_decks to list deck ids.",
172
212
  inputSchema: {
173
213
  workspaceRoot: zod_1.z.string(),
174
- deckId: zod_1.z.string().uuid().optional(),
214
+ deckId: zod_1.z.string().uuid(),
175
215
  },
176
216
  }, async ({ workspaceRoot, deckId }) => {
177
217
  try {
178
218
  const sessionId = this.getSessionId();
179
219
  this.sessionBinding.setWorkspace(sessionId, workspaceRoot);
180
- if (deckId) {
181
- const deck = await this.fetchDeck(deckId);
182
- this.sessionBinding.setDeckId(sessionId, deckId);
183
- const payload = await this.buildBindingPayload(sessionId);
184
- await this.writeBindingSidecar(sessionId);
185
- return {
186
- content: [{
187
- type: "text",
188
- text: JSON.stringify({
189
- ...payload,
190
- deck_name: deck.name,
191
- message: 'Session bound with deck override. .agent-deck/deck.yaml is unchanged; other MCP sessions can use a different deck at the same path.',
192
- }, null, 2),
193
- }],
194
- };
195
- }
196
- this.sessionBinding.clearDeckId(sessionId);
197
- const resolved = await fetch(`${this.backendUrl}/api/scope/resolve`, {
198
- method: 'POST',
199
- headers: { ...agentClientHeaders, 'Content-Type': 'application/json' },
200
- body: JSON.stringify({ workspaceRoot }),
201
- });
202
- const body = (await resolved.json());
203
- if (!body.success || !body.data) {
204
- throw new Error(body.error ??
205
- 'Failed to resolve workspace deck. Pass deckId to bind without deck.yaml, or call setup_repo_deck.');
206
- }
207
- const { data } = body;
208
- const payload = await this.buildBindingPayload(sessionId, data.manifest.deck_id);
209
- await this.writeBindingSidecar(sessionId, data.manifest.deck_id);
220
+ const deck = await this.fetchDeck(deckId);
221
+ this.sessionBinding.setDeckId(sessionId, deckId);
222
+ await this.registerLiveDisplay(sessionId);
223
+ const payload = await this.buildBindingPayload(sessionId);
210
224
  return {
211
225
  content: [{
212
226
  type: "text",
213
227
  text: JSON.stringify({
214
228
  ...payload,
215
- manifestPath: data.manifestPath,
216
- message: 'Session bound via .agent-deck/deck.yaml.',
229
+ deck_name: deck.name,
230
+ message: 'Session bound to deck.',
217
231
  }, null, 2),
218
232
  }],
219
233
  };
220
234
  }
221
235
  catch (error) {
222
- const message = String(error);
223
- const hint = message.includes('deck.yaml') || message.includes('No .agent-deck')
224
- ? ' Pass deckId on bind_workspace, call setup_repo_deck, or get_repo_deck_status to diagnose.'
225
- : '';
226
- return {
227
- content: [{ type: "text", text: JSON.stringify({ error: message + hint }, null, 2) }],
228
- };
236
+ return this.toolError(error);
229
237
  }
230
238
  });
231
239
  this.registerTool("switch_bound_deck", {
232
240
  title: "Switch Bound Deck",
233
- description: "Switch the deck for this MCP session only. Does not modify .agent-deck/deck.yaml. Use when multiple agent sessions share the same workspace path.",
241
+ description: "Switch the deck for this MCP session only. Use when multiple agent sessions share the same workspace path.",
234
242
  inputSchema: { deckId: zod_1.z.string().uuid() },
235
243
  }, async ({ deckId }) => {
236
244
  try {
237
245
  const sessionId = this.getSessionId();
238
246
  const deck = await this.fetchDeck(deckId);
239
247
  this.sessionBinding.setDeckId(sessionId, deckId);
248
+ await this.registerLiveDisplay(sessionId);
240
249
  const payload = await this.buildBindingPayload(sessionId);
241
- await this.writeBindingSidecar(sessionId);
242
250
  return {
243
251
  content: [{
244
252
  type: "text",
245
253
  text: JSON.stringify({
246
254
  ...payload,
247
255
  deck_name: deck.name,
248
- message: 'Session deck switched. Other sessions and deck.yaml are unchanged.',
256
+ message: 'Session deck switched. Other sessions are unchanged.',
249
257
  }, null, 2),
250
258
  }],
251
259
  };
@@ -256,31 +264,27 @@ class AgentDeckMCPServer {
256
264
  });
257
265
  this.registerTool("get_session_binding", {
258
266
  title: "Get Session Binding",
259
- description: "Show workspace and effective deck for this MCP session, including whether the deck comes from a session override or repo deck.yaml.",
267
+ description: "Show workspace and effective deck for this MCP session (session override or env default).",
260
268
  inputSchema: {},
261
269
  }, async () => {
262
270
  try {
263
271
  const sessionId = this.getSessionId();
264
272
  const snapshot = this.sessionBinding.getBinding(sessionId);
265
- let manifestDeckId;
266
- if (snapshot.workspaceRoot && !snapshot.deckSource) {
267
- const manifest = await (0, repo_deck_1.loadRepoDeckManifest)(snapshot.workspaceRoot);
268
- manifestDeckId = manifest?.deck_id;
269
- }
270
273
  const deck = await this.callBackendAPI('/api/scope/deck');
274
+ const badge = this.badgeBySession.get(sessionId);
271
275
  const cardCounts = deck ? (0, shared_1.countDeckCards)(deck) : { mcp: 0, credentials: 0, playbooks: 0 };
272
- const displaySummary = (0, shared_1.formatDisplayLine)(deck?.name ?? null, cardCounts);
276
+ const displaySummary = (0, shared_1.formatDisplayLine)(deck?.name ?? null, cardCounts, { badge });
273
277
  return {
274
278
  content: [{
275
279
  type: "text",
276
280
  text: JSON.stringify({
277
281
  workspaceRoot: snapshot.workspaceRoot,
278
- session_deck_override: snapshot.deckId,
282
+ session_deck_id: snapshot.deckId,
279
283
  session_deck_source: snapshot.deckSource,
280
- repo_manifest_deck_id: manifestDeckId,
281
284
  effective_deck_id: deck?.id,
282
285
  effective_deck_name: deck?.name,
283
- effective_deck_source: (0, mcp_session_binding_1.resolveDeckBindingSource)(snapshot, manifestDeckId),
286
+ effective_deck_source: (0, mcp_session_binding_1.resolveDeckBindingSource)(snapshot),
287
+ badge,
284
288
  display_summary: displaySummary,
285
289
  }, null, 2),
286
290
  }],
@@ -290,147 +294,6 @@ class AgentDeckMCPServer {
290
294
  return this.toolError(error);
291
295
  }
292
296
  });
293
- this.registerTool("get_repo_deck_status", {
294
- title: "Get Repo Deck Status",
295
- description: "Check whether .agent-deck/deck.yaml exists in a workspace and whether it links to a valid deck",
296
- inputSchema: { workspaceRoot: zod_1.z.string() },
297
- }, async ({ workspaceRoot }) => {
298
- try {
299
- const filePath = (0, repo_deck_1.repoDeckManifestFilePath)(workspaceRoot);
300
- const manifest = await (0, repo_deck_1.loadRepoDeckManifest)(workspaceRoot);
301
- if (!manifest) {
302
- const decks = await this.callBackendAPI('/api/decks');
303
- return {
304
- content: [{
305
- type: "text",
306
- text: JSON.stringify({
307
- status: 'missing',
308
- workspaceRoot,
309
- expectedPath: shared_1.REPO_DECK_MANIFEST_PATH,
310
- message: 'No .agent-deck/deck.yaml found. Use setup_repo_deck to create one.',
311
- availableDecks: Array.isArray(decks)
312
- ? decks.map((d) => ({ id: d.id, name: d.name }))
313
- : decks,
314
- }, null, 2),
315
- }],
316
- };
317
- }
318
- const resolved = await fetch(`${this.backendUrl}/api/scope/resolve`, {
319
- method: 'POST',
320
- headers: { ...agentClientHeaders, 'Content-Type': 'application/json' },
321
- body: JSON.stringify({ workspaceRoot }),
322
- });
323
- const body = (await resolved.json());
324
- return {
325
- content: [{
326
- type: "text",
327
- text: JSON.stringify({
328
- status: body.success ? 'ok' : 'invalid',
329
- workspaceRoot,
330
- filePath,
331
- manifest,
332
- deck: body.data?.deck,
333
- error: body.success ? undefined : body.error,
334
- }, null, 2),
335
- }],
336
- };
337
- }
338
- catch (error) {
339
- return {
340
- content: [{ type: "text", text: JSON.stringify({ error: String(error) }, null, 2) }],
341
- };
342
- }
343
- });
344
- this.registerTool("setup_repo_deck", {
345
- title: "Setup Repo Deck",
346
- description: "Create or verify .agent-deck/deck.yaml in a repo. Links the workspace to an Agent Deck by deck_id. Optionally writes the file.",
347
- inputSchema: {
348
- workspaceRoot: zod_1.z.string(),
349
- deckId: zod_1.z.string().uuid().optional(),
350
- deckName: zod_1.z.string().optional(),
351
- writeFile: zod_1.z.boolean().optional(),
352
- },
353
- }, async ({ workspaceRoot, deckId, deckName, writeFile = true }) => {
354
- try {
355
- const sessionId = this.getSessionId();
356
- const existing = await (0, repo_deck_1.loadRepoDeckManifest)(workspaceRoot);
357
- if (existing) {
358
- this.sessionBinding.setWorkspace(sessionId, workspaceRoot);
359
- const resolved = await fetch(`${this.backendUrl}/api/scope/resolve`, {
360
- method: 'POST',
361
- headers: { ...agentClientHeaders, 'Content-Type': 'application/json' },
362
- body: JSON.stringify({ workspaceRoot }),
363
- });
364
- const body = (await resolved.json());
365
- if (!body.success || !body.data) {
366
- throw new Error(body.error ?? 'Existing manifest is invalid');
367
- }
368
- return {
369
- content: [{
370
- type: "text",
371
- text: JSON.stringify({
372
- status: 'existing',
373
- workspaceRoot,
374
- manifestPath: body.data.manifestPath,
375
- deck_id: body.data.manifest.deck_id,
376
- deck_name: body.data.deck.name,
377
- message: 'Workspace already linked. Session bound to this deck.',
378
- }, null, 2),
379
- }],
380
- };
381
- }
382
- if (!deckId) {
383
- const decks = await this.callBackendAPI('/api/decks');
384
- return {
385
- content: [{
386
- type: "text",
387
- text: JSON.stringify({
388
- status: 'needs_deck_id',
389
- workspaceRoot,
390
- expectedPath: shared_1.REPO_DECK_MANIFEST_PATH,
391
- message: 'Pick a deck id from the dashboard (My Decks → copy icon) and call setup_repo_deck again with deckId.',
392
- availableDecks: Array.isArray(decks)
393
- ? decks.map((d) => ({ id: d.id, name: d.name }))
394
- : decks,
395
- }, null, 2),
396
- }],
397
- };
398
- }
399
- const deck = await this.callBackendAPI(`/api/decks/${deckId}`);
400
- if (!deck?.id) {
401
- throw new Error(`Deck not found: ${deckId}`);
402
- }
403
- const content = (0, repo_deck_1.formatRepoDeckManifest)(deckId, deckName ?? deck.name);
404
- const dirPath = node_path_1.default.join(node_path_1.default.resolve(workspaceRoot), '.agent-deck');
405
- const filePath = (0, repo_deck_1.repoDeckManifestFilePath)(workspaceRoot);
406
- if (writeFile) {
407
- await promises_1.default.mkdir(dirPath, { recursive: true });
408
- await promises_1.default.writeFile(filePath, content, 'utf8');
409
- }
410
- this.sessionBinding.setWorkspace(sessionId, workspaceRoot);
411
- return {
412
- content: [{
413
- type: "text",
414
- text: JSON.stringify({
415
- status: writeFile ? 'created' : 'preview',
416
- workspaceRoot,
417
- filePath,
418
- deck_id: deckId,
419
- deck_name: deck.name,
420
- manifestContent: content,
421
- message: writeFile
422
- ? 'Created .agent-deck/deck.yaml and bound this session to the deck.'
423
- : 'Preview only — set writeFile true to write the file.',
424
- }, null, 2),
425
- }],
426
- };
427
- }
428
- catch (error) {
429
- return {
430
- content: [{ type: "text", text: JSON.stringify({ error: String(error) }, null, 2) }],
431
- };
432
- }
433
- });
434
297
  // Get all decks (metadata only; credentials stripped unless bound)
435
298
  this.registerTool("get_decks", {
436
299
  title: "Get Decks",
@@ -451,7 +314,7 @@ class AgentDeckMCPServer {
451
314
  });
452
315
  this.registerTool("get_bound_deck", {
453
316
  title: "Get Bound Deck",
454
- description: "Get the deck bound to this session (session deckId override, env, or .agent-deck/deck.yaml)",
317
+ description: "Get the deck bound to this session (session deckId or env AGENT_DECK_DECK_ID)",
455
318
  inputSchema: {},
456
319
  }, async () => {
457
320
  try {
@@ -1111,7 +974,7 @@ class AgentDeckMCPServer {
1111
974
  }
1112
975
  });
1113
976
  this.server.resource("bound_deck", "agent-deck://bound-deck", {
1114
- description: "Deck bound via .agent-deck/deck.yaml in the workspace",
977
+ description: "Deck bound to this MCP session",
1115
978
  mimeType: "application/json"
1116
979
  }, async () => {
1117
980
  try {
@@ -1230,8 +1093,9 @@ class AgentDeckMCPServer {
1230
1093
  async handleMcpPost(req, res) {
1231
1094
  const sessionIdHeader = this.getSessionIdHeader(req);
1232
1095
  const existing = sessionIdHeader ? this.sessions.get(sessionIdHeader) : undefined;
1233
- if (existing) {
1096
+ if (existing && sessionIdHeader) {
1234
1097
  this.activeSessionId = sessionIdHeader;
1098
+ this.touchLiveDisplay(sessionIdHeader);
1235
1099
  await existing.transport.handleRequest(req, res, req.body);
1236
1100
  return;
1237
1101
  }
@@ -1263,6 +1127,9 @@ class AgentDeckMCPServer {
1263
1127
  if (sessionId) {
1264
1128
  this.sessions.delete(sessionId);
1265
1129
  this.sessionBinding.clearSession(sessionId);
1130
+ this.badgeBySession.delete(sessionId);
1131
+ this.lastTouchAtMs.delete(sessionId);
1132
+ void this.unregisterLiveDisplay(sessionId);
1266
1133
  }
1267
1134
  };
1268
1135
  await server.connect(transport);
@@ -1284,6 +1151,7 @@ class AgentDeckMCPServer {
1284
1151
  return;
1285
1152
  }
1286
1153
  this.activeSessionId = sessionId;
1154
+ this.touchLiveDisplay(sessionId);
1287
1155
  await session.transport.handleRequest(req, res);
1288
1156
  }
1289
1157
  async start() {
@@ -1293,12 +1161,10 @@ class AgentDeckMCPServer {
1293
1161
  this.app.listen(this.port, () => {
1294
1162
  console.log(`✅ Agent Deck MCP Server is ready to accept connections`);
1295
1163
  console.log(`📋 Available tools:`);
1296
- console.log(` - bind_workspace: Bind session to workspace; optional deckId override`);
1164
+ console.log(` - bind_workspace: Bind session to workspace + deck (deckId required)`);
1297
1165
  console.log(` - switch_bound_deck: Switch deck for this session only`);
1298
1166
  console.log(` - get_session_binding: Show session workspace + effective deck`);
1299
- console.log(` - get_repo_deck_status: Check repo deck manifest`);
1300
- console.log(` - setup_repo_deck: Create .agent-deck/deck.yaml for a repo`);
1301
- console.log(` - get_bound_deck: Get workspace-bound deck`);
1167
+ console.log(` - get_bound_deck: Get session-bound deck`);
1302
1168
  console.log(` - list_service_tools: List tools for a specific service`);
1303
1169
  console.log(` - call_service_tool: Call a tool on a service`);
1304
1170
  console.log(`📋 Available resources:`);