Skip to main content
Glama
probe-n8n.js5.55 kB
/* Probe n8n tags + workflows API to (1) detect endpoints, (2) set tag color, (3) attach tags to a workflow. Prints a compact JSON summary to stdout. No secrets are logged. */ const base = process.env.N8N_BASE_URL?.replace(/\/$/, ""); const key = process.env.N8N_API_KEY; if (!base || !key) { console.error(JSON.stringify({ error: "Missing envs" })); process.exit(1); } const HEADERS = { "X-N8N-API-KEY": key, "Content-Type": "application/json", }; const S = { startedAt: new Date().toISOString() }; async function req(method, path, data) { const url = `${base}${path}`; try { const res = await fetch(url, { method, headers: HEADERS, body: data ? JSON.stringify(data) : undefined, }); const ct = res.headers.get("content-type") || ""; let body; if (ct.includes("application/json")) { body = await res.json().catch(() => ({})); } else { body = await res.text().catch(() => ""); } if (!res.ok) return { ok: false, status: res.status, data: body }; return { ok: true, status: res.status, data: body }; } catch (e) { return { ok: false, status: e?.code || "ERR", data: String(e) }; } } function pickWorkflowId(wf) { return wf?.id ?? wf?.workflowId ?? wf?.numericId; } // Normalize arrays that may be under { data: [] } function unwrapList(x) { if (!x) return []; if (Array.isArray(x)) return x; if (Array.isArray(x.data)) return x.data; return []; } async function main() { // 1) Discover tags endpoint let tagsEndpoint = "/rest/tags"; let tags = []; let r = await req("GET", "/rest/tags"); if (!r.ok) { const r2 = await req("GET", "/api/v1/tags"); if (r2.ok) { tagsEndpoint = "/api/v1/tags"; tags = unwrapList(r2.data) || []; S.tagsEndpoint = tagsEndpoint; } else { S.tagsEndpointError = { rest: r.status, v1: r2.status }; } } else { tags = unwrapList(r.data) || []; S.tagsEndpoint = tagsEndpoint; } // Ensure demo tag exists const wantTagName = "mcp-demo-wsxp40"; let demoTag = tags.find((t) => t?.name === wantTagName); if (!demoTag) { const cr = await req("POST", tagsEndpoint, { name: wantTagName }); if (cr.ok) { demoTag = cr.data?.data ?? cr.data; } else { S.createTagError = cr.status; } } if (demoTag) S.demoTag = { id: demoTag.id, name: demoTag.name }; // 2) Attempt to set color on the tag if (demoTag) { const colorCandidates = ["#00bcd4", "#00BCD4", "00bcd4"]; const paths = [ (id) => `/rest/tags/${id}`, (id) => `/api/v1/tags/${id}`, ]; let colorSet = false; const colorErrors = []; for (const path of paths) { for (const color of colorCandidates) { // Try PATCH const pr = await req("PATCH", path(demoTag.id), { color }); if (pr.ok) { colorSet = true; S.tagColorSet = color; break; } else { colorErrors.push({ method: "PATCH", path: path("") + "{id}", status: pr.status }); } // Try PUT with name+color const ur = await req("PUT", path(demoTag.id), { name: demoTag.name, color }); if (ur.ok) { colorSet = true; S.tagColorSet = color; break; } else { colorErrors.push({ method: "PUT", path: path("") + "{id}", status: ur.status }); } } if (colorSet) break; } if (!colorSet) S.tagColorErrors = colorErrors; } // 3) Find the workflow and attach tags const wfName = "MCP Demo Workflow 2025-10-08T01:15:21.616Z wsxp40 v2"; let workflows = []; let wr = await req("GET", "/rest/workflows"); if (!wr.ok) wr = await req("GET", "/api/v1/workflows"); if (wr.ok) workflows = unwrapList(wr.data); let wf = workflows.find((w) => w?.name === wfName) || workflows.find((w) => w?.numericId === 2) || workflows.find((w) => w?.id === "c1Ct6IhoBEBiky3U"); if (!wf) { S.workflowError = "not-found"; console.log(JSON.stringify(S)); return; } const wfId = pickWorkflowId(wf); S.workflow = { id: wfId, name: wf?.name, active: !!wf?.active }; // Refresh tags list to ensure ids if (!tags.length) { const tr = await req("GET", tagsEndpoint); if (tr.ok) tags = unwrapList(tr.data); } const neededNames = ["example", "webhook", "api", "mcp-demo", wantTagName]; const tagIds = neededNames .map((n) => tags.find((t) => t?.name === n)?.id) .filter(Boolean); let attachOk = false; const attachErrors = []; // Variant A: PATCH /rest/workflows/{id} with names let ar = await req("PATCH", `/rest/workflows/${wfId}`, { tags: neededNames }); if (ar.ok) attachOk = true; // Variant B: PATCH /rest/workflows/{id} with IDs if (!attachOk) { ar = await req("PATCH", `/rest/workflows/${wfId}`, { tags: tagIds }); if (ar.ok) attachOk = true; else attachErrors.push({ restPatch: ar.status }); } // Variant C: POST /api/v1/workflows/{id}/tags { tagIds } if (!attachOk) { const pr = await req("POST", `/api/v1/workflows/${wfId}/tags`, { tagIds }); if (pr.ok) attachOk = true; else attachErrors.push({ v1Post: pr.status }); } S.tagsAttached = attachOk; if (!attachOk) S.attachErrors = attachErrors; // Verify tags on workflow const v = await req("GET", `/rest/workflows/${wfId}`); if (v.ok) { S.workflowTags = v.data?.tags || []; } S.finishedAt = new Date().toISOString(); console.log(JSON.stringify(S)); } main().catch((e) => { console.error(JSON.stringify({ error: String(e) })); process.exit(1); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/get2knowio/n8n-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server