@rubytech/create-maxy 1.0.883 → 1.0.885
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/package.json +1 -1
- package/payload/platform/lib/graph-mcp/dist/index.js +45 -0
- package/payload/platform/lib/graph-mcp/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-mcp/src/index.ts +47 -0
- package/payload/platform/lib/mcp-eager/dist/index.d.ts +61 -0
- package/payload/platform/lib/mcp-eager/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/mcp-eager/dist/index.js +49 -0
- package/payload/platform/lib/mcp-eager/dist/index.js.map +1 -0
- package/payload/platform/lib/mcp-eager/src/index.ts +78 -0
- package/payload/platform/lib/mcp-eager/tsconfig.json +8 -0
- package/payload/platform/package.json +2 -2
- package/payload/platform/plugins/admin/PLUGIN.md +1 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js +48 -76
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/contacts/mcp/dist/index.js +10 -9
- package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/docs/references/internals.md +13 -0
- package/payload/platform/plugins/docs/references/plugins-guide.md +1 -1
- package/payload/platform/plugins/docs/references/troubleshooting.md +1 -1
- package/payload/platform/plugins/email/mcp/dist/index.js +10 -9
- package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js +9 -8
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/index.js +9 -8
- package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/tasks/mcp/dist/index.js +15 -14
- package/payload/platform/plugins/tasks/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/index.js +4 -3
- package/payload/platform/plugins/telegram/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/workflows/mcp/dist/index.js +9 -8
- package/payload/platform/plugins/workflows/mcp/dist/index.js.map +1 -1
- package/payload/platform/scripts/__tests__/logs-read-prefix.sh +85 -240
- package/payload/platform/scripts/log-adherence-check.sh +100 -0
- package/payload/platform/scripts/logs-read.sh +71 -141
- package/payload/platform/scripts/logs-read.test.sh +47 -104
- package/payload/premium-plugins/real-agency/BUNDLE.md +1 -1
- package/payload/server/chunk-5PQU2HW2.js +11672 -0
- package/payload/server/chunk-NPKQWE3S.js +1431 -0
- package/payload/server/chunk-ZVO5ASQA.js +11660 -0
- package/payload/server/client-pool-QUMX7OUT.js +34 -0
- package/payload/server/maxy-edge.js +2 -2
- package/payload/server/server.js +123 -121
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { initStderrTee } from "../../../../lib/mcp-stderr-tee/dist/index.js";
|
|
2
2
|
initStderrTee("admin");
|
|
3
3
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { eagerTool } from "../../../../lib/mcp-eager/dist/index.js";
|
|
4
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
6
|
import { z } from "zod";
|
|
6
7
|
import { readFile, writeFile } from "node:fs/promises";
|
|
@@ -217,7 +218,7 @@ function checkPort(port, timeoutMs = 1000) {
|
|
|
217
218
|
socket.once("timeout", () => { socket.destroy(); res(false); });
|
|
218
219
|
});
|
|
219
220
|
}
|
|
220
|
-
server
|
|
221
|
+
eagerTool(server, "system-status", "Check health of all Maxy platform services: Neo4j, Ollama, Cloudflare tunnel, crontab (Maxy cron entries), VNC, Chrome (CDP), specialist agents, and deployed brand identity.", {}, async () => {
|
|
221
222
|
const checks = {};
|
|
222
223
|
try {
|
|
223
224
|
// Task 580: NEO4J_URI must be explicit. The outer try/catch surfaces the
|
|
@@ -403,7 +404,7 @@ server.tool("system-status", "Check health of all Maxy platform services: Neo4j,
|
|
|
403
404
|
.join("\n");
|
|
404
405
|
return { content: [{ type: "text", text: formatted }] };
|
|
405
406
|
});
|
|
406
|
-
server
|
|
407
|
+
eagerTool(server, "public-hostname", "Resolve this account's canonical public hostname. Reads cloudflared ingress + alias-domains.json — the same files the platform server trusts to route. Returns a single deterministic answer; use this immediately after publish-site to construct the full URL.", {}, async () => {
|
|
407
408
|
const TAG = "[admin:public-hostname]";
|
|
408
409
|
try {
|
|
409
410
|
const result = resolvePublicHostname(CONFIG_DIR);
|
|
@@ -433,7 +434,7 @@ server.tool("public-hostname", "Resolve this account's canonical public hostname
|
|
|
433
434
|
};
|
|
434
435
|
}
|
|
435
436
|
});
|
|
436
|
-
server
|
|
437
|
+
eagerTool(server, "remote-auth-status", "Check whether the remote access password is configured. When not configured, emits a device-bound URL affordance (maxy-device-url fenced block) pointing at the password setup page — this URL opens on the device's own screen when the operator clicks it. The agent never constructs the password file path or runs shell commands — this tool is the single authority.", {}, async () => {
|
|
437
438
|
const TAG = "[remote-auth-status]";
|
|
438
439
|
const platformPort = parseInt(PLATFORM_PORT, 10);
|
|
439
440
|
const setupUrl = `http://${osHostname()}.local:${platformPort}/__remote-auth/setup`;
|
|
@@ -469,7 +470,7 @@ server.tool("remote-auth-status", "Check whether the remote access password is c
|
|
|
469
470
|
};
|
|
470
471
|
}
|
|
471
472
|
});
|
|
472
|
-
server
|
|
473
|
+
eagerTool(server, "brand-settings", "Read the brand/styling configuration (name, tagline, colours, fonts, plugin sets). Reads from config/brand.json (stamped by the bundler at install time).", {}, async () => {
|
|
473
474
|
try {
|
|
474
475
|
const brandPath = resolve(PLATFORM_ROOT, "config", "brand.json");
|
|
475
476
|
if (!existsSync(brandPath)) {
|
|
@@ -549,7 +550,7 @@ server.tool("onboarding-plugin-options", "Return the fully-assembled multi-selec
|
|
|
549
550
|
return { content: [{ type: "text", text: `Failed: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
550
551
|
}
|
|
551
552
|
});
|
|
552
|
-
server
|
|
553
|
+
eagerTool(server, "account-manage", "Read the account configuration (tier, domains, settings).", {}, async () => {
|
|
553
554
|
try {
|
|
554
555
|
const config = await readAccountConfig();
|
|
555
556
|
return {
|
|
@@ -563,7 +564,7 @@ server.tool("account-manage", "Read the account configuration (tier, domains, se
|
|
|
563
564
|
};
|
|
564
565
|
}
|
|
565
566
|
});
|
|
566
|
-
server
|
|
567
|
+
eagerTool(server, "account-update", "Update a user-configurable setting in account.json. Valid fields: outputStyle (default|explanatory), thinkingView (default|expanded|collapsed), effort (low|medium|high|max|auto), adminModel (any Anthropic model ID), publicModel (any Anthropic model ID), defaultAgent (slug of an existing public agent, or empty string to clear). Changes take effect on the next session.", {
|
|
567
568
|
field: z.enum(["outputStyle", "thinkingView", "effort", "adminModel", "publicModel", "defaultAgent"]),
|
|
568
569
|
value: z.string(),
|
|
569
570
|
}, async ({ field, value }) => {
|
|
@@ -686,7 +687,7 @@ server.tool("plugin-toggle-enabled", "Enable or disable a plugin in this account
|
|
|
686
687
|
// ===================================================================
|
|
687
688
|
// Admin user management tools
|
|
688
689
|
// ===================================================================
|
|
689
|
-
server
|
|
690
|
+
eagerTool(server, "admin-add", "Add a new admin user to this account. Creates a device-level user entry (users.json) and adds them to this account's admins list (account.json). PIN must be at least 4 digits. If no PIN is provided, a unique 4-digit PIN is generated. Returns the userId and PIN to share with the new admin.\n\nIMPORTANT — retry behaviour: if the user already stated a specific PIN earlier in the conversation and a first admin-add call failed (e.g. tier cap reached, PIN collision), every retry MUST re-pass that PIN as the `pin` parameter. Omitting `pin` on the retry auto-generates a different 4-digit PIN, silently substituting what the user asked for.", {
|
|
690
691
|
name: z.string().describe("Display name for the new admin (stored on the AdminUser node in Neo4j)."),
|
|
691
692
|
pin: z.string().optional().describe("Optional PIN (minimum 4 digits). If omitted, a unique 4-digit PIN is generated."),
|
|
692
693
|
}, async ({ name, pin: rawPin }) => {
|
|
@@ -873,7 +874,7 @@ server.tool("admin-add", "Add a new admin user to this account. Creates a device
|
|
|
873
874
|
}],
|
|
874
875
|
};
|
|
875
876
|
});
|
|
876
|
-
server
|
|
877
|
+
eagerTool(server, "admin-remove", "Remove an admin from this account. Removes them from the account's admins list (account.json) and deletes the ADMIN_OF relationship in Neo4j. Does NOT remove the device-level user entry (they may admin other accounts). Cannot remove the last admin on the account.", {
|
|
877
878
|
userId: z.string().describe("The userId of the admin to remove (use admin-list to find userIds)"),
|
|
878
879
|
}, async ({ userId }) => {
|
|
879
880
|
const TAG = "[admin]";
|
|
@@ -949,7 +950,7 @@ server.tool("admin-remove", "Remove an admin from this account. Removes them fro
|
|
|
949
950
|
}],
|
|
950
951
|
};
|
|
951
952
|
});
|
|
952
|
-
server
|
|
953
|
+
eagerTool(server, "admin-list", "List all admins for this account with their names and roles.", {}, async () => {
|
|
953
954
|
const TAG = "[admin]";
|
|
954
955
|
let admins;
|
|
955
956
|
try {
|
|
@@ -991,7 +992,7 @@ server.tool("admin-list", "List all admins for this account with their names and
|
|
|
991
992
|
content: [{ type: "text", text: `Admins for this account:\n\n${lines.join("\n")}` }],
|
|
992
993
|
};
|
|
993
994
|
});
|
|
994
|
-
server
|
|
995
|
+
eagerTool(server, "admin-update-pin", "Update an existing admin user's PIN. Defaults to the calling admin if no userId is given. PIN must be at least 4 digits and unique across all users on the device. PINs are device-level: updating another admin's PIN does not require shared account membership — any admin on the device can rotate any other admin's PIN, matching the existing trust model used by admin-remove.", {
|
|
995
996
|
userId: z.string().optional().describe("The userId of the admin whose PIN to update. Defaults to the caller (the admin invoking this tool)."),
|
|
996
997
|
newPin: z.string().describe("The new PIN. Minimum 4 digits, no upper bound."),
|
|
997
998
|
}, async ({ userId: targetUserId, newPin }) => {
|
|
@@ -1254,7 +1255,7 @@ server.tool("agent-list", "List all public (non-admin) agents with their full co
|
|
|
1254
1255
|
};
|
|
1255
1256
|
}
|
|
1256
1257
|
});
|
|
1257
|
-
server
|
|
1258
|
+
eagerTool(server, "logs-read", "Read recent logs. Stream logs (type=agent-stream/error/session/public) are per-session — pass `sessionKey` (preferred) or the legacy `conversationId` alias to retrieve a single session's log from first [spawn] to final [process-exit]. type=agent-stream: per-session tool-use/tool-result archive (every `[tool-use]` and `[tool-result]` pair with full input + output JSON, plus raw Claude stream-json, agent events, and MCP server stderr via tee). USE THIS when investigating what an agent ACTUALLY did with its tools — server.log only carries `[persist] tool-call persisted` markers, not bodies. (`type=system` is a backwards-compatible alias for the same archive.) type=session: SSE events sent to client. type=error: Claude subprocess stderr (raw — NODE_DEBUG HTTP/NET/UNDICI traces land in agent-stream via the stream tee, not here). type=heartbeat: platform event dispatcher (check-due-events cron). type=public: public agent diagnostic log. type=server: platform server log. type=mcp: MCP server stderr (per-plugin raw). type=vnc: VNC browser viewer lifecycle.", {
|
|
1258
1259
|
type: z.enum(["agent-stream", "system", "session", "error", "heartbeat", "public", "server", "mcp", "vnc"]).optional(),
|
|
1259
1260
|
lines: z.number().optional(),
|
|
1260
1261
|
sessionKey: z.string().optional(),
|
|
@@ -1262,28 +1263,11 @@ server.tool("logs-read", "Read recent logs. Stream logs (type=agent-stream/error
|
|
|
1262
1263
|
}, async ({ type, lines = 50, sessionKey, conversationId }) => {
|
|
1263
1264
|
try {
|
|
1264
1265
|
const LOG_DIR = resolve(getAccountDir(), "logs");
|
|
1265
|
-
// Task
|
|
1266
|
-
//
|
|
1267
|
-
//
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
// Task 671: resolves two filename shapes from the same identifier —
|
|
1271
|
-
// FULL: {prefix}-{conversationId}.log (post first-completed-turn flush)
|
|
1272
|
-
// PREFLUSH: {prefix}-preflush-{id:0:12}.log (pre-flush, first turn;
|
|
1273
|
-
// slice is of the sessionKey)
|
|
1274
|
-
// Full is tried first; if absent, preflush is the fallback. If both
|
|
1275
|
-
// exist, full wins and the preflush sibling is logged to server.log
|
|
1276
|
-
// as stale (best-effort housekeeping signal).
|
|
1277
|
-
//
|
|
1278
|
-
// Identity note: for the abrupt-exit case this branch exists to solve,
|
|
1279
|
-
// the operator passes the sessionKey (no conversationId was ever
|
|
1280
|
-
// assigned). For post-flush retrievals the operator passes the
|
|
1281
|
-
// conversationId and the FULL file is expected to exist.
|
|
1282
|
-
//
|
|
1283
|
-
// MIRROR: keep the two-shape contract in sync with:
|
|
1284
|
-
// - platform/ui/app/lib/logs-read-resolve.ts (canonical TS helper + tests)
|
|
1285
|
-
// - platform/scripts/logs-read.sh (per_conversation_mode)
|
|
1286
|
-
if (conversationId) {
|
|
1266
|
+
// Task 1006 — sessionKey is the single identifier on disk.
|
|
1267
|
+
// `conversationId` is retained as a legacy alias that maps to the
|
|
1268
|
+
// sessionKey-named file; Task 1007 will collapse the alias in the UI.
|
|
1269
|
+
const idForResolve = sessionKey ?? conversationId;
|
|
1270
|
+
if (idForResolve) {
|
|
1287
1271
|
if (!existsSync(LOG_DIR)) {
|
|
1288
1272
|
return { content: [{ type: "text", text: `Log directory does not exist: ${LOG_DIR}` }] };
|
|
1289
1273
|
}
|
|
@@ -1298,41 +1282,29 @@ server.tool("logs-read", "Read recent logs. Stream logs (type=agent-stream/error
|
|
|
1298
1282
|
const prefix = prefixMap[resolvedType];
|
|
1299
1283
|
if (!prefix) {
|
|
1300
1284
|
return {
|
|
1301
|
-
content: [{ type: "text", text: `type=${resolvedType} is not per-
|
|
1285
|
+
content: [{ type: "text", text: `type=${resolvedType} is not per-session. Valid per-session types: agent-stream, error, session, public. For platform-scoped types (server, vnc, heartbeat, mcp) omit the id.` }],
|
|
1302
1286
|
isError: true,
|
|
1303
1287
|
};
|
|
1304
1288
|
}
|
|
1305
|
-
const
|
|
1306
|
-
const
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
appendFileSync(resolve(CONFIG_DIR, "logs", "server.log"), `${ts} [logs-read] stale-preflush-detected path=${preflushPath}\n`);
|
|
1317
|
-
}
|
|
1318
|
-
catch {
|
|
1319
|
-
// swallow
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
const result = execFileSync("tail", ["-n", String(lines), fullPath], { timeout: 5000 }).toString();
|
|
1323
|
-
return { content: [{ type: "text", text: `# ${fullName} (matched_shape=full)\n\n${result}` }] };
|
|
1289
|
+
const fileName = `${prefix}-${idForResolve}.log`;
|
|
1290
|
+
const filePath = resolve(LOG_DIR, fileName);
|
|
1291
|
+
if (existsSync(filePath)) {
|
|
1292
|
+
const result = execFileSync("tail", ["-n", String(lines), filePath], { timeout: 5000 }).toString();
|
|
1293
|
+
return { content: [{ type: "text", text: `# ${fileName}\n\n${result}` }] };
|
|
1294
|
+
}
|
|
1295
|
+
// Emit missing-on-resolve so the writer-side existence contract is
|
|
1296
|
+
// the authoritative signal. One such line on any device is a P0.
|
|
1297
|
+
try {
|
|
1298
|
+
const ts = new Date().toISOString();
|
|
1299
|
+
appendFileSync(resolve(CONFIG_DIR, "logs", "server.log"), `${ts} [log-tee] missing-on-resolve sessionKey=${idForResolve.slice(0, 8)} surface=mcp-logs-read reason="file-not-found-in-LOG_DIR"\n`);
|
|
1324
1300
|
}
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
const result = execFileSync("tail", ["-n", String(lines), preflushPath], { timeout: 5000 }).toString();
|
|
1328
|
-
return { content: [{ type: "text", text: `# ${preflushName} (matched_shape=preflush)\n\n${result}` }] };
|
|
1301
|
+
catch {
|
|
1302
|
+
// best-effort
|
|
1329
1303
|
}
|
|
1330
|
-
// Neither shape present: enumerate both filenames in the miss
|
|
1331
|
-
// message so the reader sees exactly what was tried.
|
|
1332
1304
|
return {
|
|
1333
1305
|
content: [{
|
|
1334
1306
|
type: "text",
|
|
1335
|
-
text: `No log file found for
|
|
1307
|
+
text: `No log file found for id=${idForResolve} type=${resolvedType}. tried=[${filePath}] reason=file-not-found`,
|
|
1336
1308
|
}],
|
|
1337
1309
|
};
|
|
1338
1310
|
}
|
|
@@ -1491,7 +1463,7 @@ server.tool("logs-read", "Read recent logs. Stream logs (type=agent-stream/error
|
|
|
1491
1463
|
// autoDeliverUserPlugins. The agent supplies pluginName/skillName/body —
|
|
1492
1464
|
// path is computed by this tool from ACCOUNT_ID. Symmetric write counterpart
|
|
1493
1465
|
// to plugin-read (Task 916).
|
|
1494
|
-
server
|
|
1466
|
+
eagerTool(server, "store-skill", "Save an operator-authored skill on disk as part of an admin-managed plugin. " +
|
|
1495
1467
|
"The skill becomes immediately discoverable by the admin agent (and the public agent if publicEmbed=true). " +
|
|
1496
1468
|
"Path is computed internally from the active account; the agent supplies content + names only. " +
|
|
1497
1469
|
"Re-running for the same skillName overwrites in place (drops orphan reference files).", {
|
|
@@ -1614,7 +1586,7 @@ ${body}
|
|
|
1614
1586
|
return { content: [{ type: "text", text: `Failed to write skill: ${msg}` }], isError: true };
|
|
1615
1587
|
}
|
|
1616
1588
|
});
|
|
1617
|
-
server
|
|
1589
|
+
eagerTool(server, "plugin-read", "Read a plugin definition (PLUGIN.md) or one of its reference files.", {
|
|
1618
1590
|
pluginName: z.string().describe("Name of the plugin directory (e.g. 'sales', 'business-assistant')"),
|
|
1619
1591
|
file: z.string().optional().describe("Specific file to read (e.g. 'references/pricing.md'). Defaults to PLUGIN.md."),
|
|
1620
1592
|
}, async ({ pluginName, file }) => {
|
|
@@ -1741,7 +1713,7 @@ server.tool("skill-find", "Find which plugin owns a skill by name. Walks plugins
|
|
|
1741
1713
|
// returns richer JSON so the doctrine grep
|
|
1742
1714
|
// `render-component.*"rendered"` resolves to a persistence-aware handler
|
|
1743
1715
|
// rather than a bare stub.
|
|
1744
|
-
server
|
|
1716
|
+
eagerTool(server, "render-component", "Render a pre-built UI component inline in the conversation. " +
|
|
1745
1717
|
"Call this instead of describing a UI — the component handles input collection. " +
|
|
1746
1718
|
"Wait for the user's response before continuing.", {
|
|
1747
1719
|
name: z.string().describe("Component name from the UI suite (e.g. single-select, confirm, info-card, form, progress)"),
|
|
@@ -1768,10 +1740,10 @@ server.tool("render-component", "Render a pre-built UI component inline in the c
|
|
|
1768
1740
|
}
|
|
1769
1741
|
return { content: [{ type: "text", text: "rendered" }] };
|
|
1770
1742
|
});
|
|
1771
|
-
server
|
|
1743
|
+
eagerTool(server, "session-reset", "Reset the current session. Compacts conversation history to memory, clears the visible conversation, " +
|
|
1772
1744
|
"and starts a fresh session with a new greeting. Call when the user asks to start a new session, " +
|
|
1773
1745
|
"clear the conversation, or start fresh.", {}, async () => ({ content: [{ type: "text", text: "reset" }] }));
|
|
1774
|
-
server
|
|
1746
|
+
eagerTool(server, "session-resume", "Resume a previous session. Loads the selected session's message history into the chat timeline " +
|
|
1775
1747
|
"and routes new messages to that conversation. Call after the user selects a session from the " +
|
|
1776
1748
|
"session-list results. Pass the conversationId from the selected session.", { conversationId: z.string().uuid().describe("The conversationId of the session to resume") }, async () => ({ content: [{ type: "text", text: "resumed" }] }));
|
|
1777
1749
|
server.tool("remote-auth-set-password", "Set the remote access password. Hashes with scrypt and writes to the platform's brand-specific config directory with mode 0600. " +
|
|
@@ -1815,7 +1787,7 @@ server.tool("remote-auth-set-password", "Set the remote access password. Hashes
|
|
|
1815
1787
|
};
|
|
1816
1788
|
}
|
|
1817
1789
|
});
|
|
1818
|
-
server
|
|
1790
|
+
eagerTool(server, "api-key-store", "Validate and store an Anthropic API key. Key must start with sk-ant- and be at least 90 characters. " +
|
|
1819
1791
|
"Writes to the platform's brand-specific config directory. Takes effect immediately for the public agent.", { apiKey: z.string() }, async ({ apiKey }) => {
|
|
1820
1792
|
try {
|
|
1821
1793
|
writeKey(apiKey);
|
|
@@ -1828,7 +1800,7 @@ server.tool("api-key-store", "Validate and store an Anthropic API key. Key must
|
|
|
1828
1800
|
};
|
|
1829
1801
|
}
|
|
1830
1802
|
});
|
|
1831
|
-
server
|
|
1803
|
+
eagerTool(server, "api-key-verify", "Verify the stored Anthropic API key works by making a minimal probe call. " +
|
|
1832
1804
|
"Returns one of: valid (key works, credits available), billing (key valid but credit balance too low — " +
|
|
1833
1805
|
"user must add credits at console.anthropic.com/settings/billing), auth_error (key rejected — invalid or revoked), " +
|
|
1834
1806
|
"missing (no key file found), error (could not verify — network issue or Anthropic outage). " +
|
|
@@ -1972,7 +1944,7 @@ function hasPublicEndpointConfigured() {
|
|
|
1972
1944
|
reason: "no entries in alias-domains.json and no public.* hostname in cloudflared/config.yml",
|
|
1973
1945
|
};
|
|
1974
1946
|
}
|
|
1975
|
-
server
|
|
1947
|
+
eagerTool(server, "anthropic-setup", "Deterministic state machine for Anthropic API key acquisition. " +
|
|
1976
1948
|
"Checks current state, advances as far as possible, and returns a structured JSON result. " +
|
|
1977
1949
|
"On first call (no consoleResult): first verifies a public-facing endpoint is configured " +
|
|
1978
1950
|
"(the key only powers the public agent); if not, returns status 'not_needed'. Otherwise " +
|
|
@@ -2311,7 +2283,7 @@ server.tool("onboarding-step9-mode", "Step 9 onboarding fork: record the operato
|
|
|
2311
2283
|
// ===================================================================
|
|
2312
2284
|
// Utility tools
|
|
2313
2285
|
// ===================================================================
|
|
2314
|
-
server
|
|
2286
|
+
eagerTool(server, "qr-generate", "Generate a QR code from text or a URL. Returns the QR code as a data URI (PNG) or saves to a file path.", {
|
|
2315
2287
|
data: z.string().describe("The text or URL to encode in the QR code"),
|
|
2316
2288
|
outputPath: z
|
|
2317
2289
|
.string()
|
|
@@ -2362,7 +2334,7 @@ server.tool("qr-generate", "Generate a QR code from text or a URL. Returns the Q
|
|
|
2362
2334
|
// ===================================================================
|
|
2363
2335
|
// WiFi management (Task 429)
|
|
2364
2336
|
// ===================================================================
|
|
2365
|
-
server
|
|
2337
|
+
eagerTool(server, "wifi", "Manage WiFi connections on this device. Actions: " +
|
|
2366
2338
|
"scan (list visible networks with signal strength and security), " +
|
|
2367
2339
|
"connect (join a network by SSID and password — warn user first if signal is below 30%), " +
|
|
2368
2340
|
"status (current WiFi connection details), " +
|
|
@@ -2938,7 +2910,7 @@ server.tool("premium-deliver", "Deliver a purchased premium plugin. Copies plugi
|
|
|
2938
2910
|
// ---------------------------------------------------------------------------
|
|
2939
2911
|
// file-attach — copy a generated file into the attachment store for download
|
|
2940
2912
|
// ---------------------------------------------------------------------------
|
|
2941
|
-
server
|
|
2913
|
+
eagerTool(server, "file-attach", "Attach a file from the account directory for delivery to the user. " +
|
|
2942
2914
|
"Copies the file into the attachment store and returns metadata for rendering " +
|
|
2943
2915
|
"a download component. The file must be within the account directory tree. " +
|
|
2944
2916
|
"After calling this tool, call render-component with name 'file-attachment' " +
|
|
@@ -3228,7 +3200,7 @@ async function dispatchApprovedAction(plugin, tool, args, timeoutMs = 30_000) {
|
|
|
3228
3200
|
await client.close().catch(() => { });
|
|
3229
3201
|
}
|
|
3230
3202
|
}
|
|
3231
|
-
server
|
|
3203
|
+
eagerTool(server, "action-pending", "List actions that are queued for human approval. Returns each pending action's ID, " +
|
|
3232
3204
|
"tool name, input summary, and when it was queued. Use this to review what the agent " +
|
|
3233
3205
|
"wanted to do before approving or rejecting.", {}, async () => {
|
|
3234
3206
|
const actions = readPendingActions();
|
|
@@ -3245,7 +3217,7 @@ server.tool("action-pending", "List actions that are queued for human approval.
|
|
|
3245
3217
|
content: [{ type: "text", text: `## Pending Actions (${actions.length})\n\n${lines.join("\n\n")}` }],
|
|
3246
3218
|
};
|
|
3247
3219
|
});
|
|
3248
|
-
server
|
|
3220
|
+
eagerTool(server, "action-approve", "Approve a pending action and execute it immediately. The original tool call is " +
|
|
3249
3221
|
"executed via the target plugin's MCP server. The result is returned and an audit " +
|
|
3250
3222
|
"record is written to Neo4j with approvalState: approved.", {
|
|
3251
3223
|
actionId: z.string().describe("The action ID to approve (from action-pending)"),
|
|
@@ -3315,7 +3287,7 @@ server.tool("action-approve", "Approve a pending action and execute it immediate
|
|
|
3315
3287
|
};
|
|
3316
3288
|
}
|
|
3317
3289
|
});
|
|
3318
|
-
server
|
|
3290
|
+
eagerTool(server, "action-reject", "Reject a pending action. The action is not executed. An audit record is written " +
|
|
3319
3291
|
"to Neo4j with approvalState: rejected.", {
|
|
3320
3292
|
actionId: z.string().describe("The action ID to reject (from action-pending)"),
|
|
3321
3293
|
reason: z.string().optional().describe("Optional reason for rejection"),
|
|
@@ -3356,7 +3328,7 @@ server.tool("action-reject", "Reject a pending action. The action is not execute
|
|
|
3356
3328
|
content: [{ type: "text", text: `Action rejected. ${action.toolName} will not be executed.${reasonSuffix}` }],
|
|
3357
3329
|
};
|
|
3358
3330
|
});
|
|
3359
|
-
server
|
|
3331
|
+
eagerTool(server, "action-edit", "Edit a pending action's input and then execute the modified version. The original " +
|
|
3360
3332
|
"input is preserved in the audit trail. Use this when the admin wants to modify the " +
|
|
3361
3333
|
"action (e.g., change the email subject) before approving.", {
|
|
3362
3334
|
actionId: z.string().describe("The action ID to edit (from action-pending)"),
|
|
@@ -3444,7 +3416,7 @@ server.tool("action-edit", "Edit a pending action's input and then execute the m
|
|
|
3444
3416
|
// so the returned score matches the on-disk state byte-for-byte. File-only
|
|
3445
3417
|
// access keeps the answer authoritative even if the Hono server is down.
|
|
3446
3418
|
// ---------------------------------------------------------------------------
|
|
3447
|
-
server
|
|
3419
|
+
eagerTool(server, "adherence-read", "Read the attention-weighted adherence ledger for an agent in this account. Returns the ledger JSON (rules with counts, rolling_7d, samples, streaks) and the score. Reads the file on every call — the value is authoritative and matches `{accountDir}/agents/<agent>/adherence-ledger.json` on disk. Use to answer 'what is my adherence score' or to surface the top offenders. Defaults to the admin agent when no name is supplied.", {
|
|
3448
3420
|
agent: z.string().optional().describe("Agent name to query. Defaults to 'admin'."),
|
|
3449
3421
|
}, async ({ agent }) => {
|
|
3450
3422
|
const TAG = "[adherence-read]";
|