MCP / AI agent integration

Inkie exposes a Model Context Protocol (MCP) server so AI agents - Claude, Cursor, Windsurf, and others - can create and schedule content directly.

Endpoint

https://app.inkie.ink/api/mcp

This is a Streamable HTTP MCP endpoint (MCP spec 2025-03-26). A legacy SSE endpoint is still available at /api/mcp/sse for clients that haven't migrated yet.

Tool discovery: https://app.inkie.ink/.well-known/mcp/manifest

Authentication

Inkie's MCP server speaks OAuth 2.1. Most modern clients (Claude, Claude Code, Cursor) handle this automatically - on first connect they pop a browser window, you sign in to Inkie, and the client stores the token from there on. You don't need to set an Authorization header by hand.

If your client doesn't speak OAuth yet, fall back to an API key: get one from Settings → API keys and pass it as a Bearer token:

Authorization: Bearer YOUR_API_KEY

Set up in Claude (web and Desktop)

Claude connects to remote MCP servers through custom connectors, not through claude_desktop_config.json. In Claude, open Settings → Connectors → Add custom connector, enter the URL below, and save:

https://app.inkie.ink/api/mcp

Then click Connect. A browser window opens for you to sign in to Inkie and approve access; after that Claude remembers you. No API key or header is needed.

Set up in Claude Code

claude mcp add --transport http inkie https://app.inkie.ink/api/mcp

Then run /mcp inside Claude Code and choose Authenticate for inkie. A browser window opens for sign-in; once approved the token is stored.

Set up in Cursor

In Cursor settings → MCP, add a new server:

  • Name: Inkie
  • Transport: HTTP
  • URL: https://app.inkie.ink/api/mcp

No header to set - Cursor will negotiate OAuth on first request.

Available tools

ToolWhat it does
create_social_postSchedule a social post with complete content
generate_social_postGenerate a social post with AI from a brief
get_postRetrieve a social post's details
get_post_statusPoll generation progress
update_social_postUpdate the caption, short-form caption or target platforms on an existing social post (or a blog's promo post)
create_blog_postSchedule a blog post with complete content
generate_blog_postGenerate a blog post with AI from a topic
get_blogRetrieve a blog post's details
get_blog_statusPoll generation progress
update_blogUpdate an existing blog post's title, body, meta description, slug or published status
delete_blogPermanently delete a blog post and its content-plan item. Refuses a blog whose promo social post has already been published, so read the item's state first
get_content_planGet the content plan for a given month
get_platformsList enabled publishing platforms
attach_video_assetAttach an already-uploaded, ready video to a content item, replacing any existing image or video
attach_image_assetAttach an already-uploaded image to a content item, replacing the targeted blog or social image
update_imageReplace the image on an existing content item (blog or social post) with a new image from a public URL
delete_itemPermanently delete a content-plan item of any type (social post, carousel or blog). Refuses items that already have published posts, so read the item's state first
reschedule_itemMove a content item to a new scheduled date, including across months, if it's not yet published
list_onboarding_docsList all onboarding documents for the client, with completion status
get_onboarding_docGet an onboarding document's content by reference ID
update_onboarding_docUpdate or create an onboarding document's content
get_onboarding_promptGet the AI prompt used to guide an onboarding conversation for a given document
add_content_ideaCapture a content idea for the current month; the content planner will use it
list_content_ideasReturn the Content Ideas document for the current (or specified) month

Attaching a video

A video is uploaded and processed separately, then attached to an existing content item. The flow is four steps:

  1. Request an upload with POST /api/video-upload-url, sending { filename, fileSize }. It returns { uploadUrl, uploadId, clientImageId }. Keep both ids: uploadId is for polling, clientImageId is for attaching.
  2. Upload the video bytes with PUT to the returned uploadUrl.
  3. Poll GET /api/video-upload-url?assetId=UPLOAD_ID, passing the uploadId from step 1 (not the clientImageId), until it reports the video as ready. A still-processing video is rejected.
  4. Call attach_video_asset with { contentItemId, clientImageId }. The contentItemId is the same id that get_blog and get_post return.

Like the image route, the video upload route accepts an Inkie API key or an OAuth access token as a Bearer token, as well as a signed-in browser session.

Attaching an image

Use attach_image_asset to attach a local image to an existing content item. Upload the image first, then attach it. Images need no processing or readiness polling:

  1. Request a signed upload URL with POST /api/image-upload-url, using Authorization: Bearer YOUR_API_KEY and Content-Type: application/json. This route accepts an Inkie API key or an OAuth access token as a Bearer token, as well as a signed-in browser session. Send { filename, fileSize, contentType }: filename is the original filename, fileSize is the positive size in bytes, and contentType is the image's MIME type. Allowed types are image/png, image/jpeg, image/gif, image/webp and image/svg+xml. The maximum size is 10 MiB (10,485,760 bytes). The response contains { clientImageId, uploadUrl, token, path }.
  2. Upload the raw image bytes with PUT to the returned uploadUrl, setting Content-Type to the image's MIME type. Send no Authorization header: the signed token is already in the URL's query string. Wait for a 200 response before attaching.
  3. Call attach_image_asset with { contentItemId, clientImageId, target }. Use the clientImageId from step 1 and the content item ID returned by get_blog or get_post. The tool verifies that the uploaded bytes exist in storage.

target is optional. For blog items, "blog" (the default) replaces the main featured image, while "social" replaces the blog's promo social image. Only the chosen image is replaced. For standalone social posts, target is ignored and the post's image is replaced.

If the image is already available at a public URL, use update_image with { blogId, imageUrl, target } instead. It imports the image from that URL, so no signed upload is needed. Despite its name, blogId is the content item ID for either a blog or a social post.

Example prompt

Once connected, you can instruct your AI agent naturally:

"Schedule a LinkedIn post for next Tuesday at 9am: 'Three things we learned shipping our first AI-powered feature. Thread below.' Use the Inkie MCP tool."

Discovery manifest

The manifest at /.well-known/mcp/manifest lists all tools and their schemas. It is supplementary metadata: MCP clients discover tools through the standard tools/list call on /api/mcp, which returns the same list.

See also