This workflow follows the Agent → Discord recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "recherche-stage",
"nodes": [
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "// Fusionner les donn\u00e9es Tavily avec les scores MCP\nconst searchData = $('Search').item.json;\nconst mcpData = $input.item.json;\n\nreturn {\n query: searchData.query,\n results: searchData.results,\n city_score: mcpData\n};"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
512,
0
],
"id": "dad4f474-f4f9-498f-a24d-de9491eaf2f1",
"name": "Fusion"
},
{
"parameters": {
"query": "=internship {{ $json.technology }} in {{ $json.city }}",
"options": {
"max_results": 5
}
},
"type": "@tavily/n8n-nodes-tavily.tavily",
"typeVersion": 1,
"position": [
128,
0
],
"id": "aec1211e-adf5-4c48-8e9d-b0411b24c32c",
"name": "Search",
"credentials": {
"tavilyApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"url": "=http://mcp-scorer:8080/find_city_score?city={{ $json.query.split(' in ')[1] }}",
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
320,
0
],
"id": "1566058a-472d-4492-8c8f-6f227e98b363",
"name": "MCP"
},
{
"parameters": {
"jsCode": "// Make an array of all combinaisons with the technologies and \n\nconst cities = [\n {\"city\": \"Berlin\"},\n {\"city\": \"Barcelona\"},\n {\"city\": \"Amsterdam\"}\n];\n\nconst technology = [\n {\"technology\": \"devops\"},\n {\"technology\": \"software engineer\"}\n];\n\nconst combinations = cities.flatMap(cityObj => {\n return technology.map(techObj => ({\n city: cityObj.city,\n technology: techObj.technology\n }));\n});\n\nreturn combinations;"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
-80,
0
],
"id": "e85a4e15-8c2f-43ea-a7fc-1989a9dee7f7",
"name": "Return combinations of cities and technology matrix"
},
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
-304,
0
],
"id": "40a9ace1-a9b1-4d7b-918d-61acdd120271",
"name": "When clicking \u2018Execute workflow\u2019"
},
{
"parameters": {
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatMistralCloud",
"typeVersion": 1,
"position": [
736,
224
],
"id": "58ec7cb4-daae-438b-9d89-5201a8eea98f",
"name": "Mistral Cloud Chat Model",
"credentials": {
"mistralCloudApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"binaryPropertyName": "rapport",
"options": {
"fileName": "=stages_{{ $now.format('yyyy-MM-dd') }}.csv"
}
},
"type": "n8n-nodes-base.convertToFile",
"typeVersion": 1.1,
"position": [
1744,
416
],
"id": "d8020a44-d70d-4213-8142-7c8adc79d89b",
"name": "Convert to File"
},
{
"parameters": {
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatMistralCloud",
"typeVersion": 1,
"position": [
1760,
208
],
"id": "b0de2c42-befa-44db-a564-d57be78f80fb",
"name": "Mistral Cloud Chat Model1",
"credentials": {
"mistralCloudApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"authentication": "webhook",
"content": "={{ $json.output }}",
"options": {},
"files": {
"values": [
{
"inputFieldName": "rapport"
}
]
}
},
"type": "n8n-nodes-base.discord",
"typeVersion": 2,
"position": [
2384,
16
],
"id": "3a0c7f9b-e0ed-45f7-b1a5-7569444ea05e",
"name": "Discord",
"credentials": {
"discordWebhookApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"aggregate": "aggregateAllItemData",
"options": {}
},
"type": "n8n-nodes-base.aggregate",
"typeVersion": 1,
"position": [
1536,
0
],
"id": "8a0d90ed-2e56-49d6-968b-f9151bf52a96",
"name": "Aggregate"
},
{
"parameters": {
"promptType": "define",
"text": "=Tu es un assistant expert en recrutement.\n\nTA T\u00c2CHE :\nAnalyse les offres ci-dessous pour la ville concern\u00e9e.\nSCORE DE LA VILLE : {{ $json.city_score }} / 10\n\nR\u00c8GLES STRICTES :\n1. Retourne UNIQUEMENT un JSON valide.\n2. Pour CHAQUE offre, ajoutes-y le champ \"city_score\" avec la valeur {{ $json.city_score }} (ne l'invente pas, recopie-le).\n3. Format de sortie :\n[{\n \"company\": \"Nom\",\n \"title\": \"Titre\",\n \"location\": \"Ville\",\n \"city_score\": {{ $json.city_score }},\n \"src_url\": \"URL\",\n \"deadline\": \"Date ou Non sp\u00e9cifi\u00e9\",\n \"skills\": [\"Comp\u00e9tence 1\"],\n \"summary\": \"R\u00e9sum\u00e9 en 1 phrase\"\n}]\n\n4. IMPORTANT : Pas de markdown (```json), juste le JSON brut.\n\nDONN\u00c9ES \u00c0 ANALYSER :\n{{ JSON.stringify($json) }}",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 3.1,
"position": [
736,
0
],
"id": "7795a8eb-8977-4cad-a425-7a7cd9cfe482",
"name": "Analyse"
},
{
"parameters": {
"promptType": "define",
"text": "=Tu es un analyste Data pour le recrutement.\nVoici un tableau JSON complet des offres de stage trouv\u00e9es (avec un \"personal_score\" calcul\u00e9) :\n{{ JSON.stringify($json) }}\n\nG\u00e9n\u00e8re un rapport de synth\u00e8se structur\u00e9 et professionnel en suivant strictement ce plan :\n\n### 1. \ud83d\udcca Vue d'ensemble\n* **Nombre total d'offres pertinentes :** [Nombre]\n* **Villes repr\u00e9sent\u00e9es :** [Ex: Berlin (5), Amsterdam (3)]\n* **Fourchettes identifi\u00e9es :** [Ex: Salaire moyen observ\u00e9 / Dur\u00e9es fr\u00e9quentes]\n\n### 2. \ud83c\udfc6 Top 3 Recommandations (Bas\u00e9 sur le score personnel)\n*S\u00e9lectionne les 3 offres avec le meilleur 'personal_score' ou les plus prestigieuses.*\n1. **[Titre] chez [Entreprise]** ([Ville]) - Score: [Score]\n * *Pourquoi ce choix :* [Justification courte]\n2. ...\n3. ...\n\n### 3. \ud83d\udca1 Analyse & Conseils\n* **Tendance du march\u00e9 :** [Ex: Beaucoup de demande en DevOps...]\n* **Conseil strat\u00e9gique :** [Un conseil pour postuler]\n\nGarde un ton professionnel mais encourageant. Utilise des \u00e9mojis pour la lisibilit\u00e9.\n\nIMPORTANT : La r\u00e9ponse DOIT faire MOINS DE 1800 CARACT\u00c8RES (pour Discord).\nSois concis, utilise des listes \u00e0 puces (bullets) et va droit au but. Pas de blabla inutile.",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 3.1,
"position": [
1760,
0
],
"id": "3086aa6c-e74b-4d6a-a8db-ebfd31915a2a",
"name": "Synth\u00e8se"
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "llama3.2:1b",
"mode": "list",
"cachedResultName": "llama3.2:1b"
},
"messages": {
"values": [
{
"content": "=Tu es un assistant expert en recrutement et analyse d'offres d'emploi.\n\nTA T\u00c2CHE :\nAnalyse le contenu brut des r\u00e9sultats de recherche fournis ci-dessous (format JSON) et extrais les informations cl\u00e9s pour chaque offre de stage pertinente.\n\nR\u00c8GLES STRICTES :\n1. Tu dois retourner UNIQUEMENT un objet JSON valide.\n2. Si une information est manquante, utilise \"Non sp\u00e9cifi\u00e9\".\n3. Le format de sortie doit \u00eatre exactement celui-ci :\n[{\n \"company\": \"Nom de l'entreprise\",\n \"title\": \"Intitul\u00e9 du poste\",\n \"location\": \"Ville/Pays\",\n \"src_url\": \"L'URL d'origine\",\n \"deadline\": \"Date limite ou 'Non sp\u00e9cifi\u00e9'\",\n \"salary\": \"Salaire/Gratification ou 'Non sp\u00e9cifi\u00e9'\",\n \"duration\": \"Dur\u00e9e du stage ou 'Non sp\u00e9cifi\u00e9'\",\n \"skills\": [\"Comp\u00e9tence 1\"],\n \"summary\": \"R\u00e9sum\u00e9 en 1 phrase\"\n}]\n\n4. Si l'input contient plusieurs r\u00e9sultats, retourne un tableau d'objets : [...]\n5. IMPORTANT : Ne mets JAMAIS de backticks (```) ou de balises markdown. Donne uniquement le JSON brut.\n\nVOICI LES DONN\u00c9ES :\n{{ JSON.stringify($json) }}"
}
]
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.ollama",
"typeVersion": 1,
"position": [
-176,
448
],
"id": "a6a918dc-e7a1-4a23-897c-74d83d391442",
"name": "Analyse2",
"credentials": {
"ollamaApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"modelId": {
"__rl": true,
"value": "llama3.2:1b",
"mode": "list",
"cachedResultName": "llama3.2:1b"
},
"messages": {
"values": [
{
"content": "=Tu es mon assistant de recherche de stage.\nVoici la liste finale des offres retenues :\n{{ JSON.stringify($json) }}\n\nFais-moi une synth\u00e8se rapide :\n1. Combien d'offres as-tu trouv\u00e9es ?\n2. Quelle est la MEILLEURE offre selon toi et pourquoi ?\n3. Donne-moi un conseil cl\u00e9 pour r\u00e9ussir ma candidature \u00e0 cette entreprise."
}
]
},
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.ollama",
"typeVersion": 1,
"position": [
224,
448
],
"id": "708bda41-beb9-4713-876c-10b179a3162c",
"name": "Synthese2",
"credentials": {
"ollamaApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"content": "## Mod\u00e8les Ollama \nVous pouvez aussi utiliser un LLM qui tourne en local (si jamais vous avez d\u00e9pass\u00e9 le nombre de tokens par exemple). \n\nPour t\u00e9l\u00e9chargez un mod\u00e8le : \n```bash\ndocker exec ollama ollama pull llama3.2:1b\n```",
"height": 416,
"width": 976
},
"type": "n8n-nodes-base.stickyNote",
"position": [
-320,
240
],
"typeVersion": 1,
"id": "491bc6c6-d711-431f-8708-036c075a7658",
"name": "Sticky Note"
},
{
"parameters": {
"jsCode": "const results = [];\n\n// 1. Parsing du JSON\nfor (const item of $input.all()) {\n try {\n let content = item.json.output || item.json.message?.content || \"\";\n content = content.replace(/```json/g, \"\").replace(/```/g, \"\").trim();\n \n const parsed = JSON.parse(content);\n\n if (Array.isArray(parsed)) {\n results.push(...parsed);\n } else {\n results.push(parsed);\n }\n } catch (error) {\n }\n}\n\n// 2. BONUS : Calcul du Score Personnalis\u00e9\nresults.forEach(offer => {\n if (offer.city_score) {\n const qol = offer.city_score.quality_of_life_index || 0;\n const safety = offer.city_score.safety_index || 0;\n const climate = offer.city_score.climate_index || 0;\n\n const score = (qol * 0.5) + (safety * 0.3) + (climate * 0.2);\n\n offer.personal_score = parseFloat(score.toFixed(2));\n } else {\n offer.personal_score = 0;\n }\n});\n\nreturn results;"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1072,
0
],
"id": "9f111e0d-bb22-4b8a-b851-6bdf5853e5ff",
"name": "Formattage"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 3
},
"conditions": [
{
"id": "00a39bac-8d03-4b87-9a7d-a1dfbbccdf02",
"leftValue": "={{ $json.personal_score }}",
"rightValue": 100,
"operator": {
"type": "number",
"operation": "gt"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.filter",
"typeVersion": 2.3,
"position": [
1280,
0
],
"id": "11b06c7c-ccd8-4c2d-85ae-8c76cbf9d0fc",
"name": "Filtres"
},
{
"parameters": {
"mode": "combine",
"combineBy": "combineByPosition",
"options": {}
},
"type": "n8n-nodes-base.merge",
"typeVersion": 3.2,
"position": [
2144,
16
],
"id": "d88ce249-f9ac-4cae-b63e-a911afdfd001",
"name": "Merge"
}
],
"connections": {
"Fusion": {
"main": [
[
{
"node": "Analyse",
"type": "main",
"index": 0
}
]
]
},
"Search": {
"main": [
[
{
"node": "MCP",
"type": "main",
"index": 0
}
]
]
},
"MCP": {
"main": [
[
{
"node": "Fusion",
"type": "main",
"index": 0
}
]
]
},
"Return combinations of cities and technology matrix": {
"main": [
[
{
"node": "Search",
"type": "main",
"index": 0
}
]
]
},
"When clicking \u2018Execute workflow\u2019": {
"main": [
[
{
"node": "Return combinations of cities and technology matrix",
"type": "main",
"index": 0
}
]
]
},
"Mistral Cloud Chat Model": {
"ai_languageModel": [
[
{
"node": "Analyse",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Mistral Cloud Chat Model1": {
"ai_languageModel": [
[
{
"node": "Synth\u00e8se",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Aggregate": {
"main": [
[
{
"node": "Synth\u00e8se",
"type": "main",
"index": 0
}
]
]
},
"Analyse": {
"main": [
[
{
"node": "Formattage",
"type": "main",
"index": 0
}
]
]
},
"Synth\u00e8se": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"Formattage": {
"main": [
[
{
"node": "Filtres",
"type": "main",
"index": 0
}
]
]
},
"Filtres": {
"main": [
[
{
"node": "Aggregate",
"type": "main",
"index": 0
},
{
"node": "Convert to File",
"type": "main",
"index": 0
}
]
]
},
"Convert to File": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
},
"Merge": {
"main": [
[
{
"node": "Discord",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1",
"availableInMCP": false
},
"versionId": "cc60ebaf-a2bd-45a7-bd42-f778431f74ee",
"meta": {
"templateCredsSetupCompleted": true
},
"id": "C7wJhwtzndzvDniSRdzkx",
"tags": []
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
discordWebhookApimistralCloudApiollamaApitavilyApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
recherche-stage. Uses @tavily/n8n-nodes-tavily, httpRequest, lmChatMistralCloud, discord. Event-driven trigger; 18 nodes.
Source: https://github.com/obitw/recherche-stage/blob/6371770c34496f0a7f7d8ec712ec727b9bbce3c7/rendu/recherche-stage.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This n8n template automates B2B lead research and enrichment for Attio CRM. It combines data from Apollo.io, LinkedIn scraping, and news sources with AI-powered analysis to generate actionable sales i
This workflow is designed for marketers, content creators, agencies, and solo founders who want to publish long‑form posts with visuals on autopilot using n8n and AI agents.
Description
This workflow contains community nodes that are only compatible with the self-hosted version of n8n.
Who Is This For?