Les notes de réunion contiennent souvent des éléments d'action qui nécessitent du contexte : recherches concurrentielles, consultations de prix, comparaisons de fonctionnalités ou données de marché. Un pipeline MCP peut extraire automatiquement les éléments d'action des transcriptions de réunions et enrichir chacun d'eux avec des données de recherche pertinentes, transformant des tâches vagues en briefs exploitables. Ce tutoriel construit un serveur d'outils MCP avec deux outils : l'un qui analyse les notes de réunion en actions structurées, et l'autre qui enrichit chaque action avec le contexte de recherche web. L'enrichissement par recherche coûte 0,005 $ par élément d'action via Scavio.
Prérequis
- Python 3.10+ installé
- Paquets mcp et openai installés
- Une clé API Scavio depuis scavio.dev
- Une clé API OpenAI pour l'extraction d'actions
Parcours
Étape 1: Construire l'outil d'extraction d'actions
Créez un outil MCP qui prend des notes de réunion brutes et utilise un LLM pour extraire des éléments d'action structurés avec les responsables, les échéances et les requêtes de recherche.
from mcp.server import Server
import json
from openai import OpenAI
server = Server('meeting-actions')
llm = OpenAI()
@server.tool()
async def extract_actions(meeting_notes: str) -> str:
"""Extract action items from meeting notes."""
response = llm.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'system', 'content': '''Extract action items from meeting notes.
Return JSON array with: task, assignee, deadline, search_query (a web search
that would provide useful context for this task).'''},
{'role': 'user', 'content': meeting_notes}],
response_format={'type': 'json_object'},
max_tokens=1000
)
return response.choices[0].message.contentÉtape 2: Construire l'outil d'enrichissement par recherche
Créez un deuxième outil MCP qui prend un élément d'action et l'enrichit avec des données de recherche web pertinentes. Cela transforme une tâche nue en un brief contextuel.
import requests, os
API_KEY = os.environ['SCAVIO_API_KEY']
@server.tool()
async def enrich_action(search_query: str, task_context: str = '') -> str:
"""Enrich an action item with web search context."""
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': API_KEY, 'Content-Type': 'application/json'},
json={'query': search_query, 'country_code': 'us'})
results = resp.json().get('organic_results', [])[:5]
enrichment = {
'query': search_query,
'key_findings': [
{'source': r['link'], 'title': r['title'],
'insight': r.get('snippet', '')}
for r in results
],
'sources_count': len(results)
}
return json.dumps(enrichment, indent=2)Étape 3: Construire l'outil de pipeline complet
Créez un outil combiné qui effectue à la fois l'extraction et l'enrichissement en un seul appel. C'est le point d'entrée principal pour le pipeline de réunion à actions.
@server.tool()
async def meeting_to_actions(meeting_notes: str) -> str:
"""Extract action items from meeting notes and enrich each with search context."""
# Step 1: Extract actions
actions_json = await extract_actions(meeting_notes)
actions = json.loads(actions_json).get('actions', [])
# Step 2: Enrich each action
enriched = []
for action in actions:
search_query = action.get('search_query', action.get('task', ''))
enrichment_json = await enrich_action(search_query, action.get('task', ''))
enrichment = json.loads(enrichment_json)
enriched.append({
**action,
'enrichment': enrichment
})
result = {
'total_actions': len(enriched),
'credits_used': len(enriched), # 1 search per action
'cost': f'${len(enriched) * 0.005:.3f}',
'actions': enriched
}
return json.dumps(result, indent=2)Étape 4: Exécuter le serveur MCP
Démarrez le serveur afin qu'il puisse être connecté à Claude Desktop, Cursor ou tout client MCP.
import asyncio
from mcp.server.stdio import stdio_server
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream)
if __name__ == '__main__':
asyncio.run(main())
# .mcp.json configuration:
# {
# "mcpServers": {
# "meeting-actions": {
# "command": "python",
# "args": ["meeting_actions_server.py"],
# "env": {
# "SCAVIO_API_KEY": "your_key",
# "OPENAI_API_KEY": "your_key"
# }
# }
# }
# }Étape 5: Tester avec des notes de réunion exemple
Introduisez une véritable transcription de réunion et vérifiez que le pipeline extrait et enrichit correctement les actions.
# Test the pipeline directly (without MCP client):
import asyncio
async def test():
notes = """Product sync 2026-05-13:
- Sarah: Research competitor pricing for CRM tools, need comparison by Friday
- Mike: Look into TikTok API options for our influencer vetting feature
- Lisa: Find best practices for AI Overview optimization, present next week
- Team: Evaluate n8n vs Zapier for our lead gen automation"""
result = await meeting_to_actions(notes)
data = json.loads(result)
print(f'Extracted {data["total_actions"]} actions (cost: {data["cost"]})')
for a in data['actions']:
print(f'\n Task: {a["task"]}')
print(f' Assignee: {a.get("assignee", "unassigned")}')
print(f' Context: {len(a["enrichment"]["key_findings"])} sources found')
asyncio.run(test())Exemple Python
import os, json, requests
from openai import OpenAI
llm = OpenAI()
SCAVIO_KEY = os.environ['SCAVIO_API_KEY']
def extract_actions(notes):
resp = llm.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'system', 'content': 'Extract action items as JSON array with task, assignee, search_query fields.'},
{'role': 'user', 'content': notes}],
response_format={'type': 'json_object'}, max_tokens=500)
return json.loads(resp.choices[0].message.content).get('actions', [])
def enrich(query):
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
json={'query': query, 'country_code': 'us'})
return [r.get('snippet', '') for r in resp.json().get('organic_results', [])[:3]]
notes = 'Sarah: Research CRM pricing. Mike: Evaluate TikTok APIs.'
for action in extract_actions(notes):
context = enrich(action.get('search_query', action['task']))
print(f'{action["task"]}: {len(context)} sources')Exemple JavaScript
const SCAVIO_KEY = process.env.SCAVIO_API_KEY;
async function enrich(query) {
const resp = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: { 'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ query, country_code: 'us' })
});
const data = await resp.json();
return (data.organic_results || []).slice(0, 3)
.map(r => ({ title: r.title, snippet: r.snippet || '' }));
}
async function main() {
const actions = [
{ task: 'Research CRM pricing', query: 'CRM pricing comparison 2026' },
{ task: 'Evaluate TikTok APIs', query: 'TikTok API options developers 2026' }
];
for (const a of actions) {
const context = await enrich(a.query);
console.log(`${a.task}: ${context.length} sources found`);
}
}
main().catch(console.error);Sortie attendue
Extracted 4 actions (cost: $0.020)
Task: Research competitor CRM pricing comparison
Assignee: Sarah
Context: 5 sources found
Task: Evaluate TikTok API options for influencer vetting
Assignee: Mike
Context: 5 sources found
Task: Research AI Overview optimization best practices
Assignee: Lisa
Context: 5 sources found
Task: Compare n8n vs Zapier for lead generation
Assignee: Team
Context: 5 sources found