
One SKILL.md, Every Agent: How to Give Any AI Agent the Ability to Post to X
Short answer: One markdown file with a small YAML header, dropped into a skills directory, teaches more than 30 different AI agents to post to X. You write it once. Claude Code, Codex CLI, Cursor, Gemini CLI, GitHub Copilot and OpenClaw all read the same format without modification.
The qualifier: a skill is instructions, not a connection. The file below tells your agent how to post. The actual posting happens through the OpenTweet REST API, which holds your X connection, so there is no X developer account and no OAuth app in the loop.
The 30-Second Version
Three things:
- The file. Copy the SKILL.md further down this page.
- The directory. Save it as
post-to-x/SKILL.mdinside your agent's skills folder. For Claude Code that is.claude/skills/. - The key.
export OPENTWEET_API_KEY=ot_your_keyfrom your developer page.
Then say: "Draft a tweet about the release and schedule it for 9am tomorrow." Your agent finds the skill, reads it, and calls the API.
What SKILL.md Is (And Why It Matters More Than MCP Here)
The SKILL.md spec was published at agentskills.io on December 18, 2025 as an open, vendor-neutral format. It is deliberately unglamorous: a markdown file, a name, a description, and a body. The description is the routing layer. The agent reads it to decide whether the skill is relevant to what you just asked, and only then loads the body into context.
That design is why adoption moved so fast. There is no SDK, no runtime and no protocol handshake to implement. Supporting skills means "read markdown files from a directory," which is a weekend of work for an agent vendor. By March 2026 more than 30 tools were reading the same files, and Google's Antigravity shipped support in January 2026.
For anyone trying to make an agent do a specific job, that is the whole point. Write one file, and every agent that follows the convention can do the job. No per-agent plugin, no per-agent config schema.
Which agents read SKILL.md today
As of September 2026, the format is read by, among others:
| Category | Agents |
|---|---|
| Terminal agents | Claude Code, Codex CLI, Gemini CLI, OpenCode, Amp, Goose |
| Editors and IDEs | Cursor, GitHub Copilot, Google Antigravity, Trae, Roo Code, Junie, Kiro |
| Autonomous frameworks | OpenClaw, Hermes Agent, OpenHands, Letta |
If your agent is not on that list, check its docs for a skills directory before assuming it is unsupported. The count keeps moving. For a fuller breakdown of which agents can actually publish to X versus which can only draft, see which AI agents can post to X in 2026.
Skill vs MCP: when to use which
They solve different halves of the same problem.
| You want | Use |
|---|---|
| One tightly scoped job with your own rules attached | A SKILL.md |
| Typed tools, structured arguments, live data back | An MCP server |
| Posting policy: voice, cadence, never-publish-directly | A SKILL.md |
| The full surface: threads, evergreen, articles, analytics | MCP, currently 43 tools |
| Something that also works in a shell script or CI | The REST API directly |
The honest recommendation: if you only want your agent to post and schedule, the skill is enough and it is 40 lines. If you want the agent to read analytics before deciding what to post, you want MCP. Running both is fine, and it is what I do. The MCP server supplies the tools, the skill supplies the rules.
One thing to be clear about, because other pages get this wrong: OpenTweet does not publish a skills-marketplace package, and there is no npx one-liner that installs an OpenTweet skill. Any page telling you to run one is inventing it. The file below is the artifact. Copy it.
The Skill, In Full
Save this as SKILL.md inside a folder named post-to-x.
---
name: post-to-x
description: Post, schedule, and thread on X (Twitter) through the OpenTweet API. Use when the user asks to tweet, post to X, schedule a post, or announce something on Twitter.
---
# Post to X
Post through the OpenTweet REST API. The key is in the OPENTWEET_API_KEY
environment variable. Base URL: https://opentweet.io
## Check first, always
curl -s https://opentweet.io/api/v1/me \
-H "Authorization: Bearer $OPENTWEET_API_KEY"
Confirm subscription.has_access is true and limits.remaining_posts_today
is above zero before scheduling or publishing.
## Schedule a tweet (preferred default)
curl -X POST https://opentweet.io/api/v1/posts \
-H "Authorization: Bearer $OPENTWEET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Tweet text here",
"scheduled_date": "2026-09-10T09:00:00Z",
"platforms": ["x"]}'
## Publish immediately (ask the user first)
curl -X POST https://opentweet.io/api/v1/posts \
-H "Authorization: Bearer $OPENTWEET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "Tweet text here", "publish_now": true,
"platforms": ["x"]}'
## Post a thread
curl -X POST https://opentweet.io/api/v1/posts \
-H "Authorization: Bearer $OPENTWEET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "First tweet", "is_thread": true,
"thread_tweets": ["Second tweet", "Third tweet"],
"scheduled_date": "2026-09-10T09:00:00Z"}'
## Rules
- 280 characters per tweet. Count before sending, do not truncate silently.
- Never send publish_now and scheduled_date in the same request.
- scheduled_date is ISO 8601 and must be in the future.
- Always show the drafted text to the user and wait for approval
before publishing. Publishing to X cannot be undone.
- Default to scheduled_date. Only use publish_now when asked for it.
- Omitting "platforms" does not mean X only. It hands targeting to the
user's cross-post setting. Send ["x"] when the user asked for X only.
- Report the id from the response. It is a 24-character hex string.
If you cannot show one, the call did not happen.
- A published post returns status "posted" and a url field. Use that
url. Never construct an X URL yourself.
- 403 means no subscription or no connected X account. 429 means a rate
or daily limit was hit. Report the error. Do not retry blindly.
- Rate limits: 60 requests per minute on Pro, 300 on Advanced.
Frontmatter
Two fields carry all the weight. name must match the folder name. description is the only part the agent reads before deciding to load the skill, so it should contain the words a user would actually say: "tweet," "post to X," "schedule," "Twitter." A vague description means the skill never fires.
The body: what the agent needs to know
Everything else is context that gets loaded once the skill is selected. Four things belong in it:
- The endpoint and the auth header.
POST /api/v1/postswithAuthorization: Bearer $OPENTWEET_API_KEY. Note the path: it is/api/v1/posts, not/tweets. - The fields.
textis required. Addscheduled_dateto schedule,publish_now: trueto send now,platformsto pin the networks,is_threadplusthread_tweetsfor threads. - The limits. 280 characters per tweet. Dates must be ISO 8601 and in the future. 60 requests per minute on Pro, 300 on Advanced.
- The failure modes. A 403 means no active subscription or no connected X account. A 429 means a rate limit, a daily post limit, or the separate daily cap on posts containing links. Telling the agent what these codes mean is the difference between a useful error message and a retry loop.
The ## Rules section is the real payload. It is where posting policy lives, and it is the reason to write a skill instead of just handing the agent an API doc. Extend it with your own voice guidelines, banned topics, or a hard "never publish on a Friday" rule.
Where to put the file
| Agent | Path |
|---|---|
| Claude Code, project | .claude/skills/post-to-x/SKILL.md |
| Claude Code, everywhere | ~/.claude/skills/post-to-x/SKILL.md |
| Cross-vendor convention | .agents/skills/post-to-x/SKILL.md in the project root |
Agents that follow the shared convention read .agents/skills/ from the project root, which is what makes the file portable. Some vendors also keep a home-directory location for globally available skills. Check your agent's docs for the exact global path rather than guessing, because that is the one detail that still varies between them.
Installing It in Six Agents
Claude Code. Create .claude/skills/post-to-x/SKILL.md, export OPENTWEET_API_KEY, and ask it to tweet. Full walkthrough with both paths on the Claude Code Twitter skill page.
Codex CLI. Drop the file in the project's skills directory and start a session from the project root so it is discovered.
Cursor. Project-local skills folder. Cursor loads skills for the workspace you have open, so put it in the repo you post from.
Gemini CLI. Same shared convention, project root. Confirm the skill loaded by asking "which skills do you have?" before you trust it with a live post.
Google Antigravity. Added skill support in January 2026 and reads the same file.
OpenClaw. Skills go in OpenClaw's skills directory alongside its other capabilities. OpenTweet ships a much larger OpenClaw skill covering evergreen, articles, DM campaigns and analytics, described in the OpenClaw posting guide.
In every case, verify before you trust it. Ask the agent to run the GET /api/v1/me call from the skill. If it returns your handle and your remaining daily posts, the skill loaded and the key works. If it returns 403, the key is wrong or no X account is connected.
Making It Safe
Publishing to X is irreversible. Deleting a tweet is not the same as never having sent it. Three rules keep an agent from embarrassing you:
- Default to
scheduled_date, notpublish_now. A scheduled post sits in a queue you can look at. A published one is already public. The skill above encodes this as an explicit rule, which matters more than you would think, because agents copy whichever example is closest to the request. - Keep a human in the loop for the send. "Show me the draft and wait" is one line in the rules section and it removes the entire class of failure where the agent posts a half-finished thought.
- Never hardcode the
ot_key in the file. Use$OPENTWEET_API_KEY. Skills get committed to repos and pasted into issues. Keys in markdown end up on GitHub.
One more, less obvious: be explicit about platforms. Leaving it out does not mean "X only," it hands targeting to whatever cross-post setting your account has. If you have Bluesky connected and the agent omits the field, the post goes to both. Sending "platforms": ["x"] is always honored exactly.
When You Outgrow the Skill
A skill is a good fit for one job. Once you want the agent to do several, the markdown starts fighting you, because every additional endpoint is more context loaded on every invocation.
That is the point to switch to the MCP server. It exposes 43 typed tools: threads up to 25 tweets, batch scheduling up to 50 posts at once, the evergreen recycling queue, X Articles from markdown, best-posting-time analysis, content gaps, multi-account targeting and engagement analytics. The agent gets structured arguments and structured responses instead of parsing curl output.
Setup is one command in most clients. See the MCP server docs or how to give an MCP agent a Twitter tool. If you are wiring this into a framework rather than a chat client, tweeting from the OpenAI Agents SDK covers the same API from code.
Frequently Asked Questions
What is a SKILL.md file and how do I write one?
A plain markdown file with a YAML frontmatter block containing name and description. The agent reads the description to decide when the skill applies, then loads the body as instructions. Write the job in the frontmatter, the exact commands and rules in the body. No build step, no code.
Is there a skill that lets any AI agent post to Twitter?
Yes, and it is the file in this post. Because the format is read unmodified across more than 30 agents, one file covers Claude Code, Codex CLI, Cursor, Gemini CLI, OpenClaw and the rest. Paste it, set OPENTWEET_API_KEY, ask your agent to post.
Do I need an X developer account?
No. The skill calls OpenTweet, which holds the X connection. You connect X once in the dashboard. No developer application, no OAuth app, no per-post X API billing.
How much does it cost?
OpenTweet Pro is 11.99 per month and includes API access, which is what the skill uses. There is a 7-day free trial. Creating drafts through the API is free; scheduling and publishing need an active subscription.
Why does my agent ignore the skill?
Almost always the description. If it does not contain the words you actually say, the agent never routes to it. Make sure "tweet," "post to X," "schedule" and "Twitter" all appear in it. Second most common cause: the file is in a project-local directory and you started the agent from a different folder.
Can the skill post threads?
Yes. is_thread: true plus a thread_tweets array, up to 25 tweets, each under 280 characters. The example above includes it.
Get Started
- Sign up for OpenTweet (7-day free trial, no credit card) and connect your X account.
- Get your API key. It starts with
ot_. - Copy the SKILL.md above into
post-to-x/SKILL.mdin your agent's skills directory. export OPENTWEET_API_KEY=ot_your_key- Ask your agent to draft and schedule a post. Check the
idit reports back. - For the full walkthrough with per-agent detail, read how to write a SKILL.md that posts to X.
Pricing on the pricing page. The endpoint reference the skill is built on is in the quickstart.
The OpenTweet MCP server is open-source and available on npm. Learn more at opentweet.io/mcp.
Start Scheduling Your X Posts Today
Join hundreds of creators using OpenTweet to stay consistent, save time, and grow their audience.