360 lines
No EOL
12 KiB
Text
360 lines
No EOL
12 KiB
Text
#!/usr/bin/env nu
|
|
# Content Generation Tools for AuroraFrame MCP Server
|
|
#
|
|
# Provides AI-powered content generation capabilities:
|
|
# - Generate content from KCL schemas
|
|
# - Enhance existing content
|
|
# - Create content variations for A/B testing
|
|
# - Multi-format optimization (web, email, mobile)
|
|
|
|
use mcp-server.nu call_openai_api
|
|
use mcp-server.nu extract_frontmatter
|
|
use mcp-server.nu generate_frontmatter
|
|
|
|
# Generate content from KCL schema and prompt
|
|
export def generate_content_tool [args: record, config: record, debug: bool] {
|
|
let schema = $args.schema
|
|
let prompt = $args.prompt
|
|
let format = ($args.format? | default "markdown")
|
|
|
|
debug_log $"Generating ($format) content from schema and prompt" $debug
|
|
|
|
# Analyze the KCL schema
|
|
let schema_analysis = (analyze_kcl_schema $schema)
|
|
|
|
# Build the system prompt
|
|
let system_prompt = (build_content_generation_prompt $schema_analysis $format)
|
|
|
|
# Call OpenAI API
|
|
let messages = [
|
|
{ role: "system", content: $system_prompt }
|
|
{ role: "user", content: $prompt }
|
|
]
|
|
|
|
let api_response = (call_openai_api $messages $config 0.7)
|
|
|
|
if "error" in $api_response {
|
|
return $api_response
|
|
}
|
|
|
|
# Process the generated content
|
|
let processed_content = (process_generated_content $api_response.content $schema $format)
|
|
|
|
{
|
|
content: [
|
|
{
|
|
type: "text"
|
|
text: $"Generated ($format) content:\n\n($processed_content)"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
# Enhance existing content with AI improvements
|
|
export def enhance_content_tool [args: record, config: record, debug: bool] {
|
|
let content = $args.content
|
|
let enhancements = $args.enhancements
|
|
|
|
debug_log $"Enhancing content with: ($enhancements | str join ', ')" $debug
|
|
|
|
let enhancement_prompts = {
|
|
seo: "Optimize this content for SEO by improving keywords, meta descriptions, and structure"
|
|
readability: "Improve readability by simplifying language and enhancing flow"
|
|
structure: "Reorganize the content structure for better information hierarchy"
|
|
metadata: "Generate appropriate frontmatter metadata for this content"
|
|
images: "Suggest relevant images and alt text for this content"
|
|
}
|
|
|
|
mut enhanced_content = $content
|
|
mut applied_enhancements = []
|
|
|
|
for enhancement in $enhancements {
|
|
if $enhancement in ($enhancement_prompts | columns) {
|
|
let system_prompt = $"You are an expert content enhancer. ($enhancement_prompts | get $enhancement). Maintain the original tone and message while making improvements. Return only the enhanced content."
|
|
|
|
let messages = [
|
|
{ role: "system", content: $system_prompt }
|
|
{ role: "user", content: $enhanced_content }
|
|
]
|
|
|
|
let api_response = (call_openai_api $messages $config 0.5)
|
|
|
|
if "error" not-in $api_response {
|
|
$enhanced_content = $api_response.content
|
|
$applied_enhancements = ($applied_enhancements | append $enhancement)
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
content: [
|
|
{
|
|
type: "text"
|
|
text: $"Enhanced content with: ($applied_enhancements | str join ', ')\n\n($enhanced_content)"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
# Generate content variations for A/B testing
|
|
export def generate_variations_tool [args: record, config: record, debug: bool] {
|
|
let content = $args.content
|
|
let count = ($args.count? | default 3)
|
|
let focus = ($args.focus? | default "tone")
|
|
|
|
debug_log $"Generating ($count) variations focused on ($focus)" $debug
|
|
|
|
let focus_prompts = {
|
|
tone: "Create variations with different tones (professional, casual, friendly, authoritative)"
|
|
length: "Create variations with different lengths (short, medium, detailed)"
|
|
structure: "Create variations with different structures (list, narrative, Q&A, step-by-step)"
|
|
conversion: "Create variations optimized for different conversion goals (engagement, action, information)"
|
|
}
|
|
|
|
let system_prompt = $"You are an expert content strategist. Generate ($count) distinct variations of the provided content. ($focus_prompts | get $focus). Each variation should be unique while maintaining the core message. Number each variation clearly."
|
|
|
|
let messages = [
|
|
{ role: "system", content: $system_prompt }
|
|
{ role: "user", content: $content }
|
|
]
|
|
|
|
let api_response = (call_openai_api $messages $config 0.8)
|
|
|
|
if "error" in $api_response {
|
|
return $api_response
|
|
}
|
|
|
|
{
|
|
content: [
|
|
{
|
|
type: "text"
|
|
text: $"Generated ($count) variations focused on ($focus):\n\n($api_response.content)"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
# Generate multi-format content (web, email, mobile)
|
|
export def generate_multi_format [content: string, formats: list, config: record] {
|
|
let format_prompts = {
|
|
web: "Optimize for web viewing with proper HTML structure, headings, and responsive design considerations"
|
|
email: "Optimize for email with inline styles, table layouts, and email client compatibility"
|
|
mobile: "Optimize for mobile with shorter paragraphs, scannable content, and touch-friendly elements"
|
|
}
|
|
|
|
mut results = {}
|
|
|
|
for format in $formats {
|
|
if $format in ($format_prompts | columns) {
|
|
let system_prompt = $"You are a content formatter specialist. ($format_prompts | get $format). Return only the formatted content."
|
|
|
|
let messages = [
|
|
{ role: "system", content: $system_prompt }
|
|
{ role: "user", content: $content }
|
|
]
|
|
|
|
let api_response = (call_openai_api $messages $config 0.3)
|
|
|
|
if "error" not-in $api_response {
|
|
$results = ($results | insert $format $api_response.content)
|
|
} else {
|
|
$results = ($results | insert $format $content)
|
|
}
|
|
}
|
|
}
|
|
|
|
$results
|
|
}
|
|
|
|
# Analyze KCL schema to understand content requirements
|
|
def analyze_kcl_schema [schema: any] {
|
|
let schema_str = if ($schema | describe) == "record" {
|
|
$schema | to json
|
|
} else {
|
|
$schema | to text
|
|
}
|
|
|
|
# Extract key information about the schema
|
|
let structure_info = (extract_structure_info $schema)
|
|
let required_fields = (extract_required_fields $schema)
|
|
let field_types = (extract_field_types $schema)
|
|
let constraints = (extract_constraints $schema)
|
|
|
|
$"Schema Analysis:
|
|
- Content structure: ($structure_info)
|
|
- Required fields: ($required_fields)
|
|
- Field types: ($field_types)
|
|
- Constraints: ($constraints)"
|
|
}
|
|
|
|
# Build content generation prompt based on schema analysis
|
|
def build_content_generation_prompt [schema_analysis: string, format: string] {
|
|
let format_instructions = {
|
|
markdown: "Generate content in Markdown format with proper headings, formatting, and structure"
|
|
html: "Generate content in semantic HTML with appropriate tags and structure"
|
|
json: "Generate content as structured JSON data matching the schema"
|
|
}
|
|
|
|
$"You are an expert content generator for AuroraFrame, a type-safe static site generator.
|
|
|
|
($schema_analysis)
|
|
|
|
Instructions:
|
|
- Generate high-quality, engaging content that matches the schema requirements
|
|
- ($format_instructions | get $format)
|
|
- Ensure all required fields are properly filled
|
|
- Use appropriate tone and style for the content type
|
|
- Include relevant metadata and frontmatter where applicable
|
|
- Make content SEO-friendly and accessible
|
|
- Maintain consistency with AuroraFrame best practices
|
|
|
|
Generate content that is informative, well-structured, and follows modern web content standards."
|
|
}
|
|
|
|
# Process generated content based on format
|
|
def process_generated_content [content: string, schema: any, format: string] {
|
|
match $format {
|
|
"markdown" => (process_markdown_content $content)
|
|
"html" => (process_html_content $content)
|
|
"json" => (process_json_content $content $schema)
|
|
_ => $content
|
|
}
|
|
}
|
|
|
|
# Process Markdown content
|
|
def process_markdown_content [content: string] {
|
|
let parsed = (extract_frontmatter $content)
|
|
|
|
if ($parsed.frontmatter | is-empty) {
|
|
# Add frontmatter if not present
|
|
let title = (extract_title_from_content $content)
|
|
let frontmatter = (generate_frontmatter $title)
|
|
$"---\n($frontmatter)\n---\n\n($content)"
|
|
} else {
|
|
$content
|
|
}
|
|
}
|
|
|
|
# Process HTML content
|
|
def process_html_content [content: string] {
|
|
# Ensure semantic HTML structure
|
|
if not (($content | str contains "<article>") or ($content | str contains "<section>")) {
|
|
$"<article>\n($content)\n</article>"
|
|
} else {
|
|
$content
|
|
}
|
|
}
|
|
|
|
# Process JSON content
|
|
def process_json_content [content: string, schema: any] {
|
|
try {
|
|
# Validate JSON structure
|
|
$content | from json | to json
|
|
} catch {
|
|
# If invalid JSON, wrap in basic structure
|
|
{ content: $content } | to json
|
|
}
|
|
}
|
|
|
|
# Extract title from content
|
|
def extract_title_from_content [content: string] {
|
|
let lines = ($content | lines)
|
|
|
|
for line in $lines {
|
|
let trimmed = ($line | str trim)
|
|
if ($trimmed | str starts-with "# ") {
|
|
return ($trimmed | str substring 2.. | str trim)
|
|
}
|
|
}
|
|
|
|
"Generated Content"
|
|
}
|
|
|
|
# Schema analysis helpers
|
|
def extract_structure_info [schema: any] {
|
|
if ($schema | describe) == "record" {
|
|
let keys = ($schema | columns)
|
|
if ($keys | length) > 0 {
|
|
$keys | str join ", "
|
|
} else {
|
|
"No specific structure defined"
|
|
}
|
|
} else {
|
|
"Complex schema structure"
|
|
}
|
|
}
|
|
|
|
def extract_required_fields [schema: any] {
|
|
# This would need more sophisticated KCL parsing
|
|
# For now, return a placeholder
|
|
"Schema-defined required fields"
|
|
}
|
|
|
|
def extract_field_types [schema: any] {
|
|
# This would need more sophisticated KCL parsing
|
|
# For now, return a placeholder
|
|
"Mixed field types (str, int, bool, arrays, objects)"
|
|
}
|
|
|
|
def extract_constraints [schema: any] {
|
|
# This would need more sophisticated KCL parsing
|
|
# For now, return a placeholder
|
|
"Schema-defined constraints and validations"
|
|
}
|
|
|
|
# Content quality analysis
|
|
export def analyze_content_quality [content: string] {
|
|
let word_count = ($content | split row ' ' | length)
|
|
let reading_time = ($word_count / 200 | math ceil)
|
|
let sentences = ($content | split row '.' | length)
|
|
let avg_sentence_length = ($word_count / $sentences | math round)
|
|
|
|
{
|
|
word_count: $word_count
|
|
reading_time: $reading_time
|
|
sentence_count: $sentences
|
|
avg_sentence_length: $avg_sentence_length
|
|
readability_score: (calculate_readability_score $avg_sentence_length)
|
|
}
|
|
}
|
|
|
|
# Simple readability score calculation
|
|
def calculate_readability_score [avg_sentence_length: int] {
|
|
# Simple algorithm: shorter sentences = better readability
|
|
if $avg_sentence_length <= 15 {
|
|
"Excellent"
|
|
} else if $avg_sentence_length <= 20 {
|
|
"Good"
|
|
} else if $avg_sentence_length <= 25 {
|
|
"Fair"
|
|
} else {
|
|
"Poor"
|
|
}
|
|
}
|
|
|
|
# Generate content suggestions
|
|
export def generate_content_suggestions [content: string, target_audience: string] {
|
|
let quality = (analyze_content_quality $content)
|
|
mut suggestions = []
|
|
|
|
if $quality.word_count < 300 {
|
|
$suggestions = ($suggestions | append "Consider expanding content for better SEO (aim for 300+ words)")
|
|
}
|
|
|
|
if $quality.avg_sentence_length > 25 {
|
|
$suggestions = ($suggestions | append "Break down long sentences for better readability")
|
|
}
|
|
|
|
if not ($content | str contains "##") {
|
|
$suggestions = ($suggestions | append "Add subheadings to improve content structure")
|
|
}
|
|
|
|
$suggestions
|
|
}
|
|
|
|
# Debug helper
|
|
def debug_log [message: string, debug: bool] {
|
|
if $debug {
|
|
print $"🐛 CONTENT-GEN: ($message)"
|
|
}
|
|
} |