We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Liam-Deacon/zettelkasten-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
"""Utility functions for the Zettelkasten MCP server."""
import logging
import sys
from datetime import datetime
from typing import Optional
def setup_logging(level: str = "INFO", log_file: Optional[str] = None):
"""Set up logging configuration.
Args:
level: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
log_file: Optional path to a log file
"""
# Convert string level to logging level
numeric_level = getattr(logging, level.upper(), None)
if not isinstance(numeric_level, int):
numeric_level = logging.INFO
# Base configuration
log_config = {
"level": numeric_level,
"format": "%(asctime)s [%(levelname)s] %(name)s: %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S",
}
# Add file handler if log file is specified
if log_file:
log_config["filename"] = log_file
log_config["filemode"] = "a"
else:
# Otherwise, log to stderr
log_config["stream"] = sys.stderr
# Apply configuration
logging.basicConfig(**log_config)
def generate_timestamp_id() -> str:
"""Generate a timestamp-based ID in ISO 8601 Zettelkasten format with nanosecond precision.
Returns:
A string in format "YYYYMMDDTHHMMSSsssssssss" where:
- YYYYMMDD is the date
- T is the ISO 8601 date/time separator
- HHMMSS is the time (hours, minutes, seconds)
- sssssssss is the 9-digit nanosecond component
"""
# Get nanoseconds since epoch
ns_timestamp = time.time_ns()
# Convert to seconds and nanosecond fraction
seconds = ns_timestamp // 1_000_000_000
nanoseconds = ns_timestamp % 1_000_000_000
# Convert seconds to datetime
timestamp = datetime.fromtimestamp(seconds)
# Format as ISO 8601 basic format (YYYYMMDDThhmmss) with nanoseconds
date_time = timestamp.strftime("%Y%m%dT%H%M%S")
# Return the ISO 8601 timestamp with nanosecond precision
return f"{date_time}{nanoseconds:09d}"
def parse_tags(tags_str: str) -> list[str]:
"""Parse a comma-separated list of tags into a list of tag strings.
Args:
tags_str: Comma-separated string of tags
Returns:
List of tag strings
"""
if not tags_str:
return []
return [tag.strip() for tag in tags_str.split(",") if tag.strip()]
def format_note_for_display(
title: str,
id: str,
content: str,
tags: list[str],
created_at: datetime,
updated_at: datetime,
links: Optional[list] = None,
) -> str:
"""Format a note for display in the console.
Args:
title: Note title
id: Note ID
content: Note content
tags: List of tags
created_at: Creation timestamp
updated_at: Update timestamp
links: Optional list of links
Returns:
Formatted string representation of the note
"""
result = f"# {title}\n"
result += f"ID: {id}\n"
result += f"Created: {created_at.isoformat()}\n"
result += f"Updated: {updated_at.isoformat()}\n"
if tags:
result += f"Tags: {', '.join(tags)}\n"
result += f"\n{content}\n"
if links:
result += "\n## Links\n"
for link in links:
if hasattr(link, "description") and link.description:
result += (
f"- {link.link_type.value}: {link.target_id} - {link.description}\n"
)
else:
result += f"- {link.link_type.value}: {link.target_id}\n"
return result