Article Manager
A complete full-stack TypeScript monolithic article management system designed for AI agents to save and manage research content. This self-hosted single-user POC system handles hundreds of markdown articles with multiple interfaces: Web UI, REST API, and MCP server.
Features
- ๐ Markdown-based articles with frontmatter support
- ๐ Search functionality with partial title matching
- ๐จ Dark/Light theme toggle
- ๐ฑ Mobile-first responsive design
- ๐ Bearer token authentication for all interfaces
- ๐ REST API for programmatic access
- ๐ค MCP server integration for AI agent access
- ๐ณ Docker support with multi-stage builds and non-root user
- โก Bun runtime for fast TypeScript execution
- ๐ Request logging for monitoring and debugging
Architecture
Monolithic Structure
/src
/backend
/routes - REST API endpoints
/mcp - MCP server tools
/services - Shared business logic (articles CRUD)
/middleware - Auth, error handling
server.ts - Main server (API + MCP + static serving)
/frontend
/components - React components
/pages - Page components
/styles - CSS files
App.tsx
Technology Stack
- Runtime: Bun (fast TypeScript execution)
- Backend: TypeScript, @modelcontextprotocol/sdk
- Frontend: React, react-markdown
- Storage: File-based markdown with frontmatter
- Deployment: Docker with oven/bun base image
Quick Start
Prerequisites
- Bun installed (v1.0+)
- Docker and Docker Compose (for containerized deployment)
Development Setup
1. Clone and install dependencies
cd article_manager
bun install
2. Configure environment
cp .env.example .env
# Edit .env and set your AUTH_TOKEN
3. Run development servers
Terminal 1 (Backend):
bun run dev:backend
Terminal 2 (Frontend):
bun run dev:frontend
4. Access the application
- Web UI: http://localhost:5000
- API: http://localhost:5000/api/*
- MCP: http://localhost:5000/mcp
To test the MCP Server you can use the MCP inspector
npx @modelcontextprotocol/inspector
Production Build
# Build frontend
bun run build
# Start production server
bun run start
Docker Deployment
Using Docker Compose (Recommended)
1. Configure environment
cp .env.example .env
# Edit .env and set AUTH_TOKEN
2. Start the container
docker-compose up -d
3. View logs
docker-compose logs -f
4. Stop the container
docker-compose down
Using Docker directly
# Build image
docker build -t article-manager .
# Run container
docker run -d \
-p 5000:5000 \
-e AUTH_TOKEN=your-secret-token \
-v $(pwd)/data:/data \
--name article-manager \
article-manager
GitHub Container Registry
To push to GitHub Container Registry:
# Build and tag
docker build -t ghcr.io/YOUR_USERNAME/article-manager:latest .
# Login to GHCR
echo $GITHUB_TOKEN | docker login ghcr.io -u YOUR_USERNAME --password-stdin
# Push
docker push ghcr.io/YOUR_USERNAME/article-manager:latest
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
AUTH_TOKEN |
Yes | - | Authentication token for all interfaces |
DATA_DIR |
No | /data |
Directory where markdown articles are stored |
PORT |
No | 5000 |
Server port |
NODE_ENV |
No | development |
Environment mode |
REST API Documentation
All API endpoints require Bearer token authentication via the Authorization header:
Authorization: Bearer YOUR_AUTH_TOKEN
Endpoints
Health Check
GET /health
Returns server health status (no auth required).
Response:
{
"status": "ok"
}
List Articles
GET /api/articles
Returns all articles with metadata, sorted by creation date (newest first).
Response:
[
{
"filename": "my-article.md",
"title": "My Article",
"created": "2025-01-15T10:30:00Z"
}
]
Search Articles
GET /api/articles?q=search+term
Search articles by title (partial match, case-insensitive).
Query Parameters:
q- Search query string
Response:
[
{
"filename": "matching-article.md",
"title": "Matching Article",
"created": "2025-01-15T10:30:00Z"
}
]
Read Article
GET /api/articles/:filename
Read a single article by filename.
Response:
{
"filename": "my-article.md",
"title": "My Article",
"content": "Article content in markdown...",
"created": "2025-01-15T10:30:00Z"
}
Error Response (404):
{
"error": "Article not found"
}
Create Article
POST /api/articles
Content-Type: application/json
{
"title": "My New Article",
"content": "Article content in markdown..."
}
Creates a new article. Filename is auto-generated from title (e.g., "My New Article" โ "my-new-article.md").
Response (201):
{
"filename": "my-new-article.md",
"title": "My New Article",
"content": "Article content in markdown...",
"created": "2025-01-15T10:30:00Z"
}
Error Response (400):
{
"error": "Title and content are required"
}
Update Article
PUT /api/articles/:filename
Content-Type: application/json
{
"title": "Updated Title",
"content": "Updated content..."
}
Updates an existing article. Preserves original creation date.
Response:
{
"filename": "my-article.md",
"title": "Updated Title",
"content": "Updated content...",
"created": "2025-01-15T10:30:00Z"
}
Delete Article
DELETE /api/articles/:filename
Deletes an article.
Response:
{
"success": true
}
Authentication Errors
All authenticated endpoints return 401 for invalid/missing tokens:
{
"error": "Unauthorized"
}
MCP Server Documentation
The MCP (Model Context Protocol) server provides AI agents with tools to manage articles.
Endpoint
POST /mcp
Authorization: Bearer YOUR_AUTH_TOKEN
Content-Type: application/json
Available Tools
listArticles
List all articles with metadata.
Input Schema:
{
"method": "tools/call",
"params": {
"name": "listArticles",
"arguments": {}
}
}
Response:
{
"content": [
{
"type": "text",
"text": "[{\"filename\":\"article.md\",\"title\":\"Article\",\"created\":\"2025-01-15T10:30:00Z\"}]"
}
]
}
searchArticles
Search articles by title.
Input Schema:
{
"method": "tools/call",
"params": {
"name": "searchArticles",
"arguments": {
"query": "search term"
}
}
}
readArticle
Read a single article.
Input Schema:
{
"method": "tools/call",
"params": {
"name": "readArticle",
"arguments": {
"filename": "my-article.md"
}
}
}
createArticle
Create a new article.
Input Schema:
{
"method": "tools/call",
"params": {
"name": "createArticle",
"arguments": {
"title": "New Article",
"content": "Article content..."
}
}
}
updateArticle
Update an existing article.
Input Schema:
{
"method": "tools/call",
"params": {
"name": "updateArticle",
"arguments": {
"filename": "my-article.md",
"title": "Updated Title",
"content": "Updated content..."
}
}
}
deleteArticle
Delete an article.
Input Schema:
{
"method": "tools/call",
"params": {
"name": "deleteArticle",
"arguments": {
"filename": "my-article.md"
}
}
}
List Available Tools
{
"method": "tools/list"
}
Article Format
Articles are stored as markdown files with YAML frontmatter:
---
title: Article Title
created: 2025-01-15T10:30:00Z
---
# Article Title
Article content goes here...
## Section
More content...
Filename Generation
- User provides title when creating articles
- Filename is auto-generated: "My Article Name" โ "my-article-name.md"
- Title is extracted from first
#heading in markdown for display - Filename may differ from displayed title
Frontmatter Fields
title: Article title (string)created: ISO 8601 timestamp (string)
If frontmatter is missing, the system falls back to file system timestamps.
Web UI Usage
Login
- Navigate to http://localhost:5000
- Enter your AUTH_TOKEN
- Click "Login"
Home Page
- View last 10 articles (newest first)
- Search articles by title
- Click "New Article" to create
- Click any article to view
Article View
- Read rendered markdown
- See creation date
- Click "Edit" to modify
- Click "Delete" to remove
Article Edit
- Edit title and content
- Live preview pane (desktop)
- Save or cancel changes
Theme Toggle
- Click sun/moon icon in header
- Switches between dark and light themes
- Preference saved in browser
Development
Project Scripts
# Install dependencies
bun install
# Development (backend)
bun run dev:backend
# Development (frontend)
bun run dev:frontend
# Build frontend
bun run build
# Production server
bun run start
# Type checking
bun run typecheck
File Structure
article_manager/
โโโ src/
โ โโโ backend/
โ โ โโโ middleware/
โ โ โ โโโ auth.ts # Authentication middleware
โ โ โโโ mcp/
โ โ โ โโโ server.ts # MCP server implementation
โ โ โโโ routes/
โ โ โ โโโ api.ts # REST API routes
โ โ โโโ services/
โ โ โ โโโ articles.ts # Article CRUD logic
โ โ โโโ server.ts # Main server
โ โโโ frontend/
โ โโโ components/
โ โ โโโ ArticleList.tsx # Article list component
โ โ โโโ Header.tsx # Header with theme toggle
โ โ โโโ Login.tsx # Login page
โ โโโ pages/
โ โ โโโ ArticleEdit.tsx # Edit/create page
โ โ โโโ ArticleView.tsx # Article view page
โ โ โโโ Home.tsx # Home page
โ โโโ styles/
โ โ โโโ main.css # All styles
โ โโโ App.tsx # Main app component
โโโ public/ # Built frontend (generated)
โโโ data/ # Article storage (gitignored)
โโโ Dockerfile # Multi-stage Docker build
โโโ docker-compose.yml # Docker Compose config
โโโ package.json # Dependencies and scripts
โโโ tsconfig.json # TypeScript config
โโโ .env.example # Environment template
โโโ .gitignore # Git ignore rules
โโโ README.md # This file
Troubleshooting
Port already in use
# Find process using port 5000
lsof -i :5000
# Kill the process
kill -9 <PID>
Permission denied on data directory
# Fix permissions
chmod -R 755 ./data
Docker build fails
# Clean build cache
docker builder prune -a
# Rebuild without cache
docker-compose build --no-cache
Frontend not loading
# Rebuild frontend
bun run build
# Check if public/index.html exists
ls -la public/
Limitations
- Single user only (no multi-tenancy)
- Optimized for hundreds of articles (not thousands)
- Simple partial text search (no full-text indexing)
- Manual article creation (paste markdown)
- No image uploads or media management
- No tags, categories, or advanced metadata
- File-based storage only (no database)
- Bearer token auth only (no OAuth, sessions)
- Single Docker container (not microservices)
Security Considerations
- Store AUTH_TOKEN securely (use environment variables)
- Use HTTPS in production (reverse proxy recommended)
- Regularly backup the data directory
- Keep dependencies updated
- Docker container runs as non-root user (UID 1001) for security
- Request logging enabled for monitoring and audit trails
License
MIT License - feel free to use and modify as needed.
Contributing
This is a POC project. For production use, consider:
- Adding database support for better scalability
- Implementing full-text search (e.g., Elasticsearch)
- Adding user management and roles
- Implementing rate limiting
- Adding comprehensive test coverage
- Setting up CI/CD pipelines
Support
For issues and questions, please open an issue on the GitHub repository.