"""
Gmail Prompts with Jinja2 Templates for FastMCP2
This module provides clean Gmail prompts using professional Jinja2 templating
instead of problematic f-strings. Integrates with Template Parameter Middleware
for automatic resource resolution.
Features:
- Clean Jinja2 templates (no f-string CSS parsing errors!)
- Template Parameter Middleware integration
- Automatic {{resource://uri}} resolution
- Professional email templates
- Proper error handling and fallbacks
"""
from datetime import datetime, timezone
from pathlib import Path
try:
from jinja2 import ChoiceLoader, DictLoader, Environment, FileSystemLoader, Template
JINJA2_AVAILABLE = True
except ImportError:
JINJA2_AVAILABLE = False
print("β οΈ Jinja2 not available, falling back to simple templates")
from fastmcp import Context, FastMCP
from fastmcp.prompts.prompt import PromptMessage, TextContent
from config.enhanced_logging import setup_logger
from prompts.tool_optimization_helper import ToolOptimizationHelper
logger = setup_logger()
class GmailPrompts:
"""Gmail prompts with professional Jinja2 templates and Template Parameter Middleware integration."""
def __init__(self):
"""Initialize with Jinja2 template environment."""
self.jinja_env = None
if JINJA2_AVAILABLE:
# Setup template directories (corrected paths)
template_dirs = [
"prompts/templates/gmail", # Primary location after move
"prompts/templates",
"templates/gmail", # Fallback locations
"templates",
]
# Find existing directories
loaders = []
for template_dir in template_dirs:
path = Path(template_dir)
if path.exists():
loaders.append(FileSystemLoader(str(path)))
logger.info(f"π Found Gmail template directory: {path}")
if loaders:
loader = ChoiceLoader(loaders)
logger.info(f"β
Using {len(loaders)} template directories")
else:
# Use built-in templates as fallback
loader = DictLoader(
{"quick_demo_simple.txt": self._get_builtin_simple_template()}
)
logger.info(
"π Using built-in Gmail template (no template directories found)"
)
self.jinja_env = Environment(
loader=loader,
autoescape=False, # We're generating text/markdown
trim_blocks=True,
lstrip_blocks=True,
)
logger.info("β
Clean Gmail prompts initialized with Jinja2")
def _get_builtin_simple_template(self) -> str:
"""Get the built-in simple template."""
return """# β‘ Quick Email Demo (Simple) - With Real Gmail Resources
*Request ID: {{ request_id }} | Generated: {{ current_time }}*
## π― Zero-Configuration Demo with Live Gmail Data
### π **GMAIL RESOURCE INTEGRATION:**
- **Resource Status**: {{ auth_status }}
- **Your Email**: `{{ user_email }}` (from `{{user://current/email}}`)
{% if labels %}
- **Your Labels**:
{% for label in labels[:3] %}
- {{ label.name }} ({{ label.id }})
{% endfor %}
{% else %}
- **Labels**: Authentication required
{% endif %}
- **System Labels**: {{ system_count }} available
- **Custom Labels**: {{ user_count }} total
## π§ Instant Send Example
```xml
<mcp_tool_call>
<tool>send_gmail_message</tool>
<params>
<user_google_email>{{user://current/email}}</user_google_email>
<to>demo@example.com</to>
<subject>FastMCP2 Email Demo - {{ current_date }}</subject>
<body><![CDATA[
Hello!
This is a FastMCP2 email demo with live Gmail integration.
π₯ Real Gmail Data:
{% for label in labels[:5] %}
β’ {{ label.name }} ({{ label.id }})
{% endfor %}
β’ Total: {{ user_count + system_count }} labels
Best regards,
FastMCP2 Team
]]></body>
</params>
</mcp_tool_call>
```
## π Benefits
- **Instant Results**: Works with real Gmail data
- **Professional Look**: Clean design
- **Zero Config**: No setup required
{% if not authenticated %}
### π§ Setup Required
Run: `start_google_auth('your.email@gmail.com')`
{% endif %}
---
*Generated with Jinja2 templates β’ No CSS errors!*"""
async def quick_email_demo(self, context: Context) -> PromptMessage:
"""
Gmail Quick Email Demo that returns template content with {{resource://uri}} variables.
The Template Parameter Middleware will automatically resolve these variables
in the on_get_prompt hook after this function returns.
"""
try:
# Get template content with resource variables that middleware will resolve
template_content = self._get_template_content_with_resources()
# Basic template context for non-resource variables only
basic_context = {
"request_id": context.request_id,
"current_time": datetime.now(timezone.utc).strftime(
"%Y-%m-%d %H:%M:%S UTC"
),
"current_date": datetime.now(timezone.utc).strftime("%Y-%m-%d"),
}
# Do ONLY basic string replacement for non-resource variables
# Let Template Parameter Middleware handle {{resource://uri}} and Jinja2 syntax
content = template_content
for key, value in basic_context.items():
content = content.replace(f"{{{{ {key} }}}}", str(value))
return PromptMessage(
role="user", content=TextContent(type="text", text=content)
)
except Exception as e:
logger.error(f"Error in Gmail prompt: {e}")
return self._create_error_prompt(str(e))
def _get_template_content_with_resources(self) -> str:
"""Get template content with resource variables that middleware will resolve."""
# Generate tool optimization section dynamically using the helper
tool_optimization = ToolOptimizationHelper.generate_optimization_section(
include_services=["gmail"],
include_service_list=False,
)
return f"""# β‘ Quick Email Demo - Template Middleware Resource Resolution
*Request ID: {{{{ request_id }}}} | Generated: {{{{ current_time }}}}*
{tool_optimization}
## π― Zero-Configuration Demo with Live Gmail Data
### π **GMAIL RESOURCE INTEGRATION:**
- **Your Email**: `{{user://current/email}}` (from middleware)
- **Gmail Labels**: `{{service://gmail/labels}}` (from middleware)
- **Auth Status**: Template Parameter Middleware active
### π§ **How Template Middleware Works:**
1. **Prompt Function**: Returns content with `{{resource://uri}}` variables
2. **Middleware Hook**: `on_get_prompt` processes the result
3. **Resource Resolution**: Fetches live Gmail data via FastMCP resources
4. **Final Result**: Variables automatically resolved to real data
## π§ **Instant Send Example**
This tool call will also have its resource URIs resolved by the middleware:
```xml
<mcp_tool_call>
<tool>send_gmail_message</tool>
<params>
<user_google_email>{{user://current/email}}</user_google_email>
<to>demo@example.com</to>
<subject>FastMCP2 Email Demo - {{ current_date }}</subject>
<body><![CDATA[
Hello!
This is a FastMCP2 email demo with live Gmail integration via Template Middleware.
π₯ Real Gmail Data (resolved by middleware):
- Your email: {{user://current/email}}
- Gmail labels available: {{service://gmail/labels}}
β¨ Benefits:
- Live data from your actual Gmail account
- Zero configuration required
- Template Parameter Middleware handles everything
Best regards,
FastMCP2 Team
]]></body>
</params>
</mcp_tool_call>
```
## π **Benefits**
- **Live Gmail Data**: Template middleware fetches real labels, email, etc.
- **Zero Setup**: No manual configuration needed
- **Unified System**: Same `{{resource://uri}}` syntax for prompts AND tools
- **Professional Look**: Clean design with real data
---
*Template Parameter Middleware β’ Live Gmail data in prompts!*"""
def _create_error_prompt(self, error: str) -> PromptMessage:
"""Create error prompt."""
return PromptMessage(
role="user",
content=TextContent(
type="text",
text=f"""# β‘ Gmail Demo (Error)
*Error: {error}*
## π§ Basic Demo
```xml
<mcp_tool_call>
<tool>send_gmail_message</tool>
<params>
<user_google_email>test_example@gmail.com</user_google_email>
<to>demo@example.com</to>
<subject>FastMCP2 Email Demo</subject>
<body>Hello! This is a basic email demo.</body>
</params>
</mcp_tool_call>
```""",
),
)
# Global instance
_gmail_prompts = None
def get_gmail_prompts() -> GmailPrompts:
"""Get the global Gmail prompts instance."""
global _gmail_prompts
if _gmail_prompts is None:
_gmail_prompts = GmailPrompts()
return _gmail_prompts
# Setup function for server.py integration
def setup_gmail_prompts(mcp: FastMCP):
"""
Setup Gmail prompts with Jinja2 templates and Template Parameter Middleware integration.
This function registers the clean Gmail prompts that work with both:
1. Jinja2 templates for presentation
2. Template Parameter Middleware for {{resource://uri}} resolution
"""
gmail_prompts = get_gmail_prompts()
@mcp.prompt(
name="quick_email_demo",
description="Simple: Zero-config Gmail demo with Jinja2 templates and resource integration",
tags={"gmail", "simple", "demo", "jinja2", "resources"},
meta={
"version": "4.0",
"author": "FastMCP2-Jinja2",
"uses_jinja2": True,
"uses_template_middleware": True,
"resource_dependencies": ["service://gmail/labels", "user://current/email"],
},
)
async def quick_email_demo_prompt(context: Context) -> PromptMessage:
"""Gmail Quick Email Demo - Clean Jinja2 version with Template Parameter Middleware support."""
return await gmail_prompts.quick_email_demo(context)
logger.info("β
Gmail prompts with Jinja2 templates registered successfully")
logger.info(
" β’ quick_email_demo: Simple zero-config demo with resource integration"
)
logger.info(
" β’ Template Parameter Middleware: {{resource://uri}} expressions supported"
)
logger.info(" β’ Jinja2 Templates: Professional presentation layer")
return gmail_prompts
# Export for server.py
__all__ = ["setup_gmail_prompts", "get_gmail_prompts"]