Ghedee Content Hub MCP Server
OfficialIntegrates with Google Drive for uploading, downloading, and managing social calendar Excel files and generated media assets, using the Drive API for file operations.
Integrates with Google Cloud's Gemini API for AI-powered image and video generation from text prompts, used to create social media content assets.
Click on "Install 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., "@Ghedee Content Hub MCP Servergenerate media for the Q3 2026 social calendar"
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.
Ghedee Content Hub — MCP server
MCP server backing the Content Hub Cowork workflows for The Ghedee Centre. Planned to cover three content types — Blog Posts, Social Media Calendar, Emails. Phase 1 (this build) is the Social Calendar workflow only.
The canonical calendar is a living Google Sheet (Ghedee_Social_Calendar_<id>,
e.g. Ghedee_Social_Calendar_Q3_2026) in 00_Calendar & Docs, which the team edits
in place. The tools:
Tool | What it does |
| Start a new calendar: create the Drive folder tree (folder named by the Calendar ID) + an empty, styled living-sheet shell in |
| Append new rows to the live sheet in bulk (this is how you seed a fresh calendar). Each row is keyed by header name and must carry a Row ID; new rows default to Status |
| Edit cells of existing rows in the live sheet in place. Edits name a row by Row ID and a column by header name; only the named cells are written. Status is not editable (human-only approval); the machine-owned columns are |
| Read the live sheet's Draft rows → generate the missing AI images/videos → upload to Drive → write each link / cost / model / notes back into the live sheet in place (Sheets API). |
| Build an HTML review page from the live sheet and publish it next to the calendar as |
| Write a scheduler's bulk-import file from the live sheet — Metricool or Publer CSV. Exports finished rows only, resolves each Drive asset to a fetchable URL, and expands carousels into their slides. |
Direct Metricool publishing (via their API) is planned for a later phase; today the handoff is the export file above.
Architecture
Three content types are planned (Blog Posts, Social Calendar, Emails). Their
common thread — generate AI images/video, push files to a specific Drive
folder, pull files back for Cowork — lives in core/. Each content type is its
own package on top; social is the only one built so far.
server.py thin MCP tools (FastMCP, stdio)
content_hub/
core/ ← the common thread, content-agnostic
config.py env / Google credentials / paths / brand + model defaults
media.py generate images + video (the AI-image primitive)
drive.py push to a Drive folder / pull latest / exists-check
social/ ← workflow #1 (blog/ and email/ become siblings)
rules.py calendar naming, id→folder, aspect-ratio, Drive layout
calendar.py read the .xlsx → jobs; write Drive link + cost back
workflow.py orchestrator: generate → push → writeback (the 3 operations)
exporters/ bulk-import files for external schedulers
__init__.py sheet → neutral Post objects + the Drive media-URL rule
metricool.py Metricool's CSV (one file, per-platform boolean columns)
publer.py Publer's CSV (one file PER platform; Drive share links)
cli.py manual dry/mock/live test harness for the same 3 operationsThe core engine never writes to stdout and never calls sys.exit — required for
an MCP stdio server, where stdout is the protocol channel. All progress goes to
stderr; tools return structured results. Adding a workflow = a new package
(e.g. blog/) that parses its own input format, defines its own Drive layout, and
reuses core.media + core.drive for the three shared primitives.
Related MCP server: atlas-social-mcp
How a calendar row becomes media
Each entry is described by Platform (Instagram / Facebook / Tiktok) +
Format (Post / Reel / Carousel) — e.g. "Instagram Reel". Only rows with
Status = Draft and Visual Type AI text-to-image / AI text-to-video /
AI text-to-carousel are generated. Recorded video of Wiah rows are never
AI-generated — they're Wiah's own clips, so they only come from a Created Asset Link (copied into 01_Wiah Videos); without one the row is left alone until the
film is uploaded. Kind + aspect ratio are derived from Visual Type:
Visual Type | Format | Kind | Aspect | Files |
AI text-to-carousel |
| carousel | 4:5 | N slides ( |
AI text-to-video |
| video | 9:16 | 1 ( |
AI text-to-video |
| video | 16:9 | 1 ( |
AI text-to-image |
| image | 1:1 | 1 ( |
Recorded video of Wiah |
| video (copied) | 9:16 | 1 in |
(AI text-to-image + Format Carousel is still accepted as a carousel for backward
compatibility.) The Row ID is the stable key: the Drive existence check matches on the
{RowID}_ prefix, so editing a headline never orphans an already-generated file.
Carousel prompts: the Prompt cell holds one prompt per slide, each marked
Slide N: (a dash —/- is also accepted) — each is generated as its own slide:
Slide 1: a serene forest at dawn, mist rising through tall pines
Slide 2: a lone figure on a mountain path, seen from behind
Slide 3: sunrise breaking over a wide valley, golden lightThe Slides column is required for a carousel and the number of prompts must
match it; a blank Slides or a mismatch fails the row with a clear message. Optional
extras: a trailing Style: … / Palette: … clause applies to every slide, and a
per-slide On-image text: "…" supplies overlay wording (the art is rendered text-free
and the words are stamped on afterward).
Idempotency: a row is skipped if its asset already exists on Drive. Deleting the Drive file (or a carousel's group folder) is how you request a regeneration.
Bring-your-own asset: if a single-image or single-video row's
Created Asset Link column (just after Generated Asset Link) is
filled, generate skips the model and instead copies that asset into the file
the model would have produced — an image into the .png slot, a video into the
.mp4 slot (accepts a Drive share link, a plain http(s) URL, or a local path).
If the source can't be reached the row is marked Failed and the run continues. A
carousel row's Created Asset Link points at a Drive folder: its images, in
alphabetical order, become the slides. Both that source folder and the existing
destination folder must hold exactly Slides images, or the row errors (a stale
destination left over from a different slide count is caught before anything is written).
For these rows the idempotency check is content-aware (compared by MD5): if the copy already on Drive matches the Created Asset it's skipped, and pointing the Created Asset Link at a different file makes the next generate re-copy it (no need to delete the old Drive file first). For a folder-sourced carousel this is per-slide — only the slides whose source image changed are re-uploaded.
Three modes (every tool + CLI command)
Mode | Drive | API/credits | Writes |
| none | none | nothing — plans jobs + reports worst-case cost |
| yes (safe) | none | placeholder files; upload + write-back routed to a mock destination and a |
| yes | spends | real generation + upload + write-back to the working sheet |
Mock uploads go to SOCIAL_CALENDAR_MOCK_ROOT_ID if set, else a _mock rehearsal
subfolder under the calendar folder — so a rehearsal can never overwrite a real asset.
Setup
python -m venv .venv && .venv\Scripts\Activate.ps1 # PowerShell
pip install -r requirements.txt
cp .env.example .env # fill in OPENAI_API_KEY (+ GEMINI_API_KEY for video) + SOCIAL_CALENDAR_ROOT_IDMedia keys: images use OpenAI gpt-image-2 (OPENAI_API_KEY; your account may
need org verification to access it); video uses Google Veo (GEMINI_API_KEY). A run
only needs the key(s) for the media types it generates — an --only image run never
touches the Gemini key, and vice-versa.
Google auth (one-time, interactive): create a Desktop-app OAuth client in
Google Cloud Console → APIs & Services → Credentials, enable both the Drive
API and the Google Sheets API, save the client JSON as credentials.json.
Authorise once so the browser consent can run and cache token.json:
python -m content_hub.cli auth # grants Drive + Sheets, caches token.jsonAfter that every headless run (server, mock, live) reuses token.json.
Never commit credentials.json, token.json, or .env.
The calendar lifecycle
# 0. Start a brand-new calendar: Drive folders + an empty living-sheet shell. The
# Calendar ID is the Drive folder name verbatim (a quarter, a date range, or a day):
python -m content_hub.cli social create Q3_2026
# 1. Seed the living Google Sheet by appending rows directly (bulk). rows.json is a
# JSON list of {header: value} dicts, each with a Row ID; dry-run previews first:
python -m content_hub.cli social add Q3_2026 @rows.json --mode dry-run
python -m content_hub.cli social add Q3_2026 @rows.json --mode live
# 1b. Later tweaks: edit cells of existing rows in place (by Row ID + column):
python -m content_hub.cli social edit Q3_2026 '[{"row_id":"IG-014","column":"Caption","value":"New copy"}]' --mode live
# 2. Generate media and write links/cost/model/notes back INTO the live sheet:
python -m content_hub.cli social generate Q3_2026 --mode dry-run # plan + cost only
python -m content_hub.cli social generate Q3_2026 --mode mock # rehearse (live sheet untouched)
python -m content_hub.cli social generate Q3_2026 --mode live # spends; edits the sheet in place
# 3. Review page from the live sheet, published beside the calendar on Drive:
python -m content_hub.cli social preview Q3_2026
# 4. Hand the approved round to a scheduler as a bulk-import file:
python -m content_hub.cli social export Q3_2026 --target metricool
python -m content_hub.cli social export Q3_2026 --target publer # one file per platform
# generate options: --only image|video --video-model <id> --video-duration 30generate reads the living Google Sheet and, in live mode, writes only the
machine-owned columns (Generated Asset Link / Est. Cost / AI Model / Notes) in
place via the Sheets API — so a teammate editing captions or Status at the same
time is never clobbered. dry-run touches nothing; mock writes a local
*.mock.xlsx instead of the live sheet.
Editing the live sheet directly
All calendar changes go straight to the living Google Sheet — there is no local
.xlsx to download, edit, and re-upload. Two tools edit it in place over the same Sheets
API generate uses, so only the cells named are touched (concurrent human edits survive):
social_edit_calendar— change cells of existing rows. Each edit names a row by its Row ID and a column by header name (or a known alias, e.g.Hook→ Headline):{"row_id": "IG-014", "column": "Caption", "value": "New copy…"}. The batch is validated as a whole — if any edit is invalid, nothing is written and the errors come back, so the sheet is never left half-edited.social_add_rows— append new rows in bulk to seed a calendar. Each row is a{header: value}dict and must carry a Row ID; rows land after the last used row.
Both take mode = dry-run (preview the resolved writes, touch nothing) or live
(write). There's no mock here — nothing is generated or spent, so dry-run already is
the safe rehearsal. Guardrails (why this is a schema-aware tool, not a raw Sheets
connector): Status is never editable — approval is a human-only decision; new rows
default to Draft and an approval status can't be set. The machine-owned columns
(Generated Asset Link / Est. Cost / AI Model) are written by generate — edit refuses
them unless force=true, add refuses them outright. Constrained columns (Platform /
Format / Visual Type) are validated against their allowed values, and an unknown column
or Row ID is rejected with a clear message.
The review page (and the platform simulators)
social preview builds a single self-contained HTML file from the living sheet and
publishes it beside the calendar in 00_Calendar & Docs. Every asset is read from Drive,
downscaled, and inlined as a data URI, so the page opens offline and can be shared as one
file. A per-calendar cache keyed by Drive md5 means a rebuild only re-fetches assets that
actually changed. --no-publish builds locally without uploading; --no-cache re-encodes
everything.
The page has two views:
Review feed (default) — every post in its platform's chrome, inside a status-framed card carrying the Row ID, date, format, a status pill and copy-caption / Sheet / Asset buttons, grouped by week with an approval roll-up per week. Chips filter by platform, Reels, Carousels and status. Served through the Apps Script web app, the status pill is a live dropdown that writes straight back to the sheet.
Simulator — the Simulator chip opens a full-screen overlay showing the calendar as each platform's real feed, inside a phone chassis (toggleable via Phone frame):
Platform | Surfaces |
Feed · Profile grid · Reels | |
Feed (caption above media, reaction bar) | |
TikTok | Full-bleed 9:16, snap-scrolled, with the action rail |
Posts run newest-first, the review page's status filter carries through (mirrored as the
Showing dropdown in the overlay header), and hovering a post reveals its Row ID, date
and status. Engagement counts are simulated — the calendar holds no performance data —
and are derived deterministically from the Row ID, so they never change between rebuilds.
Adjust their scale via _ENGAGE_LIKES / _ENGAGE_VIEWS in social/preview.py.
Video posts show the clip's first frame with a play button that opens it on Drive in a
new tab. There is no inline playback, and that is deliberate — both routes were tried and
measured. A native <video> cannot load the file: Drive answers a browser's Origin: null
(any page opened from disk) with 403 and no Access-Control-Allow-Origin, and blocks the
non-CORS path with Cross-Origin-Resource-Policy: same-site. Drive's own /preview player
does work, but it is a player page and draws a toolbar above the picture, which reads as a
black band across the top of a feed post. A paused feed shows stills anyway.
The simulator adds no image bytes to the file: each surface is built in the browser by cloning media nodes the review feed already contains, so every asset is embedded exactly once. There is no separate "IG Grid" view — it is now the Instagram simulator's Profile tab.
Exporting to a scheduler
social export turns the finished part of the calendar into a scheduler's bulk-import
file. Two targets exist — Metricool and Publer (--target, default metricool).
content_hub/social/exporters/ is a registry, so a new one is a module exposing NAME,
EXTENSION and write(posts, out, **opts) — it receives fully-resolved Post objects
and never touches Drive or the sheet.
python -m content_hub.cli social export Q3_2026 # metricool, Approved, drafts
python -m content_hub.cli social export Q3_2026 --target publer # Publer, per-platform files
python -m content_hub.cli social export Q3_2026 --statuses Approved,Draft
python -m content_hub.cli social export Q3_2026 --schedule # live scheduled postsOnly rows whose Status says they're finished are exported (Approved by default —
--statuses to widen). Posts are written as scheduler drafts unless --schedule,
so an import can never auto-publish before someone confirms the media resolved. A row
with no asset, or a carousel whose Drive folder disagrees with its Slides count, is
skipped and reported rather than exported broken.
Times are exported verbatim from Time (PT), so the destination account's timezone must
be Pacific or the whole calendar lands hours off.
The Drive media-URL rule
The export is only as good as the URL it hands over, and the right form depends on the scheduler — specifically whether it fetches the URL for bytes or parses it as a Drive share link and resolves the file through its own Google integration. The forms, and what each returns to an anonymous fetcher (all measured):
URL form ( | Behaviour |
|
|
| 200 with the true content-type, images and video alike — the answer for anything that fetches bytes. |
| 303 with an empty body; a fetcher that doesn't follow redirects sees nothing. |
| 200 — but for a video it serves a poster JPEG, so the post silently publishes as a still. Avoid where video is possible. |
Each target sets its own default (LINK_STYLE); override with --link-style. Whichever
form is used, the assets must be link-shared ("Anyone with the link") or nothing
resolves — the exporter never changes Drive permissions, that's a human decision — and if
sharing is later tightened, every already-scheduled post loses its media.
Publer specifics
Publer's CSV has no platform column: an import applies every row to the same
accounts picked in the import wizard. Because the calendar carries different copy per
platform, social export --target publer writes one file per platform
(…_publer_instagram.csv, …_publer_facebook.csv, …) — import each against the matching
account. Media go in a single comma-separated column (carousels expand there), alt text
uses ||, and the IG first-comment hashtags map to Publer's Comment(s) column (Facebook
files omit them). Publer has no draft flag in the file — drafts-vs-scheduled is chosen in
the wizard — so --schedule only affects the summary for this target.
Run as an MCP server
python server.py # serves over stdioRegister it as a local MCP/connector in Claude Cowork — copy
cowork-mcp-config.example.json, fill in the absolute paths and env values:
{
"mcpServers": {
"ghedee-content-hub": {
"command": "…\\content-hub-mcp\\.venv\\Scripts\\python.exe",
"args": ["…\\content-hub-mcp\\server.py"],
"env": {
"SOCIAL_CALENDAR_ROOT_ID": "your-social-calendar-folder-id",
"OPENAI_API_KEY": "your-openai-key-here",
"GEMINI_API_KEY": "your-gemini-key-here"
}
}
}
}command should point at the venv's Python (so the deps resolve). The env block
is optional — the server also reads .env on startup — but keeping the connector
self-contained avoids surprises. The three social_* tools then drive the same
workflow, each taking a mode argument. Do the one-time Drive consent first
(above) so token.json exists before Cowork launches the server headless.
Notes
Images: OpenAI
gpt-image-2. Rendered at native aspect ratios (1:1 → 1024×1024, 4:5 carousel → 1024×1280),quality=high,moderation=low. No crop step — the model renders the requested ratio directly.Cost figures come from a rough price table in
core/media.py— a guide, not a bill. Images bill per output token (~$0.12–0.16/image at high quality); confirm with OpenAI's image-cost calculator. Video is per-second Veo pricing.Model access / deprecations: if images 404, confirm your OpenAI account can use
gpt-image-2(org verification may be required). Google rotates Veo-previewnames — on a video model-not-found error, updateDEFAULT_VIDEO_MODELincore/config.py.Veo resolution/duration:
1080ponly renders at 8s; hero clips default to 720p/6s. Validated before spending.Reference docs (the Cowork workflow prompt and the Drive asset-structure layout) live in the Cowork Content Hub project, not this repo.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables AI assistants to manage scheduled social media posts and content automation across multiple platforms (X/Twitter, Reddit, LinkedIn, Instagram, TikTok, YouTube). Supports organizing campaigns into tracks, scheduling posts with natural language, and automating content workflows with local SQLite storage.Last updated732MIT
- AlicenseAqualityCmaintenanceAI-powered social media posting across 14 platforms. Post to Twitter, Instagram, TikTok, Facebook, LinkedIn, YouTube and more with one command. AI adapts content per platform, schedules posts, and generates 30-day content calendars.Last updated6MIT
- AlicenseCqualityCmaintenanceEnables users to publish, schedule, and manage social media content across nine platforms including TikTok, Instagram, and X via the CodivUpload API. It also supports media management and the creation of YouTube live streams through natural language commands.Last updated3031MIT
- AlicenseAqualityAmaintenanceSchedule and manage social media posts across 10 platforms (Instagram, Facebook, TikTok, X, LinkedIn, YouTube, Threads, Pinterest, Bluesky, Telegram) from any MCP-compatible AI assistant. Supports batch posting, media uploads, analytics, and platform-specific features like Reels, Shorts, and carousels.Last updated111295MIT
Related MCP Connectors
AI social media management: create, schedule, approve, and analyze posts across 11 platforms.
Schedule and publish social posts to 11 platforms with media, campaigns, analytics and AI captions
World-class creative social media content studio, powered by AI.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/GhedeePhilsophy/content-hub-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server