Inkcast
OfficialPublishes, cross-posts, schedules and drafts markdown stories on DEV (dev.to, or any Forem community via a custom API URL). Applies DEV's formatting rules (lowercase alphanumeric tags, no title in the body) and supports series, organization destinations and canonical URLs, with per-platform result reporting and automatic retries on rate limits or server errors.
Publishes, cross-posts and schedules markdown stories to Medium. Handles Medium-specific formatting (title/subtitle embedded in the body, 3-tag limit), supports publishing to publications by name, slug or id, and per-post options such as tags, canonical URL, cover image and publish status (public, draft or unlisted). Without an integration token it returns Medium's manual "Import a story" steps, which also set the canonical link.
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., "@Inkcastpublish docs/blog/why-we-built-an-agent.md to Medium and DEV with canonical to my blog"
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.
Inkcast
Write once in markdown. Publish everywhere — from your AI assistant.
Docs · Start here: bootstrap · API reference · Licensing · Commercial licences · Contributing · Security
Inkcast is an MCP server that lets Claude Code, or any MCP client, publish, cross-post and schedule your writing on Medium, DEV and more. You manage it all from the terminal, with a canonical link back to your own blog on every copy.
> Publish docs/blog/why-we-built-an-agent.md to Medium and DEV with the subtitle
"Lessons from year one" and the canonical URL pointing to our blog
● inkcast_publish (dry run) → per-platform preview checked
● inkcast_publish → “Why we built an agent” — Lessons from year one
- medium: published → https://medium.com/@ada/why-we-built-an-agent-…
- devto: published → https://dev.to/ada/why-we-built-an-agent-…Why it's built this way
Principle | What it means in practice |
Plug-in platforms | Each platform is a class behind one small interface. Medium and DEV ship built in; others install as Python packages through the |
Pluggable sign-in | OpenID Connect (Keycloak recommended), a GitHub OAuth app, or a single password for local use. With Keycloak, Google/GitHub/Microsoft social login and account linking happen in Keycloak, not in Inkcast. |
Secrets never touch the AI | Platform keys are entered on a one-time browser page, never pasted into chat. They are encrypted at rest. OAuth codes and tokens are stored only as SHA-256 hashes. |
Explicit consent | Every MCP client that registers itself is shown a consent page before it gets a token. Codes are single-use; refresh tokens rotate. |
Platform-correct output | Each plug-in knows its platform's rules. Medium needs the title and subtitle inside the body and uses only 3 tags. DEV wants lowercase alphanumeric tags and no title in the body. Writers in a Medium publication can only create drafts. |
Own scheduler | Scheduled posts live in Inkcast's database and go out on time. Rate limits and server errors are retried automatically. |
More detail on the design is in docs/architecture.md.
Related MCP server: Substack MCP Server
Platforms
Platform | Status | Credentials | Notes |
Medium | ✅ built in | Integration token | Medium stopped issuing new tokens on 1 Jan 2025; tokens created earlier still work. Without one, Inkcast gives you Medium's Import a story steps instead. That import also sets the canonical link. |
DEV (dev.to) | ✅ built in | API key | Works with any Forem community via |
X, Instagram, TikTok | 🧭 designed | OAuth per platform | Needs per-platform OAuth, a media pipeline, async publishing and platform review gates. See the social epic. |
Hashnode, WordPress, Ghost, … | 📋 planned | See docs/issues/. |
Tools
Tool | What it does |
| Lists the platforms and which ones you've connected. |
| Returns a one-time link where you enter a platform's credentials. |
| Deletes your stored credentials for a platform. |
| Lists Medium publications, or finds DEV organizations. |
| Publishes now, schedules, or creates drafts on one or more platforms. Supports |
| Lists your scheduled, published, draft, failed and cancelled posts. |
| Cancels a scheduled post. |
REST API and docs
Every tool is also a REST endpoint under /api/v1, with a Bearer token carrying the inkcast scope.
URL | What |
| Swagger UI |
| ReDoc reference |
| OpenAPI 3.1 document |
Both doc pages render from vendored, hash-locked bundles in src/inkcast/static/vendor:
Pinned versions: Swagger UI 5.32.11 and ReDoc 2.5.3, pinned in
ASSETS.lock.No CDN: nothing is fetched at run or build time, so the pages work on air-gapped clusters.
Strict CSP: the pages carry a strict Content-Security-Policy with no inline scripts.
Long-lived caching: assets are served with an ETag and a one-year
immutablecache under a content-hash URL.
scripts/vendor-assets.sh verifies the bundles offline and upgrades them from npm, checking npm's published integrity digest, never a CDN.
Quick start (local, single user)
pip install . # Python ≥ 3.11
export INKCAST_LOGIN=password INKCAST_PASSWORD='a long passphrase here' \
INKCAST_SECRET_KEY=$(python -c 'import secrets;print(secrets.token_urlsafe(48))')
inkcast # serves http://localhost:8787/mcpThen set it up in Claude Code:
Add the server once:
claude mcp add --transport http inkcast http://localhost:8787/mcpRun
/mcp, select inkcast, choose Authenticate, sign in and approve.Ask "connect DEV to Inkcast", open the link Claude gives you, and paste your DEV API key.
Ask "publish posts/hello.md to DEV".
Claude reads the file and passes its text to Inkcast. Inkcast never touches your filesystem, so it works the same locally or hosted.
Hosted (team or SaaS) with Keycloak
1. Set up Keycloak
Create a realm, e.g.
inkcast.Clients → Create client, OpenID Connect, client ID
inkcast:Client authentication on, Standard flow on.
Valid redirect URI:
https://inkcast.example.com/auth/callback.Under Advanced, set PKCE method to S256.
Copy the client secret.
Realm roles → Create
inkcast-user. Assign it to the people who may use Inkcast. For open SaaS sign-up, add it to the realm's default roles.Optional social login: Identity providers → Google / GitHub / Microsoft…. Keycloak's first broker login flow links a social account to an existing user with the same verified email. Inkcast always sees one stable user ID per person.
2. Run Inkcast on OpenShift or Kubernetes
oc new-project inkcast
oc create secret generic inkcast-secrets \
--from-literal=secret-key="$(openssl rand -base64 48)" --from-literal=oidc-client-secret=…
# edit deploy/kustomize/base/kustomization.yaml (public URL, OIDC issuer, image) and the Route host, then:
oc apply -k deploy/kustomize/overlays/openshift # or: kubectl apply -k deploy/kustomize/overlays/kubernetesWhat the manifests set up:
Pod security: restricted, i.e. arbitrary non-root UID, read-only root filesystem, no capabilities.
Network: default-deny NetworkPolicy, plus optional FQDN egress allow-lists (Cilium, or OVN EgressFirewall).
Probes and metrics:
/livezand/readyzprobes, with Prometheus metrics on a separate, unrouted port.Exposure: an edge-TLS Route with HSTS on OpenShift, or a Gateway API HTTPRoute on Kubernetes.
Optional components add a ServiceMonitor, an ExternalSecret and a Kyverno image-signature policy. The cloud-native design is in docs/architecture.md; the build and deploy pipeline is in docs/cicd.md.
Without Kubernetes:
docker run -d --env-file .env -v inkcast-data:/data -p 8787:8787 ghcr.io/inkcasthq/inkcast:0.1.0Put it behind HTTPS. Then everyone adds it with:
claude mcp add --transport http inkcast https://inkcast.example.com/mcpNo Keycloak yet? Set INKCAST_LOGIN=github with a GitHub OAuth app. Its callback URL is https://inkcast.example.com/auth/callback. Restrict access with GITHUB_ALLOWED_ORGS or GITHUB_ALLOWED_USERS.
Writing a story
Front matter is optional. Arguments in your request override it, and a per-platform block overrides both for that platform:
---
title: Why we built an agent
subtitle: Lessons from year one # Medium: shown under the title · DEV: description
tags: [ai, agents, developer-tools] # each platform applies its own limits, with warnings
canonical_url: https://blog.example.com/why-we-built-an-agent
cover_image: https://blog.example.com/img/cover.png
platforms: [medium, devto]
publish_at: 2026-10-21T09:00:00-05:00 # omit to publish now; a UTC offset is required
image_base_url: https://blog.example.com/why-we-built-an-agent/ # fixes relative image paths
medium:
title: A punchier title for Medium
destination: Better Programming # publication by name, slug or id
publish_status: unlisted # public | draft | unlisted
devto:
destination: my-org # organization username or id
series: Building agents
---
# Why we built an agent ← used as the title if none is given; never duplicated
Your story…Some behaviour to know:
Partial failure: a failure on one platform doesn't stop the others. The reply lists the result per platform.
Preview first:
dry_runshows exactly what each platform will receive.
Configuration
All settings are environment variables; see .env.example.
Development
pip install -e . 'playwright==1.56.0' && python -m playwright install chromium
PYTHONPATH=src:tests python -m unittest discover -s tests -vCI runs on every pull request (docs/cicd.md):
lint and the vendored-asset lock;
the test suite on Python 3.11–3.13;
rendering of every manifest overlay;
an image build and vulnerability scan;
a kind deployment of the real image under Pod Security
restricted;CodeQL.
Merges to main publish a signed image (cosign keyless), with an SBOM and SLSA provenance, and open a staging promotion PR.
The end-to-end test starts fake versions of a Keycloak-style identity provider, the Medium API and the DEV API. An unmodified MCP SDK client then:
Signs in the way Claude Code does: discovery, dynamic client registration, PKCE, then the consent page.
Connects both platforms through the connect page.
Cross-posts and schedules stories.
Runs the scheduler, including a rate-limit retry, a Medium-writer draft and a DEV organization post.
Lists and cancels posts.
Other tests cover:
the REST API, OpenAPI document and docs pages, including a real-browser render under the CSP;
duplicate-safe publishing (an ambiguous 5xx is never retried);
probes and metrics;
the vendored-asset lock.
Real Keycloak, Medium and DEV have not been exercised by the test suite yet. Try a dry_run and a draft first.
Status
Early: v0.1, single node, SQLite. The path to the hosted SaaS — Postgres, workspaces, an admin portal on Keycloak, API tokens on sign-up, a mobile app — is in docs/product-spec.md and docs/issues/.
Documentation
Everything is indexed in docs/README.md: product spec, architecture, CI/CD, plug-in guide, roadmap and legal.
License
Inkcast is source-available: PolyForm Shield 1.0.0, plus an additional permission for consultants.
Licence | |
Publishing your own content, self-hosting, modifying, building plug-ins | Free |
Consultants setting it up for one client's own content | Free |
Publishing for clients (agencies), selling Inkcast-powered services, embedding it in your product or SaaS, hosting or white-labelling it | Commercial |
Details and a quick check: LICENSING.md. Contributions require the CLA. "Inkcast" is a trademark of ScalarOps Technologies LLC (TRADEMARKS.md).
Copyright 2026 ScalarOps Technologies LLC.
This server cannot be deployed
Maintenance
Related MCP Connectors
Publish and share access-controlled Markdown documents from any MCP-enabled AI tool.
Publish Markdown or HTML to a shareable link from your AI assistant. OAuth, no API keys.
Connect any AI agent to 11+ social platforms: schedule, publish & track posts via hosted MCP.
Publish AI-generated HTML & Markdown to a hosted, shareable URL via MCP.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to publish posts to Medium with support for drafts, tags, canonical URLs, and follower notifications. Supports various publishing statuses and SEO optimization features.9 npm2MIT
- AlicenseNot gradedqualityDmaintenanceEnables programmatic management of Substack content, including creating drafts, publishing posts, and uploading images. It supports specialized features like live blogging and posting to Substack Notes through MCP-compatible AI tools.MIT
- AlicenseAqualityCmaintenanceCross-publishes a single markdown draft to 5 CMS platforms (dev.to, Hashnode, Ghost, WordPress, Medium) and 4 social networks (Bluesky, Mastodon, LinkedIn, X) from any MCP client, with built-in SEO scoring, schema.org JSON-LD, and canonical URL handling so cross-posts compound search rankings instead of competing.2954 npm5MIT
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to create, update, delete, and query posts on any Micropub-compatible blog with IndieAuth authentication.1-