@unboundai/tb-ppt-mgagent 0.1.1 → 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.
- package/README.md +24 -0
- package/build/index.js +76 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,6 +38,30 @@ The one thing still **not** confirmed against a real response is the exact `file
|
|
|
38
38
|
- `ANTHROPIC_API_BASE` — optional, overrides the SDK's default API base URL.
|
|
39
39
|
- `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
40
|
- `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
|
+
- `TB_PPT_LOG_FILE` — optional. Path to the task log file. Defaults to `logs/tasks.log` next to this package.
|
|
42
|
+
|
|
43
|
+
## Task logging
|
|
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
|
+
| `not_end_turn` | `get_tb_powerpoint_result` | idle, but `stop_reason` wasn't `end_turn`; includes `stopReason` |
|
|
53
|
+
| `no_output_file` | `get_tb_powerpoint_result` | finished cleanly but no `.pptx` found; includes `fileCount` |
|
|
54
|
+
| `success` | `get_tb_powerpoint_result` | file downloaded; includes `filename`, `downloadUrl` |
|
|
55
|
+
| `invalid_arguments` | either | zod validation failed; includes `details` |
|
|
56
|
+
| `error` | either | anything else threw; includes `message` |
|
|
57
|
+
|
|
58
|
+
All entries include `durationMs` except the initial `started` line. To see what's been run:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
tail -f logs/tasks.log
|
|
62
|
+
# or, for just failures:
|
|
63
|
+
grep -E '"status":"(error|not_end_turn|no_output_file)"' logs/tasks.log
|
|
64
|
+
```
|
|
41
65
|
|
|
42
66
|
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:
|
|
43
67
|
|
package/build/index.js
CHANGED
|
@@ -37,6 +37,11 @@
|
|
|
37
37
|
* - TB_PPT_PUBLIC_URL_BASE: required for retrieval. Base URL the synced
|
|
38
38
|
* folder is served from (e.g. "https://storage.googleapis.com/bucket/prefix"),
|
|
39
39
|
* used to build the download link returned to the caller.
|
|
40
|
+
* - TB_PPT_LOG_FILE: optional. Path to a JSON-lines task log (one call per
|
|
41
|
+
* line: tool, status, timing, and relevant ids). Defaults to logs/tasks.log
|
|
42
|
+
* next to this package. Every entry is also mirrored to stderr, which the
|
|
43
|
+
* MCP host's own log viewer already picks up - the file exists so history
|
|
44
|
+
* survives a host restart and can be grepped/tailed directly.
|
|
40
45
|
*/
|
|
41
46
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
42
47
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -45,7 +50,27 @@ import { z } from "zod";
|
|
|
45
50
|
import Anthropic from "@anthropic-ai/sdk";
|
|
46
51
|
import fs from "fs";
|
|
47
52
|
import path from "path";
|
|
53
|
+
import { fileURLToPath } from "url";
|
|
48
54
|
const MANAGED_AGENTS_BETA = "managed-agents-2026-04-01";
|
|
55
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
56
|
+
const LOG_FILE = process.env.TB_PPT_LOG_FILE || path.join(__dirname, "..", "logs", "tasks.log");
|
|
57
|
+
/**
|
|
58
|
+
* Records one line of task history: a JSON object to LOG_FILE, and a short
|
|
59
|
+
* human-readable echo to stderr (stdout is reserved for MCP's own JSON-RPC
|
|
60
|
+
* traffic in stdio mode - never write task logs there).
|
|
61
|
+
*/
|
|
62
|
+
function logTask(event) {
|
|
63
|
+
const record = { timestamp: new Date().toISOString(), ...event };
|
|
64
|
+
const line = JSON.stringify(record);
|
|
65
|
+
try {
|
|
66
|
+
fs.mkdirSync(path.dirname(LOG_FILE), { recursive: true });
|
|
67
|
+
fs.appendFileSync(LOG_FILE, line + "\n");
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
console.error(`Failed to write task log to ${LOG_FILE}: ${err.message}`);
|
|
71
|
+
}
|
|
72
|
+
console.error(`[task] ${line}`);
|
|
73
|
+
}
|
|
49
74
|
const API_KEY = process.env.ANTHROPIC_API_KEY;
|
|
50
75
|
const AGENT_ID = process.env.ANTHROPIC_AGENT_ID;
|
|
51
76
|
const ENVIRONMENT_ID = process.env.ANTHROPIC_ENVIRONMENT_ID;
|
|
@@ -168,9 +193,19 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
168
193
|
});
|
|
169
194
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
170
195
|
const { name, arguments: args } = request.params;
|
|
196
|
+
const startedAt = Date.now();
|
|
171
197
|
try {
|
|
172
198
|
if (name === "generate_tb_powerpoint") {
|
|
173
199
|
const { outline, deckType, clientName, filename } = GenerateTbPowerpointSchema.parse(args);
|
|
200
|
+
logTask({
|
|
201
|
+
tool: name,
|
|
202
|
+
status: "started",
|
|
203
|
+
outlinePreview: outline.slice(0, 120),
|
|
204
|
+
outlineLength: outline.length,
|
|
205
|
+
deckType: deckType ?? null,
|
|
206
|
+
clientName: clientName ?? null,
|
|
207
|
+
filename: filename ?? null
|
|
208
|
+
});
|
|
174
209
|
const { apiKey, agentId, environmentId } = requireSessionConfig();
|
|
175
210
|
const client = getClient(apiKey);
|
|
176
211
|
const session = await client.beta.sessions.create({
|
|
@@ -191,6 +226,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
191
226
|
{ type: "user.message", content: [{ type: "text", text: messageText }] }
|
|
192
227
|
]
|
|
193
228
|
});
|
|
229
|
+
logTask({
|
|
230
|
+
tool: name,
|
|
231
|
+
status: "success",
|
|
232
|
+
sessionId: session.id,
|
|
233
|
+
durationMs: Date.now() - startedAt
|
|
234
|
+
});
|
|
194
235
|
return {
|
|
195
236
|
content: [{
|
|
196
237
|
type: "text",
|
|
@@ -204,10 +245,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
204
245
|
}
|
|
205
246
|
if (name === "get_tb_powerpoint_result") {
|
|
206
247
|
const { sessionId } = GetTbPowerpointResultSchema.parse(args);
|
|
248
|
+
logTask({ tool: name, status: "started", sessionId });
|
|
207
249
|
const apiKey = requireApiKey();
|
|
208
250
|
const client = getClient(apiKey);
|
|
209
251
|
const session = await client.beta.sessions.retrieve(sessionId);
|
|
210
252
|
if (session.status !== "idle") {
|
|
253
|
+
logTask({
|
|
254
|
+
tool: name,
|
|
255
|
+
status: "not_ready",
|
|
256
|
+
sessionId,
|
|
257
|
+
sessionStatus: session.status,
|
|
258
|
+
durationMs: Date.now() - startedAt
|
|
259
|
+
});
|
|
211
260
|
return {
|
|
212
261
|
content: [{
|
|
213
262
|
type: "text",
|
|
@@ -230,6 +279,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
230
279
|
}
|
|
231
280
|
const stopReasonType = latestIdleEvent?.stop_reason.type;
|
|
232
281
|
if (stopReasonType !== "end_turn") {
|
|
282
|
+
logTask({
|
|
283
|
+
tool: name,
|
|
284
|
+
status: "not_end_turn",
|
|
285
|
+
sessionId,
|
|
286
|
+
stopReason: stopReasonType ?? null,
|
|
287
|
+
durationMs: Date.now() - startedAt
|
|
288
|
+
});
|
|
233
289
|
return {
|
|
234
290
|
content: [{
|
|
235
291
|
type: "text",
|
|
@@ -252,6 +308,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
252
308
|
const outputPptxFiles = pptxFiles.filter((f) => /outputs?[\\/]/i.test(f.filename));
|
|
253
309
|
const chosen = outputPptxFiles[0] ?? pptxFiles[0];
|
|
254
310
|
if (!chosen) {
|
|
311
|
+
logTask({
|
|
312
|
+
tool: name,
|
|
313
|
+
status: "no_output_file",
|
|
314
|
+
sessionId,
|
|
315
|
+
fileCount: fileList.length,
|
|
316
|
+
durationMs: Date.now() - startedAt
|
|
317
|
+
});
|
|
255
318
|
return {
|
|
256
319
|
content: [{
|
|
257
320
|
type: "text",
|
|
@@ -277,6 +340,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
277
340
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
278
341
|
fs.writeFileSync(destPath, Buffer.from(arrayBuffer));
|
|
279
342
|
const downloadUrl = `${publicUrlBase.replace(/\/$/, "")}/${encodeURIComponent(safeName)}`;
|
|
343
|
+
logTask({
|
|
344
|
+
tool: name,
|
|
345
|
+
status: "success",
|
|
346
|
+
sessionId,
|
|
347
|
+
filename: safeName,
|
|
348
|
+
downloadUrl,
|
|
349
|
+
durationMs: Date.now() - startedAt
|
|
350
|
+
});
|
|
280
351
|
return {
|
|
281
352
|
content: [{
|
|
282
353
|
type: "text",
|
|
@@ -294,17 +365,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
294
365
|
throw new Error(`Unknown tool: ${name}`);
|
|
295
366
|
}
|
|
296
367
|
catch (error) {
|
|
368
|
+
const durationMs = Date.now() - startedAt;
|
|
297
369
|
if (error instanceof z.ZodError) {
|
|
370
|
+
const details = error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
|
|
371
|
+
logTask({ tool: name, status: "invalid_arguments", details, durationMs });
|
|
298
372
|
return {
|
|
299
373
|
content: [{
|
|
300
374
|
type: "text",
|
|
301
375
|
text: JSON.stringify({
|
|
302
376
|
error: "Invalid arguments",
|
|
303
|
-
details
|
|
377
|
+
details
|
|
304
378
|
}, null, 2)
|
|
305
379
|
}]
|
|
306
380
|
};
|
|
307
381
|
}
|
|
382
|
+
logTask({ tool: name, status: "error", message: error.message, durationMs });
|
|
308
383
|
return {
|
|
309
384
|
content: [{
|
|
310
385
|
type: "text",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unboundai/tb-ppt-mgagent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.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": {
|