Give your agent a seat in Harbor.
Harbor ships an MCP server, so Claude or any MCP client can read Brand Memory, check best times, draft and propose posts. Proposals wait in Approvals like everything else.
One endpoint, 23 tools.
- Endpoint
POST /mcpon the Harbor API, over Streamable HTTP. Stateless: every request stands alone, so there are no sessions to keep alive.- Auth
Authorization: Bearer hbr_…. Create a key in Harbor under Settings → API keys.- stdio
- For clients that only speak stdio,
pnpm --dir apps/api mcp:stdiorelays every message to/mcpwithHARBOR_API_URLandHARBOR_API_KEY. Keep the API running while you use it. - Resource
harbor://brands/{brandId}/memory: a brand’s Brand Memory as JSON.- Results
- Every tool result says which mode Harbor runs in, like
(sandbox · simulated model), so an agent knows when nothing is real.
claude mcp add --transport http harbor http://localhost:4800/mcp \
--header "Authorization: Bearer hbr_your_key"{
"mcpServers": {
"harbor": {
"command": "pnpm",
"args": [
"--dir",
"/path/to/harbor/apps/api",
"mcp:stdio"
],
"env": {
"HARBOR_API_URL": "http://localhost:4800",
"HARBOR_API_KEY": "hbr_your_key"
}
}
}
}Harbor’s Settings → API keys page shows these same snippets with your API’s real URL, the path to your checkout and a freshly created key filled in.
Agents propose, people publish.
There is a propose_post tool. There is no schedule_post and no publish.
The write scope stops at proposing: an approval with the draft, the account, a slot, the reason and the voice-check results.
A person approves, edits or rejects it in Harbor. Every MCP run is recorded with its source and the key that made it.
Everything an agent can do.
The full list, as the server defines it. Read tools look; write tools create ideas, drafts, notes and proposals.
list_brands()List the brands in this workspace with their connected channels.
get_brand_memory({ brandId })A brand’s Brand Memory: voice (summary, do, avoid, banned words), audiences, products, knowledge, style rules and example posts.
list_posts({ brandId, status?, limit? })A brand’s posts, newest first, with their latest metrics and verdict against the brand’s median.
get_post({ postId })One post with its outcome (verdict against the median), its growth curve (snapshots) and the baseline curve.
get_digest({ brandId })The brand’s latest weekly digest: headline, stats, wins, misses, patterns and recommendations.
best_times({ brandId, platform? })The best posting slots (weekday 0 = Sunday, hour 0-23, server local time), scored 0..1, for one platform or all of the brand’s platforms.
list_campaigns({ brandId? })Campaigns, newest first, optionally for one brand.
list_ideas({ brandId, status? })A brand’s ideas, most recently updated first, optionally by status (idea, drafting, approved, scheduled, live).
list_approvals({ status?, brandId? })The approval queue: posts agents proposed, each with its draft, slot, reason and voice checks. Pending first.
list_memory_proposals({ status?, brandId? })Brand Memory changes agents proposed (the voice refresh): each change with why, its lift and the posts behind it. Pending first. A person applies them in Harbor.
get_conversation({ brandId, days? })What people say in replies to a brand’s posts, grouped into themes (questions, objections, praise, requests), largest first.
list_media({ brandId, tag? })A brand’s media library, newest first: images and videos with alt text, tags, focal point and the public URL of each crop. Attach one to a draft with attach_media.
list_agents()Harbor’s built-in agents: what each does, its inputs and its steps. Run one with run_agent.
get_agent_run({ runId })One agent or model run with its inputs, steps, child runs and the approvals it queued.
create_idea({ brandId, title, angle?, notes? })Add an idea to a brand. It starts in the Idea column.
suggest_ideas({ brandId, count? })Suggest ideas for a brand from its signals (post performance, audience pain points); each cites a signal.
draft_idea({ ideaId, platforms })Draft an idea for one or more platforms (x, linkedin, instagram, threads). Each draft is a new version.
propose_post({ draftId, proposedFor?, reason? })Propose a draft for posting. Creates a pending approval (a person approves, edits or rejects it); nothing is published or scheduled. Without proposedFor it takes the next free best-time slot on the channel.
attach_media({ draftId, assetId, variant? })Attach an image or video from the brand’s media library to a draft’s first part. Saves a new draft version; the crop defaults to the platform’s preferred one.
run_agent({ agentId, inputs })Run one of the built-in agents (see list_agents) with its inputs. Agents never publish; posts they want out become approvals.
add_knowledge({ brandId, title, body })Add a note to a brand’s Brand Memory knowledge (facts, positioning, FAQs the drafts should know).
save_to_memory({ brandId, from, as, text?, note?, title? })Save a post, reply, reply theme, signal or draft to a brand’s Brand Memory as a knowledge note, an excerpt, an example post, or a do / avoid rule. Rules link back to their source as evidence.
add_canvas_note({ campaignId, text })Put a sticky note on a campaign’s canvas, to the right of everything on it.
weekly_planWeekly planReads last week, finds open best-time slots, suggests ideas, drafts them per platform, runs the voice check and queues approvals.repurpose_winnerRepurpose a winnerTakes a published post that did well and drafts it for your other platforms.voice_checkVoice checkChecks a draft for banned words, length, hashtags, media and an empty hook. No model call.digestWeekly digestWins, misses, patterns and recommendations, each citing posts and signals.voice_refreshVoice refreshCompares winners with the rest and proposes Brand Memory changes with their lift and evidence.The API the app is built on.
Harbor’s web app is a client of the /v1 API, so anything you can do in the app, you can do with curl. JSON in, JSON out.
The REST API acts as you, so it can publish. Until Harbor has user accounts, it only answers on the machine it runs on. Requests through a tunnel or a public hostname only reach OAuth callbacks, /media, /mcp and /health. Agents use the MCP server and an API key instead.
Behind your own auth, HARBOR_PUBLIC_API=all turns the gate off.
curl -X POST localhost:4800/v1/brands \
-H 'content-type: application/json' \
-d '{"name":"My Brand"}'
# Errors always look like this:
{ "error": { "code": "NOT_FOUND",
"message": "…",
"request_id": "…" } }/v1/brandsBrands and their Brand Memory: voice, audiences, offers, knowledge.
GET /v1/brandsPOST /v1/brandsGET · PATCH · DELETE /v1/brands/:idGET /v1/brands/:id/best-timesPOST /v1/brands/:id/memory/savePOST /v1/brands/:id/memory/preview
/v1/ideasThe Idea → Drafting → Approved → Scheduled → Live pipeline.
GET · POST /v1/ideasPOST /v1/ideas/generatePATCH · DELETE /v1/ideas/:id
/v1/draftsPer-platform drafts with versions, generation and rewrites.
GET · POST /v1/draftsGET /v1/drafts/:idPOST /v1/drafts/generatePOST /v1/drafts/:id/rewrite
/v1/postsPublish now or schedule, with metrics, verdicts and replies.
GET · POST /v1/postsGET · PATCH · DELETE /v1/posts/:idPOST /v1/posts/:id/resolveGET /v1/posts/:id/replies
/v1/approvalsWhat agents proposed: approve, edit, reject, or do it in bulk.
GET /v1/approvalsPOST /v1/approvals/:id/approvePOST /v1/approvals/:id/rejectPOST /v1/approvals/bulk
/v1/agentsThe built-in agents and their runs, step by step.
GET /v1/agentsGET /v1/agents/:idPOST /v1/agents/:id/runsGET /v1/agent-runs/:id
/v1/automationsAgents on a schedule or after a post event, with a daily cap.
GET · POST /v1/automationsGET · PATCH · DELETE /v1/automations/:idPOST /v1/automations/:id/run
/v1/insightsVerdicts against the median, digests and the cross-brand overview.
GET /v1/insights/overviewGET · POST /v1/brands/:id/digestPOST /v1/brands/:id/outcomesGET /v1/brands/:id/signals
/v1/mediaUploads, focal points and per-platform crops at public URLs.
GET · POST /v1/brands/:id/mediaPOST /v1/brands/:id/media/from-urlGET · PATCH · DELETE /v1/media/:idGET /v1/media/:id/fit
Also: channels and the connect flow, campaigns and their canvas, memory proposals, API keys, GET /v1/mode (sandbox or live, simulated or real model) and the sandbox clock. Each response carries a Harbor-Request-Id header. Full reference in the repo README.
Two scopes. Neither can publish.
- A key is
hbr_plus 32 random base62 characters. - Harbor stores only its SHA-256 hash and the first 12 characters, for display. The full key is shown once, when you create it.
- Each key records when it was last used. Revoke one at any time and it stops working immediately.
- A call without the right scope fails with a message saying which scope it needs and which ones the key has.
List brands, posts, ideas, campaigns, media, agents and approvals. Read Brand Memory, digests, best times, conversation themes and agent runs.
Create and suggest ideas, draft them, attach media, add knowledge and canvas notes, save to Brand Memory, run agents and propose posts.
Publish, schedule, approve, delete or connect accounts. Those stay with a person in the app.
GET /v1/api-keys
POST /v1/api-keys { "name": "Claude Code", "scopes": ["read", "write"] }
DELETE /v1/api-keys/:idPoint Claude at your own Harbor.
Run Harbor locally in sandbox mode, create a key and connect Claude Code in one command. Nothing it proposes goes out until you approve it.