MCP Harbor
Provides tools for interacting with Harbor container registry, enabling management of projects, repositories, tags, and Helm charts.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@MCP Harborshow me the tags for the nginx repository"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
MCP Harbor
This is a fork of nomagicln/mcp-harbor, the original MCP server for Harbor. It has been extended with additional configuration options and security hardening around the SSE transport (see What's Different From the Original). All credit for the original implementation goes to the upstream author; see LICENSE for the MIT license and copyright notice.
MCP Harbor is a Node.js application that provides a Model Context Protocol (MCP) server for interacting with Harbor container registry.
Table of Contents
Related MCP server: Harbor MCP Server
What's Different From the Original
This fork keeps all the original Harbor MCP tools and behavior, and adds:
TLS verification is secure by default: the original disabled TLS certificate validation for the whole process unconditionally. It is now opt-in via
--insecure-tls/HARBOR_INSECURE_TLS, only relevant whenHARBOR_URLuseshttps://with a self-signed/internal certificate.Configurable SSE bind address: the SSE server used to always bind
0.0.0.0(all network interfaces). It now defaults to127.0.0.1and is configurable via--sse-host/HARBOR_SSE_HOST.Bearer token authentication for SSE:
/sseand/messagescan now require anAuthorization: Bearer <token>header via--sse-auth-token/HARBOR_SSE_AUTH_TOKEN, since the SSE transport otherwise has no authentication of its own.Correct multi-client SSE sessions: SSE connections are now tracked per session instead of a single shared/global connection, so concurrent clients no longer risk having their messages cross-routed.
No more crash on a second SSE connection: the original reused a single MCP
Serverinstance across every connection, which throws once a second client connects (the SDK only allows one transport perServerinstance) — and since it was unhandled, it took the whole process down. Every connection now gets its ownServerinstance and errors are caught per-request instead of crashing the server.Streamable HTTP support (
/mcp): the original only implemented the deprecated HTTP+SSE transport (/sse+/messages). Most current MCP clients (2025-03-26+ spec) expect the newer Streamable HTTP transport instead and get a404against/sse-only servers. This fork adds a/mcpendpoint supporting both, following the SDK's own backwards-compatible server pattern.
Features
MCP Server: Exposes tools for interacting with Harbor through the Model Context Protocol
Harbor Operations: Supports operations for projects, repositories, tags, and Helm charts
TypeScript: Written in TypeScript for better type safety and developer experience
Automated Tests: Comprehensive test suite for reliable functionality
Prerequisites
Before installing MCP Harbor, ensure you have:
Node.js 18.x or higher
npm 8.x or higher
Access to a Harbor registry instance
Git (for cloning the repository)
Installation
Clone the repository:
git clone https://github.com/nurawiguna/mcp-harbor.gitNavigate to the project directory:
cd mcp-harborInstall dependencies:
npm installBuild the project:
npm run build
Usage
Transport Modes
MCP Harbor supports two transport modes:
stdio (default): the MCP client (e.g. Claude Desktop, Cursor) spawns
mcp-harbordirectly as a subprocess and communicates over stdin/stdout. No network port is opened at all, so no SSE-related configuration is needed. Use this when the client andmcp-harborrun on the same machine.HTTP (
--sse, orHARBOR_SSE=truein.env— the flag name is--ssefor historical reasons, but it now enables both transports below): runs an HTTP server so MCP clients on a different machine/process can connect over the network, exposing both/mcp(Streamable HTTP, current spec) and/sse(deprecated HTTP+SSE) — see Connecting to the SSE Endpoint for which one to use. This must be explicitly turned on — plainnpm start/node dist/app.jswith no flags always runs stdio mode, even ifHARBOR_SSE_HOST/HARBOR_SSE_AUTH_TOKENare set. Because enabling it opens a network port, see Securing the SSE Transport below before enabling it.
Command Line Arguments
The application accepts the following command line arguments:
Options:
--url Harbor API URL (the remote Harbor server mcp-harbor
connects to) [string] [required]
--username Harbor username [string] [required]
--password Harbor password [string] [required]
--insecure-tls Disable TLS certificate verification when connecting to
the Harbor URL over HTTPS. Only for trusted internal
networks with a self-signed certificate.
[boolean] [default: false]
--debug Enable debug mode [boolean] [default: false]
--sse Enable SSE transport [boolean] [default: false]
--port Port for the local SSE server to listen on
[number] [default: 3000]
--sse-host Host/interface the local SSE server binds to (this
machine, not the Harbor server)
[string] [default: "127.0.0.1"]
--sse-auth-token Bearer token required to authenticate SSE connections to
this MCP server [string]
--help Show help [boolean]Environment Variables
Instead of command line arguments, you can also use environment variables. Create a .env file in the root directory (see .env.example):
# Harbor API Configuration
# HARBOR_URL is the remote Harbor server this app connects OUT to.
# Works with either http:// or https:// (matches the Harbor server's own setup).
# Must point at the Harbor REST API base path, i.e. it needs a /api/v2.0
# suffix - just the bare host (e.g. https://harbor.example.com) usually hits
# Harbor's web UI instead of its JSON API and every tool call fails/returns
# empty results. If you forget it, mcp-harbor appends /api/v2.0 for you.
HARBOR_URL=https://harbor.example.com/api/v2.0
HARBOR_USERNAME=admin
HARBOR_PASSWORD=Harbor12345
# Only set this to true if HARBOR_URL is https:// with a self-signed/internal
# certificate. Leave it false (default) whenever the certificate is trusted,
# or when HARBOR_URL is http:// (in which case it has no effect anyway).
HARBOR_INSECURE_TLS=false
# Debug Mode (true/false)
DEBUG=false
# --- Local SSE server ---
# These configure mcp-harbor's OWN inbound server, i.e. where MCP clients
# (Claude, Cursor, etc.) connect TO this app. Unrelated to HARBOR_URL above.
# Enables SSE mode (equivalent to the --sse flag). Without this set to true
# (in either this file or the actual environment) and without --sse passed on
# the command line, mcp-harbor runs in stdio mode instead and never opens a
# port at all - "npm start" alone does NOT turn SSE on by itself.
HARBOR_SSE=true
# Host/interface this app listens on (only relevant when SSE is enabled above).
# Keep 127.0.0.1 unless this server sits behind a trusted reverse proxy/firewall
# that restricts who can reach it.
HARBOR_SSE_HOST=127.0.0.1
# Bearer token required on the Authorization header for /sse and /messages.
# Required in practice whenever HARBOR_SSE_HOST is anything other than 127.0.0.1.
HARBOR_SSE_AUTH_TOKEN=change-me-to-a-long-random-valueSecuring the SSE Transport
The SSE transport has no authentication of its own, so treat these as required whenever mcp-harbor is
reachable by more than just your own machine:
Keep
HARBOR_SSE_HOSTat127.0.0.1unless a client genuinely needs to connect from another host.If it must be reachable from other hosts, set a long random
HARBOR_SSE_AUTH_TOKEN(e.g.openssl rand -hex 32) and put a firewall rule in front of the port restricting which hosts can reach it.Prefer a trusted reverse proxy with TLS termination in front of the SSE port if it is exposed beyond
localhost, since the SSE server itself speaks plain HTTP.
Connecting to the SSE Endpoint
When --sse is enabled, this server exposes two endpoints at the same time, for two different MCP
protocol versions. Which one your MCP client actually uses depends on the client, not on you — most
current-generation clients (2025+) speak Streamable HTTP; some older clients/SDKs only speak the deprecated
SSE transport. Both work here; pick the URL that matches what your client expects:
Endpoint | Protocol | Methods | Use when |
| Streamable HTTP (current spec) | GET, POST, DELETE | Default choice — most clients today (2025-03-26+ spec) |
| HTTP+SSE (deprecated, 2024-11-05 spec) | GET (+ internal POST | Only if your client specifically requires the older SSE transport |
<host>/<port>are whateverHARBOR_SSE_HOST/--portare set to (default127.0.0.1:3000).For
/sse,/messagesis a separate, internal endpoint the server tells the client about after the connection is established (it includes asessionIdquery parameter). You never configure/messagesdirectly — the client library handles that handshake automatically.If you point a client at
/sseand it logs something likeSSE connection establishedfollowed immediately by a404/Not Founderror, that almost always means the client actually speaks Streamable HTTP and tried to POST back to the same URL. Switch the client's URL to/mcpinstead.
Examples for HARBOR_SSE_HOST=0.0.0.0, default port, using the recommended /mcp endpoint:
Where the client runs | URL to use |
Same machine as |
|
A different machine on the network |
|
If HARBOR_SSE_AUTH_TOKEN is set, the client must send it as a bearer token on every request to whichever
endpoint it uses. For an MCP client config that supports a remote/URL-based server entry, this typically
looks like:
{
"mcpServers": {
"harbor": {
"url": "http://<mcp-harbor-host-ip>:3000/mcp",
"headers": {
"Authorization": "Bearer <your HARBOR_SSE_AUTH_TOKEN>"
}
}
}
}The exact field names (url, headers, etc.) vary by MCP client — check that client's docs for how it
configures a remote MCP server, and whether it lets you pick the transport/protocol explicitly.
Running in Production
Build once, then run the compiled output directly — no TypeScript tooling needed at runtime:
npm run build
npm start -- --url https://harbor.example.com --username admin --password ***
# or, with a .env file in place (see Environment Variables above):
npm startnpm start just runs node dist/app.js; any flags after -- are forwarded to it. You can also invoke
node dist/app.js directly, or install it as a global command:
npm install -g .
mcp-harbor --url https://harbor.example.com --username admin --password ***
npm starton its own does NOT enable SSE mode. It runs in stdio mode by default (no port opened at all), regardless ofHARBOR_SSE_HOST/HARBOR_SSE_AUTH_TOKENbeing set — those only configure SSE, they don't turn it on. If you hit/sseand get a connection error or 404, this is almost always why. To actually enable SSE, either:npm start -- --sseor set
HARBOR_SSE=truein your.env(see .env.example) and then plainnpm startis enough. Either way, check the startup log for[MCP Server] Using SSE transport/SSE server running on ...to confirm it actually turned on before pointing a client at it.
For SSE mode in production, the process needs to keep running in the background (it doesn't daemonize itself). Use a process manager such as pm2 or a systemd unit, for example:
pm2 start dist/app.js --name mcp-harbor -- --sse --sse-host 127.0.0.1 --sse-auth-token "$HARBOR_SSE_AUTH_TOKEN"See Securing the SSE Transport before exposing SSE mode beyond localhost.
Using It From an MCP Client (e.g. Claude Desktop)
The most common way to run this in "production" is not from a terminal at all — the MCP client spawns it
for you via stdio (see Transport Modes). Point the client at the built binary and
pass credentials as arguments or environment variables, e.g. in Claude Desktop's claude_desktop_config.json:
{
"mcpServers": {
"harbor": {
"command": "node",
"args": ["/absolute/path/to/mcp-harbor/dist/app.js"],
"env": {
"HARBOR_URL": "https://harbor.example.com",
"HARBOR_USERNAME": "admin",
"HARBOR_PASSWORD": "***"
}
}
}
}If installed globally (npm install -g .), you can use "command": "mcp-harbor" with "args": [] instead.
MCP Tools
The MCP server exposes the following tools:
Tool Name | Description | Parameters |
| List all projects in Harbor | None |
| Get project details by ID |
|
| Create a new project |
|
| Delete a project |
|
| List repositories in a project |
|
| Delete a repository |
|
| List tags in a repository |
|
| Delete a tag |
|
| List Helm charts |
|
| List chart versions |
|
| Delete chart version |
|
Development
Running in Development Mode
Runs the TypeScript source directly (via ts-node's ESM loader), no npm run build needed:
npm run dev -- --url https://harbor.example.com --username admin --password ***
# or, with a .env file in place:
npm run devRunning Tests
# Run all tests
npm test
# Run tests with coverage
npm run test:coverageProject Structure
mcp-harbor
├── src
│ ├── app.ts # Main application entry point (MCP server)
│ ├── definitions
│ │ └── tool.definitions.ts # Tool definitions for MCP
│ ├── services
│ │ └── harbor.service.ts # Harbor service implementation
│ └── types
│ └── index.ts # TypeScript type definitions
├── test
│ └── harbor.test.ts # Tests for Harbor service
├── .env.example # Example environment variables
├── .gitignore # Git ignore file
├── .eslintrc.json # ESLint configuration
├── package.json # Project dependencies
├── jest.config.js # Jest configuration
├── tsconfig.test.json # TypeScript configuration for tests
├── tsconfig.json # TypeScript configuration
├── LICENSE # Project license
└── README.md # Project documentationTroubleshooting
Common Issues
Connection Failed
Error: Unable to connect to Harbor instanceVerify HARBOR_URL is correct and accessible
Check network connectivity
Ensure Harbor instance is running
Authentication Failed
Error: Invalid credentialsVerify HARBOR_USERNAME and HARBOR_PASSWORD are correct
Check if user has required permissions
Tool calls fail with
X.map is not a function, or return an empty{}for a project/repo that existsThis means requests are reaching Harbor's web UI instead of its REST API — almost always because
HARBOR_URLis missing the/api/v2.0path (e.g.https://harbor.example.cominstead ofhttps://harbor.example.com/api/v2.0). mcp-harbor auto-appends/api/v2.0if it's missing, but double checkHARBOR_URLdoesn't already point somewhere else unexpected (e.g. a path-based reverse proxy) if this still happens after upgrading.Build Errors
Error: TypeScript compilation failedRun
npm installto ensure all dependencies are installedCheck TypeScript version compatibility
Clear the
distdirectory and rebuild
[MCP Error] SyntaxError: Unexpected end of JSON inputright afternpm start/npm run devThis is not a crash — the process keeps running. It happens because the default transport (stdio) expects every line on stdin to be a complete JSON-RPC message. If you run
npm start/npm run devdirectly in a terminal and press Enter (sending an empty line) or type plain text, it can't be parsed as JSON and this gets logged.stdio mode isn't meant to be typed into manually — it's meant to be spawned by an MCP client (see Using It From an MCP Client). To sanity-check it from a terminal instead, either:
Pipe in a real JSON-RPC message:
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | npm startOr use
--ssemode and test withcurl/a browser against the SSE endpoint, which is easier to interact with manually.
npm startalone doesn't open any port at allnpm startjust runsnode dist/app.jswith no flags, which defaults to stdio mode. Pass--sse(npm start -- --sse) or setHARBOR_SSE=truein.envto actually enable the HTTP server. Check the startup log for[MCP Server] Using SSE transport/SSE server running on ...to confirm it turned on before pointing a client at it.MCP client logs
SSE connection established(or similar) and then immediately gets a404 Not FoundYour MCP client tried the deprecated
/ssetransport first, got connected, then attempted to POST a follow-up request back to the same URL — which only exists as a/mcpendpoint here. This means the client actually speaks the newer Streamable HTTP protocol. Point it at/mcpinstead of/sse(see Connecting to the SSE Endpoint).
Debug Mode
Enable debug mode by using the --debug flag or setting:
DEBUG=trueSupport
For additional help:
Review the application logs
License
This project is licensed under the MIT License - see the LICENSE file for details.
This server cannot be deployed
Maintenance
Related MCP Connectors
Enable secure connectivity between Sentry issues and debugging data, and LLM clients, using a Model Context Protocol (MCP) server.
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
A Model Context Protocol server for Wix AI tools
Model Context Protocol server for the Apideck Unified API. Connect any MCP-compatible agent framework to 100+ accounting systems, HRIS platforms, file storage providers, and more through one integration. More information https://www.apideck.com/mcp-server
Related MCP Servers
- AlicenseNot gradedqualityFmaintenanceA Node.js application that provides a Model Context Protocol server for interacting with Harbor container registry, supporting operations for projects, repositories, tags, and Helm charts.13 npm7MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI models to interact with Harbor container registries to manage projects, repositories, and system configurations in real time. It provides a suite of tools for monitoring health, retrieving statistics, and performing searches across registry data.15 npm5MIT
- AlicenseAqualityBmaintenanceCP server for Harbor Registry — projects, repositories, artifacts, storage reports, cleanup candidates. Works with any Harbor 2.x instance.831 PyPI1MIT
- AlicenseNot gradedqualityDmaintenanceEnables interaction with Hashnode's tools and services through a unified API using the Model Context Protocol.1MIT