deckbuilder 1.0.0b1__py3-none-any.whl

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.
mcp_server/__init__.py ADDED
@@ -0,0 +1,9 @@
1
+ """
2
+ MCP Server - Model Context Protocol Server
3
+
4
+ MCP server implementation for content-first presentation intelligence
5
+ and deck building capabilities.
6
+ """
7
+
8
+ __version__ = "1.0.0"
9
+ __all__ = []
@@ -0,0 +1,436 @@
1
+ """
2
+ Content Analysis Module for Deck Builder MCP
3
+
4
+ This module implements content-first presentation intelligence, analyzing user input
5
+ to understand communication goals and recommend presentation structure.
6
+
7
+ Design Philosophy: Start with "what does the user want to communicate?"
8
+ not "what layouts exist?"
9
+ """
10
+
11
+ import re
12
+ from typing import Any, Dict, List
13
+
14
+
15
+ class ContentAnalyzer:
16
+ """
17
+ Analyzes user presentation needs using content-first approach.
18
+ Acts as intelligent presentation consultant, not layout picker.
19
+ """
20
+
21
+ def __init__(self):
22
+ """Initialize the content analyzer with audience profiles."""
23
+ self.audience_profiles = {
24
+ "board": {
25
+ "expertise_level": "expert",
26
+ "attention_span": "short",
27
+ "decision_authority": "decision-maker",
28
+ "preferred_format": "high-level",
29
+ },
30
+ "team": {
31
+ "expertise_level": "intermediate",
32
+ "attention_span": "medium",
33
+ "decision_authority": "implementer",
34
+ "preferred_format": "detailed",
35
+ },
36
+ "customers": {
37
+ "expertise_level": "general",
38
+ "attention_span": "medium",
39
+ "decision_authority": "decision-maker",
40
+ "preferred_format": "visual-heavy",
41
+ },
42
+ "technical": {
43
+ "expertise_level": "expert",
44
+ "attention_span": "extended",
45
+ "decision_authority": "influencer",
46
+ "preferred_format": "detailed",
47
+ },
48
+ "general": {
49
+ "expertise_level": "intermediate",
50
+ "attention_span": "medium",
51
+ "decision_authority": "influencer",
52
+ "preferred_format": "balanced",
53
+ },
54
+ }
55
+
56
+ def analyze_presentation_needs(
57
+ self,
58
+ user_input: str,
59
+ audience: str = "general",
60
+ constraints: str = None,
61
+ presentation_goal: str = "inform",
62
+ ) -> Dict[str, Any]:
63
+ """
64
+ Analyze user's presentation needs and recommend structure.
65
+
66
+ Args:
67
+ user_input: Raw description of what they want to present
68
+ audience: Target audience type
69
+ constraints: Time/slide constraints
70
+ presentation_goal: Primary goal of presentation
71
+
72
+ Returns:
73
+ Dictionary with content analysis and structural recommendations
74
+ """
75
+ # Content Analysis
76
+ key_messages = self._extract_key_messages(user_input)
77
+ narrative_arc = self._determine_narrative_arc(user_input, key_messages)
78
+ complexity_level = self._assess_complexity_level(user_input, audience)
79
+ data_density = self._analyze_data_density(user_input)
80
+ emotional_tone = self._detect_emotional_tone(user_input, key_messages)
81
+
82
+ # Audience Considerations
83
+ audience_analysis = self._analyze_audience(audience, constraints)
84
+
85
+ # Recommended Structure
86
+ recommended_structure = self._build_recommended_structure(
87
+ key_messages, narrative_arc, audience_analysis, presentation_goal
88
+ )
89
+
90
+ # Presentation Strategy
91
+ presentation_strategy = self._develop_presentation_strategy(
92
+ narrative_arc, audience_analysis, presentation_goal
93
+ )
94
+
95
+ return {
96
+ "content_analysis": {
97
+ "key_messages": key_messages,
98
+ "narrative_arc": narrative_arc,
99
+ "complexity_level": complexity_level,
100
+ "data_density": data_density,
101
+ "emotional_tone": emotional_tone,
102
+ },
103
+ "audience_considerations": audience_analysis,
104
+ "recommended_structure": recommended_structure,
105
+ "presentation_strategy": presentation_strategy,
106
+ }
107
+
108
+ def _extract_key_messages(self, user_input: str) -> List[str]:
109
+ """Extract key messages from user input using pattern matching."""
110
+ # Look for metrics, percentages, numbers
111
+ metrics = re.findall(
112
+ r"\d+%|\$[\d,]+|\d+[\w\s]*(?:growth|increase|decrease|revenue|users|customers)",
113
+ user_input.lower(),
114
+ )
115
+
116
+ # Look for key topics/concepts
117
+ topics = []
118
+ if re.search(r"growth|expand|increase|up", user_input.lower()):
119
+ topics.append("growth/expansion")
120
+ if re.search(r"churn|loss|decrease|down|problem|issue|challenge", user_input.lower()):
121
+ topics.append("challenges/concerns")
122
+ if re.search(r"strategy|plan|solution|fix|improve", user_input.lower()):
123
+ topics.append("solutions/strategy")
124
+ if re.search(r"new|launch|feature|product", user_input.lower()):
125
+ topics.append("new initiatives")
126
+ if re.search(r"market|competitor|industry", user_input.lower()):
127
+ topics.append("market/competitive")
128
+
129
+ # Combine metrics and topics
130
+ key_messages = []
131
+ if metrics:
132
+ key_messages.extend([f"metric: {m}" for m in metrics[:3]]) # Limit to top 3
133
+ key_messages.extend(topics)
134
+
135
+ return key_messages[:5] # Limit to 5 key messages
136
+
137
+ def _determine_narrative_arc(self, user_input: str, _key_messages: List[str]) -> str:
138
+ """Determine the narrative structure from content."""
139
+ import re
140
+
141
+ input_lower = user_input.lower()
142
+
143
+ # Use word boundary matching to avoid false positives
144
+ def has_word_pattern(patterns, text):
145
+ """Check if any pattern exists as whole words in text."""
146
+ for pattern in patterns:
147
+ if re.search(r"\b" + re.escape(pattern) + r"\b", text):
148
+ return True
149
+ return False
150
+
151
+ # Check for success-challenge-solution pattern with word boundaries
152
+ success_patterns = [
153
+ "growth",
154
+ "success",
155
+ "increase",
156
+ "expand",
157
+ "win",
158
+ "achievement",
159
+ "improvement",
160
+ "grew",
161
+ "expanded",
162
+ "gains",
163
+ "positive",
164
+ ]
165
+ challenge_patterns = [
166
+ "but",
167
+ "however",
168
+ "problem",
169
+ "issue",
170
+ "churn",
171
+ "decrease",
172
+ "challenge",
173
+ "concern",
174
+ "difficulty",
175
+ "bottleneck",
176
+ "drops",
177
+ "causing",
178
+ ]
179
+ solution_patterns = [
180
+ "strategy",
181
+ "plan",
182
+ "solution",
183
+ "address",
184
+ "fix",
185
+ "propose",
186
+ "recommend",
187
+ "implement",
188
+ "adding",
189
+ "need",
190
+ ]
191
+
192
+ has_success = has_word_pattern(success_patterns, input_lower)
193
+ has_challenge = has_word_pattern(challenge_patterns, input_lower)
194
+ has_solution = has_word_pattern(solution_patterns, input_lower)
195
+
196
+ if has_success and has_challenge and has_solution:
197
+ return "success-challenge-solution"
198
+ elif has_challenge and has_solution:
199
+ return "problem-solution"
200
+ elif has_word_pattern(["compare", "vs", "versus", "option", "alternative"], input_lower):
201
+ return "compare-contrast"
202
+ elif has_word_pattern(["convince", "persuade", "recommend", "should"], input_lower):
203
+ return "persuasive"
204
+ else:
205
+ return "informational"
206
+
207
+ def _assess_complexity_level(self, user_input: str, audience: str) -> str:
208
+ """Assess the complexity level needed."""
209
+ if audience in ["board", "executive"]:
210
+ return "executive"
211
+ elif audience == "technical":
212
+ return "technical"
213
+ elif len(user_input.split()) > 50 or "detail" in user_input.lower():
214
+ return "detailed"
215
+ else:
216
+ return "overview"
217
+
218
+ def _analyze_data_density(self, user_input: str) -> str:
219
+ """Analyze how data-heavy the presentation should be."""
220
+ # Count numeric mentions
221
+ numeric_mentions = len(re.findall(r"\d+", user_input))
222
+
223
+ if numeric_mentions >= 5 or any(
224
+ word in user_input.lower() for word in ["data", "metrics", "numbers", "statistics"]
225
+ ):
226
+ return "metrics-heavy"
227
+ elif any(word in user_input.lower() for word in ["story", "example", "case", "experience"]):
228
+ return "story-driven"
229
+ else:
230
+ return "balanced"
231
+
232
+ def _detect_emotional_tone(self, user_input: str, _key_messages: List[str]) -> str:
233
+ """Detect the emotional tone of the content."""
234
+ input_lower = user_input.lower()
235
+
236
+ if any(word in input_lower for word in ["urgent", "critical", "immediate", "crisis"]):
237
+ return "urgent"
238
+ elif any(
239
+ word in input_lower for word in ["concern", "worry", "issue", "problem", "challenge"]
240
+ ):
241
+ return "cautious"
242
+ elif any(word in input_lower for word in ["success", "growth", "achievement", "win"]):
243
+ return "positive"
244
+ else:
245
+ return "neutral"
246
+
247
+ def _analyze_audience(self, audience: str, constraints: str) -> Dict[str, str]:
248
+ """Analyze audience characteristics and constraints."""
249
+ profile = self.audience_profiles.get(audience, self.audience_profiles["general"]).copy()
250
+
251
+ # Adjust based on constraints
252
+ if constraints:
253
+ if "minutes" in constraints.lower() and any(
254
+ time in constraints for time in ["5", "10"]
255
+ ):
256
+ profile["attention_span"] = "short"
257
+ elif "slides" in constraints.lower():
258
+ profile["preferred_format"] = "high-level"
259
+
260
+ return profile
261
+
262
+ def _build_recommended_structure(
263
+ self,
264
+ _key_messages: List[str],
265
+ narrative_arc: str,
266
+ _audience_analysis: Dict[str, str],
267
+ _presentation_goal: str,
268
+ ) -> List[Dict[str, str]]:
269
+ """Build recommended slide structure."""
270
+ structure = []
271
+
272
+ if narrative_arc == "success-challenge-solution":
273
+ structure = [
274
+ {
275
+ "position": 1,
276
+ "purpose": "lead with strength",
277
+ "content_focus": "positive metrics/achievements",
278
+ "slide_intent": "establish credibility",
279
+ "estimated_time": "1-2 minutes",
280
+ },
281
+ {
282
+ "position": 2,
283
+ "purpose": "show momentum",
284
+ "content_focus": "expansion/progress",
285
+ "slide_intent": "build on success",
286
+ "estimated_time": "1-2 minutes",
287
+ },
288
+ {
289
+ "position": 3,
290
+ "purpose": "acknowledge challenge",
291
+ "content_focus": "problem/concern",
292
+ "slide_intent": "transparent communication",
293
+ "estimated_time": "2-3 minutes",
294
+ },
295
+ {
296
+ "position": 4,
297
+ "purpose": "present solution",
298
+ "content_focus": "strategy/plan",
299
+ "slide_intent": "show path forward",
300
+ "estimated_time": "2-3 minutes",
301
+ },
302
+ ]
303
+ elif narrative_arc == "problem-solution":
304
+ structure = [
305
+ {
306
+ "position": 1,
307
+ "purpose": "set context",
308
+ "content_focus": "background/situation",
309
+ "slide_intent": "establish understanding",
310
+ "estimated_time": "1-2 minutes",
311
+ },
312
+ {
313
+ "position": 2,
314
+ "purpose": "define problem",
315
+ "content_focus": "challenges/issues",
316
+ "slide_intent": "create urgency",
317
+ "estimated_time": "2-3 minutes",
318
+ },
319
+ {
320
+ "position": 3,
321
+ "purpose": "present solution",
322
+ "content_focus": "strategy/approach",
323
+ "slide_intent": "provide resolution",
324
+ "estimated_time": "3-4 minutes",
325
+ },
326
+ ]
327
+ elif narrative_arc == "compare-contrast":
328
+ structure = [
329
+ {
330
+ "position": 1,
331
+ "purpose": "establish criteria",
332
+ "content_focus": "comparison framework",
333
+ "slide_intent": "set evaluation context",
334
+ "estimated_time": "1-2 minutes",
335
+ },
336
+ {
337
+ "position": 2,
338
+ "purpose": "present options",
339
+ "content_focus": "alternatives/choices",
340
+ "slide_intent": "show possibilities",
341
+ "estimated_time": "3-4 minutes",
342
+ },
343
+ {
344
+ "position": 3,
345
+ "purpose": "recommend choice",
346
+ "content_focus": "preferred option",
347
+ "slide_intent": "guide decision",
348
+ "estimated_time": "2-3 minutes",
349
+ },
350
+ ]
351
+ else: # informational/other
352
+ structure = [
353
+ {
354
+ "position": 1,
355
+ "purpose": "overview",
356
+ "content_focus": "main topics",
357
+ "slide_intent": "set expectations",
358
+ "estimated_time": "1-2 minutes",
359
+ },
360
+ {
361
+ "position": 2,
362
+ "purpose": "key information",
363
+ "content_focus": "primary content",
364
+ "slide_intent": "deliver core message",
365
+ "estimated_time": "3-4 minutes",
366
+ },
367
+ {
368
+ "position": 3,
369
+ "purpose": "summary",
370
+ "content_focus": "takeaways",
371
+ "slide_intent": "reinforce learning",
372
+ "estimated_time": "1-2 minutes",
373
+ },
374
+ ]
375
+
376
+ return structure
377
+
378
+ def _develop_presentation_strategy(
379
+ self, narrative_arc: str, audience_analysis: Dict[str, str], _presentation_goal: str
380
+ ) -> Dict[str, Any]:
381
+ """Develop presentation strategy."""
382
+ strategy = {
383
+ "opening_approach": "data-driven",
384
+ "closing_approach": "summary",
385
+ "flow_pattern": "chronological",
386
+ "engagement_tactics": ["visual"],
387
+ }
388
+
389
+ # Adjust based on narrative arc
390
+ if narrative_arc == "success-challenge-solution":
391
+ strategy["opening_approach"] = "data-driven"
392
+ strategy["flow_pattern"] = "problem-solution"
393
+ strategy["closing_approach"] = "call-to-action"
394
+ elif narrative_arc == "problem-solution":
395
+ strategy["opening_approach"] = "problem-focused"
396
+ strategy["flow_pattern"] = "problem-solution"
397
+ strategy["closing_approach"] = "next-steps"
398
+ elif narrative_arc == "persuasive":
399
+ strategy["opening_approach"] = "story-based"
400
+ strategy["closing_approach"] = "call-to-action"
401
+ strategy["engagement_tactics"].append("storytelling")
402
+ elif narrative_arc == "compare-contrast":
403
+ strategy["opening_approach"] = "framework-based"
404
+ strategy["flow_pattern"] = "compare-contrast"
405
+ strategy["closing_approach"] = "recommendation"
406
+
407
+ # Adjust based on audience
408
+ if audience_analysis["attention_span"] == "short":
409
+ strategy["engagement_tactics"].append("interactive")
410
+ if audience_analysis["preferred_format"] == "visual-heavy":
411
+ strategy["engagement_tactics"] = ["visual", "interactive"]
412
+
413
+ return strategy
414
+
415
+
416
+ # Helper function for easy import
417
+ def analyze_presentation_needs(
418
+ user_input: str,
419
+ audience: str = "general",
420
+ constraints: str = None,
421
+ presentation_goal: str = "inform",
422
+ ) -> Dict[str, Any]:
423
+ """
424
+ Convenience function for analyzing presentation needs.
425
+
426
+ Args:
427
+ user_input: Raw description of what they want to present
428
+ audience: Target audience type
429
+ constraints: Time/slide constraints
430
+ presentation_goal: Primary goal of presentation
431
+
432
+ Returns:
433
+ Dictionary with content analysis and structural recommendations
434
+ """
435
+ analyzer = ContentAnalyzer()
436
+ return analyzer.analyze_presentation_needs(user_input, audience, constraints, presentation_goal)