kenari-media-mcp
kenari-media-mcp
An MCP (Model Context Protocol) server for the Kenari.id media APIs (https://kenari.id/v1). It gives any MCP client (Claude Desktop, Cursor, OpenCode, etc.) 8 tools for image generation and editing plus video generation, extension, status polling, and download. It runs over stdio and is designed for agentic use: all generated media is written to disk under a configurable output directory and returned as absolute file paths — never base64 or ImageContent — so the calling agent can open, move, or post-process files directly.
This project fills a gap: Kenari's own official MCP server covers docs, balance, and search, but not the media generation endpoints. This is a third-party, unaffiliated implementation for those endpoints.
⚠️ Cost warning — real money
Generation endpoints spend real Indonesian Rupiah (IDR) per call, billed to your Kenari account. Prices vary per model —
gpt-image-2is roughly 175 IDR per image, while others cost several times more. Treat the catalog prices you see vialist_media_modelsas examples only; live prices come fromlist_media_models.Note that
previewdoes not save money — files are always written to disk regardless.Never point automated tests or scheduled jobs at generation tools; the test suite uses a mocked
fetchand spends nothing.
Requirements
Node.js >= 20 (
enginesfield is enforced;node --versionto check)pnpm (or npm) for installing and running scripts
A Kenari API key (
kn-...), obtained from the Kenari dashboard. Required for everything exceptlist_media_models(GET /v1/modelsis public).
Install from source
This is the primary installation path until the package is published to npm (it is not on npm yet — npx will not work yet).
git clone https://github.com/dnshtm9/kenari-media-mcp.git
cd kenari-media-mcp
pnpm install
pnpm buildThe build writes the server entry to dist/index.js (the bin is kenari-media-mcp).
Environment variables
Var | Default | Notes |
| (none) | Required for everything except |
|
| Trailing slashes trimmed. |
|
| Media save directory (created recursively). Absolute paths are returned. |
| allow | Set to |
|
| Preflight cap for |
|
| Preflight cap for |
| (none) | Optional per-call IDR ceiling (image tools; best-effort catalog lookup). |
.env files — important caveat
The repository includes a .env.example. Copy it to .env if you want pnpm start (or other local runs) to pick up defaults — pnpm start loads .env via a small built-in reader; existing environment variables always win.
MCP clients do not reliably load .env. When a client spawns this server, the server's working directory is often not the repository, so any .env next to it may be invisible. You must inject environment variables in the client JSON env block. Treat .env as a local-run convenience only.
Never commit .env or any file containing your API key.
MCP client configuration
Recommended (local build): node + absolute path
Point the client at the built server. Most MCP clients require an absolute path in args — relative paths are resolved against the client's own working directory, not yours.
Substitute the location where you cloned the repository. On Windows, double every backslash in JSON strings and save the config file as UTF-8 without BOM (e.g. VS Code → "Save with Encoding → UTF-8"); a BOM can break config parsing.
Windows pattern:
{
"mcpServers": {
"kenari-media": {
"command": "node",
"args": ["C:\\\\path\\\\to\\\\kenari-media-mcp\\\\dist\\\\index.js"],
"env": {
"KENARI_API_KEY": "kn-REPLACE_ME",
"KENARI_OUTPUT_DIR": "C:\\\\path\\\\to\\\\kenari-output"
}
}
}
}macOS / Linux pattern:
{
"mcpServers": {
"kenari-media": {
"command": "node",
"args": ["/path/to/kenari-media-mcp/dist/index.js"],
"env": {
"KENARI_API_KEY": "kn-REPLACE_ME",
"KENARI_OUTPUT_DIR": "/path/to/kenari-output"
}
}
}
}KENARI_OUTPUT_DIR is any directory you choose; if you omit it, media is saved to ./kenari-output relative to the server's working directory (which may not be where you expect — setting it explicitly is safer).
After npm publish: npx (not yet possible)
The package is not published to npm yet, so the following does not work today. Once it is published, npx-based config will be:
Unix / macOS:
{
"mcpServers": {
"kenari-media": {
"command": "npx",
"args": ["-y", "kenari-media-mcp"],
"env": {
"KENARI_API_KEY": "kn-REPLACE_ME"
}
}
}
}Windows (npx is npx.cmd, which cannot be spawned directly, so clients must go through cmd):
{
"mcpServers": {
"kenari-media": {
"command": "cmd",
"args": ["/c", "npx", "-y", "kenari-media-mcp"],
"env": {
"KENARI_API_KEY": "kn-REPLACE_ME"
}
}
}
}Until then, use the local node + absolute-path config above.
Tools (8)
list_media_models { modality?: "image"|"video" }— public catalog with per-image IDR costs. Snapshot example: 5 image models, 0 video-generation models (the video list may be[]— video tools are still implemented and pass through to the API).generate_image { model, prompt, n?, size?, background?: transparent|opaque|auto, preview?=false }— text-to-image;ncapped byKENARI_MAX_IMAGE_N.edit_image { model, prompt, image_path, mask_path?, n?, size?, background?: opaque|auto }— reads local image files you pass;background: transparentis rejected by schema.create_video { model, prompt, duration?, resolution?, image_url?, end_image_url?, input_images?, video_url?, aspect_ratio? }— text/image-to-video;durationcapped byKENARI_MAX_VIDEO_DURATION.extend_video { model, source, prompt?, duration? }—sourceis a job id or an https URL; local file paths are rejected before any HTTP request.get_video_status { id }— checks a video job; auto-downloads the result when done.wait_for_video { id, timeout_ms?=1200000, download?=true }— polls every 5s with backoff capped at 15s; emitsnotifications/progresswhen the client sends a progress token. On timeout it returnsstill_renderingwithisError: falseso the agent can retry later.download_video { id }— downloads a finished video; returns astill_renderingerror while rendering.
Error codes, heartbeat, timeouts
Error codes surfaced in structuredContent: unauthorized | insufficient_balance | content_policy_violation | rate_limited | invalid_model | video_failed | video_expired | still_rendering | bad_request | upstream_error.
401 error bodies are plain text and are never assumed to be JSON.
Kenari image POSTs may stream ASCII-space "heartbeat" characters (~every 20s) before the JSON body — the server reads the full body, trims, then parses.
HTTP timeout: 300s for image calls.
wait_for_videodefault budget: 20 minutes.stdout carries JSON-RPC only; all logs go to stderr (
console.error).
Manual live smoke test
⚠️ This spends real money. Do it once by hand, never in CI or automated tests.
Build (
pnpm build) and setKENARI_API_KEY.Call
list_media_models→ expect the image model catalog.Call
generate_imagewith the cheapest model,n: 1→ confirm a file appears in the output dir.Call
get_video_statuson a bogus id → expectvideo_expired/bad_request, no spend.Only then try
create_videoif a video model exists in the catalog.
Development
pnpm test # tsx --test tests/**/*.test.ts — mocked fetch, no live spend
pnpm typecheck # tsc --noEmit
pnpm lint # tsc --noEmitTests mock fetch entirely, so running them costs nothing and requires no API key. The model-catalog fixture lives at tests/fixtures/models.json (a copy of a real GET /v1/models snapshot).
Security notes
kn-*API key values are redacted in logs.Never commit API keys (
.env, shell history, config dumps).edit_imagereads local files you pass to it — only pass files you intend to upload.Generation costs money on every call; beware of an agent looping on generation tools and burning IDR unattended.
License
See LICENSE.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/dnshtm9/kenari-media-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server