> ๐จ **MAJOR UPDATE** (Latest): **Natural Resource URI Syntax** now supported! Use `{{user://current/email.email}}` directly in templates. See [Natural Resource URI Syntax](#-new-natural-resource-uri-syntax-breakthrough-feature) for details.
# Template Parameter Middleware with Jinja2 Integration
## Overview
The Template Parameter Middleware provides automatic parameter substitution using FastMCP resources with **full Jinja2 templating capabilities**. This powerful middleware allows tools to use both simple template expressions like `{{resource://uri}}` and complex Jinja2 templates with conditionals, loops, and filters in their parameters, which are automatically resolved at runtime using the FastMCP resource system.
**โจ NEW: Jinja2 Integration Features:**
- **Professional templating engine** with full Jinja2 syntax support
- **Complex template logic**: `{% if %}`, `{% for %}`, `{{ variable | filter }}`
- **Automatic HTML/CSS escaping** (no more f-string parsing errors!)
- **Template file organization** and caching
- **Template inheritance** and includes
- **Custom filters** for FastMCP-specific operations
- **Sandboxed execution** for security
```mermaid
graph TB
A["๐ง Tool Call"] --> B["๐ญ Template Middleware"]
B --> C{"Has Templates?"}
C -->|"Yes"| D["๐ Parse Templates"]
C -->|"No"| I["โก๏ธ Pass Through"]
D --> E["๐ก Fetch Resources"]
E --> F["โก Cache Check"]
F -->|"Hit"| G["๐ Use Cache"]
F -->|"Miss"| H["๐ Call Resource"]
G --> J["๐ Resolve Parameters"]
H --> K["๐พ Store Cache"]
K --> J
J --> L["โจ Execute Tool"]
I --> L
L --> M["๐ค Return Result"]
style A fill:#e1f5fe,stroke:#01579b,stroke-width:2px
style B fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
style E fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px
style L fill:#fff3e0,stroke:#e65100,stroke-width:2px
style M fill:#e0f2f1,stroke:#00695c,stroke-width:2px
```
## ๐ฏ Key Features
- **๐จ Full Jinja2 Templating**: Professional template engine with conditionals, loops, filters, and inheritance
- **๐ก Automatic Parameter Substitution**: Resolve `{{resource://uri}}` expressions in tool parameters
- **๐ JSON Path Extraction**: Extract specific data with `{{resource://uri}}["path"]` syntax
- **๐ Advanced Template Logic**: Use `{% if %}`, `{% for %}`, `{{ variable | filter }}` for complex rendering
- **๐ Automatic Escaping**: HTML/CSS escaping prevents parsing errors (solved f-string CSS issues!)
- **๐ Template File Organization**: Load templates from organized directory structures
- **๐ Template Inheritance**: Use Jinja2 extends and includes for template reuse
- **โก Performance Optimized**: Resource caching and template compilation caching
- **๐ก๏ธ Security Controls**: Sandboxed execution and resource scheme allowlists
- **๐ Debug Logging**: Comprehensive logging for troubleshooting
- **๐ Backwards Compatibility**: Non-templated tools continue to work normally
```mermaid
mindmap
root)Template Parameter Middleware(
(๐ฏ Core Features)
::icon(fa fa-cogs)
Auto Substitution
JSON Path Extract
Resource Caching
Security Controls
(๐ Resources)
::icon(fa fa-database)
User Auth
Gmail Services
Workspace Content
Tools Directory
(๐ ๏ธ Integration)
::icon(fa fa-puzzle-piece)
FastMCP Server
Auth Middleware
Custom Tools
Client SDK
(๐ Benefits)
::icon(fa fa-rocket)
Context Aware
Performance
User Friendly
Developer DX
```
```mermaid
mindmap
root)Template Parameter Middleware(
(๐ฏ Core Features)
::icon(fa fa-cogs)
Auto Substitution
JSON Path Extract
Resource Caching
Security Controls
(๐ Resources)
::icon(fa fa-database)
User Auth
Gmail Services
Workspace Content
Tools Directory
(๐ ๏ธ Integration)
::icon(fa fa-puzzle-piece)
FastMCP Server
Auth Middleware
Custom Tools
Client SDK
(๐ Benefits)
::icon(fa fa-rocket)
Context Aware
Performance
User Friendly
Developer DX
```
## ๐ง Template Expression Syntax
### ๐ฏ NEW: Natural Resource URI Syntax (Breakthrough Feature!)
**โจ MAJOR UPDATE**: The middleware now supports **natural resource URI syntax** directly in Jinja2 templates!
```python
# ๐ Natural property access - NOW WORKING!
"{{user://current/email.email}}" # โ "test_example@gmail.com"
"{{user://current/email.name}}" # โ "Seth Rivers"
"{{workspace://content/recent.total_files}}" # โ 42
# ๐ Complex property paths - FULLY SUPPORTED!
"{{service://gmail/labels.0.name}}" # โ "Important"
"{{workspace://content/recent.content_summary.documents}}" # โ 10
# ๐ Use in templates naturally
"Hello {{user://current/email.name}}!" # โ "Hello Seth Rivers!"
"User {{user://current/email.email}} says hello" # โ "User test_example@gmail.com says hello"
```
**๐ง How It Works (Behind the Scenes):**
1. **Simple Preprocessing**: `{{user://current/email.email}}` โ `{{user___current_email_dot_email}}`
2. **Resource Resolution**: Fetches base URI `user://current/email` once
3. **Property Extraction**: ResourceUndefined extracts `.email` property
4. **Performance**: Only 1 API call instead of separate calls per property
**๐จ Key Benefits:**
- **Natural syntax**: Write `{{user://current/email.email}}` instead of complex JSON paths
- **Performance optimized**: Single resource fetch with multiple property access
- **Jinja2 compatible**: Works seamlessly with all Jinja2 features
- **Error resilient**: Graceful handling of missing properties
### Basic Resource Access (Original FastMCP Style)
```python
# Simple resource access - returns the entire resource
"{{user://current/email}}"
# Returns: "user@example.com"
```
### JSON Path Extraction (Original FastMCP Style)
```python
# Extract specific fields from resource responses
"{{user://current/profile}}['email']"
# Extract from nested objects
"{{workspace://content/recent}}['content_summary']['total_files']"
# Extract from arrays
"{{tools://enhanced/list}}['enhanced_tools'][0]['name']"
```
### ๐จ NEW: Advanced Jinja2 Template Logic
```python
# ๐ NATURAL SYNTAX with conditionals - NOW WORKING!
"Hello {{user://current/email.name}}!" # โ "Hello Seth Rivers!"
"User {{user://current/email.email}} says hello" # โ "User test_example@gmail.com says hello"
# ๐ NATURAL SYNTAX in expressions
"Total files: {{workspace://content/recent.total_files}} | User: {{user://current/email.email}}"
# ๐ Traditional resource access (still supported)
"{% if resources.user_current_email %}Welcome {{resources.user_current_email}}!{% else %}Please log in{% endif %}"
# Jinja2 filters
"{{resources.workspace_recent.content_summary.total_files | default(0)}}"
# Complex template with multiple features
"""
{% if resources.user_current_profile.authenticated %}
Hello {{resources.user_current_profile.email}}!
Your recent activity:
{% for doc in resources.workspace_recent.content_by_type.documents[:5] %}
โข {{doc.name}} ({{doc.modified | format_datetime}})
{% endfor %}
You have {{resources.gmail_labels | length}} Gmail labels.
{% endif %}
"""
```
### Template File Organization
```python
# Load templates from files
# File: prompts/templates/gmail/welcome_email.txt
"""
Subject: Welcome {{user_email}}!
{% extends "base_email.txt" %}
{% block content %}
Hello {{user_name}},
Welcome to our platform! Here's what you can do:
{% for feature in available_features %}
โข {{feature.name}}: {{feature.description}}
{% endfor %}
Best regards,
Your FastMCP Assistant
{% endblock %}
"""
# Use in tool parameter
@mcp.tool()
async def send_welcome_email(
user_email: str = "{{template://user_email}}",
email_body: str = "{% include 'gmail/welcome_email.txt' %}"
):
# Template automatically loaded and rendered
pass
```
## ๐ Available Resources
```mermaid
graph TB
subgraph "๐ Authentication Layer"
A["user://current/email"]
B["user://current/profile"]
C["auth://session/current"]
D["template://user_email"]
end
subgraph "๐ง Gmail Services"
E["service://gmail/labels"]
F["service://gmail/filters"]
G["gmail://content/suggestions"]
H["gmail://messages/recent"]
end
subgraph "๐ผ Workspace Content"
I["workspace://content/recent"]
J["workspace://content/search"]
end
subgraph "๐ ๏ธ Tools & Services"
K["tools://list/all"]
L["tools://enhanced/list"]
M["google://services/scopes"]
end
N["๐ญ Template Middleware"] --> A
N --> E
N --> I
N --> K
style N fill:#f3e5f5,stroke:#4a148c,stroke-width:3px
style A fill:#e1f5fe,stroke:#01579b
style E fill:#e8f5e8,stroke:#2e7d32
style I fill:#fff3e0,stroke:#e65100
style K fill:#fce4ec,stroke:#c2185b
```
### User & Authentication Resources
> ๐ **Authentication Layer**: Core user identity and session management
| Resource URI | Description | Example Usage |
|-------------|-------------|---------------|
| `user://current/email` | Current user's email address | `{{user://current/email}}` |
| `user://current/profile` | Full user profile with auth status | `{{user://current/profile}}['email']` |
| `user://profile/{email}` | Profile for specific user | `{{user://profile/user@domain.com}}['auth_status']['authenticated']` |
| `template://user_email` | Simple user email string | `{{template://user_email}}` |
| `auth://session/current` | Current session information | `{{auth://session/current}}['session_id']` |
| `auth://sessions/list` | List all active sessions | `{{auth://sessions/list}}['count']` |
| `auth://credentials/{email}/status` | Credential status for user | `{{auth://credentials/user@domain.com/status}}['credentials_valid']` |
### Google Services Resources
| Resource URI | Description | Example Usage |
|-------------|-------------|---------------|
| `google://services/scopes/{service}` | Required scopes for Google service | `{{google://services/scopes/drive}}['default_scopes']` |
### Workspace Content Resources
| Resource URI | Description | Example Usage |
|-------------|-------------|---------------|
| `workspace://content/recent` | Recent workspace content | `{{workspace://content/recent}}['content_by_type']['documents']` |
| `workspace://content/search/{query}` | Search workspace content | `{{workspace://content/search/reports}}['total_results']` |
### Gmail Resources
| Resource URI | Description | Example Usage |
|-------------|-------------|---------------|
| `service://gmail/labels` | Gmail labels (system & user labels) | `{{service://gmail/labels}}["user_labels"][0]["id"]` |
| `service://gmail/filters` | Gmail filter rules | `{{service://gmail/filters}}["filters"][0]["description"]` |
| `service://gmail/lists` | Available Gmail list types | `{{service://gmail/lists}}["list_types"]` |
| `gmail://content/suggestions` | Content suggestions for emails | `{{gmail://content/suggestions}}['quick_links']['items']` |
| `gmail://allow-list` | Gmail send allow list | `{{gmail://allow-list}}['allow_list_count']` |
| `gmail://messages/recent` | Recent Gmail messages | `{{gmail://messages/recent}}['messages'][0]['subject']` |
### Tools Resources
| Resource URI | Description | Example Usage |
|-------------|-------------|---------------|
| `tools://list/all` | Complete tools directory | `{{tools://list/all}}['total_tools']` |
| `tools://enhanced/list` | Enhanced tools collection | `{{tools://enhanced/list}}['enhanced_tools']` |
| `tools://usage/guide` | Tools usage guide | `{{tools://usage/guide}}['quick_start']` |
### Qdrant Resources
> ๐พ **Vector Database**: Access stored tool responses and historical data
| Resource URI | Description | Example Usage |
|-------------|-------------|---------------|
| `qdrant://collection/{name}/{point_id}` | Get specific stored tool response by UUID | `{{qdrant://collection/mcp_tool_responses/01d61daf-ab9d-4dc1-9f54-80c317cef056}}` |
| `qdrant://search/{query}` | Search across all tool responses | `{{qdrant://search/recent emails}}['results'][0]['id']` |
| `qdrant://collection/{name}/responses/recent` | Recent tool responses in collection | `{{qdrant://collection/mcp_tool_responses/responses/recent}}['responses'][0]` |
| `qdrant://cache` | Tool response cache metadata | `{{qdrant://cache}}['search'][0]['point_id']` |
| `qdrant://status` | Qdrant middleware status | `{{qdrant://status}}['qdrant_enabled']` |
## ๐ ๏ธ Implementation Examples
### Basic Setup with Jinja2
```python
from fastmcp import FastMCP
from middleware.template_middleware import setup_template_middleware
# Create FastMCP server
mcp = FastMCP("MyServer")
# Add template middleware with Jinja2 support
template_middleware = setup_template_middleware(
mcp,
template_dirs=[
"templates",
"templates/prompts",
"templates/tools",
"prompts/templates"
],
enable_debug=True,
enable_caching=True,
cache_ttl_seconds=300,
sandbox_mode=True # Enable sandboxed Jinja2 execution
)
```
### Advanced Configuration
```python
from middleware.template_middleware import TemplateParameterMiddleware
# Full configuration options
middleware = TemplateParameterMiddleware(
# Template loading
template_dirs=["prompts/templates", "templates"],
template_string_cache=True,
# Jinja2 configuration
autoescape=True, # Automatic HTML/CSS escaping
trim_blocks=True,
lstrip_blocks=True,
sandbox_mode=True, # Secure execution
# Performance
enable_caching=True,
cache_ttl_seconds=300,
template_cache_size=400,
# Security
allowed_resource_schemes=[
"user", "auth", "template", "google",
"tools", "workspace", "gmail", "service"
],
max_recursion_depth=3,
# Debugging
enable_debug_logging=True,
debug_template_source=True
)
mcp.add_middleware(middleware)
```
### Tool with Jinja2 Template Parameters
```python
### ๐ Tool with Natural Resource URI Syntax (Latest Update!)
```python
@mcp.tool()
async def send_smart_email_with_natural_syntax(
recipient: str,
# ๐ Natural syntax - direct property access!
user_email: str = "{{user://current/email.email}}", # โ "test_example@gmail.com"
user_name: str = "{{user://current/email.name}}", # โ "Seth Rivers"
total_files: int = "{{workspace://content/recent.total_files}}", # โ 42
greeting: str = "Hello {{user://current/email.name}}!", # โ "Hello Seth Rivers!"
# Mixed syntax - new + traditional
summary: str = "User {{user://current/email.email}} has {{workspace://content/recent.total_files}} files"
) -> str:
"""NEW: Tool demonstrating natural resource URI syntax - no complex JSON paths needed!"""
# All parameters are automatically resolved:
# user_email: "test_example@gmail.com"
# user_name: "Seth Rivers"
# total_files: 42
# greeting: "Hello Seth Rivers!"
# summary: "User test_example@gmail.com has 42 files"
return f"""
๐ง **Smart Email Composed!**
**From:** {user_name} ({user_email})
**To:** {recipient}
**Greeting:** {greeting}
**Summary:** {summary}
๐ฏ **Natural Syntax Benefits:**
- Simple property access: {{user://current/email.email}}
- Performance optimized: Single API call for multiple properties
- Jinja2 compatible: Works in conditionals, loops, and filters
- Error resilient: Graceful handling of missing properties
"""
```
### Traditional Tool with Jinja2 Template Parameters
```python
@mcp.tool()
async def send_personalized_email(
recipient: str,
subject: str = "Update from {{resources.user_current_email}}",
greeting: str = "{% if resources.user_current_profile.authenticated %}Hello {{resources.user_current_profile.email}}!{% else %}Hello!{% endif %}",
summary: str = """
{% if resources.workspace_recent %}
You have {{resources.workspace_recent.content_summary.total_files}} files:
{% for doc in resources.workspace_recent.content_by_type.documents[:3] %}
โข {{doc.name}}{% if not loop.last %}
{% endif %}{% endfor %}
{% if resources.workspace_recent.content_by_type.documents | length > 3 %}
... and {{resources.workspace_recent.content_by_type.documents | length - 3}} more
{% endif %}
{% else %}
No recent activity
{% endif %}
"""
) -> str:
"""Send personalized email with Jinja2 template resolution."""
# All Jinja2 template expressions are automatically resolved before tool execution
# recipient: "boss@company.com"
# subject: "Update from john.doe@company.com"
# greeting: "Hello john.doe@company.com!"
# summary: "You have 42 files:\nโข Report.docx\nโข Presentation.pptx\nโข Budget.xlsx\n... and 39 more"
return f"Email sent to {recipient}\nSubject: {subject}\nGreeting: {greeting}\nSummary: {summary}"
```
### Template File-Based Tool
```python
# Create template file: prompts/templates/gmail/status_report.txt
"""
Subject: Weekly Status Report - {{now() | strftime('%B %d, %Y')}}
Hello {{resources.user_current_email}},
{% if resources.workspace_recent.content_summary.total_files > 0 %}
## Recent Activity
You've been productive! Here's your activity summary:
**Documents ({{resources.workspace_recent.content_by_type.documents | length}}):**
{% for doc in resources.workspace_recent.content_by_type.documents[:5] %}
โข {{doc.name}} - {{doc.modified | format_datetime('%m/%d')}}
{% endfor %}
**Gmail Status:**
{% if resources.gmail_labels %}
โข Total labels: {{resources.gmail_labels | length}}
โข System labels: {{resources.gmail_labels.system_labels | length}}
โข Custom labels: {{resources.gmail_labels.user_labels | length}}
{% endif %}
{% else %}
No recent activity to report.
{% endif %}
Best regards,
Your FastMCP Assistant
"""
# Use the template in a tool
@mcp.tool()
async def generate_status_report(
report_content: str = "{% include 'gmail/status_report.txt' %}"
) -> str:
"""Generate a status report using Jinja2 template file."""
# The entire template file is loaded, rendered with current resources,
# and the result is passed as the report_content parameter
return f"Generated report:\n\n{report_content}"
```
### Real Gmail Integration Example
```python
@mcp.tool()
async def create_gmail_filter_report(
user_email: str = "{{template://user_email}}",
inbox_label: str = "{{service://gmail/labels}}[\"system_labels\"][2][\"id\"]", # INBOX
work_label: str = "{{service://gmail/labels}}[\"user_labels\"][0][\"id\"]", # First user label
total_labels: int = "{{service://gmail/labels}}[\"count\"]",
filter_count: int = "{{service://gmail/filters}}[\"count\"]"
) -> str:
"""Create Gmail filter report with real label IDs from client tests."""
# Based on our successful client test results:
# user_email: "srivers@FastMCP.com"
# inbox_label: "INBOX"
# work_label: "CHAT" (or other real label ID)
# total_labels: 33
# filter_count: 3
return f"""
Gmail Report for {user_email}:
๐ง Total Labels: {total_labels}
๐ Inbox Label: {inbox_label}
๐ท๏ธ Work Label: {work_label}
๐ง Active Filters: {filter_count}
"""
```
### Advanced Template Usage
```python
@mcp.tool()
async def create_smart_report(
user_email: str = "{{template://user_email}}",
recent_docs: str = "{{workspace://content/recent}}['content_by_type']['documents']",
auth_status: str = "{{user://current/profile}}['auth_status']['credentials_valid']",
session_info: str = "{{auth://session/current}}['session_id']"
) -> str:
"""Create a smart report with automatically populated data."""
# All parameters are pre-resolved:
# user_email: "john.doe@company.com"
# recent_docs: JSON array of recent documents
# auth_status: true/false
# session_info: "uuid-session-id"
import json
docs = json.loads(recent_docs) if isinstance(recent_docs, str) else recent_docs
report = f"""
Smart Report for {user_email}
Session: {session_info}
Auth Status: {'โ
Authenticated' if json.loads(auth_status) else 'โ Not authenticated'}
Recent Documents: {len(docs)} found
"""
return report
```
### Qdrant Vector Database Integration
The template middleware seamlessly integrates with Qdrant resources, enabling dynamic content generation from stored tool responses:
```python
@mcp.tool()
async def send_historical_data_email(
recipient: str,
point_id: str = "01d61daf-ab9d-4dc1-9f54-80c317cef056",
# ๐ Automatically fetch complete Qdrant point data
response_data: str = "{{ qdrant://collection/mcp_tool_responses/01d61daf-ab9d-4dc1-9f54-80c317cef056 }}",
# Extract specific fields using JSON paths
tool_name: str = "{{ qdrant://collection/mcp_tool_responses/01d61daf-ab9d-4dc1-9f54-80c317cef056 }}['tool_name']",
execution_time: int = "{{ qdrant://collection/mcp_tool_responses/01d61daf-ab9d-4dc1-9f54-80c317cef056 }}['execution_time_ms']",
timestamp: str = "{{ qdrant://collection/mcp_tool_responses/01d61daf-ab9d-4dc1-9f54-80c317cef056 }}['timestamp']"
) -> str:
"""Send email with embedded Qdrant tool response data."""
# Template middleware automatically resolves all Qdrant URIs:
# response_data: Full QdrantPointDetailsResponse as dict/JSON
# tool_name: "get_doc_content"
# execution_time: 15
# timestamp: "2025-09-26T14:06:39.230441+00:00"
import json
data = json.loads(response_data) if isinstance(response_data, str) else response_data
return f"""
๐ง **Historical Tool Response Email**
**To:** {recipient}
**Tool Execution Details:**
- Tool: {tool_name}
- Executed at: {timestamp}
- Execution time: {execution_time}ms
- User: {data['user_email']}
- Session: {data['session_id']}
**Complete Response Data:**
```json
{json.dumps(data, indent=2)}
```
๐ฏ **Template Integration Benefits:**
- Direct Qdrant resource access in templates
- Automatic decompression of stored data
- JSON path extraction for specific fields
- Seamless integration with other FastMCP resources
"""
```
**Real Example from Production:**
This actual tool call successfully embedded Qdrant data in an email:
```json
{
"method": "tools/call",
"params": {
"name": "send_gmail_message",
"arguments": {
"subject": "test",
"body": "{{ qdrant://collection/mcp_tool_responses/01d61daf-ab9d-4dc1-9f54-80c317cef056 }}",
"to": "myself"
}
}
}
```
The email body was automatically populated with the complete point data:
- Full payload with all metadata
- Tool execution details (name, timing, user)
- Complete response data structure
- Automatic decompression (if applicable)
- Formatted as structured JSON
**Key Features:**
- โ
**UUID Support**: Hyphens in point IDs automatically handled (converted to underscores in variable names)
- โ
**Auto-Decompression**: Compressed Qdrant data transparently decompressed
- โ
**Nested Access**: Extract specific fields using JSON path syntax
- โ
**Performance**: Resources cached with 5-minute TTL by default
- โ
**Error Handling**: Graceful fallback if point doesn't exist
**Advanced Qdrant Workflow Example:**
```python
@mcp.tool()
async def create_execution_audit_report(
user_email: str = "{{template://user_email}}",
# Step 1: Search for relevant tool executions
search_results: str = "{{ qdrant://search/email tool executions }}",
# Step 2: Get detailed data for top result
top_result_id: str = "{{ qdrant://search/email tool executions }}['results'][0]['id']",
top_result_data: str = "{{ qdrant://collection/mcp_tool_responses/{{top_result_id}} }}",
# Mix with other resources for complete context
workspace_files: int = "{{workspace://content/recent.total_files}}",
report_template: str = """
# Tool Execution Audit Report
**Generated:** {{now() | strftime('%Y-%m-%d %H:%M:%S')}}
**User:** {{user_email}}
## Recent Tool Activity
Found {{search_results.total_results}} relevant tool executions.
### Top Result Details
- **Tool:** {{top_result_data.tool_name}}
- **Executed:** {{top_result_data.timestamp}}
- **Duration:** {{top_result_data.execution_time_ms}}ms
- **Session:** {{top_result_data.session_id}}
### Response Data
```json
{{top_result_data.response_data | json_pretty(2)}}
```
## Current Workspace Status
Active files: {{workspace_files}}
---
*Powered by Qdrant Vector Database + Template Middleware*
"""
) -> str:
"""Create comprehensive audit report combining Qdrant search with other resources."""
# All template URIs automatically resolved - ready to use!
return report_template
```
**Technical Details:**
The template middleware handles Qdrant URIs through a sophisticated pipeline:
1. **URI Detection**: Identifies `qdrant://collection/{name}/{uuid}` patterns
2. **UUID Normalization**: Converts hyphens to underscores (e.g., `01d61daf-ab9d-4dc1` โ `01d61daf_ab9d_4dc1`)
3. **Resource Fetching**: Calls Qdrant resource handler to retrieve point data
4. **Payload Parsing**: Automatically extracts nested JSON from `ToolResult.content` fields
5. **Model Conversion**: Converts Pydantic models to plain dicts for Jinja2 compatibility
6. **Variable Injection**: Adds to template context with safe variable names
7. **Template Rendering**: Jinja2 renders with full access to structured Qdrant data
See [`documentation/middleware/qdrant-middleware-architecture.md`](documentation/middleware/qdrant-middleware-architecture.md#-template-integration-with-jinja2) for complete Qdrant template integration architecture.
### Complex JSON Path Examples
```python
@mcp.tool()
async def analyze_workspace_activity(
total_files: int = "{{workspace://content/recent}}['content_summary']['total_files']",
doc_count: int = "{{workspace://content/recent}}['content_summary']['documents']",
first_doc_name: str = "{{workspace://content/recent}}['content_by_type']['documents'][0]['name']",
user_domain: str = "{{user://current/profile}}['email'].split('@')[1]",
template_suggestions: str = "{{gmail://content/suggestions}}['email_templates']['status_update']['opening_lines'][0]"
) -> str:
"""Analyze workspace activity with complex data extraction."""
return f"""
Workspace Analysis:
- Total files: {total_files}
- Documents: {doc_count}
- Latest doc: {first_doc_name}
- User domain: {user_domain}
- Suggested opening: {template_suggestions}
"""
```
## ๐ JSON Path Syntax Reference
### Basic Access
```python
# Object property access
"{{resource://uri}}['property']"
"{{resource://uri}}['nested']['property']"
# Array index access
"{{resource://uri}}[0]"
"{{resource://uri}}[0]['property']"
# Combined access
"{{resource://uri}}['items'][0]['name']"
```
### Advanced Path Expressions
```python
# Multiple levels of nesting
"{{workspace://content/recent}}['content_by_type']['documents'][0]['web_view_link']"
# Access different parts of complex resources
"{{gmail://content/suggestions}}['email_templates']['meeting_follow_up']['subject_template']"
# Extract counts and metadata
"{{tools://list/all}}['tools_by_category']['drive_tools']['tool_count']"
```
## ๐จ Error Handling
```mermaid
flowchart TD
A["๐ญ Template Processing"] --> B{"Valid Syntax?"}
B -->|"No"| C["โ ๏ธ Log Warning"]
B -->|"Yes"| D["๐ก Fetch Resource"]
D --> E{"Resource Found?"}
E -->|"No"| F["โ Log Error"]
E -->|"Yes"| G["๐ Apply JSON Path"]
G --> H{"Path Valid?"}
H -->|"No"| I["โ ๏ธ Log Path Error"]
H -->|"Yes"| J["โ
Return Value"]
C --> K["๐ Keep Original"]
F --> K
I --> K
style A fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
style J fill:#e8f5e8,stroke:#388e3c,stroke-width:2px
style K fill:#fff3e0,stroke:#e65100,stroke-width:2px
style C fill:#fff8e1,stroke:#f57c00
style F fill:#ffebee,stroke:#d32f2f
style I fill:#fff8e1,stroke:#f57c00
```
### Template Resolution Errors
The middleware handles errors gracefully:
1. **Template Parsing Errors**: Invalid syntax logs warning, keeps original value
2. **Resource Resolution Errors**: Failed resource access logs error, keeps original value
3. **JSON Path Errors**: Invalid paths log warning, keeps original value
4. **Authentication Errors**: Resource access denied logs info, keeps original value
### Example Error Scenarios
```python
# Invalid JSON path - logs warning, keeps original
"{{user://current/profile}}['nonexistent_field']"
# Invalid resource URI - logs error, keeps original
"{{invalid://resource/uri}}"
# Resource access denied - logs info, keeps original
"{{restricted://resource}}"
# Malformed template syntax - logs warning, keeps original
"{{unclosed template"
```
## ๐๏ธ Configuration Options
### Middleware Configuration with Jinja2
```python
from middleware.template_middleware import TemplateParameterMiddleware
middleware = TemplateParameterMiddleware(
# Template discovery and loading
template_dirs=[
"templates",
"templates/prompts",
"templates/tools",
"prompts/templates" # Gmail prompts location
],
template_string_cache=True, # Cache compiled templates
# Resource URI patterns
resource_pattern=r'\{\{resource://([^}]+)\}\}', # Resource URI syntax
template_pattern=r'\{\{([^}]+)\}\}', # General template syntax
# Jinja2 configuration
autoescape=True, # Auto-escape HTML/CSS
trim_blocks=True, # Remove first newline after block
lstrip_blocks=True, # Strip leading spaces
sandbox_mode=True, # Enable sandboxed execution
# Caching and performance
enable_caching=True, # Cache resource responses
cache_ttl_seconds=300, # Cache TTL (5 minutes)
template_cache_size=400, # Max templates to cache
# Security and validation
allowed_resource_schemes=[ # Allowed URI schemes
"user", "auth", "template", "google",
"tools", "workspace", "gmail", "service"
],
max_recursion_depth=3, # Max template nesting
# Debugging
enable_debug_logging=False, # Debug logging
debug_template_source=False # Log template source code
)
```
### Jinja2 Environment Features
```python
# The middleware automatically configures Jinja2 with:
# Custom FastMCP filters
{{resources.workspace_recent | json_extract('content_summary.total_files')}}
{{resources.user_current_profile.created_at | format_datetime('%Y-%m-%d')}}
{{long_text | truncate_text(100, '...')}}
# Custom global functions
{{now()}} # Current datetime
{{utc_now()}} # Current UTC datetime
# Auto-escaping for HTML/CSS (prevents f-string parsing errors)
{% autoescape true %}
<style>
.status { color: {{status_color}}; }
</style>
{% endautoescape %}
```
### Alternative Template Patterns
```python
# Use different delimiters if needed
template_pattern=r'\$\{([^}]+)\}' # ${...} syntax
template_pattern=r'%\{([^}]+)\}' # %{...} syntax
template_pattern=r'#\{([^}]+)\}' # #{...} syntax
```
## ๐ Performance Considerations
### Caching Strategy
- **Enable Caching**: Use `enable_caching=True` for production
- **Cache TTL**: Set appropriate TTL based on resource update frequency
- **Cache Stats**: Monitor cache hit rates with `middleware.get_cache_stats()`
### Resource Access Optimization
- **Resource Schemes**: Restrict `allowed_resource_schemes` for security
- **Recursion Limits**: Set appropriate `max_recursion_depth`
- **Debug Logging**: Disable `enable_debug_logging` in production
## ๐ Security Considerations
### Resource Access Control
```python
# Restrict to safe resource schemes only
allowed_resource_schemes=[
"user", "auth", "template", # Authentication resources
"tools", "workspace", # Content resources
"gmail" # Service-specific resources
]
```
### JSON Path Safety
The middleware uses `ast.parse()` and restricted `eval()` for JSON path evaluation:
- Only basic subscript operations allowed
- No arbitrary code execution
- Sandboxed evaluation environment
- Safe handling of malformed expressions
## ๐งช Testing and Debugging
```mermaid
flowchart TB
A["๐งช Template Testing"] --> B["๐ก Client SDK Test"]
A --> C["๐ Debug Mode"]
A --> D["๐ Manual Testing"]
B --> E["Connect to Server"]
E --> F["Call Real Tools"]
F --> G["Extract Gmail Data"]
G --> H["Validate Templates"]
C --> I["Enable Logging"]
I --> J["Monitor Resolution"]
J --> K["Check Cache Stats"]
D --> L["Test Parameters"]
L --> M["Verify JSON Paths"]
M --> N["Validate Output"]
H --> O["โ
Tests Pass"]
K --> O
N --> O
style A fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
style O fill:#e8f5e8,stroke:#388e3c,stroke-width:2px
style B fill:#f3e5f5,stroke:#4a148c
style C fill:#fff3e0,stroke:#e65100
style D fill:#e0f2f1,stroke:#00695c
```
### Client Testing with FastMCP Client SDK
```python
# Test template middleware integration with real server
import pytest
from fastmcp import Client
from .test_auth_utils import get_client_auth_config
class TestTemplateMiddleware:
@pytest.fixture
async def client(self):
"""Create client connected to running server."""
auth_config = get_client_auth_config("test@example.com")
client = Client("http://localhost:8002/mcp/", auth=auth_config)
async with client:
yield client
@pytest.mark.asyncio
async def test_real_gmail_data(self, client):
"""Test getting real Gmail label IDs via tools."""
result = await client.call_tool("list_gmail_labels", {
"user_google_email": "test@example.com"
})
# Extract real label IDs (format: "(ID: LABELID)")
import re
label_ids = re.findall(r'\(ID:\s*([^)]+)\)', str(result))
# Verify real data - from our test we got:
# ['CHAT', 'SENT', 'INBOX', 'IMPORTANT', 'TRASH', 'DRAFT', 'SPAM'...]
assert len(label_ids) > 0, "Should find real Gmail label IDs"
assert "INBOX" in label_ids, "Should have INBOX label"
assert "SENT" in label_ids, "Should have SENT label"
return label_ids
# Run with: uv run pytest tests/test_template_middleware_integration.py -v
```
### Enable Debug Mode
```python
# Enable detailed logging
template_middleware.enable_debug_mode(True)
# Check cache statistics
stats = template_middleware.get_cache_stats()
print(f"Cache entries: {stats['entries']}")
print(f"Cached resources: {stats['cached_resources']}")
```
### Manual Testing
```python
# Test template resolution manually
from middleware.template_middleware import TemplateParameterMiddleware
middleware = TemplateParameterMiddleware(enable_debug_logging=True)
# Test with real Gmail service resources
test_params = {
"user_email": "{{template://user_email}}",
"inbox_label": "{{service://gmail/labels}}[\"system_labels\"][2][\"id\"]", # INBOX
"label_count": "{{service://gmail/labels}}[\"count\"]"
}
```
## ๐ Migration from Manual Parameters
### Before (Manual Parameters)
```python
@mcp.tool()
async def send_email(
user_google_email: str, # Manual parameter
recipient: str,
subject: str
) -> str:
# Tool had to require email parameter
pass
```
### After (Template Parameters)
```python
@mcp.tool()
async def send_email(
recipient: str,
subject: str,
user_email: str = "{{template://user_email}}" # Automatic resolution
) -> str:
# Email automatically resolved from session context
pass
```
## ๐ Best Practices
### Template Design
1. **Use Appropriate Resources**: Choose the most specific resource for your needs
2. **Handle Failures Gracefully**: Template resolution failures keep original values
3. **Cache Wisely**: Enable caching for frequently accessed resources
4. **Secure Access**: Use allowed schemes to restrict resource access
5. **Debug Thoroughly**: Enable debug logging during development
### Performance Optimization
1. **Minimize Nesting**: Keep JSON paths as shallow as possible
2. **Cache Configuration**: Set appropriate TTL based on data freshness needs
3. **Resource Selection**: Use the most efficient resource for your data needs
4. **Batch Access**: Design resources to return multiple related data points
### Error Handling
1. **Expect Failures**: Always assume template resolution might fail
2. **Provide Defaults**: Use meaningful default values in parameters
3. **Log Appropriately**: Use debug logging to troubleshoot issues
4. **Fallback Logic**: Implement fallback behavior in your tools
## ๐ Integration with Existing Middleware
```mermaid
sequenceDiagram
participant Client
participant Auth as ๐ AuthMiddleware
participant Template as ๐ญ TemplateMiddleware
participant Resource as ๐ก ResourceSystem
participant Tool as ๐ ๏ธ Tool
Client->>Auth: 1. Tool Request
Auth->>Auth: 2. Validate Credentials
Auth->>Template: 3. Forward with Context
Template->>Template: 4. Parse Templates
Template->>Resource: 5. Fetch Resources
Resource-->>Template: 6. Resource Data
Template->>Template: 7. Resolve Parameters
Template->>Tool: 8. Execute with Resolved Params
Tool-->>Template: 9. Tool Result
Template-->>Auth: 10. Forward Result
Auth-->>Client: 11. Final Response
rect rgb(240, 248, 255)
Note over Auth,Template: Middleware Chain Processing
end
rect rgb(248, 255, 240)
Note over Template,Resource: Template Resolution
end
```
The Template Parameter Middleware works seamlessly with existing middleware:
### With AuthMiddleware
```python
# Add both middleware - order matters
mcp.add_middleware(AuthMiddleware()) # Sets up authentication context
mcp.add_middleware(TemplateParameterMiddleware()) # Resolves templates using auth context
```
### With Custom Middleware
```python
class CustomMiddleware(Middleware):
async def on_call_tool(self, context: MiddlewareContext, call_next):
# Your custom logic here
result = await call_next(context)
# Template resolution happens automatically
return result
# Add in correct order
mcp.add_middleware(AuthMiddleware())
mcp.add_middleware(CustomMiddleware())
mcp.add_middleware(TemplateParameterMiddleware())
```
## ๐ Complete Example with Jinja2 Integration
Here's a complete example showing the enhanced Template Parameter Middleware with Jinja2 in action:
```python
from fastmcp import FastMCP
from middleware.template_middleware import setup_template_middleware
from auth.middleware import AuthMiddleware
from resources.user_resources import setup_user_resources
from prompts.gmail_prompts import setup_gmail_prompts
# Create server
mcp = FastMCP("Enhanced Template Demo Server")
# Setup resources first
setup_user_resources(mcp)
setup_gmail_prompts(mcp) # Includes Jinja2 Gmail templates
# Add middleware in correct order
mcp.add_middleware(AuthMiddleware())
template_middleware = setup_template_middleware(
mcp,
template_dirs=[
"prompts/templates", # Gmail templates location
"templates/prompts",
"templates/tools"
],
enable_debug=True,
sandbox_mode=True,
autoescape=True # Prevent CSS f-string parsing errors
)
@mcp.tool()
async def smart_email_composer(
recipient: str,
subject: str = "Weekly Update from {{resources.user_current_email}}",
email_body: str = """
{% if resources.user_current_profile.authenticated %}
Hi {{recipient}}!
Here's your weekly update from {{resources.user_current_email}}:
## ๐ Recent Activity
{% if resources.workspace_recent.content_summary.total_files > 0 %}
I've been working on {{resources.workspace_recent.content_summary.total_files}} files:
{% for doc in resources.workspace_recent.content_by_type.documents[:5] %}
โข **{{doc.name}}** (modified {{doc.modified | format_datetime('%m/%d/%Y')}})
{% endfor %}
{% if resources.workspace_recent.content_by_type.documents | length > 5 %}
...and {{resources.workspace_recent.content_by_type.documents | length - 5}} more documents.
{% endif %}
{% else %}
No recent document activity.
{% endif %}
## ๐ง Gmail Status
{% if resources.gmail_labels %}
My Gmail is organized with:
- {{resources.gmail_labels.system_labels | length}} system labels
- {{resources.gmail_labels.user_labels | length}} custom labels
- Total: {{resources.gmail_labels | length}} labels
Most used labels:
{% for label in resources.gmail_labels.user_labels[:3] %}
โข {{label.name}} ({{label.messages_total}} messages)
{% endfor %}
{% endif %}
## โก Quick Actions
{% if resources.gmail_suggestions %}
Suggested actions:
{% for suggestion in resources.gmail_suggestions.quick_actions[:3] %}
โข {{suggestion.title}}: {{suggestion.description}}
{% endfor %}
{% endif %}
Best regards,
{{resources.user_current_profile.email}}
P.S. This email was generated at {{now() | strftime('%Y-%m-%d %H:%M:%S')}} using FastMCP with Jinja2! ๐
{% else %}
Unable to generate personalized content - please authenticate first.
{% endif %}
"""
) -> str:
"""Compose smart emails with Jinja2 template resolution and real resource data."""
# All Jinja2 templates are automatically resolved by the middleware
# The email_body parameter contains fully rendered HTML with:
# - Conditional logic based on authentication status
# - Loops through workspace documents and Gmail labels
# - Formatted dates and counts
# - Automatic HTML escaping for safety
return f"""
๐ง **Email Composed Successfully!**
**To:** {recipient}
**Subject:** {subject}
**Body Preview:**
{email_body[:500]}{'...' if len(email_body) > 500 else ''}
**Full Email Length:** {len(email_body)} characters
**Template Features Used:** Jinja2 conditionals, loops, filters, and resource integration
"""
# Template file example: prompts/templates/gmail/signature.html
"""
<div class="signature">
<p>Best regards,<br>
<strong>{{resources.user_current_profile.display_name | default(resources.user_current_email)}}</strong>
</p>
{% if resources.user_current_profile.company %}
<p><em>{{resources.user_current_profile.company}}</em></p>
{% endif %}
<p><small>Powered by FastMCP with Jinja2 โข Generated {{now() | strftime('%Y-%m-%d')}}</small></p>
</div>
"""
@mcp.tool()
async def add_email_signature(
content: str,
signature: str = "{% include 'gmail/signature.html' %}"
) -> str:
"""Add professional email signature using Jinja2 template file."""
return f"{content}\n\n{signature}"
if __name__ == "__main__":
# Run the enhanced server
mcp.run()
```
This creates a **powerful, context-aware FastMCP server** where:
โจ **Jinja2 templates** provide professional formatting with conditionals and loops
๐ฏ **Resource integration** automatically populates real user data
๐ก๏ธ **Auto-escaping** prevents CSS/HTML parsing errors (solved the f-string issues!)
๐ **Template files** organize complex templates in reusable files
โก **Performance optimized** with template compilation and resource caching
๐ **Security focused** with sandboxed template execution
## ๐ Next Steps
### ๐จ Jinja2 Integration Benefits
**โ
Solved Problems:**
- **F-string CSS parsing errors**: Auto-escaping prevents `NameError: name 'font' is not defined`
- **Complex template logic**: Use conditionals, loops, and filters instead of string concatenation
- **Template organization**: Load templates from organized file structures
- **HTML/CSS safety**: Automatic escaping prevents injection and parsing issues
**๐ New Capabilities:**
- **Professional templating**: Full Jinja2 feature set with inheritance and includes
- **Enhanced Gmail prompts**: Clean templates without f-string issues
- **Performance optimization**: Template compilation and resource caching
- **Security improvements**: Sandboxed execution with allowlists
### Migration Path
```mermaid
journey
title Jinja2 Template Middleware Migration
section Assessment
Identify F-string Issues: 2: Developer
Review Current Templates: 3: Developer
Plan Jinja2 Migration: 4: Developer
section Implementation
Install Enhanced Middleware: 5: Developer
Update Gmail Prompts: 4: Developer
Convert Templates: 3: Developer
Test Integration: 4: Developer
section Production
Deploy Enhanced System: 5: Developer
Monitor Performance: 4: Developer
User Experience: 5: Users
section Optimization
Fine-tune Templates: 4: Developer
Optimize Caching: 3: Developer
Scale Usage: 5: Users
```
1. **๐ง Upgrade**: The middleware now includes full Jinja2 support by default
2. **๐งช Test**: Verify template resolution with real data (Gmail prompts working!)
3. **โจ Enhance**: Convert existing tools to use Jinja2 template features
4. **๐ Monitor**: Watch performance and debug logs
5. **๐ฏ Optimize**: Tune cache settings and add template files
## ๐ API Documentation
### Core Classes
#### EnhancedTemplateMiddleware
The main middleware class that provides hybrid template resolution capabilities.
**Class Attributes:**
- `SIMPLE_TEMPLATE_PATTERN`: Regex pattern for detecting simple {{resource://uri}} templates
- `RESOURCE_URI_PATTERN`: Enhanced regex for resource URIs with property access
- `JINJA2_DETECTION_PATTERNS`: List of patterns for detecting Jinja2 syntax
**Instance Attributes:**
- `enable_caching`: Boolean flag for resource caching
- `cache_ttl_seconds`: Cache time-to-live configuration
- `enable_debug_logging`: Debug logging control
- `templates_dir`: Path to template files directory
- `jinja2_env`: Configured Jinja2 Environment (if available)
#### Key Methods
##### `__init__(enable_caching=True, cache_ttl_seconds=300, enable_debug_logging=False, jinja2_options=None, templates_dir=None)`
Initialize the enhanced template middleware with comprehensive configuration options.
**Parameters:**
- `enable_caching` (bool): Enable TTL-based resource caching for performance
- `cache_ttl_seconds` (int): Cache expiration time in seconds (default: 300)
- `enable_debug_logging` (bool): Enable detailed logging for development
- `jinja2_options` (Dict[str, Any]): Advanced Jinja2 Environment options
- `templates_dir` (str): Path to .j2 template files directory
**Key Features:**
- Automatic Jinja2 detection and fallback to simple templating
- Custom template loaders (FileSystem + Dynamic)
- Resource caching with configurable TTL
- Comprehensive error handling and logging
##### `async on_call_tool(context, call_next)`
Main middleware hook for intercepting and processing tool calls.
**Parameters:**
- `context` (MiddlewareContext): FastMCP middleware context
- `call_next`: Continuation function for middleware chain
**Returns:**
- Result from original tool execution after template resolution
**Processing Flow:**
1. Extract tool name and arguments
2. Validate FastMCP context availability
3. Recursively resolve template expressions
4. Update context with resolved values
5. Continue with tool execution
##### `clear_cache()`
Clear all cached resources and reset cache statistics.
**Use Cases:**
- Development: Force fresh data when resources change
- Testing: Ensure clean state between tests
- Memory management: Free cache memory
- Runtime: Force refresh of potentially stale data
##### `get_cache_stats()`
Get detailed cache statistics for monitoring and debugging.
**Returns:**
```python
{
"enabled": bool, # Caching configuration status
"jinja2_available": bool, # Jinja2 package availability
"total_entries": int, # Total cache entries
"valid_entries": int, # Non-expired entries
"expired_entries": int, # Expired but not cleaned
"ttl_seconds": int, # Configured TTL
"cached_uris": List[str] # List of cached resource URIs
}
```
### Exception Classes
#### TemplateResolutionError
Raised when template parameter resolution fails.
**Attributes:**
- `message`: Human-readable error description
- `resource_uri`: The resource URI that failed (optional)
- `template_text`: The template text that caused the error (optional)
**Common Causes:**
- Resource URI cannot be fetched from FastMCP context
- Invalid Jinja2 template syntax
- Property extraction failures
- Authentication/permission issues
#### SilentUndefined
Custom Jinja2 Undefined class for graceful template variable handling.
**Behavior:**
- Missing variables resolve to empty string ('')
- Boolean evaluation returns False
- Prevents UndefinedError exceptions
- Enables progressive template enhancement
### Setup Functions
#### `setup_enhanced_template_middleware(mcp, **kwargs)`
Primary setup function for integrating template middleware into FastMCP servers.
**Parameters:**
- `mcp`: FastMCP server instance
- `enable_caching` (bool): Enable resource caching (default: True)
- `cache_ttl_seconds` (int): Cache TTL in seconds (default: 300)
- `enable_debug` (bool): Enable debug logging (default: False)
- `jinja2_options` (Dict): Advanced Jinja2 configuration
- `templates_dir` (str): Template files directory path
**Returns:**
- `EnhancedTemplateMiddleware`: Configured middleware instance
**Example:**
```python
from middleware.template_middleware import setup_enhanced_template_middleware
# Basic setup
middleware = setup_enhanced_template_middleware(mcp)
# Production configuration
middleware = setup_enhanced_template_middleware(
mcp,
enable_caching=True,
cache_ttl_seconds=600, # 10 minutes
enable_debug=False,
jinja2_options={'autoescape': True}
)
# Development configuration
middleware = setup_enhanced_template_middleware(
mcp,
enable_debug=True,
cache_ttl_seconds=60, # Short TTL for development
templates_dir='./custom_templates'
)
```
### Custom Filters and Functions
The middleware provides specialized Jinja2 filters for FastMCP integration:
#### Global Functions
- `now()`: Returns current UTC datetime
- `utcnow()`: Alias for now() for compatibility
#### Custom Filters
- `extract(data, path)`: Extract nested properties using dot notation
- `safe_get(data, key, default='')`: Safely access dict/object properties
- `format_date(date_input, format='%Y-%m-%d %H:%M')`: Format datetime objects
- `json_pretty(data, indent=2)`: Pretty-print JSON with indentation
- `strftime(date_input, format='%Y-%m-%d %H:%M:%S')`: Format dates with strftime
- `map_list(items, attribute=None)`: Map object attributes to list values
- `map_attr(items, attribute)`: Alias for map_list
#### Filter Usage Examples
```jinja2
{# Extract nested properties #}
{{ user_data | extract('profile.settings.theme') }}
{# Safe property access with defaults #}
{{ user | safe_get('name', 'Anonymous User') }}
{# Format dates and timestamps #}
{{ created_at | format_date('%B %d, %Y') }}
{{ timestamp | strftime('%H:%M:%S') }}
{# Pretty-print JSON data #}
{{ api_response | json_pretty(4) }}
{# Map object attributes to lists #}
{{ documents | map_list('title') }}
{{ users | map_attr('email') }}
```
## ๐ง Integration Patterns
### Middleware Chain Integration
The template middleware works seamlessly with existing FastMCP middleware:
```python
from fastmcp import FastMCP
from auth.middleware import AuthMiddleware
from middleware.template_middleware import setup_enhanced_template_middleware
# Create server
mcp = FastMCP("MyServer")
# Add middleware in correct order
mcp.add_middleware(AuthMiddleware()) # Authentication first
template_middleware = setup_enhanced_template_middleware(mcp) # Templates second
# Templates can now access authenticated user context
@mcp.tool()
async def personalized_greeting(
user_name: str = "{{user://current/profile.name}}",
user_email: str = "{{user://current/email}}"
) -> str:
return f"Hello {user_name} ({user_email})!"
```
### Resource Provider Integration
Custom resource providers can be used with the template middleware:
```python
# Custom resource provider
@mcp.resource("custom://data/{key}")
async def custom_data_resource(key: str) -> str:
# Fetch data from custom source
data = await fetch_custom_data(key)
return json.dumps(data)
# Use in templates
@mcp.tool()
async def process_custom_data(
data: str = "{{custom://data/user_preferences}}"
) -> str:
# data is automatically resolved from custom resource
return f"Processing: {data}"
```
### Template File Organization
Organize complex templates in dedicated files:
```
templates/
โโโ email/
โ โโโ welcome.j2
โ โโโ notification.j2
โ โโโ signature.j2
โโโ reports/
โ โโโ daily_summary.j2
โ โโโ weekly_stats.j2
โโโ macros/
โโโ common.j2
โโโ formatting.j2
```
```python
# templates/macros/common.j2
{% macro user_greeting(formal=False) %}
{% if formal %}
Dear {{ user.name | default('Valued User') }}
{% else %}
Hi {{ user.name | default('there') }}
{% endif %}
{% endmacro %}
# templates/email/welcome.j2
{% from 'macros/common.j2' import user_greeting %}
Subject: Welcome to Our Platform!
{{ user_greeting(formal=True) }},
Welcome to our platform! Here's what you can do:
{% for feature in features %}
โข {{ feature.name }}: {{ feature.description }}
{% endfor %}
Best regards,
The Team
```
### Performance Optimization
#### Caching Strategy
```python
# Production: Longer TTL for stable resources
middleware = setup_enhanced_template_middleware(
mcp,
enable_caching=True,
cache_ttl_seconds=3600 # 1 hour for user profiles
)
# Development: Shorter TTL for changing data
middleware = setup_enhanced_template_middleware(
mcp,
enable_caching=True,
cache_ttl_seconds=60 # 1 minute for development
)
# Monitor cache performance
stats = middleware.get_cache_stats()
print(f"Cache hit rate: {stats['valid_entries']/stats['total_entries']*100:.1f}%")
```
#### Template Optimization
```python
# Efficient: Pre-resolve common resources
@mcp.tool()
async def efficient_template(
user_email: str = "{{user://current/email}}", # Cached
workspace_files: int = "{{workspace://content/recent.total_files}}" # Cached
) -> str:
return f"User {user_email} has {workspace_files} files"
# Less efficient: Multiple resource calls
@mcp.tool()
async def inefficient_template(
content: str = """
User: {{user://current/email}}
Profile: {{user://current/profile.name}}
Settings: {{user://current/profile.settings}}
""" # Multiple calls to user resources
) -> str:
return content
```
> ๐ **The enhanced Template Parameter Middleware with comprehensive API documentation transforms your FastMCP platform into a professional, well-documented system that's easy to understand, integrate, and extend!** โจ๏ฟฝ