joelmnz

Article Manager

Community joelmnz
Updated

Manage a Markdown knowledge library with AI MCP API

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

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

  1. Navigate to http://localhost:5000
  2. Enter your AUTH_TOKEN
  3. 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.

MCP Server ยท Populars

MCP Server ยท New