Skip to main content
Glama
bamr87

BASH-CMS

by bamr87

BASH-CMS

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 / source frontmatter — 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.dump output, so the file never churns in git.

Related MCP server: linkedin-pages-mcp

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

  1. Install the extension, open your content repo.

  2. Set bashCms.linkedin.orgUrn and bashCms.site.baseUrl (Settings → BASH-CMS).

  3. 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).

  4. Run BASH-CMS: LinkedIn: Who Am I to confirm the token, scopes, and expiry.

  5. 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:

  1. Every post is approved by whoever's name goes on it. There is no unattended path to the live page.

  2. Each account is authorized by its own owner's consent — a token the owner minted, stored in SecretStorage.

  3. The audience you write for is declared by you — never derived from connections, followers, or anyone's profile data.

  4. 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 .env is 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.

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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

View all related MCP servers

Related MCP Connectors

  • Managed LinkedIn MCP server for AI agents: search, connect, message and enrich on accounts you own.

  • MCP server for LeadDelta — manage LinkedIn connections and CRM data via AI assistants.

  • Scrape and analyze public LinkedIn posts as structured JSON via the Apify LinkedIn Posts API.

View all MCP Connectors

Latest Blog Posts

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/bamr87/bash-cms'

If you have feedback or need assistance with the MCP directory API, please join our Discord server