@unboundai/tb-ppt-mgagent 0.3.0 → 0.5.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/README.md +62 -45
- package/build/index.js +64 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# Traffic Builders PowerPoint MCP Server
|
|
2
2
|
|
|
3
|
-
A Model Context Protocol (MCP) server that hands a presentation outline to Anthropic's hosted **Managed Agent** for building Traffic Builders-branded PowerPoints, then retrieves the finished file once it's done.
|
|
3
|
+
A Model Context Protocol (MCP) server that hands a presentation outline to Anthropic's hosted **Managed Agent** for building Traffic Builders-branded PowerPoints, then retrieves the finished file once it's done.
|
|
4
|
+
|
|
5
|
+
Brand assets (the design brief, logos, Outfit font) are **not** assumed to already exist in the agent's environment. A Managed Agents "deployment" (scheduled/cron runs) was investigated as a way to pin a session to a specific pre-configured setup, but it only re-sends a fixed baked-in message on a schedule — it offers no extra asset-targeting capability over a plain session. So instead, this server uploads each brand asset once via the Files API (a one-time step, see [Setup](#setup)) and attaches them explicitly to **every** session it creates via the `resources` array, mounted into the sandbox for the agent to read.
|
|
4
6
|
|
|
5
7
|
Built against the official Managed Agents docs (session creation, session operations, events & streaming, files, event/type reference) using the official `@anthropic-ai/sdk` client, which sets the `managed-agents-2026-04-01` beta header automatically for session endpoints.
|
|
6
8
|
|
|
7
9
|
## Available tools
|
|
8
10
|
|
|
9
|
-
- **`generate_tb_powerpoint(outline, deckType?, clientName?, filename?)`** — creates a session against the configured agent + environment, sends the outline as the first message, returns `{ success, sessionId, message }`. Does not wait for the deck to finish.
|
|
11
|
+
- **`generate_tb_powerpoint(outline, deckType?, clientName?, filename?)`** — creates a session against the configured agent + environment, attaches whichever brand asset `resources` are configured (see Setup), sends the outline as the first message, returns `{ success, sessionId, message }`. Does not wait for the deck to finish.
|
|
10
12
|
|
|
11
13
|
- **`get_tb_powerpoint_result(sessionId)`** — checks on a generation started above:
|
|
12
14
|
1. Retrieves the session. If its `status` isn't `idle`, returns `{ ready: false, status }` — still running, try again later.
|
|
@@ -15,7 +17,7 @@ Built against the official Managed Agents docs (session creation, session operat
|
|
|
15
17
|
|
|
16
18
|
## What's still an assumption
|
|
17
19
|
|
|
18
|
-
Every SDK call in `src/index.ts` (`sessions.create`, `sessions.events.send`, `sessions.retrieve`, `sessions.events.list`, `files.list`, `files.download`) is checked against `@anthropic-ai/sdk`'s own `.d.ts`/source (installed version, not just the docs prose) — that's how a real bug was caught in 0.1.0: the docs' TypeScript sample for downloading a file shows `content.writeToFile(path)`, but that method doesn't exist on the installed SDK. `files.download()` actually resolves to a plain `Response`; 0.1.1 fixes this by reading it with `.arrayBuffer()` and writing the bytes directly.
|
|
20
|
+
Every SDK call in `src/index.ts` (`sessions.create`, `sessions.events.send`, `sessions.retrieve`, `sessions.events.list`, `files.list`, `files.download`) is checked against `@anthropic-ai/sdk`'s own `.d.ts`/source (installed version, not just the docs prose) — that's how a real bug was caught in 0.1.0: the docs' TypeScript sample for downloading a file shows `content.writeToFile(path)`, but that method doesn't exist on the installed SDK. `files.download()` actually resolves to a plain `Response`; 0.1.1 fixes this by reading it with `.arrayBuffer()` and writing the bytes directly. The `resources` shape (`{ type: "file", file_id, mount_path }`) is likewise checked against the SDK source, not just the docs.
|
|
19
21
|
|
|
20
22
|
The one thing still **not** confirmed against a real response is the exact `filename`/path convention the Files API returns for a file the agent wrote to its outputs directory — the code guesses by matching `/outputs?[\/]/i` against the filename and falling back to any `.pptx` file on the session if nothing matches that pattern. If a real run's file list looks different, adjust the filter in `get_tb_powerpoint_result` in `src/index.ts`.
|
|
21
23
|
|
|
@@ -31,7 +33,13 @@ The one thing still **not** confirmed against a real response is the exact `file
|
|
|
31
33
|
npm run build
|
|
32
34
|
```
|
|
33
35
|
|
|
34
|
-
3. **
|
|
36
|
+
3. **Upload the brand assets** (one time, or again whenever an asset changes):
|
|
37
|
+
```
|
|
38
|
+
.\uploadAssets.ps1 -ApiKey "sk-ant-..."
|
|
39
|
+
```
|
|
40
|
+
Uploads `TrafficBuilders-PPT-Handoff.md`, all five `logos/*.png`, and `fonts/Outfit-VariableFont_wght.ttf` to the Files API, and prints a `file_id` per asset along with the matching env var name. These are just config, not code — plug them into step 5 below and there's nothing else to change or republish.
|
|
41
|
+
|
|
42
|
+
4. **Configure environment variables**:
|
|
35
43
|
- `ANTHROPIC_API_KEY` — Anthropic API key with Managed Agents access.
|
|
36
44
|
- `ANTHROPIC_AGENT_ID` — e.g. `agent_01H8osDSMeEtGcEHT1ajzuin`.
|
|
37
45
|
- `ANTHROPIC_ENVIRONMENT_ID` — e.g. `env_01VHzZmqBababsghFjQoAGFx`.
|
|
@@ -39,40 +47,9 @@ The one thing still **not** confirmed against a real response is the exact `file
|
|
|
39
47
|
- `TB_PPT_OUTPUT_DIR` — required for `get_tb_powerpoint_result`. Local folder that's synced to GCS by something outside this server; finished `.pptx` files are written here.
|
|
40
48
|
- `TB_PPT_PUBLIC_URL_BASE` — required for `get_tb_powerpoint_result`. Base URL the synced folder is served from (e.g. `https://storage.googleapis.com/bucket/prefix`), used to build the returned download link.
|
|
41
49
|
- `TB_PPT_LOG_FILE` — optional. Path to the task log file. Defaults to `logs/tasks.log` next to this package.
|
|
50
|
+
- `TB_DESIGN_BRIEF_FILE_ID`, `TB_LOGO_HORIZONTAL_FILE_ID`, `TB_LOGO_HORIZONTAL_WHITE_FILE_ID`, `TB_LOGO_ICON_FILE_ID`, `TB_LOGO_ICON_WHITE_FILE_ID`, `TB_LOGO_ICON_TAGLINE_FILE_ID`, `TB_FONT_FILE_ID` — all optional but strongly recommended, from step 3. Any left unset are just skipped (with a warning in the log) rather than failing the call — the deck still gets built, just without that asset available to the agent.
|
|
42
51
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Every tool call writes one JSON-lines entry to `TB_PPT_LOG_FILE` (default `logs/tasks.log`) and mirrors it to stderr (which the MCP host's own log viewer already shows, as seen in the `[powerpoint-creator]` logs). Each line has a `timestamp`, `tool`, and `status`, plus whatever's relevant to that outcome:
|
|
46
|
-
|
|
47
|
-
| status | tool | meaning |
|
|
48
|
-
|---|---|---|
|
|
49
|
-
| `started` | either | call received, with its key arguments (outline preview/length, or sessionId) |
|
|
50
|
-
| `success` | `generate_tb_powerpoint` | session created; includes `sessionId` |
|
|
51
|
-
| `not_ready` | `get_tb_powerpoint_result` | session isn't `idle` yet; includes `sessionStatus` |
|
|
52
|
-
| `agent_message` | `get_tb_powerpoint_result` | the agent's own most recent `agent.message` text, up to 500 chars, in `agentMessagePreview` — this is what the agent actually said, not our derived status. Logged once the session is idle, before the stop-reason check, so it's there even on `not_end_turn` |
|
|
53
|
-
| `not_end_turn` | `get_tb_powerpoint_result` | idle, but `stop_reason` wasn't `end_turn`; includes `stopReason` |
|
|
54
|
-
| `listing_files` | `get_tb_powerpoint_result` | about to call Files API `list` for this session |
|
|
55
|
-
| `files_listed` | `get_tb_powerpoint_result` | includes `fileCount`, every `.pptx` candidate found (`pptxCandidates`, each with `id`/`filename`/`sizeBytes`), and which one was `chosenFileId` |
|
|
56
|
-
| `no_output_file` | `get_tb_powerpoint_result` | finished cleanly but no `.pptx` found; includes `fileCount` |
|
|
57
|
-
| `download_started` | `get_tb_powerpoint_result` | about to call Files API `download`; includes `fileId`, `filename`, `expectedSizeBytes` |
|
|
58
|
-
| `download_completed` | `get_tb_powerpoint_result` | bytes received; includes `downloadedBytes` |
|
|
59
|
-
| `saving_file` | `get_tb_powerpoint_result` | about to write to `destPath` inside `outputDir` |
|
|
60
|
-
| `file_saved` | `get_tb_powerpoint_result` | write confirmed via `fs.statSync`; includes `savedBytes` and `bytesMatch` (should be `true`) |
|
|
61
|
-
| `success` | `get_tb_powerpoint_result` | fully done; includes `filename`, `destPath`, `downloadUrl` |
|
|
62
|
-
| `invalid_arguments` | either | zod validation failed; includes `details` |
|
|
63
|
-
| `error` | either | anything else threw; includes `message` |
|
|
64
|
-
|
|
65
|
-
The agent's actual final message is also returned to the caller as `agentMessage` on every `get_tb_powerpoint_result` response (not just logged), so a not-quite-right run can be diagnosed from the tool's own output, not just the log file.
|
|
66
|
-
|
|
67
|
-
All entries include `durationMs` except the intermediate progress lines (`agent_message`, `listing_files`, `files_listed`, `download_started`, `download_completed`, `saving_file`) — those are pure checkpoints, timed by their surrounding `started`/`success`/`error` lines instead. To see what's been run:
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
tail -f logs/tasks.log
|
|
71
|
-
# or, for just failures:
|
|
72
|
-
grep -E '"status":"(error|not_end_turn|no_output_file)"' logs/tasks.log
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
4. **Register with your MCP client** (Claude Desktop / TypingMind's local MCP connector — same pattern as this repo's siblings, e.g. `tb-gcs-assembly-mcp`). Published to npm as [`@unboundai/tb-ppt-mgagent`](https://www.npmjs.com/package/@unboundai/tb-ppt-mgagent), so `npx` can run it without a local checkout:
|
|
52
|
+
5. **Register with your MCP client** (Claude Desktop / TypingMind's local MCP connector — same pattern as this repo's siblings, e.g. `tb-gcs-assembly-mcp`). Published to npm as [`@unboundai/tb-ppt-mgagent`](https://www.npmjs.com/package/@unboundai/tb-ppt-mgagent), so `npx` can run it without a local checkout:
|
|
76
53
|
|
|
77
54
|
```json
|
|
78
55
|
"tb-ppt-mgagent": {
|
|
@@ -86,7 +63,14 @@ grep -E '"status":"(error|not_end_turn|no_output_file)"' logs/tasks.log
|
|
|
86
63
|
"ANTHROPIC_AGENT_ID": "agent_...",
|
|
87
64
|
"ANTHROPIC_ENVIRONMENT_ID": "env_...",
|
|
88
65
|
"TB_PPT_OUTPUT_DIR": "/path/to/gcs-synced-folder",
|
|
89
|
-
"TB_PPT_PUBLIC_URL_BASE": "https://storage.googleapis.com/your-bucket/your-prefix"
|
|
66
|
+
"TB_PPT_PUBLIC_URL_BASE": "https://storage.googleapis.com/your-bucket/your-prefix",
|
|
67
|
+
"TB_DESIGN_BRIEF_FILE_ID": "file_...",
|
|
68
|
+
"TB_LOGO_HORIZONTAL_FILE_ID": "file_...",
|
|
69
|
+
"TB_LOGO_HORIZONTAL_WHITE_FILE_ID": "file_...",
|
|
70
|
+
"TB_LOGO_ICON_FILE_ID": "file_...",
|
|
71
|
+
"TB_LOGO_ICON_WHITE_FILE_ID": "file_...",
|
|
72
|
+
"TB_LOGO_ICON_TAGLINE_FILE_ID": "file_...",
|
|
73
|
+
"TB_FONT_FILE_ID": "file_..."
|
|
90
74
|
}
|
|
91
75
|
}
|
|
92
76
|
```
|
|
@@ -103,12 +87,45 @@ grep -E '"status":"(error|not_end_turn|no_output_file)"' logs/tasks.log
|
|
|
103
87
|
}
|
|
104
88
|
```
|
|
105
89
|
|
|
106
|
-
|
|
90
|
+
### Publishing a new version
|
|
107
91
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
92
|
+
```bash
|
|
93
|
+
npm version patch # or minor/major
|
|
94
|
+
npm publish # prepublishOnly rebuilds automatically; will prompt for a 2FA OTP
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Task logging
|
|
98
|
+
|
|
99
|
+
Every tool call writes one JSON-lines entry to `TB_PPT_LOG_FILE` (default `logs/tasks.log`) and mirrors it to stderr (which the MCP host's own log viewer already shows, as seen in the `[powerpoint-creator]` logs). Each line has a `timestamp`, `tool`, and `status`, plus whatever's relevant to that outcome:
|
|
100
|
+
|
|
101
|
+
| status | tool | meaning |
|
|
102
|
+
|---|---|---|
|
|
103
|
+
| `started` | either | call received, with its key arguments (outline preview/length, or sessionId) |
|
|
104
|
+
| `attaching_assets` | `generate_tb_powerpoint` | which brand asset `resources` are being attached (`attached`, a list of mount paths) and which env vars are unset (`missing`) |
|
|
105
|
+
| `success` | `generate_tb_powerpoint` | session created; includes `sessionId` and `assetsAttached` count |
|
|
106
|
+
| `not_ready` | `get_tb_powerpoint_result` | session isn't `idle` yet; includes `sessionStatus` |
|
|
107
|
+
| `agent_message` | `get_tb_powerpoint_result` | the agent's own most recent `agent.message` text, up to 500 chars, in `agentMessagePreview` — this is what the agent actually said, not our derived status. Logged once the session is idle, before the stop-reason check, so it's there even on `not_end_turn` |
|
|
108
|
+
| `not_end_turn` | `get_tb_powerpoint_result` | idle, but `stop_reason` wasn't `end_turn`; includes `stopReason` |
|
|
109
|
+
| `listing_files` | `get_tb_powerpoint_result` | about to call Files API `list` for this session |
|
|
110
|
+
| `files_listed` | `get_tb_powerpoint_result` | includes `fileCount`, every `.pptx` candidate found (`pptxCandidates`, each with `id`/`filename`/`sizeBytes`), and which one was `chosenFileId` |
|
|
111
|
+
| `no_output_file` | `get_tb_powerpoint_result` | finished cleanly but no `.pptx` found; includes `fileCount` |
|
|
112
|
+
| `download_started` | `get_tb_powerpoint_result` | about to call Files API `download`; includes `fileId`, `filename`, `expectedSizeBytes` |
|
|
113
|
+
| `download_completed` | `get_tb_powerpoint_result` | bytes received; includes `downloadedBytes` |
|
|
114
|
+
| `saving_file` | `get_tb_powerpoint_result` | about to write to `destPath` inside `outputDir` |
|
|
115
|
+
| `file_saved` | `get_tb_powerpoint_result` | write confirmed via `fs.statSync`; includes `savedBytes` and `bytesMatch` (should be `true`) |
|
|
116
|
+
| `success` | `get_tb_powerpoint_result` | fully done; includes `filename`, `destPath`, `downloadUrl` |
|
|
117
|
+
| `invalid_arguments` | either | zod validation failed; includes `details` |
|
|
118
|
+
| `error` | either | anything else threw; includes `message` |
|
|
119
|
+
|
|
120
|
+
The agent's actual final message is also returned to the caller as `agentMessage` on every `get_tb_powerpoint_result` response (not just logged), so a not-quite-right run can be diagnosed from the tool's own output, not just the log file.
|
|
121
|
+
|
|
122
|
+
All entries include `durationMs` except the intermediate progress lines (`attaching_assets`, `agent_message`, `listing_files`, `files_listed`, `download_started`, `download_completed`, `saving_file`) — those are pure checkpoints, timed by their surrounding `started`/`success`/`error` lines instead. To see what's been run:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
tail -f logs/tasks.log
|
|
126
|
+
# or, for just failures:
|
|
127
|
+
grep -E '"status":"(error|not_end_turn|no_output_file)"' logs/tasks.log
|
|
128
|
+
```
|
|
112
129
|
|
|
113
130
|
## Development
|
|
114
131
|
|
|
@@ -117,8 +134,8 @@ grep -E '"status":"(error|not_end_turn|no_output_file)"' logs/tasks.log
|
|
|
117
134
|
npm run dev
|
|
118
135
|
```
|
|
119
136
|
|
|
120
|
-
Both tools were smoke-tested end-to-end over the real MCP protocol (`tools/list`, and calling each with a dummy API key to confirm the SDK calls actually reach `api.anthropic.com` and get a real `401 authentication_error` back rather than failing on a wrong method/shape before the network call).
|
|
137
|
+
Both tools were smoke-tested end-to-end over the real MCP protocol (`tools/list`, and calling each with a dummy API key to confirm the SDK calls actually reach `api.anthropic.com` and get a real `401 authentication_error` back rather than failing on a wrong method/shape before the network call) — including with asset env vars set, to confirm the `resources` array builds correctly.
|
|
121
138
|
|
|
122
139
|
## Design brief reference
|
|
123
140
|
|
|
124
|
-
`TrafficBuilders-PPT-Handoff.md`, `logos/`, and `fonts/` in this repo are the source material
|
|
141
|
+
`TrafficBuilders-PPT-Handoff.md`, `logos/`, and `fonts/` in this repo are the source material — pulled from the "Traffic Builders Design System" Claude Design project (colors, type scale, the 19 slide archetypes, brand logo PNGs, Outfit variable font) — that `uploadAssets.ps1` uploads and every session attaches via `resources`. Update the files here first if the design system changes, then re-run `uploadAssets.ps1` and update the env vars with the new file_ids. No slide-image assets are attached separately — the handoff brief embeds a literal HTML/CSS reference snippet per archetype, which is enough on its own (see its closing section for when a visual PNG reference would be worth adding).
|
package/build/index.js
CHANGED
|
@@ -3,11 +3,17 @@
|
|
|
3
3
|
* Traffic Builders PowerPoint MCP Server
|
|
4
4
|
*
|
|
5
5
|
* Hands a presentation outline to Anthropic's hosted Managed Agent that
|
|
6
|
-
* builds Traffic Builders-branded PowerPoints
|
|
7
|
-
* brief are already provisioned in that agent's environment - this server
|
|
8
|
-
* does not upload any brand files itself), then later retrieves the
|
|
6
|
+
* builds Traffic Builders-branded PowerPoints, then later retrieves the
|
|
9
7
|
* finished .pptx once the agent's turn has ended.
|
|
10
8
|
*
|
|
9
|
+
* Brand assets (design brief, logos, font) are NOT assumed to already exist
|
|
10
|
+
* in the agent's environment - a "deployment" in the Managed Agents API is
|
|
11
|
+
* only a fixed/scheduled way to re-run the same baked-in message and offers
|
|
12
|
+
* no extra asset-targeting capability over a plain session, so instead this
|
|
13
|
+
* server attaches each asset to every session explicitly via the `resources`
|
|
14
|
+
* array (mounted through the Files API - see uploadAssets.ps1 for the
|
|
15
|
+
* one-time upload step that produces the file_ids below).
|
|
16
|
+
*
|
|
11
17
|
* Built against the documented Managed Agents API:
|
|
12
18
|
* https://platform.claude.com/docs/en/managed-agents/sessions
|
|
13
19
|
* https://platform.claude.com/docs/en/managed-agents/session-operations
|
|
@@ -42,6 +48,15 @@
|
|
|
42
48
|
* next to this package. Every entry is also mirrored to stderr, which the
|
|
43
49
|
* MCP host's own log viewer already picks up - the file exists so history
|
|
44
50
|
* survives a host restart and can be grepped/tailed directly.
|
|
51
|
+
* - TB_DESIGN_BRIEF_FILE_ID, TB_LOGO_HORIZONTAL_FILE_ID,
|
|
52
|
+
* TB_LOGO_HORIZONTAL_WHITE_FILE_ID, TB_LOGO_ICON_FILE_ID,
|
|
53
|
+
* TB_LOGO_ICON_WHITE_FILE_ID, TB_LOGO_ICON_TAGLINE_FILE_ID,
|
|
54
|
+
* TB_FONT_FILE_ID: all optional but strongly recommended. Files API
|
|
55
|
+
* file_ids (from uploadAssets.ps1) for the design brief, each logo
|
|
56
|
+
* variant, and the Outfit font. Any that are set get mounted into every
|
|
57
|
+
* session via `resources`; any left unset are just skipped (with a
|
|
58
|
+
* warning), so the agent falls back to approximating the brand from the
|
|
59
|
+
* prompt text alone.
|
|
45
60
|
*/
|
|
46
61
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
47
62
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -87,6 +102,26 @@ if (!OUTPUT_DIR)
|
|
|
87
102
|
console.error("Warning: TB_PPT_OUTPUT_DIR environment variable is not set (needed for get_tb_powerpoint_result)");
|
|
88
103
|
if (!PUBLIC_URL_BASE)
|
|
89
104
|
console.error("Warning: TB_PPT_PUBLIC_URL_BASE environment variable is not set (needed for get_tb_powerpoint_result)");
|
|
105
|
+
/**
|
|
106
|
+
* Brand assets mounted into every session via `resources`. `mountPath` is
|
|
107
|
+
* relative to the session's uploads root - see file header - so the actual
|
|
108
|
+
* sandbox path is `/mnt/session/uploads${mountPath}`. Any entry whose env
|
|
109
|
+
* var isn't set is skipped (not an error): the deck still gets built, just
|
|
110
|
+
* without that asset available to the agent.
|
|
111
|
+
*/
|
|
112
|
+
const ASSET_RESOURCES = [
|
|
113
|
+
{ envVar: "TB_DESIGN_BRIEF_FILE_ID", mountPath: "/design/TrafficBuilders-PPT-Handoff.md", label: "design brief" },
|
|
114
|
+
{ envVar: "TB_LOGO_HORIZONTAL_FILE_ID", mountPath: "/design/logos/logo-horizontal.png", label: "logo-horizontal.png (full-color, light backgrounds)" },
|
|
115
|
+
{ envVar: "TB_LOGO_HORIZONTAL_WHITE_FILE_ID", mountPath: "/design/logos/logo-horizontal-white.png", label: "logo-horizontal-white.png (reversed, brand-blue backgrounds)" },
|
|
116
|
+
{ envVar: "TB_LOGO_ICON_FILE_ID", mountPath: "/design/logos/logo-icon.png", label: "logo-icon.png (full-color square mark)" },
|
|
117
|
+
{ envVar: "TB_LOGO_ICON_WHITE_FILE_ID", mountPath: "/design/logos/logo-icon-white.png", label: "logo-icon-white.png (reversed square mark)" },
|
|
118
|
+
{ envVar: "TB_LOGO_ICON_TAGLINE_FILE_ID", mountPath: "/design/logos/logo-icon-tagline.png", label: "logo-icon-tagline.png (icon + tagline lockup)" },
|
|
119
|
+
{ envVar: "TB_FONT_FILE_ID", mountPath: "/design/fonts/Outfit-VariableFont_wght.ttf", label: "Outfit variable font (weights 300-900)" }
|
|
120
|
+
];
|
|
121
|
+
const missingAssetEnvVars = ASSET_RESOURCES.filter((a) => !(process.env[a.envVar] || "").trim());
|
|
122
|
+
if (missingAssetEnvVars.length > 0) {
|
|
123
|
+
console.error(`Warning: brand asset file_ids not configured, decks will be built without them: ${missingAssetEnvVars.map((a) => a.envVar).join(", ")}`);
|
|
124
|
+
}
|
|
90
125
|
function requireSessionConfig() {
|
|
91
126
|
if (!API_KEY)
|
|
92
127
|
throw new Error("ANTHROPIC_API_KEY is not configured on this MCP server.");
|
|
@@ -208,9 +243,27 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
208
243
|
});
|
|
209
244
|
const { apiKey, agentId, environmentId } = requireSessionConfig();
|
|
210
245
|
const client = getClient(apiKey);
|
|
246
|
+
const attachedAssets = ASSET_RESOURCES
|
|
247
|
+
.map((a) => ({ ...a, fileId: (process.env[a.envVar] || "").trim() }))
|
|
248
|
+
.filter((a) => a.fileId);
|
|
249
|
+
logTask({
|
|
250
|
+
tool: name,
|
|
251
|
+
status: "attaching_assets",
|
|
252
|
+
attached: attachedAssets.map((a) => a.mountPath),
|
|
253
|
+
missing: missingAssetEnvVars.map((a) => a.envVar)
|
|
254
|
+
});
|
|
211
255
|
const session = await client.beta.sessions.create({
|
|
212
256
|
agent: agentId,
|
|
213
|
-
environment_id: environmentId
|
|
257
|
+
environment_id: environmentId,
|
|
258
|
+
...(attachedAssets.length > 0
|
|
259
|
+
? {
|
|
260
|
+
resources: attachedAssets.map((a) => ({
|
|
261
|
+
type: "file",
|
|
262
|
+
file_id: a.fileId,
|
|
263
|
+
mount_path: a.mountPath
|
|
264
|
+
}))
|
|
265
|
+
}
|
|
266
|
+
: {})
|
|
214
267
|
});
|
|
215
268
|
const lines = [];
|
|
216
269
|
if (deckType)
|
|
@@ -220,7 +273,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
220
273
|
if (filename)
|
|
221
274
|
lines.push(`Save the resulting file as "${filename}.pptx".`);
|
|
222
275
|
const meta = lines.length ? `${lines.join("\n")}\n\n` : "";
|
|
223
|
-
const
|
|
276
|
+
const assetsNote = attachedAssets.length > 0
|
|
277
|
+
? `## Attached brand assets\nThese files are mounted in your sandbox:\n${attachedAssets
|
|
278
|
+
.map((a) => `- /mnt/session/uploads${a.mountPath} - ${a.label}`)
|
|
279
|
+
.join("\n")}\n\nRead the design brief first and follow it exactly: brand colors, type scale, and the 19 slide archetypes it defines. Follow its logo lockup rule precisely - it is not a simple per-background swap: the wordmark logo appears ONLY on Title and Section-divider slides (top-left), while a small icon mark appears bottom-right on EVERY slide regardless of archetype; use the white logo variants on brand-blue backgrounds and the full-color variants on white backgrounds. Embed/register the attached font file and use it for all text - ExtraBold (800) for headings, Regular (400) for body - instead of a fallback typeface.\n\n---\n\n`
|
|
280
|
+
: "";
|
|
281
|
+
const messageText = `Build a Traffic Builders PowerPoint from the following outline.\n\n${assetsNote}${meta}## Outline\n${outline}`;
|
|
224
282
|
await client.beta.sessions.events.send(session.id, {
|
|
225
283
|
events: [
|
|
226
284
|
{ type: "user.message", content: [{ type: "text", text: messageText }] }
|
|
@@ -230,6 +288,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
230
288
|
tool: name,
|
|
231
289
|
status: "success",
|
|
232
290
|
sessionId: session.id,
|
|
291
|
+
assetsAttached: attachedAssets.length,
|
|
233
292
|
durationMs: Date.now() - startedAt
|
|
234
293
|
});
|
|
235
294
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unboundai/tb-ppt-mgagent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "MCP server that hands a presentation outline to the Traffic Builders PowerPoint Managed Agent, then retrieves the finished .pptx",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"bin": {
|