{"id":50717615,"url":"https://github.com/ptbsare/pages-mcp-server","last_synced_at":"2026-06-09T20:01:45.576Z","repository":{"id":361433231,"uuid":"1254427062","full_name":"ptbsare/pages-mcp-server","owner":"ptbsare","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-08T13:09:09.000Z","size":831,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-08T13:15:28.661Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ptbsare.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"custom":["https://ptbsare.org/about/"]}},"created_at":"2026-05-30T14:52:22.000Z","updated_at":"2026-06-08T13:09:20.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ptbsare/pages-mcp-server","commit_stats":null,"previous_names":["ptbsare/pages-mcp-server"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ptbsare/pages-mcp-server","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ptbsare%2Fpages-mcp-server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ptbsare%2Fpages-mcp-server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ptbsare%2Fpages-mcp-server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ptbsare%2Fpages-mcp-server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ptbsare","download_url":"https://codeload.github.com/ptbsare/pages-mcp-server/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ptbsare%2Fpages-mcp-server/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34123172,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-09T20:01:44.769Z","updated_at":"2026-06-09T20:01:45.567Z","avatar_url":"https://github.com/ptbsare.png","language":"TypeScript","funding_links":["https://ptbsare.org/about/"],"categories":[],"sub_categories":[],"readme":"# Pages MCP Server\n\nSelf-hosted MCP server for deploying static websites and sharing files/folders. Deploy HTML pages, entire sites, or individual files via MCP tools or REST API — get shareable public URLs instantly.\n\n## Table of Contents\n\n- [Features](#features)\n- [Quick Start](#quick-start)\n- [Architecture](#architecture)\n- [MCP Tools](#mcp-tools)\n- [API Reference](#api-reference)\n- [Client Modes](#client-modes)\n- [Security](#security)\n- [Configuration](#configuration)\n- [Docker](#docker)\n- [Nginx Reverse Proxy](#nginx-reverse-proxy)\n- [License](#license)\n\n## Features\n\n- **Deploy HTML** — send raw HTML string or local `.html` file\n- **Deploy Folder** — zip a local site folder (CSS, JS, images…), deploy all files\n- **Share Files/Folders** — file listing with preview (text with syntax highlighting, images with HEIC conversion)\n- **Public Pages** — all pages at `/s/:shareId`, no auth required\n- **File Shares** — files/folders at `/f/:shareId` with directory navigation and zip download\n- **MCP Protocol** — 5 tools: `deploy_html`, `deploy_folder`, `deploy_file`, `list_pages`, `delete_page`\n- **Admin Dashboard** — web UI for managing pages, API tokens, and 2FA\n- **Lock/Unlock** — prevent auto-cleanup of important shares\n- **Auto-cleanup** — delete expired shares (configurable via `SHARE_EXPIRE_DAYS`)\n- **2FA (TOTP)** — optional two-factor authentication for admin access\n- **Docker** — multi-arch images, multi-stage build, non-root user\n\n## Quick Start\n\n### Via npx (recommended)\n\n```bash\n# Start server\nnpx github:ptbsare/pages-mcp-server server \\\n  --port 3000 \\\n  --domain https://mysite.com \\\n  --admin-user admin \\\n  --admin-pass secret\n```\n\nAfter starting, access the **admin dashboard** at `https://mysite.com:3000/`. Create API tokens from there.\n\n**Stdio MCP client** (for AI assistants):\n```bash\nnpx github:ptbsare/pages-mcp-server client \\\n  --url https://mysite.com:3000 \\\n  --auth-token your-api-token\n```\n\n**Interactive CLI**:\n```bash\nnpx github:ptbsare/pages-mcp-server client \\\n  --url https://mysite.com:3000 \\\n  --interactive\n```\n\n### Via Docker\n\n```bash\ndocker run -d \\\n  --name pages-mcp \\\n  -p 3000:3000 \\\n  -e DOMAIN=https://mysite.com \\\n  -e ADMIN_USERNAME=admin \\\n  -e ADMIN_PASSWORD=secret \\\n  -e OUT_PORT=3000 \\\n  -v pages-data:/data \\\n  ghcr.io/ptbsare/pages-mcp-server/pages-mcp-server:latest\n```\n\n### Via Source\n\n```bash\ngit clone https://github.com/ptbsare/pages-mcp-server\ncd pages-mcp-server\nnpm install\nnpm run build\nnode dist/server/index.js\n```\n\n## Architecture\n\n```\n┌──────────────────────────────────────────────────────────────┐\n│                        Server (Express)                       │\n│                                                              │\n│  /              → Admin Dashboard (Basic auth + 2FA)         │\n│  /mcp           → MCP JSON-RPC (Bearer token auth)           │\n│  /api/deploy    → Deploy REST API (Bearer token auth)        │\n│  /api/admin     → Admin REST API (Basic auth + 2FA)          │\n│  /s/:shareId    → Public static page (no auth)               │\n│  /f/:shareId    → File share page (no auth)                  │\n│  /f/:shareId/raw/** → Direct file download (no auth)         │\n│  /health        → Health check                               │\n│                                                              │\n│  sql.js (SQLite) + File Storage                              │\n└──────────────────────────────────────────────────────────────┘\n\n┌──────────────────────────────────────────────────────────────┐\n│                        Client                                 │\n│                                                              │\n│  Mode 1: HTTP MCP Client (direct /mcp JSON-RPC calls)        │\n│  Mode 2: Stdio MCP Server (local proxy to remote, for AI)    │\n│  Mode 3: Interactive CLI (manual commands)                   │\n└──────────────────────────────────────────────────────────────┘\n```\n\n## MCP Tools\n\n| Tool | Description | Required Args | Optional Args |\n|------|-------------|---------------|---------------|\n| `deploy_html` | Deploy HTML string or local file as a public page | `value` (string) or `path` (local .html file) | `name`, `description` |\n| `deploy_folder` | Deploy a local folder as a static website (must contain index.html) | `path` (string) | `name`, `description` |\n| `deploy_file` | Share a local file or folder | `path` (string) | `name`, `description` |\n| `list_pages` | List all deployed pages and shares | — | `limit`, `offset` |\n| `delete_page` | Delete a page or share by database ID | `id` (string) | — |\n\n**Client-side path validation** (applies to `deploy_html`, `deploy_folder`, `deploy_file`):\n\n| Environment Variable | Default | Description |\n|----------------------|---------|-------------|\n| `DEPLOY_ALLOW_ALL` | — | Set to `\"1\"` to disable all path restrictions |\n| `DEPLOY_ALLOW_PATHS` | — | Comma-separated allowed path prefixes (e.g. `/tmp,/home/user`) |\n| `DEPLOY_BLOCK_PATHS` | `/etc,/var,/usr,...` | Comma-separated blocked path prefixes |\n| `DEPLOY_BLOCK_ROOT_DIRS` | `/root/.ssh,/root/.gnupg,/root/.aws,...` | Comma-sensitive directories under `/root` to block |\n\n## API Reference\n\n### Authentication\n\n| Layer | Method | Used For |\n|-------|--------|----------|\n| **Admin** | HTTP Basic + optional TOTP 2FA | Dashboard, token management, page CRUD |\n| **API/MCP** | Bearer token (`Authorization: Bearer \u003ctoken\u003e`) | Deploy, list, delete |\n\n### URL Types\n\n| Pattern | Type | Description |\n|---------|------|-------------|\n| `/s/:shareId` | Static Page | HTML/CSS/JS website |\n| `/s/:shareId/*` | Page Resource | Sub-resources (images, CSS, JS) |\n| `/f/:shareId` | File Share | File listing with preview |\n| `/f/:shareId/raw/**` | File Download | Direct file access |\n| `/f/:shareId/list/**` | Directory API | JSON directory listing |\n\n### REST Endpoints\n\n**Deploy HTML:**\n```bash\ncurl -X POST https://mysite.com/api/deploy/html \\\n  -H \"Authorization: Bearer \u003ctoken\u003e\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"value\": \"\u003chtml\u003e\u003cbody\u003e\u003ch1\u003eHello\u003c/h1\u003e\u003c/body\u003e\u003c/html\u003e\", \"name\": \"My Page\"}'\n```\n\n**Deploy Folder (zip):**\n```bash\nbase64 -w0 site.zip | xargs -I{} curl -X POST https://mysite.com/api/deploy/folder \\\n  -H \"Authorization: Bearer \u003ctoken\u003e\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"zipBase64\": \"{}\", \"name\": \"My Site\"}'\n```\n\n**Deploy File:**\n```bash\n# Single file\ncurl -X POST \"https://mysite.com/api/deploy/file?filename=report.pdf\" \\\n  -H \"Authorization: Bearer \u003ctoken\u003e\" \\\n  -H \"Content-Type: application/octet-stream\" \\\n  --data-binary @report.pdf\n\n# Folder (zip)\ncurl -X POST \"https://mysite.com/api/deploy/file?filename=my-site.zip\u0026name=My Site\" \\\n  -H \"Authorization: Bearer \u003ctoken\u003e\" \\\n  -H \"Content-Type: application/octet-stream\" \\\n  --data-binary @my-site.zip\n```\n\n**Token Management:**\n```bash\ncurl -u admin:password https://mysite.com/api/admin/tokens                    # List\ncurl -X POST -u admin:password https://mysite.com/api/admin/tokens -d '{\"name\": \"CI\"}'  # Create\ncurl -X DELETE -u admin:password https://mysite.com/api/admin/tokens/\u003cid\u003e     # Delete\n```\n\n**Page Management:**\n```bash\ncurl -u admin:password https://mysite.com/api/admin/pages              # List\ncurl -X POST -u admin:password https://mysite.com/api/admin/pages/\u003cid\u003e/lock -d '{\"locked\": true}'\ncurl -X PUT -u admin:password https://mysite.com/api/admin/pages/\u003cid\u003e -d '{\"name\": \"New Name\"}'\ncurl -X DELETE -u admin:password https://mysite.com/api/admin/pages/\u003cid\u003e\n```\n\n## Client Modes\n\n### Mode 1: HTTP MCP Client (direct)\n\nFor programmatic use with `fetch`:\n\n```json\n{\n  \"mcpServers\": {\n    \"pages-mcp\": {\n      \"url\": \"https://mysite.com:3000/mcp\",\n      \"headers\": { \"Authorization\": \"Bearer your-api-token\" }\n    }\n  }\n}\n```\n\n### Mode 2: Stdio MCP Server (for AI assistants)\n\nFor Cursor, Claude Desktop, etc. — runs locally, proxies to remote:\n\n```json\n{\n  \"mcpServers\": {\n    \"pages-mcp\": {\n      \"command\": \"npx\",\n      \"args\": [\n        \"github:ptbsare/pages-mcp-server\",\n        \"client\",\n        \"--url\", \"https://mysite.com:3000\",\n        \"--auth-token\", \"your-api-token\"\n      ]\n    }\n  }\n}\n```\n\n### Mode 3: Interactive CLI\n\n```bash\nnpx github:ptbsare/pages-mcp-server client --url https://mysite.com:3000 --interactive\n```\n\n## Security\n\n### Rate Limiting (App Layer)\n\n| Limiter | Threshold | Scope |\n|---------|-----------|-------|\n| `deployLimiter` | 20 req / 15 min / IP | `/api/deploy/*` |\n| `adminLimiter` | 100 req / 15 min / IP | `/api/admin/*` |\n| `otpLimiter` | 10 req / 15 min / IP | `/api/admin/otp/verify` |\n\n\u003e **Note:** `/s/` and `/f/` public endpoints have **no rate limiting** by design.\n\n### CORS\n\nOnly same-origin requests allowed. No cross-origin API access.\n\n### Path Validation (Client-Side)\n\nDeploy paths validated before upload:\n- **Extension**: only `.html` / `.htm` for `deploy_html`\n- **Content**: must contain `\u003chtml\u003e` tag for `deploy_html`\n- **Path whitelist**: `DEPLOY_ALLOW_PATHS`, `DEPLOY_BLOCK_PATHS`, `DEPLOY_BLOCK_ROOT_DIRS`\n- **Bypass**: `DEPLOY_ALLOW_ALL=1` disables all checks\n\n### Admin 2FA\n\nOptional TOTP-based two-factor authentication (Google Authenticator, Authy, etc.).\n\n## Environment Variables\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `PORT` | 3000 | Internal server port |\n| `OUT_PORT` | Same as `PORT` | External port for public URLs. Set when using Docker port mapping (e.g. `38300`). Standard ports (80/443) auto-omitted from URLs. |\n| `DOMAIN` | `http://localhost:3000` | Base domain without port |\n| `ADMIN_USERNAME` | `admin` | Admin dashboard username |\n| `ADMIN_PASSWORD` | `admin123` | Admin dashboard password |\n| `AUTH_TOKEN` | — | Initial API token(s), comma-separated. Seeded on first startup only. |\n| `DB_PATH` | `~/.pages-mcp/pages.db` | SQLite database path |\n| `STORAGE_PATH` | `~/.pages-mcp/storage` | File storage path |\n| `SHARE_EXPIRE_DAYS` | `0` (disabled) | Auto-delete shares older than N days |\n| `STRICT_SHARE_CSP` | (disabled) | Set to `1` to enforce `script-src 'self'` on share pages. Default allows external scripts (`* 'unsafe-inline'`) |\n\n\u003e **Note:** `AUTH_TOKEN` is only for initial seeding. Manage tokens via the admin dashboard after setup.\n\n## Docker\n\n### Images\n\n| Tag | When |\n|-----|------|\n| `beta` | Every push to `main` |\n| `latest` | On version tag push (e.g. `v1.2.3`) |\n| `v{version}` | On tag push |\n| `v{major}.{minor}` | On tag push |\n\n### Port Mapping\n\n| Scenario | PORT | OUT_PORT | Public URL |\n|----------|------|----------|------------|\n| Direct (no Docker) | 3000 | 3000 | `http://domain:3000` |\n| Docker (same port) | 3000 | 3000 | `http://domain:3000` |\n| Docker (mapped) | 3000 | 38300 | `http://domain:38300` |\n| Standard HTTPS | 443 | 443 | `https://domain` (port omitted) |\n\n### Volumes\n\nAll persistent data lives under `/data` inside the container:\n\n| Path | Description | Required |\n|------|-------------|----------|\n| `/data/db/pages.db` | SQLite database | **Yes** |\n| `/data/storage/` | Deployed files | **Yes** |\n\n### Docker Compose\n\n```yaml\nversion: \"3.8\"\nservices:\n  pages-mcp:\n    image: ghcr.io/ptbsare/pages-mcp-server/pages-mcp-server:latest\n    ports: [\"3000:3000\"]\n    environment:\n      - PORT=3000\n      - DOMAIN=http://localhost:3000\n      - ADMIN_USERNAME=admin\n      - ADMIN_PASSWORD=changeme\n      - DB_PATH=/data/db/pages.db\n      - STORAGE_PATH=/data/storage\n    volumes: [pages-data:/data]\n    restart: unless-stopped\n\nvolumes:\n  pages-data:\n    driver: local\n```\n\n## Nginx Reverse Proxy\n\nExample config included in repo (`nginx.conf`). Key locations:\n\n```nginx\nupstream pages_mcp {\n    server 127.0.0.1:38300;\n    keepalive 32;\n}\n\nserver {\n    listen 443 ssl http2;\n    server_name your.domain.com;\n\n    # Strip server CSP, use nginx's\n    proxy_hide_header Content-Security-Policy;\n\n    location / { proxy_pass http://pages_mcp; }\n    location /api/ { proxy_pass http://pages_mcp; }\n    location /api/deploy { proxy_pass http://pages_mcp; client_max_body_size 1000m; }\n    location /mcp { proxy_pass http://pages_mcp; }\n    location /s/ { proxy_pass http://pages_mcp; }\n    location /f/ { proxy_pass http://pages_mcp; client_max_body_size 1000m; }\n    location /health { proxy_pass http://pages_mcp; access_log off; }\n}\n```\n\n**Notes:**\n- `proxy_hide_header Content-Security-Policy` — required for CSP per-page in app layer\n- `client_max_body_size 1000m` — required on `/api/deploy` and `/f/` for large uploads\n\n## License\n\n[GPL v3](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fptbsare%2Fpages-mcp-server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fptbsare%2Fpages-mcp-server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fptbsare%2Fpages-mcp-server/lists"}