tools.py•3.68 kB
"""MCP tools for Jekyll blog server."""
from typing import Any
from jekyll_mcp.indexer import PostIndexer
class JekyllTools:
"""MCP tools for interacting with Jekyll blog content."""
def __init__(self, indexer: PostIndexer):
"""Initialize tools with indexer."""
self.indexer = indexer
def search_posts(
self,
query: str = None,
category: str = None,
tags: str = None,
limit: int = 10
) -> dict[str, Any]:
"""
Search for blog posts.
Args:
query: Search term
category: Filter by category
tags: Comma-separated list of tags
limit: Maximum results to return
Returns:
Dictionary with search results
"""
tag_list = [t.strip() for t in tags.split(',')] if tags else None
results = self.indexer.search_posts(
query=query,
category=category,
tags=tag_list,
limit=limit
)
return {
'count': len(results),
'results': results
}
def get_post(self, slug: str) -> dict[str, Any]:
"""
Get a specific post by slug.
Args:
slug: The post slug
Returns:
Post data or error message
"""
post = self.indexer.get_post_by_slug(slug)
if not post:
return {'error': f'Post with slug "{slug}" not found'}
return {
'title': post['metadata'].get('title', 'Untitled'),
'slug': post['metadata'].get('slug', ''),
'date': str(post['metadata'].get('date', '')),
'categories': post['metadata'].get('categories', []),
'tags': post['metadata'].get('tags', []),
'content': post['content'],
'published': post['published'],
'file_path': post['file_path']
}
def list_categories(self) -> dict[str, Any]:
"""
List all categories with post counts.
Returns:
Dictionary of categories and their post counts
"""
return {
'categories': dict(self.indexer.categories)
}
def list_tags(self) -> dict[str, Any]:
"""
List all tags with post counts.
Returns:
Dictionary of tags and their post counts
"""
return {
'tags': dict(self.indexer.tags)
}
def compare_draft(self, draft_content: str, limit: int = 5) -> dict[str, Any]:
"""
Compare draft content against published posts to find similar content.
Args:
draft_content: The draft text to compare
limit: Maximum number of similar posts to return
Returns:
List of potentially similar posts
"""
# Simple keyword-based similarity
# Extract meaningful words from draft (> 4 chars, common words filtered)
words = draft_content.lower().split()
keywords = [w for w in words if len(w) > 4][:20] # Top 20 keywords
# Search for posts containing these keywords
similar_posts = []
for keyword in keywords:
results = self.indexer.search_posts(query=keyword, limit=limit)
similar_posts.extend(results)
# Deduplicate by slug
seen = set()
unique_posts = []
for post in similar_posts:
slug = post.get('slug')
if slug not in seen:
seen.add(slug)
unique_posts.append(post)
return {
'count': len(unique_posts),
'similar_posts': unique_posts[:limit]
}