{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "d342f950-aa50-4cb3-92e1-cb8764552115",
      "name": "Decodo MCP",
      "type": "@n8n/n8n-nodes-langchain.mcpClientTool",
      "position": [
        -224,
        -160
      ],
      "parameters": {
        "options": {},
        "endpointUrl": "https://server.smithery.ai/@Decodo/decodo-mcp-server/mcp?api_key=YOUR_TOKEN_HERE&profile=YOUR_DECODO_PROFILE_ID",
        "serverTransport": "httpStreamable"
      },
      "typeVersion": 1.1
    },
    {
      "id": "c2d6c8f2-0f51-4a5f-877a-390e207b41fe",
      "name": "Split",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        288,
        -416
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "output.items"
      },
      "typeVersion": 1
    },
    {
      "id": "56bb6812-b074-4f22-a677-bd53eb04da86",
      "name": "Slack",
      "type": "n8n-nodes-base.slack",
      "position": [
        1040,
        -416
      ],
      "parameters": {
        "text": "={{\n(() => {\n  // Prefer aggregated array, else fall back to current items\n  const arr = Array.isArray($json.articles) ? $json.articles\n            : ($items && $items.length ? $items.map(i => i.json) : []);\n  if (!arr.length) return \"No fresh items today.\";\n  // Escape for Slack mrkdwn\n  const htmlEsc = s => String(s || '')\n    .replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');\n  const mdEsc = s => htmlEsc(s).replace(/([_*~`])/g, '\\\\$1');\n  const domain = u => { try { return new URL(u).hostname.replace(/^www\\./,''); } catch { return ''; } };\n  const parseDate = v => { const d = new Date(v || Date.now()); return isNaN(d) ? new Date() : d; };\n  const toIST = v => {\n    try { return parseDate(v).toLocaleString('en-IN',{ timeZone:'Asia/Kolkata', hour12:false }); }\n    catch { return ''; }\n  };\n  \n  const body = arr\n    .sort((x,y) => new Date(y.date || y?.json?.date || 0) - new Date(x.date || x?.json?.date || 0))\n    .map((it) => {\n      const url = it.url ?? it?.json?.url ?? '';\n      const title = mdEsc(it.title ?? it?.json?.title ?? '(untitled)');\n      const src = (it.source ?? it?.json?.source) || domain(url);\n      const summary = mdEsc(it.summary ?? it?.json?.summary ?? '');\n      const link = url ? `<${url}|${title}>` : title;\n      return `*${link}* (${src})\\n${summary}`;\n    })\n    .join('\\n\\n');\n  return `\\n\\n${body}`;\n})()\n}}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "name",
          "value": "#random"
        },
        "otherOptions": {}
      },
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "e79daa86-feaf-44d4-ac43-d86f42ebbc92",
      "name": "Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        -368,
        -208
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "678b965f-c475-453c-a261-6bc7107072d7",
      "name": "Remove Duplicates",
      "type": "n8n-nodes-base.removeDuplicates",
      "position": [
        496,
        -416
      ],
      "parameters": {
        "compare": "selectedFields",
        "options": {},
        "fieldsToCompare": "url, title"
      },
      "typeVersion": 2
    },
    {
      "id": "d51019dd-248b-4ab7-8237-5af487e8a0e9",
      "name": "Combine Results",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        704,
        -416
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData",
        "destinationFieldName": "articles"
      },
      "typeVersion": 1
    },
    {
      "id": "9d90b4e7-3498-48f8-836a-476158058251",
      "name": "Set Topics",
      "type": "n8n-nodes-base.set",
      "position": [
        -784,
        -416
      ],
      "parameters": {
        "mode": "raw",
        "options": {
          "dotNotation": false
        },
        "jsonOutput": "{\n  \"topics\": [\n    \"model context protocol (mcp)\",\n    \"generative AI\",\n    \"agentic AI\",\n    \"web scraping\"\n  ]\n}"
      },
      "typeVersion": 3.4
    },
    {
      "id": "85f6c739-446d-4bb2-a0ef-4b927f5d266c",
      "name": "Format Results",
      "type": "n8n-nodes-base.code",
      "position": [
        80,
        -416
      ],
      "parameters": {
        "jsCode": "// Input shape we see sometimes:\n// { output: \"```json\\n[ {...}, {...} ]\\n```\" }  OR plain \"[ {...} ]\"\nlet raw = String($json.output || \"\").trim();\n\n// 1) Strip code fences if present\nraw = raw.replace(/^```(?:json)?\\s*/i, \"\").replace(/```$/, \"\");\n\n// 2) If there is extra chatter, extract the first JSON array\nconst m = raw.match(/\\[[\\s\\S]*\\]/);\nconst arrayText = m ? m[0] : \"[]\";\n\n// 3) Parse safely (with a trailing-comma fix fallback)\nlet arr;\ntry {\n  arr = JSON.parse(arrayText);\n} catch {\n  const fixed = arrayText.replace(/,\\s*([\\]}])/g, \"$1\");\n  try { \n    arr = JSON.parse(fixed); \n  } catch { \n    arr = []; \n  }\n}\n\n// 4) Normalize minimal fields so downstream is predictable\nconst domainFrom = (u) => { \n  try { \n    return new URL(u).hostname.replace(/^www\\./, \"\"); \n  } catch { \n    return \"\"; \n  } \n};\n\nconst items = (Array.isArray(arr) ? arr : []).map(x => ({\n  title: String(x?.title || \"\").trim(),\n  url: String(x?.url || \"\").trim(),\n  source: domainFrom(x?.url) || String(x?.source || \"\").trim(),\n  date: x?.date || new Date().toISOString(),\n  topic: String(x?.topic || \"\").trim(),\n  summary: String(x?.summary || \"\").trim(),\n}));\n\n// 5) Return clean structure\nreturn { json: { output: { items } } };"
      },
      "typeVersion": 2
    },
    {
      "id": "03ce55b1-a1b5-4e25-8258-cc2eb19858c2",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -1008,
        -416
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "9dd80e81-03e3-4f51-864f-b6fbe4ef3de3",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1056,
        -528
      ],
      "parameters": {
        "color": 7,
        "width": 486,
        "height": 272,
        "content": "## \u23f0 DAILY TRIGGER & TOPICS\nRuns at 9 AM with predefined search topics (AI, MCP, web scraping)\n"
      },
      "typeVersion": 1
    },
    {
      "id": "316f4a7f-70b1-46b7-bcd7-0d91edc99445",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -512,
        -528
      ],
      "parameters": {
        "width": 496,
        "height": 496,
        "content": "## \ud83e\udd16 SMART NEWS AGENT\nAI finds fresh news using [**Decodo MCP**](https://github.com/Decodo/mcp-web-scraper) web scraping tools and Gemini to search and extract articles from last 48 hours."
      },
      "typeVersion": 1
    },
    {
      "id": "63739468-1318-4306-b161-9660c5148330",
      "name": "Agent: News Finder",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "maxTries": 2,
      "position": [
        -368,
        -416
      ],
      "parameters": {
        "text": "=You are a news scout. Topics: {{$json.topics}}.\nGoal: For each topic, use the Decodo MCP tools available to you to find fresh, high-quality news from the last 48 hours, extract essentials, and list concise candidates.\n\nRules:\n- Use only tools exposed via Decodo MCP.\n- Prefer official/reputable sources; avoid spam and duplicates (same URL or same title).\n- Up to 5 items per topic, newest first.\n- Provide for each item: title, url, topic, (optional) source domain, publish date/time if available, 2\u20133 line plain-text summary.\n- If nothing relevant is found for a topic, skip it.\n\nOutput format for THIS STEP ONLY:\nReturn a JSON array of candidate objects, one per item (not wrapped in any parent object), e.g.:\n[\n  {\"title\":\"...\", \"url\":\"https://...\", \"topic\":\"...\", \"source\":\"example.com\", \"date\":\"2025-09-15T09:00:00Z\", \"summary\":\"...\"}\n]\n\nDo not include any markdown or commentary\u2014just the JSON array.",
        "options": {},
        "promptType": "define"
      },
      "executeOnce": false,
      "retryOnFail": false,
      "typeVersion": 2.2,
      "alwaysOutputData": false
    },
    {
      "id": "66c231a2-cf5a-4d24-b883-b24fb77114bb",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        48,
        -528
      ],
      "parameters": {
        "color": 7,
        "width": 784,
        "height": 496,
        "content": "## \ud83d\udd27 CLEAN & ORGANIZE\nFormat messy data \u2192 Split articles \u2192 Remove duplicates \u2192 Combine final list"
      },
      "typeVersion": 1
    },
    {
      "id": "08047e22-607d-47fb-9963-bc4617e766f6",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        896,
        -528
      ],
      "parameters": {
        "color": 7,
        "width": 464,
        "height": 496,
        "content": "## \ud83d\udcf1 SEND TO SLACK\nDelivers clean news digest with clickable links and summaries\n"
      },
      "typeVersion": 1
    },
    {
      "id": "9e6989ca-dd0f-4780-9081-658e593e3a19",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1776,
        -800
      ],
      "parameters": {
        "width": 608,
        "height": 864,
        "content": "## Try It Out!\n**This n8n template demonstrates how to build an autonomous AI news agent using Decodo MCP that automatically finds, scrapes, and delivers fresh industry news to your team via Slack.** \n\nUse cases are many \u2013 automated news monitoring for your industry, competitive intelligence gathering, startup monitoring, regulatory updates, research automation, or daily briefings for your organization.\n\n### How it works\n- Define your news topics using the Set node \u2013 AI, MCP, web scraping, whatever matters to your business.\n- The AI Agent processes those topics using Gemini Chat Model, determining which tools to use and when.\n- Here's where it gets interesting: [Decodo MCP](https://github.com/Decodo/mcp-web-scraper) gives your AI agent the tools to search Google, scrape websites, and parse content automatically \u2013 all while bypassing geo-restrictions and anti-bot measures.\n- The agent hunts for fresh articles from the last 48 hours, extracts clean data, and returns structured JSON results.\n- Format Results cleans up the AI's messy output and removes duplicates.\n- Your polished news digest gets delivered to Slack with clickable links and summaries.\n\n### How to use\n- Schedule trigger runs daily at 9 AM \u2013 adjust timing as needed.\n- Customize topics in the Set node to match your industry.\n- Scales effortlessly: add more topics, tweak search criteria, done.\n\n### Requirements\n- [Decodo MCP credentials](https://decodo.com/blog/how-to-set-up-mcp-server) ([grab the free trial](https://dashboard.decodo.com/) today).\n- [Gemini API key](https://aistudio.google.com/app/apikey) for AI processing.\n- [Slack workspace](https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.slack/) for news delivery.\n\n### Need help?\nJoin the [Discord](https://discord.com/invite/gvJhWJPaB4) or email [support@decodo.com](mailto:support@decodo.com) for questions.\n\nHappy Automating!"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Slack": {
      "main": [
        []
      ]
    },
    "Split": {
      "main": [
        [
          {
            "node": "Remove Duplicates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Decodo MCP": {
      "ai_tool": [
        [
          {
            "node": "Agent: News Finder",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Set Topics": {
      "main": [
        [
          {
            "node": "Agent: News Finder",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Results": {
      "main": [
        [
          {
            "node": "Split",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Combine Results": {
      "main": [
        [
          {
            "node": "Slack",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Set Topics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Agent: News Finder",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Remove Duplicates": {
      "main": [
        [
          {
            "node": "Combine Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Agent: News Finder": {
      "main": [
        [
          {
            "node": "Format Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}