MCP Console
by Plantik
README.md
# MCP Console
A self-hosted **context server for AI coding agents**. It stores your project
docs, coding skills and per-branch task plans as plain markdown, serves them to
Claude Code (or any MCP client) over the [Model Context Protocol](https://modelcontextprotocol.io),
and gives you a web console to browse and edit everything.
- **Projects** — documentation pages agents load on demand (`index`, `architecture`, `deploy`, …)
- **Tasks** — one markdown plan per git branch, with an `active → pending → history` lifecycle, so an agent can resume where the last session stopped
- **Skills** — team conventions agents load only when relevant (progressive disclosure keeps token use low)
- **Templates** — per-stack scaffolds used when creating a new project
- **Knowledge base** — self-contained HTML landing pages / explainers
Built with Nuxt 4, Nuxt UI, Nuxt Content and the official MCP TypeScript SDK.
Content lives on disk under `content/` — no database to migrate, easy to back up or keep in git.
## Quick start
Requires Node 22+ and pnpm.
```bash
pnpm install
cp .env.example .env # set MCP_USERNAME / MCP_PASSWORD (and MCP_TOKEN)
pnpm dev # console: http://localhost:3000 · MCP: http://localhost:3000/mcp
```
Sign in with the credentials from `.env`. The repo ships with example content
(`example-app` project, two skills, one landing) so every screen has something to show.
### Connect Claude Code
Copy `.mcp.json.example` to `.mcp.json` in the repo you want agents to work in
(or run the CLI equivalent):
```bash
claude mcp add --transport http mcp-console http://localhost:3000/mcp \
--header "Authorization: Bearer $MCP_TOKEN"
```
Then tell the agent how to use it, e.g. in that repo's `CLAUDE.md`:
```markdown
## Session start
1. `get_project_context("my-project", "index")`
2. `list_skills` — load only the skills relevant to the task
3. `get_task("my-project", "<current git branch>")` — if missing, `upsert_task` with a plan
4. As you work: `check_task_item`, `append_to_task` for lessons
5. When the branch merges: `set_task_status(..., "history")`
```
## Configuration
| Variable | Required | Purpose |
|----------|----------|---------|
| `MCP_USERNAME` / `MCP_PASSWORD` | yes | Web console login |
| `MCP_TOKEN` | strongly recommended | Bearer token for `/mcp`. **If unset, `/mcp` is unauthenticated** and exposes write/delete tools — fine on localhost, not on the internet. Clients that can't set headers may use `/mcp?token=…` |
| `DATA_DIR` | no | Directory for the local SQLite session store, `sessions.db` (default `./data`). Created on first run; never committed or baked into the image |
## Content layout
```
content/
├── projects/<project>/
│ ├── index.md, architecture.md, deploy.md, … # doc pages
│ └── tasks/{current,pending,history}/<branch>.md
├── skills/<skill>/
│ ├── skill.md # frontmatter: name + description ("Use when …")
│ └── references/*.md # optional deep-dive material
├── templates/{projects,skills}/<stack>.md # {{project}} {{stack}} {{description}}
└── landings/<slug>.html # served at /landings/<slug>
```
To start fresh, delete `content/projects/example-app`, `content/skills/example-skill`
and `content/landings/welcome.html`, then create your own project from the
console (**New project**) or with the `init_project` tool.
Content is tracked in git by default. If the deployed server is your source of
truth (agents write to it), see the commented lines at the bottom of `.gitignore`.
## MCP tools
| Area | Tools |
|------|-------|
| Projects | `list_projects`, `list_pages`, `get_project_context`, `search_docs`, `update_project_page`, `init_project`, `delete_project` |
| Tasks | `get_task`, `get_active_task`, `list_tasks`, `upsert_task`, `create_task`, `update_task`, `append_to_task`, `check_task_item`, `set_task_status` |
| Skills | `list_skills`, `get_skill`, `write_skill`, `rename_skill`, `delete_skill`, `list_skill_references`, `get_skill_reference`, `write_skill_reference`, `delete_skill_reference` |
| Templates | `list_templates`, `write_template`, `delete_template` |
| Files | `move_file` |
Project pages are also exposed as MCP resources at `doc://<project>/<page>`.
## Deployment
### Docker
```bash
docker build -t mcp-console .
docker run -d --name mcp-console --restart unless-stopped \
-p 127.0.0.1:3000:3000 --env-file .env \
-v "$PWD/content:/app/content" \
-v mcp-console-data:/app/data \
mcp-console
```
Or with Compose:
```yaml
services:
mcp-console:
build: .
restart: unless-stopped
env_file: .env
ports:
- "127.0.0.1:3000:3000"
volumes:
- ./content:/app/content # docs, tasks, skills, landings
- mcp-console-data:/app/data # login sessions (SQLite)
volumes:
mcp-console-data:
```
### Deployment notes
- **Set `MCP_TOKEN` before exposing the server.** Without it `/mcp` is open to
anyone and includes destructive tools (`delete_project`, `write_skill`, …).
Use a strong `MCP_PASSWORD` too — generate both with `openssl rand -hex 32`.
- **Serve over HTTPS.** The session cookie is `Secure` in production, so console
login does not work over plain HTTP. Bind the container to `127.0.0.1` (as
above) and put a reverse proxy in front. Caddy is the shortest path:
```
mcp.example.com {
reverse_proxy 127.0.0.1:3000
}
```
With nginx, disable buffering for the MCP endpoint — it streams responses
over SSE: `proxy_buffering off; proxy_read_timeout 1h;` plus
`proxy_http_version 1.1;`.
- **Persist `content/`.** Agents write docs, tasks and skills at runtime; without
the volume those edits are lost on the next rebuild. The image bakes in the
repo's `content/` only as a starting point — a bind mount replaces it.
- **Back up `content/`** — it is the only state that matters (plain files; `rsync`,
a cron'd `git commit`, or volume snapshots all work). `data/sessions.db` only
holds login sessions; losing it just signs everyone out.
- **Build needs ~4 GB RAM.** The Dockerfile raises Node's heap for the Nuxt
build. On a small VPS, build the image in CI (or locally) and pull it instead.
- **Run a single instance.** MCP sessions are kept in process memory, so don't
load-balance across replicas. After a restart, clients re-initialize automatically.
- **Without Docker:** `pnpm install && pnpm build`, then
`node --env-file=.env .output/server/index.mjs` from the repo root (the server
resolves `content/` and `data/` relative to the working directory). Use
systemd or pm2 to keep it running.
## Development
```bash
pnpm test # vitest unit tests
pnpm build # production build → .output/
```
The **Claude** page in the console browses your local `~/.claude` folder. It is
dev-only and returns 404 in production builds.
## License
[MIT](./LICENSE)
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues