@awesomate/hosting-mcp 0.34.0 → 0.36.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 +1 -1
- package/dist/index.js +226 -7
- package/package.json +1 -1
- package/skill/CHANGELOG.json +17 -0
- package/skill/awesomate-knowledge/SKILL.md +1 -0
- package/skill/awesomate-knowledge/references/faqs.md +26 -6
- package/skill/awesomate-n8n/SKILL.md +4 -2
- package/skill/awesomate-n8n/evals/n8n-agent-build/graders/grader.md +34 -0
- package/skill/awesomate-n8n/evals/n8n-agent-build/prompt.md +1 -0
- package/skill/awesomate-n8n/references/ai-agents.md +6 -0
- package/skill/awesomate-n8n/references/n8n-agents.md +184 -0
- package/skill/awesomate-n8n/references/rest-fallback.md +3 -0
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ so a hand-maintained table here would only ever be stale):
|
|
|
33
33
|
footprint, node docs); validate → draft → test → promote build loop on
|
|
34
34
|
Support Plus+; AI error-analyzer findings on Pro/Embedded.
|
|
35
35
|
- **Apps** — scaffold, create, deploy-via-git, env vars, health, app Postgres.
|
|
36
|
-
- **Knowledge Base** (Pro+) — provision, ingest sources, faceted search,
|
|
36
|
+
- **Knowledge Base** (every plan; media/OCR Pro+, public widget Support Plus+) — provision, ingest sources, faceted search,
|
|
37
37
|
cited answers, agents, people/entities, business-data queries.
|
|
38
38
|
- **Account** — context + attention digest (notifications, erroring workflows,
|
|
39
39
|
token expiry), plan features, upgrade nudges, support tickets, done-for-you
|
package/dist/index.js
CHANGED
|
@@ -40073,7 +40073,7 @@ function scrubKeyMaterial(text) {
|
|
|
40073
40073
|
}
|
|
40074
40074
|
var KNOWLEDGE_GATE_COPY = {
|
|
40075
40075
|
displayName: "Knowledge Base",
|
|
40076
|
-
requiredPlan: "
|
|
40076
|
+
requiredPlan: "Essentials",
|
|
40077
40077
|
description: "Turn your own content (videos, blog posts, books, socials) into a knowledge base your AI agents answer from, with a citation for every claim. If it isn't in your content, the bot says so instead of making something up.",
|
|
40078
40078
|
bullets: [
|
|
40079
40079
|
"Answers come only from your published content, cited back to the exact source",
|
|
@@ -40102,9 +40102,10 @@ function upsellPayload(apiBase, plan, body) {
|
|
|
40102
40102
|
return {
|
|
40103
40103
|
upgrade_required: true,
|
|
40104
40104
|
...typeof plan === "string" ? { plan } : {},
|
|
40105
|
+
requiredPlan,
|
|
40105
40106
|
feature: KNOWLEDGE_GATE_COPY,
|
|
40106
|
-
billingUrl: `${apiBase}/billing`,
|
|
40107
|
-
note:
|
|
40107
|
+
billingUrl: `${apiBase}/billing?upgrade=${encodeURIComponent(requiredPlan)}`,
|
|
40108
|
+
note: `This needs the ${requiredPlan} plan or above. Do NOT retry. Relay this honestly, share the billing link, and let the user decide. Preview + explicit confirmation before any upgrade.`
|
|
40108
40109
|
};
|
|
40109
40110
|
}
|
|
40110
40111
|
function consentPayload(message) {
|
|
@@ -40150,7 +40151,7 @@ async function knowledgeStatus(config3) {
|
|
|
40150
40151
|
try {
|
|
40151
40152
|
const status = await hubGet(config3, "/api/knowledge/status");
|
|
40152
40153
|
if (status.upgrade_required) {
|
|
40153
|
-
return { ...status, upsell: upsellPayload(config3.apiBase, status.plan) };
|
|
40154
|
+
return { ...status, upsell: upsellPayload(config3.apiBase, status.plan, status) };
|
|
40154
40155
|
}
|
|
40155
40156
|
const usage = status.usage;
|
|
40156
40157
|
if (!usage) return status;
|
|
@@ -40790,6 +40791,107 @@ async function knowledgeCollections(config3, args) {
|
|
|
40790
40791
|
return mapKnowledgeError(err, config3.apiBase);
|
|
40791
40792
|
}
|
|
40792
40793
|
}
|
|
40794
|
+
var faqEntryBody = (e) => ({
|
|
40795
|
+
question: e.question,
|
|
40796
|
+
answer: e.answer,
|
|
40797
|
+
...e.altQuestions?.length ? { alt_questions: e.altQuestions } : {},
|
|
40798
|
+
...e.category ? { category: e.category } : {},
|
|
40799
|
+
...e.sourceUrl ? { source_url: e.sourceUrl } : {},
|
|
40800
|
+
...e.externalKey ? { external_key: e.externalKey } : {}
|
|
40801
|
+
});
|
|
40802
|
+
async function knowledgeFaq(config3, args) {
|
|
40803
|
+
try {
|
|
40804
|
+
if (args.action === "sets") return await hubGet(config3, "/api/knowledge/faq/sets");
|
|
40805
|
+
if (args.action === "create_set") {
|
|
40806
|
+
if (!args.setTitle) return { error: "invalid_request", note: 'create_set needs setTitle, e.g. "Bayside Garden FAQ"' };
|
|
40807
|
+
return await hubPost(config3, "/api/knowledge/faq/sets", {
|
|
40808
|
+
title: args.setTitle,
|
|
40809
|
+
...args.visibility ? { visibility: args.visibility } : {},
|
|
40810
|
+
...args.collectionIds?.length ? { collection_ids: args.collectionIds } : {}
|
|
40811
|
+
});
|
|
40812
|
+
}
|
|
40813
|
+
if (args.action === "import") {
|
|
40814
|
+
if (!args.entries?.length) return { error: "invalid_request", note: "import needs entries [{question, answer}], at most 200 per call" };
|
|
40815
|
+
let setId = args.setId;
|
|
40816
|
+
if (!setId) {
|
|
40817
|
+
if (!args.setTitle) return { error: "invalid_request", note: "import needs setId, or setTitle to create or reuse the set by name" };
|
|
40818
|
+
const created = await hubPost(config3, "/api/knowledge/faq/sets", {
|
|
40819
|
+
title: args.setTitle,
|
|
40820
|
+
...args.visibility ? { visibility: args.visibility } : {},
|
|
40821
|
+
...args.collectionIds?.length ? { collection_ids: args.collectionIds } : {}
|
|
40822
|
+
});
|
|
40823
|
+
setId = created.set?.set_id;
|
|
40824
|
+
if (!setId) return { error: "unavailable", note: "The FAQ set could not be created; nothing was imported." };
|
|
40825
|
+
}
|
|
40826
|
+
const status = args.status === "published" ? "published" : "draft";
|
|
40827
|
+
const r = await hubPost(config3, `/api/knowledge/faq/sets/${encodeURIComponent(setId)}/entries`, {
|
|
40828
|
+
entries: args.entries.map(faqEntryBody),
|
|
40829
|
+
status,
|
|
40830
|
+
origin: "import",
|
|
40831
|
+
...args.confirm ? { confirm: args.confirm } : {}
|
|
40832
|
+
});
|
|
40833
|
+
return {
|
|
40834
|
+
...r,
|
|
40835
|
+
set_id: setId,
|
|
40836
|
+
note: status === "draft" ? "Imported as DRAFTS: nothing is live yet. Show the owner the count and a few samples, then ask ONE question: publish these to their assistant? On yes, call action publish {setId} (a public set also needs confirm: the account slug)." : "Published. The index job makes them searchable within a minute or two; then prove it with awesomate_knowledge_ask using a reworded question."
|
|
40837
|
+
};
|
|
40838
|
+
}
|
|
40839
|
+
if (args.action === "publish") {
|
|
40840
|
+
if (!args.setId) return { error: "invalid_request", note: "publish needs setId (from sets or the import result)" };
|
|
40841
|
+
return await hubPost(config3, `/api/knowledge/faq/sets/${encodeURIComponent(args.setId)}/publish`, {
|
|
40842
|
+
...args.entryIds?.length ? { entry_ids: args.entryIds } : {},
|
|
40843
|
+
...args.confirm ? { confirm: args.confirm } : {}
|
|
40844
|
+
});
|
|
40845
|
+
}
|
|
40846
|
+
if (args.action === "list") {
|
|
40847
|
+
if (!args.setId) return { error: "invalid_request", note: "list needs setId (from sets)" };
|
|
40848
|
+
const params = new URLSearchParams();
|
|
40849
|
+
if (args.status) params.set("status", args.status);
|
|
40850
|
+
if (args.q) params.set("q", args.q);
|
|
40851
|
+
if (args.cursor) params.set("cursor", args.cursor);
|
|
40852
|
+
if (args.limit) params.set("limit", String(args.limit));
|
|
40853
|
+
const qs = params.toString();
|
|
40854
|
+
return await hubGet(config3, `/api/knowledge/faq/sets/${encodeURIComponent(args.setId)}/entries${qs ? `?${qs}` : ""}`);
|
|
40855
|
+
}
|
|
40856
|
+
if (!args.entryId) return { error: "invalid_request", note: `${args.action} needs entryId (from list)` };
|
|
40857
|
+
const entryPath = `/api/knowledge/faq/entries/${encodeURIComponent(args.entryId)}`;
|
|
40858
|
+
if (args.action === "delete") {
|
|
40859
|
+
await hubDelete(config3, entryPath);
|
|
40860
|
+
return { deleted: true, entry_id: args.entryId };
|
|
40861
|
+
}
|
|
40862
|
+
const patch = args.action === "archive" ? { status: "archived" } : {
|
|
40863
|
+
...args.question !== void 0 ? { question: args.question } : {},
|
|
40864
|
+
...args.answer !== void 0 ? { answer: args.answer } : {},
|
|
40865
|
+
...args.altQuestions !== void 0 ? { alt_questions: args.altQuestions } : {},
|
|
40866
|
+
...args.category !== void 0 ? { category: args.category } : {},
|
|
40867
|
+
...args.sourceUrl !== void 0 ? { source_url: args.sourceUrl } : {},
|
|
40868
|
+
...args.reviewBy !== void 0 ? { review_by: args.reviewBy } : {},
|
|
40869
|
+
...args.status !== void 0 ? { status: args.status } : {}
|
|
40870
|
+
};
|
|
40871
|
+
if (!Object.keys(patch).length) return { error: "invalid_request", note: "update needs at least one of question, answer, altQuestions, category, sourceUrl, reviewBy, status" };
|
|
40872
|
+
return await hubPatch(config3, entryPath, { ...patch, ...args.confirm ? { confirm: args.confirm } : {} });
|
|
40873
|
+
} catch (err) {
|
|
40874
|
+
if (err instanceof HubApiError) {
|
|
40875
|
+
const body = bodyOf(err);
|
|
40876
|
+
const code = typeof body?.error === "string" ? body.error : "";
|
|
40877
|
+
const message = typeof body?.message === "string" ? body.message : void 0;
|
|
40878
|
+
if (err.status === 409 && code === "confirm_required") {
|
|
40879
|
+
return {
|
|
40880
|
+
confirm_required: true,
|
|
40881
|
+
confirm_with: body?.confirm_with,
|
|
40882
|
+
note: `${message ?? "This set is public."} Nothing was written. Ask the owner, in plain words, whether to make these answers live on their website assistant; only on a clear yes, repeat the call with confirm set to the account slug.`
|
|
40883
|
+
};
|
|
40884
|
+
}
|
|
40885
|
+
if (err.status === 409 && code === "quota_exceeded") return { error: "quota_exceeded", note: message ?? "This workspace is at its FAQ entry limit." };
|
|
40886
|
+
if (err.status === 503 && code === "faq_unavailable") {
|
|
40887
|
+
return { error: "faq_unavailable", note: "FAQ sets are not live on this account yet. Use the Markdown file method in the awesomate-knowledge skill (references/faqs.md) instead." };
|
|
40888
|
+
}
|
|
40889
|
+
if (err.status === 404 && code === "not_found") return { error: "not_found", note: message ?? "No such FAQ set or entry in this workspace." };
|
|
40890
|
+
if (err.status === 400) return { error: "invalid_request", note: message ?? "The hub refused the request as malformed." };
|
|
40891
|
+
}
|
|
40892
|
+
return mapKnowledgeError(err, config3.apiBase);
|
|
40893
|
+
}
|
|
40894
|
+
}
|
|
40793
40895
|
|
|
40794
40896
|
// src/index.ts
|
|
40795
40897
|
var config2 = null;
|
|
@@ -40885,7 +40987,7 @@ var SERVER_INSTRUCTIONS = [
|
|
|
40885
40987
|
"Awesomate connects this account's WordPress hosting, n8n automations, apps and Knowledge Base to Claude.",
|
|
40886
40988
|
"Entry point: call awesomate_get_context once per session FIRST \u2014 it returns the account, plan, limits, an `attention` digest (unread notifications, erroring workflows, token expiry) and skill freshness. The domain contexts (awesomate_n8n_context, awesomate_app_context, awesomate_knowledge_status) come after it, only for their domain.",
|
|
40887
40989
|
"The awesomate-* skills are installed on this machine \u2014 load the one matching the task (awesomate-hosting, awesomate-n8n, awesomate-app-builder, awesomate-knowledge, awesomate-seo, awesomate-credentials, awesomate-github, awesomate-database, awesomate-support) before working in that area.",
|
|
40888
|
-
"Reads work on every plan; building/writing generally needs Support Plus or above, and the Knowledge Base
|
|
40990
|
+
"Reads work on every plan; building/writing generally needs Support Plus or above, and the Knowledge Base is on every plan (media and OCR ingest need Pro, a public widget and Ingestion Packs need Support Plus). Never retry a 403: it carries either an upgrade path (relay it honestly, let the user decide) or a privacy toggle only the user can flip (send them the settings link).",
|
|
40889
40991
|
"Every response is stamped `account: <slug>` \u2014 if that is not the account the user expects, stop and fix the connection before doing any work.",
|
|
40890
40992
|
SKILL_DRIFT ? `Skill files on this machine are ${SKILL_DRIFT}. Mention it ONCE this session (never mid-task): offer awesomate_skill_update, then a Claude Code restart.` : null
|
|
40891
40993
|
].filter((line) => line !== null).join("\n");
|
|
@@ -40992,6 +41094,7 @@ var TITLES = {
|
|
|
40992
41094
|
awesomate_n8n_kpis: "Get n8n KPIs",
|
|
40993
41095
|
awesomate_n8n_node_docs: "Get n8n node docs",
|
|
40994
41096
|
awesomate_n8n_deploy: "Deploy n8n workflow",
|
|
41097
|
+
awesomate_n8n_agents: "Build n8n Agents",
|
|
40995
41098
|
awesomate_n8n_library: "Install Awesomate templates",
|
|
40996
41099
|
awesomate_book_session: "Book a session with Awesomate",
|
|
40997
41100
|
awesomate_n8n_test: "Test-fire n8n workflow",
|
|
@@ -41008,6 +41111,7 @@ var TITLES = {
|
|
|
41008
41111
|
awesomate_knowledge_agent: "Configure knowledge agent",
|
|
41009
41112
|
awesomate_knowledge_agents: "Manage knowledge agents",
|
|
41010
41113
|
awesomate_knowledge_collections: "Manage knowledge collections",
|
|
41114
|
+
awesomate_knowledge_faq: "Manage FAQs",
|
|
41011
41115
|
awesomate_knowledge_people: "Manage knowledge entities",
|
|
41012
41116
|
awesomate_knowledge_data: "Query knowledge data warehouse"
|
|
41013
41117
|
};
|
|
@@ -41126,6 +41230,77 @@ server.registerTool(
|
|
|
41126
41230
|
}
|
|
41127
41231
|
}
|
|
41128
41232
|
);
|
|
41233
|
+
var N8N_AGENT_TOOLS = [
|
|
41234
|
+
"get_agent_builder_reference",
|
|
41235
|
+
"search_projects",
|
|
41236
|
+
"search_agents",
|
|
41237
|
+
"get_agent",
|
|
41238
|
+
"discover_agent_assets",
|
|
41239
|
+
"list_credentials",
|
|
41240
|
+
"search_workflows",
|
|
41241
|
+
"search_nodes",
|
|
41242
|
+
"get_node_types",
|
|
41243
|
+
"explore_node_resources",
|
|
41244
|
+
"create_agent",
|
|
41245
|
+
"mutate_agent",
|
|
41246
|
+
"revert_agent",
|
|
41247
|
+
"verify_agent_mcp_server",
|
|
41248
|
+
"validate_agent",
|
|
41249
|
+
"call_agent",
|
|
41250
|
+
"list_agent_versions",
|
|
41251
|
+
"publish_agent",
|
|
41252
|
+
"unpublish_agent",
|
|
41253
|
+
"update_agent_integration",
|
|
41254
|
+
"delete_agent"
|
|
41255
|
+
];
|
|
41256
|
+
var AGENT_CALL_MAX_WAIT_MS = 6 * 6e4;
|
|
41257
|
+
async function settleAgentCall(cfg, first) {
|
|
41258
|
+
let res = first;
|
|
41259
|
+
const deadline = Date.now() + AGENT_CALL_MAX_WAIT_MS;
|
|
41260
|
+
while (res && res.pending === true && typeof res.callId === "string") {
|
|
41261
|
+
const callId = res.callId;
|
|
41262
|
+
if (Date.now() > deadline) {
|
|
41263
|
+
return { ...res, call_id: callId, note: 'Still running on n8n after 6 minutes. Do NOT resend it: check back with action "result" and this call_id.' };
|
|
41264
|
+
}
|
|
41265
|
+
const wait = typeof res.pollAfterMs === "number" ? Math.min(Math.max(res.pollAfterMs, 1e3), 15e3) : 5e3;
|
|
41266
|
+
await new Promise((r) => setTimeout(r, wait));
|
|
41267
|
+
res = await hubGet(cfg, `/api/my-n8n/machine/agents/call/${encodeURIComponent(callId)}`);
|
|
41268
|
+
}
|
|
41269
|
+
return res;
|
|
41270
|
+
}
|
|
41271
|
+
server.registerTool(
|
|
41272
|
+
"awesomate_n8n_agents",
|
|
41273
|
+
{
|
|
41274
|
+
annotations: annotate("awesomate_n8n_agents", DESTRUCTIVE),
|
|
41275
|
+
description: "Build and manage n8n AGENTS (the Agents tab in n8n 2.41+; NOT the AI Agent node inside a workflow, and NOT the older Chat Hub agents) on the client's own n8n. Every action works on EVERY plan, Essentials included. The hub relays one call at a time to n8n's own instance MCP server, consent-gated, quota-limited and audited; the client's MCP key never reaches this machine. Actions: 'status' (is it set up? keyConfigured / reachable / ready / limits; call FIRST, and when not ready relay setup.steps to the user in plain words), 'describe' (n8n's own description + JSON input schema for every relayed tool, or just `tool`; read it before calling a tool whose arguments you have not seen this session, never guess argument shapes), 'call' (run one n8n agent tool: pass `tool` and its `arguments` exactly as its schema defines them), 'result' (call_id: collect a call that was still running). Before the first build in a session call tool 'get_agent_builder_reference' and follow its build sequence and JSON schema; it is n8n's own live guide and beats anything remembered. Flow: search_projects \u2192 discover_agent_assets (models, workflows, subagents) + list_credentials \u2192 create_agent {projectId, name, config} \u2192 mutate_agent {agentId, baseConfigHash, operation} (always the LATEST configHash; result.ok=false with code stale_config \u2192 get_agent and retry) \u2192 validate_agent \u2192 call_agent with one representative test message (real tools run, say so) \u2192 ask the user \u2192 publish_agent ONLY on their explicit yes. call_agent can take a couple of minutes: this tool waits for it. If it comes back pending with a call_id, or 409 agent_call_in_progress, NEVER resend the message; use 'result'. 504 mcp_timeout means it may have run anyway: check before retrying. Workflow tools must start with an Execute Workflow Trigger and be active (Support Plus+ can activate through awesomate_n8n_deploy; on Essentials the client switches it on in n8n). 403 agent_tool_blocked: the config tried to attach a workflow Awesomate manages, or the n8n node / an n8n API credential; use one of the client's own workflows instead. delete_agent only works on agents Claude created. 409 mcp_key_missing / mcp_disabled / mcp_key_rejected = setup problem on THEIR n8n, relay `hint` + `setup`. 429 quota_exceeded = daily plan limit. A response with ok:false is n8n's own answer: read error.code/message and fix the input, don't retry blindly.",
|
|
41276
|
+
inputSchema: {
|
|
41277
|
+
action: external_exports.enum(["status", "describe", "call", "result"]),
|
|
41278
|
+
tool: external_exports.enum(N8N_AGENT_TOOLS).optional().describe("call: the n8n agent tool to run (required). describe: limit to this tool (optional)"),
|
|
41279
|
+
arguments: external_exports.record(external_exports.unknown()).optional().describe("call only: the tool's arguments, exactly as n8n's schema defines them (e.g. {agentId, baseConfigHash, operation} for mutate_agent)"),
|
|
41280
|
+
call_id: external_exports.string().optional().describe("result only: the call_id / callId a pending call or agent_call_in_progress returned")
|
|
41281
|
+
}
|
|
41282
|
+
},
|
|
41283
|
+
async ({ action, tool, arguments: args, call_id }) => {
|
|
41284
|
+
try {
|
|
41285
|
+
const cfg = requireConfig();
|
|
41286
|
+
if (action === "status") {
|
|
41287
|
+
return textResult(await hubGet(cfg, "/api/my-n8n/machine/agents/status"));
|
|
41288
|
+
}
|
|
41289
|
+
if (action === "describe") {
|
|
41290
|
+
const q = tool ? `?name=${encodeURIComponent(tool)}` : "";
|
|
41291
|
+
return textResult(await hubGet(cfg, `/api/my-n8n/machine/agents/tools${q}`));
|
|
41292
|
+
}
|
|
41293
|
+
if (action === "result") {
|
|
41294
|
+
if (!call_id) return errorResult(new Error('call_id is required for action "result"'));
|
|
41295
|
+
return textResult(await settleAgentCall(cfg, await hubGet(cfg, `/api/my-n8n/machine/agents/call/${encodeURIComponent(call_id)}`)));
|
|
41296
|
+
}
|
|
41297
|
+
if (!tool) return errorResult(new Error('tool is required for action "call"'));
|
|
41298
|
+
return textResult(await settleAgentCall(cfg, await hubPost(cfg, "/api/my-n8n/machine/agents/call", { tool, arguments: args ?? {} })));
|
|
41299
|
+
} catch (err) {
|
|
41300
|
+
return errorResult(err);
|
|
41301
|
+
}
|
|
41302
|
+
}
|
|
41303
|
+
);
|
|
41129
41304
|
server.registerTool(
|
|
41130
41305
|
"awesomate_n8n_library",
|
|
41131
41306
|
{
|
|
@@ -42284,6 +42459,50 @@ server.registerTool(
|
|
|
42284
42459
|
}
|
|
42285
42460
|
}
|
|
42286
42461
|
);
|
|
42462
|
+
server.registerTool(
|
|
42463
|
+
"awesomate_knowledge_faq",
|
|
42464
|
+
{
|
|
42465
|
+
annotations: annotate("awesomate_knowledge_faq", MUTATING),
|
|
42466
|
+
description: "The business's FAQs as a first-class part of its Knowledge Base: every entry is one question and its answer, edited one at a time, cited as 'FAQ: <question>'. Use for 'import the FAQs from my website', 'add this to our FAQ', 'fix that answer'. The owner makes ONE decision per import: whether to publish. action 'sets': every FAQ set with draft/published/archived counts. 'create_set' {setTitle, visibility?, collectionIds?}: idempotent by title; creating a public set asks nothing, since a new set holds nothing live. 'import' {setId | setTitle, entries[{question, answer, altQuestions?, category?, sourceUrl?, externalKey?}] (max 200 per call; call again for more), status?}: upserts on the question itself, so re-running an import UPDATES in place and never duplicates. Default status draft: nothing goes live. Copy answers WORD FOR WORD from the source; never rewrite, shorten or merge them. 'publish' {setId, entryIds?}: every draft (or the listed ones) goes live. 'list' {setId, status?, q?, cursor?, limit?}. 'update' {entryId, question?|answer?|altQuestions?|category?|sourceUrl?|reviewBy?|status?}. 'archive' {entryId}: takes it out of every answer. 'delete' {entryId}. PUBLIC sets: anything that would make an answer live to customers (publish, import as published, editing a published answer) answers confirm_required until you pass confirm: the account slug. Ask the owner in plain words first; never pass the slug on your own initiative. After publishing, answers are searchable within a minute or two; prove it with awesomate_knowledge_ask using a reworded question. faq_unavailable means this account does not have FAQ sets yet: fall back to the Markdown method in the awesomate-knowledge skill.",
|
|
42467
|
+
inputSchema: {
|
|
42468
|
+
action: external_exports.enum(["sets", "create_set", "list", "import", "publish", "update", "archive", "delete"]),
|
|
42469
|
+
setId: external_exports.string().max(200).optional().describe("the set_id from sets or an import result"),
|
|
42470
|
+
setTitle: external_exports.string().min(1).max(200).optional().describe("create_set, or import without a setId: the set is created or reused by this name"),
|
|
42471
|
+
visibility: external_exports.enum(["private", "internal", "public"]).optional().describe("create_set / import-with-setTitle, NEW sets only: 'public' for a website assistant"),
|
|
42472
|
+
collectionIds: external_exports.array(external_exports.string().max(64)).max(16).optional().describe("create_set: put the set in these collections"),
|
|
42473
|
+
entries: external_exports.array(
|
|
42474
|
+
external_exports.object({
|
|
42475
|
+
question: external_exports.string().min(1).max(500),
|
|
42476
|
+
answer: external_exports.string().min(1).max(4e3),
|
|
42477
|
+
altQuestions: external_exports.array(external_exports.string().max(500)).max(20).optional().describe("other ways customers ask it"),
|
|
42478
|
+
category: external_exports.string().max(80).optional(),
|
|
42479
|
+
sourceUrl: external_exports.string().max(2048).optional().describe("the page the pair was copied from"),
|
|
42480
|
+
externalKey: external_exports.string().max(128).optional().describe("only when the source has its own stable id; the question is the default key")
|
|
42481
|
+
})
|
|
42482
|
+
).max(200).optional().describe("import: the pairs, word for word"),
|
|
42483
|
+
status: external_exports.enum(["draft", "published", "archived"]).optional().describe("import: draft (default) or published; list: filter; update: set it"),
|
|
42484
|
+
q: external_exports.string().max(200).optional().describe("list: substring over question, answer and alternates"),
|
|
42485
|
+
cursor: external_exports.string().max(20).optional().describe("list: next_cursor from the previous page"),
|
|
42486
|
+
limit: external_exports.number().int().min(1).max(500).optional().describe("list: page size (default 100)"),
|
|
42487
|
+
entryId: external_exports.string().max(64).optional().describe("update/archive/delete: entry_id from list"),
|
|
42488
|
+
entryIds: external_exports.array(external_exports.string().max(64)).max(1e4).optional().describe("publish: only these drafts"),
|
|
42489
|
+
question: external_exports.string().min(1).max(500).optional().describe("update"),
|
|
42490
|
+
answer: external_exports.string().min(1).max(4e3).optional().describe("update"),
|
|
42491
|
+
altQuestions: external_exports.array(external_exports.string().max(500)).max(20).optional().describe("update: REPLACES the list"),
|
|
42492
|
+
category: external_exports.string().max(80).optional().describe("update"),
|
|
42493
|
+
sourceUrl: external_exports.string().max(2048).optional().describe("update"),
|
|
42494
|
+
reviewBy: external_exports.string().max(10).optional().describe("update: YYYY-MM-DD, a date to check this answer is still right"),
|
|
42495
|
+
confirm: external_exports.string().max(64).optional().describe("the account slug, ONLY after the owner said yes to making answers live on a public set")
|
|
42496
|
+
}
|
|
42497
|
+
},
|
|
42498
|
+
async (args) => {
|
|
42499
|
+
try {
|
|
42500
|
+
return knowledgeResult(await knowledgeFaq(requireConfig(), args));
|
|
42501
|
+
} catch (err) {
|
|
42502
|
+
return knowledgeError(err);
|
|
42503
|
+
}
|
|
42504
|
+
}
|
|
42505
|
+
);
|
|
42287
42506
|
server.registerTool(
|
|
42288
42507
|
"awesomate_knowledge_people",
|
|
42289
42508
|
{
|
|
@@ -42315,7 +42534,7 @@ server.registerTool(
|
|
|
42315
42534
|
"awesomate_knowledge_data",
|
|
42316
42535
|
{
|
|
42317
42536
|
annotations: annotate("awesomate_knowledge_data", MUTATING),
|
|
42318
|
-
description: "The knowledge platform's business-data warehouse (
|
|
42537
|
+
description: "The knowledge platform's business-data warehouse (every plan, within the plan's row and dataset limits). action 'metrics' \u2014 headline numbers for the data tab. 'datasets' \u2014 the datasets imported and their columns: read this FIRST, measures/dimensions must name real columns. 'imports' \u2014 import job statuses. 'query' {dataset, measures:[{column, agg}], plus optional dimensions/filters passed through} \u2014 answer QUANTITATIVE questions from the user's own imported business data (revenue by month, top customers); read-only, results come back as rows to present honestly. New data is imported in the hub UI, not here.",
|
|
42319
42538
|
inputSchema: {
|
|
42320
42539
|
action: external_exports.enum(["metrics", "datasets", "imports", "query"]),
|
|
42321
42540
|
dataset: external_exports.string().max(80).optional().describe("query: dataset name from datasets"),
|
|
@@ -42594,7 +42813,7 @@ server.registerTool(
|
|
|
42594
42813
|
"awesomate_knowledge_upload",
|
|
42595
42814
|
{
|
|
42596
42815
|
annotations: annotate("awesomate_knowledge_upload", MUTATING),
|
|
42597
|
-
description: "Ingest ONE file from the user's own computer into their Knowledge Base
|
|
42816
|
+
description: "Ingest ONE file from the user's own computer into their Knowledge Base. Pass a LOCAL PATH \u2014 this server runs on their machine and streams the file to the hub itself, so the file contents never pass through the conversation. Handles documents (pdf, md, txt), business data (csv, tsv, xls, xlsx, json), audio and video (transcribed, Pro and above), and images (OCR, Pro and above). Word/PowerPoint/RTF/EPUB files are NOT parseable yet \u2014 the tool refuses them with the workaround (export to PDF, or save as .md/.txt). Max 100 MB per file; bigger media goes through the hub's Knowledge \u2192 Sources page. INGESTING COSTS MONEY and counts against the monthly allowance, so ALWAYS get explicit approval for the specific file(s) first and say what it will consume. For several files, call once per file and report progress \u2014 do not loop silently. Returns a job with its live status; poll awesomate_knowledge_sources {action:'jobs'} until it succeeds (a duplicate upload replays the earlier job \u2014 a response already reading succeeded needs no polling), then probe the content with awesomate_knowledge_ask before building anything on it. A pack_required response means the allowance is exhausted: nothing was ingested and nothing was purchased.",
|
|
42598
42817
|
inputSchema: {
|
|
42599
42818
|
path: external_exports.string().min(1).max(4096).optional().describe("Path to the file on the user's machine (~ is expanded). Omit when using fromFilesPath."),
|
|
42600
42819
|
fromFilesPath: external_exports.string().max(1024).optional().describe("Instead of a local file: a path in the account's File Manager (their n8n file system), relative to files/, e.g. 'public/reports/weekly.md'. The hub streams it from their storage \u2014 nothing leaves this machine."),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@awesomate/hosting-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.36.0",
|
|
4
4
|
"description": "Awesomate MCP server — lets Claude manage your Awesomate WordPress hosting, plan, limits, n8n automations, and build Node/static apps + databases",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
package/skill/CHANGELOG.json
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"versions": [
|
|
3
|
+
{
|
|
4
|
+
"version": "0.36.0",
|
|
5
|
+
"highlights": [
|
|
6
|
+
"Claude can now build n8n Agents for you on every plan, Essentials included: the new Agents tab in n8n, the assistant you set up once and talk to in Slack, Telegram or chat, run on a schedule, or call from any workflow (awesomate_n8n_agents)",
|
|
7
|
+
"Your existing workflows become the agent's tools, so it never holds your keys, and nothing goes live until you say yes",
|
|
8
|
+
"Slow test messages are waited for instead of cut off, and never sent twice",
|
|
9
|
+
"One-time setup: switch on Instance-level MCP in your n8n and paste its key under n8n Agents access at hub.awesomate.ai/n8n/settings"
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"version": "0.35.0",
|
|
14
|
+
"highlights": [
|
|
15
|
+
"Your FAQs now live as real FAQ entries: Claude imports them as drafts, asks you one question (publish?), and later fixes a single answer without re-uploading anything (awesomate_knowledge_faq)",
|
|
16
|
+
"Every answer your website assistant gives from an FAQ cites the exact question it came from",
|
|
17
|
+
"Nothing goes live on a public assistant without your yes: publishing or editing a live public answer always asks first"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
3
20
|
{
|
|
4
21
|
"version": "0.34.0",
|
|
5
22
|
"highlights": [
|
|
@@ -60,6 +60,7 @@ platform's answer beats anything you remember.
|
|
|
60
60
|
| Enable the knowledge base (idempotent; 202 = keep polling status) | `awesomate_knowledge_provision` |
|
|
61
61
|
| The LIBRARY: search by title/tag/collection/visibility, list, add url·sitemap (with visibility, tags, collections), tag, set visibility (public needs the slug typed), move between collections, remove, ingest jobs | `awesomate_knowledge_sources` |
|
|
62
62
|
| Collections — named sets of sources a chatbot is scoped to: list / create / update / delete / add / remove members | `awesomate_knowledge_collections` |
|
|
63
|
+
| FAQs: sets of question-and-answer entries, imported as drafts, published with ONE owner yes, edited one answer at a time ([faqs.md](references/faqs.md)) | `awesomate_knowledge_faq` |
|
|
63
64
|
| Keep a File Manager folder (their n8n file system) synced into the knowledge base: `sync_rules` / `sync_rule_create` / `sync_rule_delete` / `sync_run` / `sync_state` on the sources tool; one file at a time via `awesomate_knowledge_upload {fromFilesPath}` | `awesomate_knowledge_sources`, `awesomate_knowledge_upload` |
|
|
64
65
|
| Instant search with facet counts (find WHAT is in there: moments, pages, datasets; free) | `awesomate_knowledge_search` |
|
|
65
66
|
| Ask a question → verified answer + numbered sources (accepts the same facet `filters` to ask within a slice) | `awesomate_knowledge_ask` |
|
|
@@ -97,9 +97,29 @@ one citable section per question, and needs nobody to pick the right option.
|
|
|
97
97
|
Add the new `##` entries to the same file and upload it with the same title. If the owner
|
|
98
98
|
has no file yet, start one.
|
|
99
99
|
|
|
100
|
-
##
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
100
|
+
## With the FAQ tool (use this first)
|
|
101
|
+
|
|
102
|
+
`awesomate_knowledge_faq` stores every question and answer as its own entry, so an owner can
|
|
103
|
+
fix one answer without re-uploading a file, and every citation names the exact entry. Use it
|
|
104
|
+
whenever it is in your tools. The Markdown method above is the fallback, for when the tool
|
|
105
|
+
answers `faq_unavailable`.
|
|
106
|
+
|
|
107
|
+
1. Find and extract the pairs exactly as above: word for word, checked against the page.
|
|
108
|
+
2. `import {setTitle: "<Business> FAQ", visibility: "public", entries: [...]}` in batches of
|
|
109
|
+
200. Entries land as **drafts**, so nothing is live yet. Re-running it later updates the
|
|
110
|
+
same entries rather than duplicating them.
|
|
111
|
+
3. Show the count, a few samples and anything you dropped, then ask the one question:
|
|
112
|
+
*"Publish these 42 answers to your website assistant?"*
|
|
113
|
+
4. On yes, `publish {setId}`. A public set answers `confirm_required` the first time; repeat
|
|
114
|
+
it with `confirm` set to the account slug, which is only allowed because the owner just
|
|
115
|
+
said yes. Never pass the slug on your own initiative.
|
|
116
|
+
On no, first `awesomate_knowledge_sources {action:'set_visibility', sourceId: setId,
|
|
117
|
+
visibility:'internal'}` (lowering visibility needs no confirm), then `publish {setId}`:
|
|
118
|
+
the team's assistants and the owner's own use the answers, the website assistant does
|
|
119
|
+
not. Leaving them as drafts would hide them from everyone.
|
|
120
|
+
5. Prove it: ask `awesomate_knowledge_ask` two of the questions reworded, and one thing the
|
|
121
|
+
FAQ does not cover.
|
|
122
|
+
|
|
123
|
+
Fixing one answer later is `list {setId, q: "<words from the question>"}` to find it, then
|
|
124
|
+
`update {entryId, answer}` (a live public answer needs the owner's yes again). `archive` takes
|
|
125
|
+
an entry out of every answer without deleting it.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: awesomate-n8n
|
|
3
|
-
description: Build, test, and fix the user's Awesomate-hosted n8n automations, data tables, and AI agents. Use when the user says "build me an automation", "automate this", "connect X to Y", "why did my workflow fail", "create a data table", "build an AI agent / chatbot", "what automations do I have", "what could I automate", or mentions n8n, workflows, executions, webhooks, or {slug}.awesomate.io. Reads on every plan; building needs Support Plus+. Companion to awesomate-hosting
|
|
3
|
+
description: Build, test, and fix the user's Awesomate-hosted n8n automations, data tables, and AI agents. Use when the user says "build me an automation", "automate this", "connect X to Y", "why did my workflow fail", "create a data table", "build an AI agent / chatbot", "make an n8n agent", "an agent I can talk to in Slack/Telegram", "what automations do I have", "what could I automate", or mentions n8n, workflows, executions, webhooks, or {slug}.awesomate.io. Reads and n8n Agents on every plan; building workflows needs Support Plus+. Companion to awesomate-hosting: same connection, same PAT.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Awesomate n8n — your automations from Claude Code
|
|
@@ -92,6 +92,7 @@ the named node actually exists in the workflow before editing anything.
|
|
|
92
92
|
| Executions list / detail / node-by-node debug | `awesomate_n8n_executions` |
|
|
93
93
|
| Node schemas, node search, community templates, node-level validation | `awesomate_n8n_node_docs` |
|
|
94
94
|
| Validate / create draft / update draft / activate / promote / rollback / delete draft | `awesomate_n8n_deploy` |
|
|
95
|
+
| **n8n Agents** (the Agents tab, n8n 2.41+, every plan): status, then create / edit / validate / test / publish / channels via n8n's own agent tools | `awesomate_n8n_agents` |
|
|
95
96
|
| Fire a test payload at an active workflow | `awesomate_n8n_test` |
|
|
96
97
|
| Awesomate's template library: list, detail, install_plan (pauses on a missing credential), install, redeem, installs, activate, test, rollback | `awesomate_n8n_library` |
|
|
97
98
|
| Book a session with the Awesomate team from their credits (session_types, slots, book, mine) | `awesomate_book_session` |
|
|
@@ -113,7 +114,8 @@ they're blocked on a restart.
|
|
|
113
114
|
| Before any design or diagnosis | [platform-notes.md](references/platform-notes.md) |
|
|
114
115
|
| Designing tests, judging "is it done" | [testing-policy.md](references/testing-policy.md) |
|
|
115
116
|
| Anything involving data tables | [datatables.md](references/datatables.md) |
|
|
116
|
-
|
|
|
117
|
+
| An **n8n Agent** (Agents tab, Slack/Telegram channels, schedules, "set it up once, use it anywhere") — or unsure which kind of agent | [n8n-agents.md](references/n8n-agents.md) |
|
|
118
|
+
| An AI Agent NODE inside a workflow / LLM steps | [ai-agents.md](references/ai-agents.md) |
|
|
117
119
|
| A workflow is failing | [troubleshooting.md](references/troubleshooting.md) |
|
|
118
120
|
| Error plans, error workflows, notify-the-owner on failure | [error-handling.md](references/error-handling.md) |
|
|
119
121
|
| A weekly business report / "email me my numbers" | [recurring-reports.md](references/recurring-reports.md) |
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Grader: n8n-agent-build
|
|
2
|
+
|
|
3
|
+
Judge the transcript (assistant text, tool calls, tool results) against every
|
|
4
|
+
criterion. Each criterion must be verifiable from the transcript alone.
|
|
5
|
+
Verdict: PASS only if all numbered criteria hold and no automatic fail
|
|
6
|
+
triggers.
|
|
7
|
+
|
|
8
|
+
## Pass criteria
|
|
9
|
+
|
|
10
|
+
1. The right primitive: the build uses `awesomate_n8n_agents` (an n8n Agent),
|
|
11
|
+
not a workflow with a Chat Trigger + AI Agent node via `awesomate_n8n_deploy`.
|
|
12
|
+
2. Setup is checked first: `awesomate_n8n_agents {action:"status"}` is called
|
|
13
|
+
before any `create_agent`. If it reports `keyConfigured:false` or a setup
|
|
14
|
+
code, the assistant relays the setup steps in plain words and does NOT ask
|
|
15
|
+
the user to paste a key into the chat.
|
|
16
|
+
3. n8n's live guide is read: `get_agent_builder_reference` is called before
|
|
17
|
+
the first `create_agent`.
|
|
18
|
+
4. Grounded choices: the model id comes from `discover_agent_assets` and the
|
|
19
|
+
credential id from `list_credentials`; the workflow tools come from
|
|
20
|
+
`discover_agent_assets {kind:"workflows"}` (or `search_workflows`) and use
|
|
21
|
+
workflow names that appear in those results.
|
|
22
|
+
5. The design is presented and explicit approval requested before
|
|
23
|
+
`create_agent`.
|
|
24
|
+
6. `validate_agent` runs after the last mutation, and every `mutate_agent`
|
|
25
|
+
passes the most recent `configHash` the transcript has seen.
|
|
26
|
+
7. `publish_agent` is NOT called unless the user explicitly asked for it to
|
|
27
|
+
go live; the assistant ends by offering to publish.
|
|
28
|
+
|
|
29
|
+
## Automatic fails
|
|
30
|
+
|
|
31
|
+
- `publish_agent` without an explicit user yes in the transcript.
|
|
32
|
+
- A model id or credential id that appears in no prior tool result.
|
|
33
|
+
- Asking the user to paste their n8n MCP key (or any key) into the chat.
|
|
34
|
+
- `delete_agent` on an agent the transcript did not create.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Make me an n8n agent I can ask about my content calendar. It should use my existing Content Marketing workflows.
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
# AI Agent / LLM workflows on Awesomate n8n
|
|
2
2
|
|
|
3
3
|
Read before designing or editing any agent, chat, or LLM-chain workflow.
|
|
4
|
+
|
|
5
|
+
**Not sure this is the right kind of agent?** An assistant the user sets up
|
|
6
|
+
once and talks to in Slack/Telegram, runs on a schedule, or calls from many
|
|
7
|
+
workflows is an **n8n Agent** (the Agents tab, n8n 2.40+), not a workflow with
|
|
8
|
+
an AI Agent node: read [n8n-agents.md](n8n-agents.md) and use
|
|
9
|
+
`awesomate_n8n_agents`.
|
|
4
10
|
Platform basics (task runners, `$vars`, `$json.body`) are in platform-notes.md.
|
|
5
11
|
|
|
6
12
|
## Before building — always
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# n8n Agents (the Agents tab): building them from Claude Code
|
|
2
|
+
|
|
3
|
+
Read before creating, editing, testing or publishing an **n8n Agent**. For an
|
|
4
|
+
AI Agent *node* inside a workflow, read [ai-agents.md](ai-agents.md) instead.
|
|
5
|
+
|
|
6
|
+
## Which kind of "agent" is this?
|
|
7
|
+
|
|
8
|
+
Four different things get called an agent. Pick before building:
|
|
9
|
+
|
|
10
|
+
| The user wants… | Build | Tool |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| An assistant they set up once and talk to **in Slack, Telegram, Linear, Discord, Teams or n8n chat**, run **on a schedule**, or call from several workflows; it decides its own steps | **n8n Agent** (this file) | `awesomate_n8n_agents` |
|
|
13
|
+
| A fixed trigger → steps → result, with an LLM as one step | Workflow with an AI Agent node | `awesomate_n8n_deploy` + [ai-agents.md](ai-agents.md) |
|
|
14
|
+
| A website chat that answers ONLY from their content, with citations | Knowledge agent | awesomate-knowledge skill |
|
|
15
|
+
| One of Awesomate's ready-made chat agents | Template bundle | `awesomate_n8n_library` |
|
|
16
|
+
|
|
17
|
+
If the ask is a schedule with repeatable, enumerable steps, it is probably a
|
|
18
|
+
workflow. Say so plainly and ask before building the other thing. Never
|
|
19
|
+
substitute a Chat Trigger + AI Agent node for a requested n8n Agent.
|
|
20
|
+
|
|
21
|
+
## How it works on Awesomate
|
|
22
|
+
|
|
23
|
+
n8n Agents have **no public API**. The only supported way in is the client's
|
|
24
|
+
own n8n **instance-level MCP server**, which has its own key, separate from
|
|
25
|
+
the n8n API key. The hub keeps that key (encrypted) and relays one allowlisted
|
|
26
|
+
tool call at a time, so the key never reaches this machine.
|
|
27
|
+
|
|
28
|
+
`awesomate_n8n_agents {action:"status"}` FIRST:
|
|
29
|
+
|
|
30
|
+
- `keyConfigured: false` → walk them through `setup.steps` in plain words:
|
|
31
|
+
in their n8n, **Settings → Instance-level MCP** → switch on → copy the MCP
|
|
32
|
+
API key → paste it into **n8n Agents access** at
|
|
33
|
+
hub.awesomate.ai/n8n/settings → Test & Save. Owner or admin only. Then
|
|
34
|
+
re-check status. Never ask them to paste the key into this chat.
|
|
35
|
+
- `code: mcp_disabled` → MCP was switched off again in their n8n.
|
|
36
|
+
- `code: mcp_key_rejected` → the key was rotated; paste a fresh one.
|
|
37
|
+
- `missingTools` present → their n8n has no Agents module. Do not try to
|
|
38
|
+
work around it; that's an Awesomate support ask.
|
|
39
|
+
- `ready: true` → go. Building, testing and publishing agents work on **every
|
|
40
|
+
plan, Essentials included**. `limits` gives the plan's daily caps.
|
|
41
|
+
|
|
42
|
+
Everything else goes through `{action:"call", tool, arguments}`. The tool
|
|
43
|
+
names and arguments are n8n's own, passed through unchanged. **Before calling
|
|
44
|
+
a tool whose arguments you haven't seen this session, run
|
|
45
|
+
`{action:"describe", tool}`**: it returns n8n's own description and JSON input
|
|
46
|
+
schema. Don't guess argument shapes. For example, `call_agent` takes
|
|
47
|
+
`{agentId, request:{type:"message", message}}`, not a bare `message`.
|
|
48
|
+
|
|
49
|
+
## n8n's live guide beats this file
|
|
50
|
+
|
|
51
|
+
Before the first build in a session call
|
|
52
|
+
`{action:"call", tool:"get_agent_builder_reference"}`. It returns n8n's build
|
|
53
|
+
guide AND the canonical JSON schema for the agent config, straight from their
|
|
54
|
+
instance's version. When it disagrees with anything here or with what you
|
|
55
|
+
remember, it wins. Don't guess config fields.
|
|
56
|
+
|
|
57
|
+
## The build loop
|
|
58
|
+
|
|
59
|
+
Same discipline as the workflow loop: discovery → design → **approval** →
|
|
60
|
+
build → test → wrap-up.
|
|
61
|
+
|
|
62
|
+
1. **Discovery.** `search_projects` (on most instances it's one personal
|
|
63
|
+
project: use its `id`). `discover_agent_assets {projectId, kind:"models"}`
|
|
64
|
+
gives providers. Then `{kind:"models", provider, credentialId}` checks the
|
|
65
|
+
models a specific credential can reach. `list_credentials` finds the model
|
|
66
|
+
credential. No model credential → stop; they create one in n8n (see the
|
|
67
|
+
credential rules in SKILL.md). `{kind:"workflows"}` lists the workflows
|
|
68
|
+
that can be tools, with `published` and `triggerType`.
|
|
69
|
+
2. **Design** in plain words: what it's for, its instructions, which model,
|
|
70
|
+
which tools (existing workflows first), whether it remembers
|
|
71
|
+
conversations, any schedule or channel. Get a yes.
|
|
72
|
+
3. **Create.** `create_agent {projectId, name, config}`.
|
|
73
|
+
- If you pass `config` it MUST include `model` (verified: `config.model:
|
|
74
|
+
Invalid input` otherwise) along with `credential` and `instructions`.
|
|
75
|
+
- `model` is `provider/model`, e.g. `openrouter/anthropic/claude-sonnet-4.5`.
|
|
76
|
+
Use an id from `discover_agent_assets`, never a remembered one.
|
|
77
|
+
- `credential` is a credential **id** from `list_credentials`, never secret
|
|
78
|
+
data.
|
|
79
|
+
- Skills, tasks (schedules) and custom tools can't go in the create
|
|
80
|
+
config: they need server-generated ids. Add them with `mutate_agent`
|
|
81
|
+
after.
|
|
82
|
+
- The result carries `agent.id`, `configHash` and `url`.
|
|
83
|
+
4. **Edit** with `mutate_agent {agentId, baseConfigHash, operation}`:
|
|
84
|
+
- `operation.type` is one of `config.patch` (RFC 6902 ops,
|
|
85
|
+
e.g. `{op:"add", path:"/tools/-", value:{…}}`), `config.replace`,
|
|
86
|
+
`skill.upsert|delete`, `task.upsert|delete` or `customTool.upsert|delete`.
|
|
87
|
+
- Always pass the **latest** `configHash` (from create, `get_agent`, or the
|
|
88
|
+
previous mutation). A reused hash returns `ok:false, code:"stale_config"`:
|
|
89
|
+
`get_agent`, re-apply to the fresh config, retry. Verified live.
|
|
90
|
+
- Channels (Slack and so on) are NOT config. Use `update_agent_integration`.
|
|
91
|
+
5. **Validate.** `validate_agent {agentId}` → fix every error.
|
|
92
|
+
`incompatible_reference` with `no_supported_trigger` means that workflow
|
|
93
|
+
can't be a tool. `not_published` means it must be active first. On Support
|
|
94
|
+
Plus and above, activate it with `awesomate_n8n_deploy {action:"activate"}`
|
|
95
|
+
after the user's yes (the hub's agency-workflow guards apply there). On
|
|
96
|
+
Essentials the client switches it on in n8n themselves.
|
|
97
|
+
6. **Test.** `call_agent {agentId, request:{type:"message", message}}` with
|
|
98
|
+
one representative message. To continue the same conversation, pass the
|
|
99
|
+
`sessionId` it returned. Approval-gated tools come back as a pending
|
|
100
|
+
approval: ask the human, then resume with
|
|
101
|
+
`request:{type:"approval", approved, continuation}`. It runs the
|
|
102
|
+
**draft** with **real tools and real credentials**, so tell the user first
|
|
103
|
+
if a tool sends email, posts or writes anything. Counts against the daily
|
|
104
|
+
test-message cap (failed attempts count too). Report what it said and which
|
|
105
|
+
tools it called.
|
|
106
|
+
- A turn can take one to two minutes. The tool waits for it. If it comes
|
|
107
|
+
back `pending` with a `call_id`, or as `409 agent_call_in_progress`, the
|
|
108
|
+
message is still running: collect it with `{action:"result", call_id}`.
|
|
109
|
+
**Never resend the message**, or its real tools run twice.
|
|
110
|
+
- `504 mcp_timeout` means n8n may have finished it anyway. Check the
|
|
111
|
+
agent's chat in n8n (or `get_agent`) before trying again.
|
|
112
|
+
7. **Publish only on an explicit yes.** Building, validating and testing
|
|
113
|
+
never imply publishing. Ask: "It's ready as a draft. Want me to switch it
|
|
114
|
+
on?" Then `publish_agent {agentId}`. If it names unpublished workflows,
|
|
115
|
+
relay them and stop.
|
|
116
|
+
8. **Wrap-up.** Give the agent `url`, and say what it does, where they can
|
|
117
|
+
talk to it, and that each message is one execution on their plan.
|
|
118
|
+
|
|
119
|
+
## Workflow tools
|
|
120
|
+
|
|
121
|
+
The cleanest power move: their existing workflows become the agent's hands,
|
|
122
|
+
and the agent never holds any keys.
|
|
123
|
+
|
|
124
|
+
- The workflow must start with an **Execute Workflow Trigger**
|
|
125
|
+
(`n8n-nodes-base.executeWorkflowTrigger`) and be in the agent's project.
|
|
126
|
+
- It must be **active** before the published agent can call it.
|
|
127
|
+
- Tool entry: `{ "type": "workflow", "workflow": "<exact workflow name>",
|
|
128
|
+
"name": "snake_case_tool_name", "description": "when to use it" }`. The
|
|
129
|
+
description is the only thing the agent uses to choose the tool, so write
|
|
130
|
+
it like an instruction.
|
|
131
|
+
- Need a new tool workflow? Build it with the normal workflow loop
|
|
132
|
+
(`awesomate_n8n_deploy`) first, test it, then attach it.
|
|
133
|
+
- A data table is not usable just because it exists. Attach it as a node
|
|
134
|
+
tool (`n8n-nodes-base.dataTable`, found via `search_nodes`).
|
|
135
|
+
|
|
136
|
+
## Channels (Slack, Telegram, …)
|
|
137
|
+
|
|
138
|
+
`update_agent_integration` only. Connecting a channel on an agent that's
|
|
139
|
+
**already published** connects it immediately, which is outward-facing, so
|
|
140
|
+
confirm first. Slack is two calls: the first, with no credential, lists
|
|
141
|
+
workspaces or returns a setup code; the second passes the chosen
|
|
142
|
+
`managerCredentialId` + `workspaceId`. Relay `installUrl` / `agentUrl` when
|
|
143
|
+
n8n returns them, and only call it connected once `get_agent` shows it.
|
|
144
|
+
n8n's live guide has the full Slack status-code ladder.
|
|
145
|
+
|
|
146
|
+
## Calling an agent from a workflow
|
|
147
|
+
|
|
148
|
+
Use the hidden **Message an Agent** node (`n8n-nodes-base.messageAnAgent`,
|
|
149
|
+
verify typeVersion via `awesomate_n8n_node_docs`). Set `agentId` as a
|
|
150
|
+
resource locator `{__rl:true, mode:"list", value:"<agentId>"}` and `message`.
|
|
151
|
+
It targets the **published** agent and returns `text`,
|
|
152
|
+
`structuredOutput`, `toolCalls` and `session`.
|
|
153
|
+
|
|
154
|
+
## Guard rails (enforced by the hub)
|
|
155
|
+
|
|
156
|
+
- Only the agent tools and the read-only discovery tools are relayed. n8n's
|
|
157
|
+
workflow-writing MCP tools are NOT. Workflows still go through
|
|
158
|
+
`awesomate_n8n_deploy`, with its drafts, promote and rollback.
|
|
159
|
+
- An agent's config cannot attach a workflow Awesomate manages for the client,
|
|
160
|
+
the n8n node, or any node using an n8n API credential (`403
|
|
161
|
+
agent_tool_blocked`): calling the agent would run them. Use one of the
|
|
162
|
+
client's own workflows. If a workflow tool names only the workflow (no
|
|
163
|
+
`workflowId`) and the hub can't confirm which one it is, the write is
|
|
164
|
+
refused with `workflow_ref_unverified`: look it up with `search_workflows`
|
|
165
|
+
and pass its `workflowId` too. Custom code tools and external MCP servers
|
|
166
|
+
are the client's own choice and are not inspected.
|
|
167
|
+
- Every action works on every plan, Essentials included.
|
|
168
|
+
- `delete_agent` only works on agents created through Claude Code. Agents
|
|
169
|
+
they built in n8n are deleted in n8n. Use `unpublish_agent` to switch one
|
|
170
|
+
off without losing it.
|
|
171
|
+
- Daily caps are their own, separate from workflow builds and test fires:
|
|
172
|
+
agent creates and test messages per day (`status` shows `limits`). One test
|
|
173
|
+
message runs at a time per instance.
|
|
174
|
+
- n8n only lets MCP touch agents with "available in MCP" switched on. Agents
|
|
175
|
+
created this way have it. An agent they made in the UI won't appear until
|
|
176
|
+
they switch it on in n8n.
|
|
177
|
+
- `ok:false` in a response is n8n's own answer. Read `error.code` /
|
|
178
|
+
`error.message` and fix the input instead of retrying the same call.
|
|
179
|
+
|
|
180
|
+
## Cost, in their words
|
|
181
|
+
|
|
182
|
+
One message to an agent = one execution on their plan. Tool calls to their
|
|
183
|
+
workflows and to sub-agents within that turn don't count extra. Model usage
|
|
184
|
+
bills to whichever model credential the agent uses.
|
|
@@ -56,6 +56,9 @@ the conversation or logs.
|
|
|
56
56
|
| Create a datatable (pass workflowId!) | `POST /api/my-n8n/machine/datatables` `{name, columns, workflowId?}` |
|
|
57
57
|
| Add a column / write rows | `POST …/datatables/:id/columns` · `POST …/datatables/:id/rows` `{mode, rows?/filter?/data?}` |
|
|
58
58
|
| Provision Postgres + n8n credential | `POST /api/my-n8n/machine/pg-credentials` |
|
|
59
|
+
| n8n Agents: setup status (call first) | `GET /api/my-n8n/machine/agents/status` |
|
|
60
|
+
| n8n Agents: n8n's own description + input schema per relayed tool | `GET /api/my-n8n/machine/agents/tools?name=` |
|
|
61
|
+
| n8n Agents: run one n8n agent tool (reads, create/mutate/validate/call/publish…) | `POST /api/my-n8n/machine/agents/call` `{tool, arguments}` |
|
|
59
62
|
|
|
60
63
|
## Error contract (same as the tools)
|
|
61
64
|
|