AI Content Drop MCP Server
OAuth sign-in has been verified. Follow the connection steps below.
Two servers, one protocol. Both speak Streamable HTTP and need no SDK.
| Endpoint | Tools | Credential | For |
|---|---|---|---|
POST https://aicontentdrop.com/mcp |
42 | optional | Doing things: catalogue, guides, and generation |
POST https://aicontentdrop.com/mcp/docs |
2 | none | Learning things: documentation retrieval only |
Compatibility discovery manifest: /.well-known/mcp.json. The JSON claims no experimental server-card schema. Human landing page with per-host install steps: /mcp.
Connecting
{
"mcpServers": {
"aicontentdrop": {
"type": "http",
"url": "https://aicontentdrop.com/mcp",
"headers": { "Authorization": "Bearer acd_live_…" }
}
}
}
Drop the headers block and the public read tools still work; the account tools stay listed but answer with AUTH_REQUIRED and a pointer to the sign-in instead of running.
Hosts that support OAuth can manage the authorization header for you. The intended flow, subject to the readiness notice above, reads the protected-resource metadata at /.well-known/oauth-protected-resource/mcp, whose resource is https://aicontentdrop.com/mcp, and runs an OAuth sign-in against the issuer it names with the scopes openid email profile offline_access. The token grants the account tools permitted by that connection. An acd_live_ key in the Authorization header is the route for clients that cannot open a browser.
Tools
| Tool | Key | What it does |
|---|---|---|
list_models |
no | Catalogue with flat credit costs |
estimate_credit_cost |
no | Quote a model, with duration and resolution for per-second video models; returns a signed quote_id |
search_articles |
no | Keyword search over published guides |
get_article |
no | Full article text as markdown |
generate_video |
yes | Start a video generation from a quote_id; returns a job id |
generate_image |
yes | Start an image generation from a quote_id; returns a job id |
get_generation |
yes | Poll one job; kind is "video" or "image" |
list_generations |
yes | Recent jobs, newest first |
get_account |
yes | Plan name, credits remaining, and an info_url for plan information |
Studio tools
Where the studio surface is enabled, tools/list also carries the tools that make a finished ad, a UGC presenter video or an avatar. They follow the same contract: the reads answer anonymously, the three that render need a sign-in and a quote_id from estimate_ad_cost, and the marketing-studio-ad skill walks the whole flow.
| Tool | Key | What it does |
|---|---|---|
list_ad_formats |
no | Every ad format with its aspect ratio, duration range and shot range |
list_ugc_avatars |
no | The UGC presenter catalogue, with a portrait each |
estimate_ad_cost |
no | Quote an ad, a UGC video or an avatar; returns a signed quote_id |
write_ad_storyboard |
yes | A shot-by-shot storyboard from a brief, to review before pricing |
generate_ad |
yes | Start a finished ad from a quote_id; returns a job id |
get_ad_job |
yes | Poll one ad job: stage, percent, storyboard sheet, captioned video, credits charged |
generate_ugc_video |
yes | Start a UGC presenter video from a quote_id; image_rights_consent must be true |
generate_avatar |
yes | Create a presenter avatar from a quote_id |
import_asset |
yes | Copy a public https: image onto the platform for an ad to use |
list_brand_profiles |
yes | The account's saved brand profiles |
import_brand_profile |
yes | Read a website into a saved brand profile |
Board and research tools
Where the board surface is enabled, tools/list also carries the Visual Canvas as tools, and competitor-ad research that lands on it. Every board and every saved ad is account data, so all of these need a sign-in. Adding cards costs nothing; run_generation_card renders a card only with a quote_id from estimate_credit_cost, and decode_ad takes one from estimate_ad_cost with kind decode. A model never tracks a board's revision: every mutating tool reads the current one when it is omitted. The canvas-board skill walks the flow.
| Tool | Key | What it does |
|---|---|---|
list_canvases |
yes | The account's boards, newest first, with revision, card count and page link |
create_canvas |
yes | A new, empty board |
get_canvas |
yes | One board in full: cards, edges, revision |
rename_canvas |
yes | Rename a board |
get_canvas_link |
yes | The board's page, for hosts that cannot render the board inline |
add_text_card |
yes | A note, idea or script line; the server places it |
add_media_card |
yes | An image or video card from a public https: URL |
add_frame |
yes | A labelled region |
add_generation_card |
yes | A draft prompt and model; nothing renders until it is run. With from_card_id, connected to an image card whose finished image is its start frame |
connect_cards |
yes | Draw a line from one card to the next, so the board reads as a flow |
update_card |
yes | Change a card's data, position or size |
move_cards |
yes | Move cards; the board view's drag (_meta.ui.visibility: ["app"]) |
arrange_cards |
yes | Grid, row or column layout; the board view's multi-select |
group_cards |
yes | Wrap cards in a labelled frame |
run_generation_card |
yes | Render a card from a quote_id; the result lands in the card |
refresh_canvas |
yes | Re-read a board while a card is rendering |
search_competitor_ads |
yes | Search the public ad libraries; demo: true with a note when the results are sample data |
list_saved_ads |
yes | The account's saved ads |
get_saved_ad |
yes | One saved ad with what was decoded from it |
save_competitor_ad |
yes | Keep one ad from a search |
decode_ad |
yes | Turn a saved ad into a brief from a quote_id |
ad_to_canvas_card |
yes | Place a saved ad on a board as a media card carrying its brief |
Video agent and drama tools
Two more sets need a signed-in account, so a caller without one never sees them in tools/list; both are listed to every signed-in account. The drop tools hand a whole brief to Drop, the video agent, which researches, plans, renders and assembles on a board and stops before every step that charges credits (the video-agent skill). The drama tools write a short vertical drama from a premise, price and render its clips, and cut the episode (the drama-studio skill); the same dramas are open to everyone on the website at Drama Studio.
| Tool | Key | What it does |
|---|---|---|
start_video_agent |
yes | Open a drop from a one-sentence goal; nothing is planned or charged yet |
message_video_agent |
yes | Send the next message; a turn longer than about 40 seconds comes back as still running |
get_video_agent_run |
yes | Phase, status, latest reply, artifacts, board link and the decision it is waiting on |
approve_video_agent_spend |
yes | The only call that lets a drop charge: approve or decline one step by its exact quote_id |
list_video_agent_runs |
yes | The account's drops, newest first |
start_drama |
yes | Write a drama from a premise; the draft lands in 3 to 5 minutes |
get_drama |
yes | The drama in full: cast, episodes, clips, a draft waiting for review, finished cuts |
list_dramas |
yes | The account's dramas, newest first |
revise_drama |
yes | A new draft of the drama or one episode |
review_drama_draft |
yes | Accept or reject a draft |
quote_drama_episode |
yes | One quote per clip, valid for 10 minutes |
produce_drama_episode |
yes | Render clips from their quotes |
assemble_drama_episode |
yes | Cut the episode into one video |
Every tool carries MCP annotations — readOnlyHint, destructiveHint, idempotentHint, openWorldHint — so a client knows which ones need confirmation before it calls them. The generate_* tools, run_generation_card, decode_ad, produce_drama_episode and approve_video_agent_spend are the ones that spend credits, each only with a quote_id, and writing a drama draft reserves a small writing charge; every tool that writes to the account (imports, board cards, saved ads) is marked non-read-only, so a host asks before running it.
Quotes and idempotency
Every generation starts with a quote. estimate_credit_cost normalises the model, duration, resolution and quantity, prices them at the current rate, and returns credits_required, expires_at and a signed quote_id. The quote is valid for 15 minutes and bound to those exact parameters.
generate_video and generate_image require a quote_id with a quantity of 1. Before anything is submitted the server re-verifies the signature, the expiry, the parameters against the ones in the call, and the current price. Any difference is a typed refusal and nothing runs. The quote_id is also the idempotency key: a retry with the same quote_id returns the original job rather than starting a second one, so a dropped connection cannot charge twice.
Host confirmation dialogs are a convenience, not the safety boundary. A client that auto-approves tool calls gets the same server-side checks.
Errors
A failing tool returns a normal result with isError: true and a typed code in the payload, so the model can branch on it and correct its own call:
| Code | Meaning |
|---|---|
AUTH_REQUIRED |
The tool needs a signed-in account or a key; the result points at the sign-in |
INVALID_TOKEN |
The token or key was rejected; sign in again or check the header |
INSUFFICIENT_SCOPE |
The grant does not cover this tool |
ENTITLEMENT_REQUIRED |
The account has no generation entitlement left; carries info_url (https://aicontentdrop.com/plans) |
QUOTE_REQUIRED |
A generation was called without a quote_id |
QUOTE_EXPIRED |
The quote is older than 15 minutes; quote again |
QUOTE_MISMATCH |
The call's parameters or the current price differ from the quote; quote again |
IDEMPOTENCY_CONFLICT |
The same quote_id was reused with different parameters |
UNSAFE_CONTENT |
The prompt or asset failed the content-safety gate; nothing was charged |
UNAUTHORIZED_ASSET |
A referenced upload does not belong to this account |
MODEL_UNAVAILABLE |
The model is not available right now; re-read list_models |
RATE_LIMITED |
Too many requests; when the server knows its cooldown (the per-account generation ladder), details.retry_after_seconds and the next_step sentence say how long to wait before retrying the same call with the same quote_id |
DAILY_CAP_REACHED |
The API key's own daily credit limit is used up; new generations are refused until 00:00 UTC, while reads keep working. Not retryable |
NOT_FOUND |
No such job, article or model |
UPSTREAM_TEMPORARY |
A temporary failure on our side of the render; retry is reasonable and nothing was charged |
INTERNAL_ERROR |
Our bug; retry once, then tell us |
Protocol-level problems (unknown method, malformed message) use JSON-RPC error codes as usual.
Inline UI (MCP Apps)
Tools that return a list or a job also return _meta.ui.resourceUri naming a ui:// resource the host can render inline:
| Resource | Rendered by |
|---|---|
ui://aicontentdrop/model-picker.html |
list_models |
ui://aicontentdrop/article-list.html |
search_articles |
ui://aicontentdrop/generation-card.html |
generate_video, generate_image, get_generation, list_generations |
ui://aicontentdrop/ad-job.html |
generate_ad, get_ad_job |
ui://aicontentdrop/avatar-picker.html |
list_ugc_avatars |
ui://aicontentdrop/board.html |
the board tools: create_canvas, get_canvas, refresh_canvas, the add_* cards, update_card, move_cards, arrange_cards, group_cards, run_generation_card |
ui://aicontentdrop/ad-results.html |
search_competitor_ads, list_saved_ads |
ui://aicontentdrop/drop.html |
the drop tools: start_video_agent, message_video_agent, get_video_agent_run, approve_video_agent_spend, list_video_agent_runs |
ui://aicontentdrop/drama.html |
the drama tools: start_drama, get_drama, list_dramas, revise_drama, review_drama_draft, quote_drama_episode, produce_drama_episode, assemble_drama_episode |
A generation that is still rendering wears an animation (light drifting through a developing frame; film edges for a video, crop marks for an image) on the board's card and in the generation card, and the generation card reads get_generation itself until the result lands. The board can brief the video agent: Agent writes a brief from the board, or from the selected cards with their media as references, and starts a drop with start_video_agent and the board's canvas_id, so what the drop makes can go on that board. Each drop working on the board is drawn beside its cards, with its phases, its question or its price, and Place on board for its finished results. Its paid steps are still released only in the chat.
Every view can be shown inline or fullscreen; each resource says so in _meta["openai/ui"].availableDisplayModes as well as in its ui/initialize. The drop and drama views show a price with the sentence to answer in the chat and carry no button that spends: a paid step is released only by approve_video_agent_spend or produce_drama_episode, which the host confirms like any tool that spends.
Fetch them with resources/read. Each is one self-contained HTML document — no build step, no external script, no framework — and each carries its own policy twice over:
_meta.ui.cspon the resource, which is what a host builds the iframe sandbox from.connectDomainsandresourceDomainsare per view, not shared: the model picker and the article list load nothing external, the generation card names the origins its finished video and image come from, the ad job card names the same origins for its storyboard sheet and video, and the avatar picker names image origins only.- A
<meta http-equiv="Content-Security-Policy">inside the document, so a renderer that never read the_metastill gets a scoped policy.frame-ancestorsnameshttps://chatgpt.comandhttps://claude.ai— a view that inherited this site'sframe-ancestors 'self'would refuse to render in the only places a view is ever framed.
The two are generated from one object, because a card that declares an empty resourceDomains while rendering <video src> asks the host to block the one thing the card exists to show, and does it silently.
Hosts that read _meta["openai/outputTemplate"] instead get the same URI under that name.
Resources and prompts
resources/list also serves llms.txt, the OpenAPI document, and the pricing document, so a client can pull our reference material over the same connection it acts on. prompts/list offers pick_a_model, write_video_prompt and estimate_a_campaign, plus make_an_ad where the studio tools are enabled and plan_a_board where the board tools are.
Protocol notes
- Streamable HTTP at
/mcp:POSTfor JSON-RPC and a short-livedGETevent stream. It is stateless, issues no session id, and answersDELETEwith 405. - Deprecated 2024-11-05 HTTP+SSE at
/mcp/sse(or/mcp/docs/ssefor the docs server):GETopens the held stream and supplies the same-path POST endpoint in its first event. Only this legacy socket-bound transport uses a session id. - Single messages follow negotiated MCP 2025-06-18. JSON-RPC batch arrays are
- Generation never blocks inside a
tools/call. It returns a job id and you poll — a render outlives any request timeout.
also tolerated as a non-standard compatibility extension; accepting them does not negotiate an older MCP protocol revision.
Where this server is published
Listed in the official MCP registry as com.aicontentdrop/ai-content-drop.
| Registry entry | registry.modelcontextprotocol.io/v0/servers?search=aicontentdrop |
| Namespace | com.aicontentdrop — the reverse-DNS form of this domain |
| Ownership proof | /.well-known/mcp-registry-auth |
| Directory listing | Smithery — aicontentdrop/ai-content-drop, same server, same id |
The namespace is not self-asserted. Publishing under com.aicontentdrop required an HTTP domain proof: the registry fetched that well-known path from this origin and checked the Ed25519 public key it serves against the signature on the publish request. The entry points at this domain and this page points back at the entry, so a client can verify the listing in both directions rather than trusting either half alone.
In the browser: WebMCP
The two servers above are for agents that speak HTTP. There is a third surface for an agent that is *already inside the page* — a browser-resident assistant, an extension, or an agentic browser driving a real tab.
The site registers its tools on document.modelContext, the entry point defined by the W3C Web Machine Learning Community Group's WebMCP draft. Same protocol vocabulary as above — named tools, JSON Schema inputs, structured results — with the transport removed. document.modelContext is the canonical surface; navigator.modelContext is a deprecated alias kept for the earliest builds.
// In a page-resident agent, on any page of this site:
const tools = await document.modelContext.getTools();
const picker = tools.find((t) => t.name === "list_models");
const raw = await document.modelContext.executeTool(picker, {
type: "video",
max_credits: 30,
});
const { structuredContent } = JSON.parse(raw);
// structuredContent.models → [{ id, name, credits }, …], cheapest first
executeTool resolves with a JSON string, not an object. Parse it, then read structuredContent for the payload or content[0].text for the same payload as text. Arguments are validated before the tool runs and unknown properties are rejected, so pass only the properties in a tool's inputSchema. Nothing throws: a rejected call resolves the same way with isError: true and an error.code to branch on.
What is registered
The in-page tool names are not the HTTP server's names, and the two surfaces do not carry the same set. Eleven tools register by default: four public ones on every page load, and seven more that appear only while a visitor is signed in.
| Tool | Needs a session | readOnlyHint |
What it does |
|---|---|---|---|
list_models |
no | true |
Video or image catalogue with the flat credit cost of one generation |
estimate_credit_cost |
no | true |
Price one model, optionally times a quantity |
search_articles |
no | true |
Keyword search over published guides |
get_article |
no | true |
One article's metadata and canonical URL |
get_credit_balance |
yes | true |
Credit balance, plan and billing period |
list_credit_activity |
yes | true |
Daily credit spend plus the most recent generations and what each cost |
list_billing_history |
yes | true |
Past payments, with the invoice link where one exists |
list_generations |
yes | true |
Recent video generations, newest first |
get_generation_status |
yes | true |
Poll one video or image job |
generate_video |
yes | false |
Start a video generation; returns a job id |
generate_image |
yes | false |
Start an image generation; returns a job id |
start_credit_topup_checkout |
yes | false |
Off by default. Returns a checkout URL for a credit pack |
Treat getTools() as the authoritative live list rather than this table: the session-scoped tools are registered on sign-in and removed on sign-out, and the toolchange event on document.modelContext fires whenever the list mutates. Unregistration is by AbortSignal passed at registration; the draft has no unregisterTool.
What costs something
Two registered tools spend credits: generate_video and generate_image. Both spend the signed-in user's credits, both are marked readOnlyHint: false, and credits are charged only when a generation succeeds.
start_credit_topup_checkout is the third readOnlyHint: false tool and the only one that touches money. It is off by default, behind the build-time flag VITE_WEBMCP_CHECKOUT_TOOL, and it is absent from getTools() unless a deployment turned it on. Even then it charges nothing on its own: it creates a checkout link for a one-time credit pack and hands the URL back. It never navigates the tab and never completes a purchase — the person does that themselves.
Every other tool is readOnlyHint: true. A host deciding what an agent may call unattended should gate on that annotation rather than on a tool name, because the set of names grows.
Both generation tools accept an optional confirm_credits. Supply the number estimate_credit_cost returned and it is enforced exactly — if the real cost differs, the call returns COST_MISMATCH and nothing is submitted. It is optional by default so an agent's first call is not guaranteed to fail, and a deployment can make it mandatory.
Tools also carry untrustedContentHint, true wherever a result can quote text this site did not write — article copy, a user's own prompt, a model's failure message — which a host should never read as instructions.
Credentials: there are none
This is the whole reason the surface exists. An in-page tool runs as the person already signed in to the tab. It runs on the session the browser already holds, so there is no API key to mint, paste, store, or rotate, and no key that can outlive the tab or leak into an agent's transcript. The tools that touch an account need the visitor signed in; the catalogue and the guides do not.
Nothing about this weakens the account boundary: every call lands on the same authenticated endpoints the UI itself calls, with the same credit accounting and the same permission checks. A tool that spends credits spends the signed-in user's credits, and only the three tools marked readOnlyHint: false change anything.
| In-page (WebMCP) | HTTP (/mcp) |
|
|---|---|---|
| Who it runs as | The signed-in visitor | The owner of the key |
| Credential | None — the browser session | Authorization: Bearer acd_live_… |
| Reachable by | Agents inside the tab | Any client, anywhere |
| Best for | A visitor's assistant acting on the page they are looking at | Servers, CI, scheduled work, non-browser agents |
The HTTP server is not deprecated by any of this, and neither surface is a fallback for the other. It stays the credential-based surface for every agent that is not inside a browser tab, and an agent that cannot open a browser needs the key.
Browser support
document.modelContext is a draft, not a shipped web platform feature. Chrome exposes it behind an origin trial (Chrome 149–156); everywhere else the object is simply absent and this page behaves like any other page. A page-resident agent should feature-detect rather than assume:
if (document.modelContext) { /* tools are available here */ }
Because the trial is per origin, tools registered on this site are visible only on this site's pages, and only while its trial token is live.