BASH CMS
FreeNot checkedEnables governed LinkedIn company-page publishing from a VS Code extension's MCP server, with read-only tools for account info, post preview, and a draft tool f
About
Enables governed LinkedIn company-page publishing from a VS Code extension's MCP server, with read-only tools for account info, post preview, and a draft tool for creating pending posts, plus an optional publish tool that is off by default and requires explicit confirmation.
README
Governed publishing from your repo, inside VS Code: browse what's worth sharing, draft LinkedIn company-page posts, run the brand guard, approve with your name on it, and publish — with an idempotency ledger so nothing ever double-posts.
Built by BASH Consulting and used to run its own company page. The pipeline follows one rule end to end: drafts are files, approval is a human decision recorded in the file, and a script does the mechanical work. No post leaves your machine without a person deciding it should.
How it works
Content ──▶ Draft (status: pending) ──▶ Approve (status: approved) ──▶ Publish ──▶ Ledger
│ a file in a person, in the POST /rest/posts .github/
.cms/ index drafts/linkedin/ review panel as the org linkedin-log.json
or a frontmatter scan
- Content comes from the
.cms/contract when your repo has one (the zer0-CMS content engine's machine-readable index), and degrades honestly to a frontmatter scan when it doesn't. - Drafts are markdown files with
type/status/sourcefrontmatter — the same queue format the BASH CI lane publishes from, so the extension and a GitHub Actions workflow can share one queue and one ledger without stepping on each other. - The guard is mechanical: LinkedIn's 3,000-character limit, the 140-character "…see more" fold, a banned-phrase list, filler detection, and your own patterns file. Errors block a live post; a human can still be overruled only by a human.
- The ledger is keyed by canonical URL. Either lane (extension or CI) posts first; the other skips. Byte-compatible with the Python lane's
json.dumpoutput, so the file never churns in git.
The four views
| View | What it shows |
|---|---|
| Drafts | The queue, by status — open the review panel, approve, publish |
| Content | Distributable pages (health-ranked when .cms/ exists), with a posted badge |
| What to write next | Catering lanes: A — distribute what exists · B — write more of what landed · C — say the quiet part · D — refresh what worked |
| Published | The ledger, newest first — jump to the live post |
Getting started
- Install the extension, open your content repo.
- Set
bashCms.linkedin.orgUrnandbashCms.site.baseUrl(Settings → BASH-CMS). - Run BASH-CMS: Set LinkedIn Credentials and paste a token with
w_organization_social+r_organization_social(from the token generator; requires Community Management API access on your LinkedIn app). - Run BASH-CMS: LinkedIn: Who Am I to confirm the token, scopes, and expiry.
- Draft from the Content view, review, approve — and when you're ready to go live, enable
bashCms.publish.allow.
Publishing is off by default. Until you enable it, every path — commands, the review panel, MCP — is preview and draft only.
Commands
| Command | What it does |
|---|---|
| Set / Clear LinkedIn Credentials | Token (and optional client id/secret) into VS Code SecretStorage — never settings, never a file |
| LinkedIn: Who Am I | Token status, granted scopes, days to expiry |
| New Article Draft / New Text Draft | Stage a status: pending draft in the queue |
| Preview LinkedIn Payload | The exact /rest/posts body that would send — no network |
| Run Brand Guard on Draft | Errors, warnings, and the fold preview |
| Approve Draft | pending → approved, recorded in the file |
| Publish Draft… / Publish All Approved Drafts | The live path: guard → ledger → confirm → post → record |
| Open Review Panel | The approval gate, made legible (below) |
| Generate Catering Worklist | Write .cms/distribution/worklists/<date>-catering.md |
| Open Post on LinkedIn | Jump from the ledger to the live post |
| Configure MCP Server (.vscode/mcp.json) | Fallback wiring for older VS Code |
The review panel
One panel per draft, opened beside the editor: the commentary with the fold marker where LinkedIn truncates, every guard finding, the exact outgoing JSON, and the ledger state. The Publish button stays disabled until the gates pass — and the extension re-checks every gate on its own side when you click, because a webview is UI, never the gate.
MCP server
The extension registers a bundled MCP server with VS Code (1.101+), so Copilot agent mode — or any MCP client pointed at dist/mcp-server.js — gets six tools:
| Tool | Safe? |
|---|---|
linkedin_whoami · linkedin_list_posts · linkedin_get_post · linkedin_preview |
read-only |
linkedin_draft |
writes a pending draft for a human |
linkedin_publish |
off by default — needs BASH_CMS_MCP_ALLOW_PUBLISH=1 in the server env and confirm: true per call |
The access token is injected into the server process only at start time, from SecretStorage — it never appears in a static definition, a setting, or a log.
Settings
| Setting | Default | Purpose |
|---|---|---|
bashCms.linkedin.orgUrn |
— | The company page (urn:li:organization:…) |
bashCms.linkedin.apiVersion |
202606 |
LinkedIn-Version header (YYYYMM) |
bashCms.site.baseUrl |
— | Canonical site origin |
bashCms.site.permalinkPattern |
/posts/:year/:month/:day/:slug/ |
Canonical URL shape (the ledger key) |
bashCms.content.globs |
pages/_posts/**/*.md |
Where content lives when there's no .cms/ |
bashCms.content.previewsDir / .fallbackImage |
assets/images/… |
Card thumbnail resolution |
bashCms.drafts.folder |
drafts/linkedin |
The queue |
bashCms.ledger.path |
.github/linkedin-log.json |
The shared ledger |
bashCms.publish.allow |
false |
The master gate |
bashCms.publish.acceptStatuses |
pending, approved |
Set to approved only to require the in-editor approval step |
bashCms.guard.bannedPatternsFile |
— | Extra banned patterns ([{name, pattern, flags?}]) |
bashCms.env.readDotEnv |
true |
Read LINKEDIN_* from the workspace .env as a fallback |
Boundaries
Four rules this tool is built around, in order of importance:
- Every post is approved by whoever's name goes on it. There is no unattended path to the live page.
- Each account is authorized by its own owner's consent — a token the owner minted, stored in SecretStorage.
- The audience you write for is declared by you — never derived from connections, followers, or anyone's profile data.
- Everything read back is your own aggregate numbers. No member-level data enters the catering lanes.
Security notes
- Tokens live in VS Code SecretStorage (the OS keychain). The workspace
.envis a read-only fallback for repos that already use one; the extension never writes secrets to disk. - LinkedIn access tokens last 60 days and programmatic refresh is limited to LinkedIn partners — Who Am I tells you the days remaining, and the ledger records the expiry so a CI health-check can warn too.
- The HTTP client never puts the token in an error message or a log line.
Development
npm install
npm run compile # type-check + lint + both bundles
npm test # unit + golden + MCP stdio + integration (downloads VS Code 1.101)
npx @vscode/vsce package # build the .vsix
The test suite includes golden cross-lane fixtures generated by the Python publishing lane (src/test/fixtures/golden/) — the ledger bytes, payload previews, and catering worklists must match that lane byte-for-byte, because both write the same files in the same repos. Regenerate with python3 src/test/fixtures/golden/generate.py <path-to-bashconsultants>.
License
MIT — © Amr Abdel-Motaleb, BASH Consulting.
Installing BASH CMS
This server has no published package — it is built from source. Open the repository and follow its README.
▸ github.com/bamr87/bash-cmsFAQ
Is BASH CMS MCP free?
Yes, BASH CMS MCP is free — one-click install via Unyly at no cost.
Does BASH CMS need an API key?
No, BASH CMS runs without API keys or environment variables.
Is BASH CMS hosted or self-hosted?
Self-hosted: the server runs locally on your machine via the install command above.
How do I install BASH CMS in Claude Desktop, Claude Code or Cursor?
Open BASH CMS on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.
Related MCPs
GitHub
PRs, issues, code search, CI status
by GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
by mcpdotdirectCompare BASH CMS with
Not sure what to pick?
Find your stack in 60 seconds
Author?
Embed badge for your README
Browse similar
All development MCPs
