Harnios MCP
Harnios is a self-hosted, MCP-based storage system that gives every AI assistant you use — Claude, ChatGPT, Cursor, or anything else — a shared, persistent memory of your business: knowledge base, skills, projects, operational tables, and reports, all in one place. Connect a new tool once, and it reads and writes through the same store instead of starting from zero.
README
Harnios MCP
Harnios is a self-hosted, MCP-based storage system that gives every AI assistant you use — Claude, ChatGPT, Cursor, or anything else — a shared, persistent memory of your business: knowledge base, skills, projects, operational tables, and reports, all in one place. Connect a new tool once, and it reads and writes through the same store instead of starting from zero.
Under the hood, it's an autonomous, serverless system for running an AI-based Company OS. It can be deployed to serverless platforms like Vercel (or similar) or run locally on Node.js, and requires an external S3-compatible object storage backend — either your own, or a local self-hosted MinIO instance for development.
This repo pairs that local MinIO setup with the Next.js app that uses it. See specs/001-s3-self-hosted-storage/quickstart.md for a full end-to-end validation walkthrough.
The Next.js app (web editor + MCP server) lives entirely in frontend/ — that's the folder to point a future Vercel project's Root Directory setting at (see specs/006-frontend-folder-structure). Everything else at the repo root (docker-compose.yml, data/, scripts/) is local-dev infrastructure that isn't deployed.
Technical Overview
- App:
frontend/is a single Next.js 16 (App Router) application in TypeScript, serving both the web editor UI and the MCP server from one deployable unit — no separate backend service. - MCP server: built on
@modelcontextprotocol/sdk+mcp-handler, exposed as a Streamable HTTP endpoint at/mcp(see S3 Storage MCP Server below). - Storage: all persisted state — files/directories exposed via MCP and the editor, plus OAuth clients/sessions and personal access tokens — lives in a single S3-compatible bucket, accessed via
@aws-sdk/client-s3. No database is used; the bucket is the datastore. - Auth: two independent ways to authenticate to the MCP server — full OAuth 2.0 (for hosted AI assistants like ChatGPT/Claude adding it as a connector, spec 008-mcp-oauth) and owner-generated, non-expiring personal access tokens (for scripts/CLI tools/
.mcp.json, spec 013-mcp-token-auth). The web editor at/filesis gated by the same owner credential (spec 009-editor-login-gate). - Statelessness: because all state lives in the S3 bucket rather than on local disk or in-memory, the app is safe to run as ephemeral serverless functions (e.g. on Vercel) — any instance can serve any request.
- No code changes to switch storage backend: any S3-compatible provider works (self-hosted MinIO, AWS S3, or another compatible service) — swap it via environment variables only; see below.
Getting Started
This project includes a self-hosted, S3-compatible object storage service (MinIO) for local development. It runs entirely on your machine — no cloud account or credentials required.
Start it:
docker compose up -d
The S3 API is available at http://localhost:9000 and the web console at http://localhost:9001 (default credentials: minioadmin / minioadmin). Override the ports via MINIO_API_PORT / MINIO_CONSOLE_PORT (see .env.example) if either default port is already in use on your machine — startup will otherwise fail fast with a clear "port already in use" error rather than silently picking a different port.
Stop it:
docker compose stop
Always use docker compose (not the legacy docker-compose).
Buckets & Objects
No buckets are created automatically — create whatever buckets you need with any S3-compatible client (e.g. the MinIO Client mc or the AWS CLI) pointed at http://localhost:9000 using the credentials above. See quickstart.md for a full walkthrough.
Resetting local data
To permanently wipe all locally stored buckets/objects and start fresh:
./scripts/reset-storage.sh
This stops the service, clears its data, and starts it back up with no buckets present.
Where the data lives
Storage data is bind-mounted to ./data/minio on the host (not a Docker-managed volume), so bucket/object structure is visible outside Docker. Note that MinIO stores each object's content wrapped in its own binary xl.meta format (small objects are inlined directly into it) rather than as a plain file — so you can browse the bucket/key folder layout under ./data/minio, but you can't open an object's content directly in a text editor from there. Use the S3 API (or the web console) to read/write actual content. This folder is git-ignored and owned by root on Linux hosts (MinIO's container runs as root); use ./scripts/reset-storage.sh rather than a manual rm -rf to clear it without needing sudo.
Configuring the app's storage connection
The Next.js app (frontend/) connects to any S3-compatible storage backend — not only the local MinIO instance above — via environment variables it reads at startup (see specs/007-s3-storage-config). These are separate from the repo-root .env.example above, which only configures the local MinIO container: Next.js loads env files from its own project root, so the app's own settings belong in frontend/.env.local, copied from frontend/.env.example:
cp frontend/.env.example frontend/.env.local
The defaults match the local MinIO instance started above. To point the app at a different S3-compatible provider, edit frontend/.env.local (endpoint, region, access key, secret key, bucket, and path-style vs. virtual-hosted-style addressing) and restart the app — no code changes required.
The configured bucket (S3_BUCKET) must already exist. The app validates the connection at startup and logs a clear warning if required settings are missing, the endpoint is unreachable, credentials are rejected, or the bucket doesn't exist — but it no longer refuses to start over this (spec 014-os-init-page superseding spec 007's original fail-fast behavior): every request is instead sent to /init until storage is reachable, so the app is always up to guide you through fixing it. For the local MinIO instance, create the bucket once via the web console (http://localhost:9001) or any S3-compatible CLI before starting the app — see specs/007-s3-storage-config/quickstart.md for the full walkthrough.
Connecting storage from the app (/init)
If storage isn't connected yet (missing configuration, unreachable endpoint, rejected credentials, or a missing bucket) — including a completely fresh clone or deploy with no configuration at all — every page redirects to /init, which shows a setup helper covering everything the app needs to run in one place: the storage connection, the owner sign-in credential, and an optional system name. Fill it in and it generates one ready-to-paste configuration snippet, plus plain instructions for applying it either locally (frontend/.env.local) or on a hosting provider (e.g. Vercel: Project → Settings → Environment Variables → paste). Nothing typed into this helper is ever sent to the server; apply the snippet yourself and restart/redeploy.
Once storage is connected and signed in as the owner (see below), /init also bootstraps a fresh Company OS skeleton on an empty bucket — one confirmation click, no questions asked — creating os/, data/, a root AGENTS.md, and os/skills/init.md. Either right after that or on any later visit once the structure already exists, /init shows the MCP server URL and OAuth instructions for connecting Claude or ChatGPT as a connector, plus a link to /files — connecting an assistant is the actual next step, since it's the assistant (reading os/skills/init.md) that interviews you and fills in the business's own details (os/identity.md and everything else), not this app. See specs/014-os-init-page for the full spec.
Running with external storage (no local MinIO)
The repo-root docker-compose.yml/MinIO setup above is only for local development convenience — it is not a dependency of the app itself. frontend/ runs against any S3-compatible bucket, so you can skip docker compose entirely and point it at storage you already have (AWS S3, or any other S3-compatible provider/self-hosted instance reachable from where the app runs).
Locally, against external storage:
cp frontend/.env.example frontend/.env.local- Fill in
S3_ENDPOINT,S3_REGION,S3_ACCESS_KEY_ID,S3_SECRET_ACCESS_KEY,S3_BUCKETfor your external provider (setS3_FORCE_PATH_STYLE=falseif it needs virtual-hosted-style addressing — most providers other than self-hosted MinIO do), and setOAUTH_OWNER_USERNAME/OAUTH_OWNER_PASSWORD. - Make sure the bucket already exists (it's never created automatically).
cd frontend && npm install && npm run dev— nodocker compose upneeded.
Deployed serverless (e.g. Vercel):
- Import the repo and set the project's Root Directory to
frontend/(see specs/006-frontend-folder-structure) — this repo has no root-levelpackage.json, onlyfrontend/is a deployable Next.js app. - In the platform's environment variables UI (not a
.envfile — those aren't deployed), set the same variables listed infrontend/.env.example:S3_ENDPOINT,S3_REGION,S3_ACCESS_KEY_ID,S3_SECRET_ACCESS_KEY,S3_BUCKET,S3_FORCE_PATH_STYLE,OAUTH_OWNER_USERNAME,OAUTH_OWNER_PASSWORD, and optionallyMCP_BOOTSTRAP_PATH/OS_NAME. - Deploy. Because all app state (files, OAuth clients, personal access tokens) lives in the external bucket rather than on local disk, the deployment is stateless and safe to run as ephemeral serverless functions — no persistent volume or database needed.
- Once live,
/mcpand/fileswork exactly as in local dev, just at your deployment URL instead ofhttp://localhost:3000.
S3 Storage MCP Server
An MCP server exposes the configured storage above as filesystem-like tools (create/read/update/delete files; create/list/delete directories, recursively; move/rename either) — see specs/002-s3-mcp-server/contracts/mcp-tools.md for the full tool list, and specs/002-s3-mcp-server/quickstart.md for a runnable walkthrough.
Three additional tools — list_directory_tree, find_files_by_name, search_file_content — let a connected assistant map or search a large nested tree (e.g. a Company OS with many skills/policies folders) in a single call instead of one list_directory call per level; see specs/022-mcp-tree-search/contracts/mcp-tools-tree.md for the full contract, and specs/022-mcp-tree-search/quickstart.md for a runnable walkthrough.
Prerequisites: the storage backend must be running and reachable (e.g. docker compose up -d for local MinIO), its bucket must already exist, and frontend/.env.local must be set up per the section above.
Install dependencies once:
cd frontend
npm install
Start the MCP server:
cd frontend
npm run dev
This exposes the MCP endpoint (Streamable HTTP) at http://localhost:3000/mcp. It operates against a single, configured bucket (S3_BUCKET in frontend/.env.example, default mcp-storage) on whichever S3-compatible backend frontend/.env.local points at — separate from any bucket you create manually via the local-MinIO section above.
Dynamic tool descriptions from a bootstrap file
Optionally set MCP_BOOTSTRAP_PATH in frontend/.env.local (e.g. MCP_BOOTSTRAP_PATH=assistant/AGENTS.md) to a Markdown file already in storage. When set, every tool's description shown to a connecting client is prepended with guidance generated from that file's optional <!-- mcp-context: ... --> and <!-- mcp-triggers: ... --> HTML-comment markers — telling the client what the storage is for, when to use it, and to read the bootstrap file first. Edits to the file are picked up within about a minute, with no restart or redeploy. If the variable is unset, the file is missing, or neither marker is present, every tool simply falls back to its plain original description — see specs/010-dynamic-tool-descriptions/quickstart.md for a runnable walkthrough.
Managing which tools are active
Sign in as the owner and open /tools to see every tool's current active/disabled status, and to disable or re-enable any of them right there — no environment variable, no restart. A change is confirmed explicitly (naming the tool and the new status) before it's applied, and takes effect on the very next MCP request; the page also warns that AI assistant sessions already connected before the change may not see it until they reconnect. See specs/025-manage-tools-page/quickstart.md for a runnable walkthrough. (Earlier versions of this app used an MCP_DISABLED_TOOLS environment variable for this — spec 023-mcp-tool-toggle — which this page's storage-backed mechanism has superseded; that variable is no longer read.)
Connecting AI assistants (ChatGPT, Claude, etc.) via OAuth
The MCP server requires OAuth (spec 008-mcp-oauth) before any tool call is allowed — this is what lets you add it as a remote connector in hosted AI assistants. One-time setup, in addition to the storage setup above:
- Set an owner sign-in credential in
frontend/.env.local(separate from the S3/MinIO credentials above — this one gates who can approve AI assistants, not storage access):OAUTH_OWNER_USERNAME=owner OAUTH_OWNER_PASSWORD=<choose a password> - Start the server (
npm run dev) — it logs a clear warning at startup if these are missing (same as the storage settings above), but still starts; sign-in simply fails until they're set.
To add the server as a connector: in ChatGPT or Claude's "add connector"/"add MCP server" flow, point it at http://localhost:3000/mcp (or your deployed URL). The assistant discovers the OAuth flow automatically; you'll be prompted to sign in with the credential from step 1 and approve the connection. See specs/008-mcp-oauth/quickstart.md for the full walkthrough, including reviewing and revoking connected assistants at /settings/connected-apps.
Web File Explorer & Markdown Editor
A browser UI at /files (same app/dependencies as the MCP server above — docker compose up -d then npm run dev must both be running) lets you browse the MCP_STORAGE_BUCKET folder/file tree and edit existing files directly: .md files open in a split view (raw Markdown left, live-rendered preview right); other text files (.txt, .html, .xml, .css, .bpmn, .json, and more) open in a plain-text editor. Binary files (PDF, JPG/PNG, DOC/DOCX, XLS/XLSX, ZIP) are detected and shown with a clear "can't be edited here" message instead, with an Open/Download action to retrieve them regardless — PDFs and images open directly in a new browser tab. Uploads accept this same broad set of document, spreadsheet, image, diagram, and markup types (up to 25 MB per file), and every file in the tree shows an icon for its type. Saves are explicit (no autosave) — unsaved changes are indicated, and you're warned before navigating away or closing the tab with changes pending. A file's path is part of the URL itself (e.g. /files/notes/todo.md), so any file can be opened directly via a bookmarked or shared link (spec 018-editor-file-deep-link); the previous /editor URL still works and redirects here. See specs/003-web-file-editor/contracts/api-routes.md for the underlying API, specs/003-web-file-editor/quickstart.md for a full walkthrough, and specs/028-file-storage-upload for the mixed-file-type upload/view support.
/files requires signing in first, with the same owner credential used for the MCP connector flow above — a signed-out visit redirects to the sign-in screen, and a session started from either entry point covers both. See specs/009-editor-login-gate/quickstart.md for the full walkthrough.
Open it at: http://localhost:3000/files
License
Licensed under the PolyForm Internal Use License 1.0.0 — free to use, run, and modify for your own or your company's internal operations; distribution (including selling or offering a product/service based on it) requires a separate agreement with the copyright holder.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.