Koskamo

txtreel MCP

An official MCP server by txtreel that allows AI agents to generate realistic chat videos (iMessage, WhatsApp, Instagram DM, Reddit) and screenshots. It exposes tools for validating inputs, rendering MP4 videos, and generating PNG screenshots without requiring user authentication or API keys.

Endpoint and access

No sign-in to connect and list tools. Individual tools may still need a vendor account, credentials, or permissions; check the documented prerequisites.

txtreel MCP logo, by txtreelVendor evidenceStreamable HTTPNo sign-inEndpoint checked 2026-11-06Operated by txtreel
MCP endpoint (Streamable HTTP)
https://txtreel.com/mcp
Vendor documentation

What its tools do

7 tools, grouped by the hints the server advertises. Hints are self-declared, not verified behavior.

Read-only (1)

7 of 7 have a description · 7 of 7 declare behavior hints · 7 of 7 with parameters document them all

Checks and signals

Each line is separate evidence, not a trust score.

  • Passed Endpoint answered the live check ·
  • Passed Official MCP Registry namespace is tied to txtreel.com ·
  • Passed Endpoint is on the registry namespace domain ·
  • Availability 1 of 1 checks passed in 30 days

What you can do with it

  • Generate fake iMessage or WhatsApp chat videos for social media content
  • Create realistic Instagram DM or Reddit story screenshots
  • Produce lock screen notification videos with iOS sounds
  • Validate chat input scripts before rendering to check for errors

Source and maintenance

Vendor evidenceRegistry 1.0.0

Protocol and capabilities

Server
txtreel 0.1.0
MCP protocol negotiated in check
2025-11-25
Tools
7 advertised
Resources
Not advertised
Resource templates
Not advertised
Prompts
Not advertised
Server-advertised capabilities
{
  "tools": {
    "listChanged": true
  }
}

Server-provided instructions

Untrusted server output shown for inspection. It is not Koskamo guidance and is excluded from knowledge search.

txtreel turns a written chat conversation (iMessage, WhatsApp, Instagram DM) or a Reddit thread into a realistic phone screen-recording, as an MP4 video or a PNG screenshot. It also makes an iOS lock screen with notifications. Workflow: write the conversation as a plain-text script (one message per line), call chat_validate to catch mistakes, then chat_render_video for the video or chat_screenshot for a still. reddit_render_video and reddit_screenshot do the same for Reddit threads; they validate as they go (there is no reddit_validate), so fix the reported lines and call again. Results are links to the finished files: give the link to the user rather than describing the video. Writing chats: one short message per line, like real texting; split what a real person would send as separate texts. A typical reel is 8-16 messages and 15-30 seconds. "[pause N]" holds a beat before a reply lands. "===" starts the video with the history already on screen; "=== scroll 3" skims a longer history too fast to read, so viewers pause and rewind. Leave keyboard at its default (true). Writing Reddit threads: the title is the hook. A typical reel is a post with 1-3 short body paragraphs plus 4-10 comments, about 30-60 seconds. Nested replies and a reply from the author ("> OP: ...") read as real, and a "[N more replies]" line looks like a real collapsed thread. Leave scores and ages out unless they matter. "speed" 1.3-1.5 makes a faster reel. Lock screens: lockscreen_render_video and lockscreen_screenshot take a list of notifications (app, title, body, when, at). Each notification lands at its "at" second, growing out of the bottom of the screen as on iOS 26; "at" 0 means it is already on screen. A typical reel is 3-6 notifications spaced 1-3 seconds apart. Like Reddit, there is no separate validate tool: fix the reported errors and call again.

Tools

7 advertised

chat_validate — Validate a txtreel conversation
Advertised by the server during the last successful anonymous check.
Advertised read-only

Checks a txtreel conversation (script or messages) without rendering anything. It is fast and free. On success returns { ok: true, durationSeconds, messageCount, conversation } where "conversation" is the fully-resolved object (all defaults applied). On failure returns { ok: false, errors: string[] } — one message per problem; when the input was a script, errors are prefixed with the offending line number. Videos longer than the server limit are rejected here too. Conversation script format (one event per line): them: hey, you up? me: yeah why [pause 1.5] wait 1.5 s --- Today 9:41 PM date/time separator me: [photo URL] caption a photo (URL, local file path, or an uploaded photo's number) [them reacts ❤️] reaction on my last message [read] they read my messages === everything above is already on screen at the start === scroll 3 same, but open on the oldest message and skim down in 3 s # comment "Name: text" also means "them" when Name matches contact.name (e.g. contact.name "Sam" lets you write "Sam: omg" instead of "them: omg"). A literal "\n" inside a message becomes a line break. Lines starting with # are comments. Photos: "me: [photo https://example.com/image.jpg] optional caption" (https URLs only; local file paths are not available). "=== scroll 3" opens on the oldest message of a longer history and skims down to the live part in 3 s. With keyboard true (the default), the newest messages stay above the area where the Reels caption sits.

chat_validate input schema
{
  "type": "object",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "properties": {
    "speed": {
      "type": "number",
      "maximum": 4,
      "minimum": 0.25,
      "description": "Pacing multiplier; 2 = twice as fast, 0.5 = half speed. Default: 1."
    },
    "theme": {
      "enum": [
        "light",
        "dark"
      ],
      "type": "string",
      "description": "Color theme, \"light\" or \"dark\". Default: light."
    },
    "script": {
      "type": "string",
      "description": "Conversation as a plain-text script. Use this OR messages, not both.\n\nConversation script format (one event per line):\n\nthem: hey, you up?\nme: yeah why\n[pause 1.5]            wait 1.5 s\n--- Today 9:41 PM      date/time separator\nme: [photo URL] caption a photo (URL, local file path, or an uploaded photo's number)\n[them reacts ❤️]       reaction on my last message\n[read]                 they read my messages\n===                    everything above is already on screen at the start\n=== scroll 3           same, but open on the oldest message and skim down in 3 s\n# comment\n\n\"Name: text\" also means \"them\" when Name matches contact.name (e.g. contact.name \"Sam\" lets you write \"Sam: omg\" instead of \"them: omg\"). A literal \"\\n\" inside a message becomes a line break. Lines starting with # are comments.\n\nPhotos: \"me: [photo https://example.com/image.jpg] optional caption\" (https URLs only; local file paths are not available). \"=== scroll 3\" opens on the oldest message of a longer history and skims down to the live part in 3 s. With keyboard true (the default), the newest messages stay above the area where the Reels caption sits."
    },
    "sounds": {
      "type": "boolean",
      "description": "Real UI sounds: iOS key clicks while typing, the app's send and receive sounds. Default: true."
    },
    "contact": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string",
          "maxLength": 60,
          "description": "Contact display name shown in the header. Also usable as the \"them\" prefix in script lines. Default: Alex."
        },
        "avatar": {
          "type": "string",
          "maxLength": 3000000,
          "description": "Contact avatar as an image URL or a data: URI. Omitted: an initials monogram is drawn instead."
        },
        "unread": {
          "type": "integer",
          "maximum": 9999,
          "minimum": 0,
          "description": "Unread-count badge shown on the back button."
        },
        "subtitle": {
          "type": "string",
          "maxLength": 60,
          "description": "Text under the contact name, e.g. WhatsApp \"online\" or Instagram \"Active now\". Default depends on platform."
        },
        "verified": {
          "type": "boolean",
          "description": "Show a verified badge next to the contact name."
        }
      },
      "description": "Contact header info."
    },
    "endHold": {
      "type": "number",
      "maximum": 15,
      "minimum": 0,
      "description": "Seconds to hold on the final frame before the video ends. Default: 2."
    },
    "autoRead": {
      "type": "boolean",
      "description": "Mark \"me\" messages as read as soon as the other person starts typing. Default: true."
    },
    "keyboard": {
      "type": "boolean",
      "description": "Show the iOS keyboard, which keeps the latest messages above the Reels caption area. Default: true."
    },
    "messages": {
      "type": "array",
      "items": {
        "type": "object",
        "propertyNames": {
          "type": "string"
        },
        "additionalProperties": {}
      },
      "description": "Conversation as an array of event objects instead of a script string (use this OR script, not both). Passed through to the txtreel API as-is; each item is one of: {from:\"me\"|\"them\", text, delay?, typing?, hold?, time?, instant?} (type \"message\" is the default and can be omitted), {type:\"pause\", seconds}, {type:\"timestamp\", text, instant?}, {type:\"read\", time?}, {type:\"react\", from:\"me\"|\"them\", emoji}."
    },
    "platform": {
      "enum": [
        "imessage",
        "whatsapp",
        "instagram"
      ],
      "type": "string",
      "description": "Chat app to render: \"imessage\", \"whatsapp\", or \"instagram\". Default: imessage."
    },
    "startTime": {
      "type": "string",
      "maxLength": 20,
      "description": "Clock used for messages, read receipts, and WhatsApp bubble times, e.g. \"9:41 PM\". Default: 9:41 PM."
    },
    "statusBar": {
      "type": "object",
      "properties": {
        "time": {
          "type": "string",
          "maxLength": 10,
          "description": "Status bar clock text, e.g. \"9:41\". Default: startTime without AM/PM."
        },
        "wifi": {
          "type": "boolean",
          "description": "Show the wifi icon in the status bar. Default: true."
        },
        "signal": {
          "type": "integer",
          "maximum": 4,
          "minimum": 0,
          "description": "Cell signal bars, 0-4. Default: 4."
        },
        "battery": {
          "type": "number",
          "maximum": 100,
          "minimum": 0,
          "description": "Battery percentage shown in the status bar. Default: 100."
        },
        "recording": {
          "type": "boolean",
          "description": "Red pill behind the time, as in an iPhone screen recording. Makes the clip read as a real screen recording. Default: false."
        }
      },
      "description": "Phone status bar shown at the top of the frame."
    },
    "composerTyping": {
      "type": "boolean",
      "description": "Type \"me\" messages into the input bar before sending them. Default: true."
    }
  }
}
chat_validate annotations
{
  "title": "Validate a txtreel conversation",
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
chat_render_video — Render a txtreel video
Advertised by the server during the last successful anonymous check.

Render a txtreel conversation (script or messages) to an MP4 and wait for it to finish. This queues a render, waits for it, and returns once it is done (or after failing/timing out). A short render (a handful of messages) typically takes 20-60 seconds. Returns a public link to the MP4; the file is deleted after 24 hours. If txtreel is busy or you hit the rate limit, the result is an error with the message. Conversation script format (one event per line): them: hey, you up? me: yeah why [pause 1.5] wait 1.5 s --- Today 9:41 PM date/time separator me: [photo URL] caption a photo (URL, local file path, or an uploaded photo's number) [them reacts ❤️] reaction on my last message [read] they read my messages === everything above is already on screen at the start === scroll 3 same, but open on the oldest message and skim down in 3 s # comment "Name: text" also means "them" when Name matches contact.name (e.g. contact.name "Sam" lets you write "Sam: omg" instead of "them: omg"). A literal "\n" inside a message becomes a line break. Lines starting with # are comments. Photos: "me: [photo https://example.com/image.jpg] optional caption" (https URLs only; local file paths are not available). "=== scroll 3" opens on the oldest message of a longer history and skims down to the live part in 3 s. With keyboard true (the default), the newest messages stay above the area where the Reels caption sits.

chat_render_video input schema
{
  "type": "object",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "properties": {
    "speed": {
      "type": "number",
      "maximum": 4,
      "minimum": 0.25,
      "description": "Pacing multiplier; 2 = twice as fast, 0.5 = half speed. Default: 1."
    },
    "theme": {
      "enum": [
        "light",
        "dark"
      ],
      "type": "string",
      "description": "Color theme, \"light\" or \"dark\". Default: light."
    },
    "script": {
      "type": "string",
      "description": "Conversation as a plain-text script. Use this OR messages, not both.\n\nConversation script format (one event per line):\n\nthem: hey, you up?\nme: yeah why\n[pause 1.5]            wait 1.5 s\n--- Today 9:41 PM      date/time separator\nme: [photo URL] caption a photo (URL, local file path, or an uploaded photo's number)\n[them reacts ❤️]       reaction on my last message\n[read]                 they read my messages\n===                    everything above is already on screen at the start\n=== scroll 3           same, but open on the oldest message and skim down in 3 s\n# comment\n\n\"Name: text\" also means \"them\" when Name matches contact.name (e.g. contact.name \"Sam\" lets you write \"Sam: omg\" instead of \"them: omg\"). A literal \"\\n\" inside a message becomes a line break. Lines starting with # are comments.\n\nPhotos: \"me: [photo https://example.com/image.jpg] optional caption\" (https URLs only; local file paths are not available). \"=== scroll 3\" opens on the oldest message of a longer history and skims down to the live part in 3 s. With keyboard true (the default), the newest messages stay above the area where the Reels caption sits."
    },
    "sounds": {
      "type": "boolean",
      "description": "Real UI sounds: iOS key clicks while typing, the app's send and receive sounds. Default: true."
    },
    "contact": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string",
          "maxLength": 60,
          "description": "Contact display name shown in the header. Also usable as the \"them\" prefix in script lines. Default: Alex."
        },
        "avatar": {
          "type": "string",
          "maxLength": 3000000,
          "description": "Contact avatar as an image URL or a data: URI. Omitted: an initials monogram is drawn instead."
        },
        "unread": {
          "type": "integer",
          "maximum": 9999,
          "minimum": 0,
          "description": "Unread-count badge shown on the back button."
        },
        "subtitle": {
          "type": "string",
          "maxLength": 60,
          "description": "Text under the contact name, e.g. WhatsApp \"online\" or Instagram \"Active now\". Default depends on platform."
        },
        "verified": {
          "type": "boolean",
          "description": "Show a verified badge next to the contact name."
        }
      },
      "description": "Contact header info."
    },
    "endHold": {
      "type": "number",
      "maximum": 15,
      "minimum": 0,
      "description": "Seconds to hold on the final frame before the video ends. Default: 2."
    },
    "autoRead": {
      "type": "boolean",
      "description": "Mark \"me\" messages as read as soon as the other person starts typing. Default: true."
    },
    "keyboard": {
      "type": "boolean",
      "description": "Show the iOS keyboard, which keeps the latest messages above the Reels caption area. Default: true."
    },
    "messages": {
      "type": "array",
      "items": {
        "type": "object",
        "propertyNames": {
          "type": "string"
        },
        "additionalProperties": {}
      },
      "description": "Conversation as an array of event objects instead of a script string (use this OR script, not both). Passed through to the txtreel API as-is; each item is one of: {from:\"me\"|\"them\", text, delay?, typing?, hold?, time?, instant?} (type \"message\" is the default and can be omitted), {type:\"pause\", seconds}, {type:\"timestamp\", text, instant?}, {type:\"read\", time?}, {type:\"react\", from:\"me\"|\"them\", emoji}."
    },
    "platform": {
      "enum": [
        "imessage",
        "whatsapp",
        "instagram"
      ],
      "type": "string",
      "description": "Chat app to render: \"imessage\", \"whatsapp\", or \"instagram\". Default: imessage."
    },
    "startTime": {
      "type": "string",
      "maxLength": 20,
      "description": "Clock used for messages, read receipts, and WhatsApp bubble times, e.g. \"9:41 PM\". Default: 9:41 PM."
    },
    "statusBar": {
      "type": "object",
      "properties": {
        "time": {
          "type": "string",
          "maxLength": 10,
          "description": "Status bar clock text, e.g. \"9:41\". Default: startTime without AM/PM."
        },
        "wifi": {
          "type": "boolean",
          "description": "Show the wifi icon in the status bar. Default: true."
        },
        "signal": {
          "type": "integer",
          "maximum": 4,
          "minimum": 0,
          "description": "Cell signal bars, 0-4. Default: 4."
        },
        "battery": {
          "type": "number",
          "maximum": 100,
          "minimum": 0,
          "description": "Battery percentage shown in the status bar. Default: 100."
        },
        "recording": {
          "type": "boolean",
          "description": "Red pill behind the time, as in an iPhone screen recording. Makes the clip read as a real screen recording. Default: false."
        }
      },
      "description": "Phone status bar shown at the top of the frame."
    },
    "composerTyping": {
      "type": "boolean",
      "description": "Type \"me\" messages into the input bar before sending them. Default: true."
    }
  }
}
chat_render_video annotations
{
  "title": "Render a txtreel video",
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": false,
  "destructiveHint": false
}
chat_screenshot — Screenshot a txtreel conversation
Advertised by the server during the last successful anonymous check.

Render a single frame of a txtreel conversation (script or messages) to a PNG and return it as an image. Defaults to the last frame — pass "frame" to capture an earlier moment (30 fps). Returns the PNG as image content, plus a text line with the PNG's URL (expires after 24 hours). Conversation script format (one event per line): them: hey, you up? me: yeah why [pause 1.5] wait 1.5 s --- Today 9:41 PM date/time separator me: [photo URL] caption a photo (URL, local file path, or an uploaded photo's number) [them reacts ❤️] reaction on my last message [read] they read my messages === everything above is already on screen at the start === scroll 3 same, but open on the oldest message and skim down in 3 s # comment "Name: text" also means "them" when Name matches contact.name (e.g. contact.name "Sam" lets you write "Sam: omg" instead of "them: omg"). A literal "\n" inside a message becomes a line break. Lines starting with # are comments. Photos: "me: [photo https://example.com/image.jpg] optional caption" (https URLs only; local file paths are not available). "=== scroll 3" opens on the oldest message of a longer history and skims down to the live part in 3 s. With keyboard true (the default), the newest messages stay above the area where the Reels caption sits.

chat_screenshot input schema
{
  "type": "object",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "properties": {
    "frame": {
      "type": "integer",
      "maximum": 9007199254740991,
      "minimum": 0,
      "description": "Frame index to capture, at 30 fps (e.g. 30 = one second in). Omit to capture the final frame of the conversation."
    },
    "speed": {
      "type": "number",
      "maximum": 4,
      "minimum": 0.25,
      "description": "Pacing multiplier; 2 = twice as fast, 0.5 = half speed. Default: 1."
    },
    "theme": {
      "enum": [
        "light",
        "dark"
      ],
      "type": "string",
      "description": "Color theme, \"light\" or \"dark\". Default: light."
    },
    "script": {
      "type": "string",
      "description": "Conversation as a plain-text script. Use this OR messages, not both.\n\nConversation script format (one event per line):\n\nthem: hey, you up?\nme: yeah why\n[pause 1.5]            wait 1.5 s\n--- Today 9:41 PM      date/time separator\nme: [photo URL] caption a photo (URL, local file path, or an uploaded photo's number)\n[them reacts ❤️]       reaction on my last message\n[read]                 they read my messages\n===                    everything above is already on screen at the start\n=== scroll 3           same, but open on the oldest message and skim down in 3 s\n# comment\n\n\"Name: text\" also means \"them\" when Name matches contact.name (e.g. contact.name \"Sam\" lets you write \"Sam: omg\" instead of \"them: omg\"). A literal \"\\n\" inside a message becomes a line break. Lines starting with # are comments.\n\nPhotos: \"me: [photo https://example.com/image.jpg] optional caption\" (https URLs only; local file paths are not available). \"=== scroll 3\" opens on the oldest message of a longer history and skims down to the live part in 3 s. With keyboard true (the default), the newest messages stay above the area where the Reels caption sits."
    },
    "sounds": {
      "type": "boolean",
      "description": "Real UI sounds: iOS key clicks while typing, the app's send and receive sounds. Default: true."
    },
    "contact": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string",
          "maxLength": 60,
          "description": "Contact display name shown in the header. Also usable as the \"them\" prefix in script lines. Default: Alex."
        },
        "avatar": {
          "type": "string",
          "maxLength": 3000000,
          "description": "Contact avatar as an image URL or a data: URI. Omitted: an initials monogram is drawn instead."
        },
        "unread": {
          "type": "integer",
          "maximum": 9999,
          "minimum": 0,
          "description": "Unread-count badge shown on the back button."
        },
        "subtitle": {
          "type": "string",
          "maxLength": 60,
          "description": "Text under the contact name, e.g. WhatsApp \"online\" or Instagram \"Active now\". Default depends on platform."
        },
        "verified": {
          "type": "boolean",
          "description": "Show a verified badge next to the contact name."
        }
      },
      "description": "Contact header info."
    },
    "endHold": {
      "type": "number",
      "maximum": 15,
      "minimum": 0,
      "description": "Seconds to hold on the final frame before the video ends. Default: 2."
    },
    "autoRead": {
      "type": "boolean",
      "description": "Mark \"me\" messages as read as soon as the other person starts typing. Default: true."
    },
    "keyboard": {
      "type": "boolean",
      "description": "Show the iOS keyboard, which keeps the latest messages above the Reels caption area. Default: true."
    },
    "messages": {
      "type": "array",
      "items": {
        "type": "object",
        "propertyNames": {
          "type": "string"
        },
        "additionalProperties": {}
      },
      "description": "Conversation as an array of event objects instead of a script string (use this OR script, not both). Passed through to the txtreel API as-is; each item is one of: {from:\"me\"|\"them\", text, delay?, typing?, hold?, time?, instant?} (type \"message\" is the default and can be omitted), {type:\"pause\", seconds}, {type:\"timestamp\", text, instant?}, {type:\"read\", time?}, {type:\"react\", from:\"me\"|\"them\", emoji}."
    },
    "platform": {
      "enum": [
        "imessage",
        "whatsapp",
        "instagram"
      ],
      "type": "string",
      "description": "Chat app to render: \"imessage\", \"whatsapp\", or \"instagram\". Default: imessage."
    },
    "startTime": {
      "type": "string",
      "maxLength": 20,
      "description": "Clock used for messages, read receipts, and WhatsApp bubble times, e.g. \"9:41 PM\". Default: 9:41 PM."
    },
    "statusBar": {
      "type": "object",
      "properties": {
        "time": {
          "type": "string",
          "maxLength": 10,
          "description": "Status bar clock text, e.g. \"9:41\". Default: startTime without AM/PM."
        },
        "wifi": {
          "type": "boolean",
          "description": "Show the wifi icon in the status bar. Default: true."
        },
        "signal": {
          "type": "integer",
          "maximum": 4,
          "minimum": 0,
          "description": "Cell signal bars, 0-4. Default: 4."
        },
        "battery": {
          "type": "number",
          "maximum": 100,
          "minimum": 0,
          "description": "Battery percentage shown in the status bar. Default: 100."
        },
        "recording": {
          "type": "boolean",
          "description": "Red pill behind the time, as in an iPhone screen recording. Makes the clip read as a real screen recording. Default: false."
        }
      },
      "description": "Phone status bar shown at the top of the frame."
    },
    "composerTyping": {
      "type": "boolean",
      "description": "Type \"me\" messages into the input bar before sending them. Default: true."
    }
  }
}
chat_screenshot annotations
{
  "title": "Screenshot a txtreel conversation",
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": false,
  "destructiveHint": false
}
reddit_render_video — Render a Reddit thread video
Advertised by the server during the last successful anonymous check.

Render a Reddit thread (script, or post+comments) to an MP4 and wait for it to finish. This queues a render, waits for it, and returns once it is done (or after failing/timing out). A short render typically takes 20-60 seconds. Returns a public link to the MP4; the file is deleted after 24 hours. If txtreel is busy or you hit the rate limit, the result is an error with the message. If the thread is invalid, this returns an error result with the validation messages (script errors are prefixed with the offending line number) instead of rendering. Reddit thread script format (post + nested comments): title: AITA for not going to my sister's wedding? body: First paragraph of the post (optional, one line per paragraph) body: Second paragraph [photo URL] a photo in the post (URL, file path or uploaded photo's number) sleepy_joe: A top-level comment > OP: A reply by the post's author (blue OP label) >> sleepy_joe [2.1K 3h]: A reply to that reply, with its votes and age [91 more replies] collapsed replies under the comment above [pause 2] wait 2 s # comment The video is a screen recording of a thread on mobile reddit.com in iOS Safari: the page is laid out once and the view scrolls from block to block, holding each one long enough to read. The title is on screen from frame 0. Scores and ages left out of the script are generated (decreasing down the thread, never older than the post).

reddit_render_video input schema
{
  "type": "object",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "properties": {
    "post": {
      "type": "object",
      "properties": {
        "age": {
          "type": "string",
          "maxLength": 20,
          "description": "Post age, e.g. \"5h\". Default: \"5h\"."
        },
        "body": {
          "type": "string",
          "maxLength": 40000,
          "description": "Post body text; each line becomes its own paragraph and gets its own reading beat."
        },
        "flair": {
          "type": "string",
          "maxLength": 60,
          "description": "Post flair, e.g. \"Not the A-hole\"."
        },
        "image": {
          "type": "string",
          "maxLength": 15000000,
          "description": "A photo under the title: image URL or data: URI."
        },
        "score": {
          "type": "string",
          "maxLength": 12,
          "description": "Post votes as shown, e.g. \"14K\". Default: made up, always above the top comment."
        },
        "title": {
          "type": "string",
          "maxLength": 400,
          "minLength": 1,
          "description": "The post title. Required when not using script — this is the hook, on screen from frame 0."
        },
        "author": {
          "type": "string",
          "maxLength": 40,
          "description": "Poster's username. Default: \"throwaway_4412\"."
        },
        "comments": {
          "type": "string",
          "maxLength": 12,
          "description": "Comment count shown on the post, e.g. \"1.2K\". Default: made up."
        },
        "flairColor": {
          "type": "string",
          "maxLength": 30,
          "description": "Flair background color (CSS color). Default: a Reddit blue."
        }
      },
      "description": "The post. Use this OR script, not both."
    },
    "speed": {
      "type": "number",
      "maximum": 4,
      "minimum": 0.25,
      "description": "Pacing multiplier; 2 = twice as fast, 0.5 = half speed. Default: 1. Use 1.3-1.5 for a faster reel."
    },
    "theme": {
      "enum": [
        "light",
        "dark"
      ],
      "type": "string",
      "description": "Color theme, \"light\" or \"dark\". Default: light."
    },
    "script": {
      "type": "string",
      "description": "Thread as a plain-text script. Use this OR post/comments, not both.\n\nReddit thread script format (post + nested comments):\n\ntitle: AITA for not going to my sister's wedding?\nbody: First paragraph of the post (optional, one line per paragraph)\nbody: Second paragraph\n[photo URL]                    a photo in the post (URL, file path or uploaded photo's number)\nsleepy_joe: A top-level comment\n> OP: A reply by the post's author (blue OP label)\n>> sleepy_joe [2.1K 3h]: A reply to that reply, with its votes and age\n[91 more replies]              collapsed replies under the comment above\n[pause 2]                      wait 2 s\n# comment\n\nThe video is a screen recording of a thread on mobile reddit.com in iOS Safari: the page is laid out once and the view scrolls from block to block, holding each one long enough to read. The title is on screen from frame 0. Scores and ages left out of the script are generated (decreasing down the thread, never older than the post)."
    },
    "endHold": {
      "type": "number",
      "maximum": 15,
      "minimum": 0,
      "description": "Seconds to hold on the final frame before the video ends. Default: 2."
    },
    "comments": {
      "type": "array",
      "items": {
        "type": "object",
        "propertyNames": {
          "type": "string"
        },
        "additionalProperties": {}
      },
      "description": "Comments as an array of event objects instead of a script string (use this OR script, not both). Each item is one of: {author, text, depth (0 top-level, 1 reply to the nearest depth-0 comment above, 2 reply to that, …), score?, age?, op?, avatar?, moreReplies?, hold?} or {type:\"pause\", seconds}. \"op\" or an author matching the post author gets the blue OP label automatically."
    },
    "statusBar": {
      "type": "object",
      "properties": {
        "time": {
          "type": "string",
          "maxLength": 10,
          "description": "Status bar clock text, e.g. \"9:41\". Default: \"9:41\"."
        },
        "wifi": {
          "type": "boolean",
          "description": "Show the wifi icon in the status bar. Default: true."
        },
        "signal": {
          "type": "integer",
          "maximum": 4,
          "minimum": 0,
          "description": "Cell signal bars, 0-4. Default: 4."
        },
        "battery": {
          "type": "number",
          "maximum": 100,
          "minimum": 0,
          "description": "Battery percentage shown in the status bar. Default: 100."
        },
        "recording": {
          "type": "boolean",
          "description": "Red pill behind the time, as in an iPhone screen recording. Makes the clip read as a real screen recording. Default: false."
        }
      },
      "description": "Phone status bar shown above the Safari toolbar."
    },
    "subreddit": {
      "type": "object",
      "properties": {
        "icon": {
          "type": "string",
          "maxLength": 3000000,
          "description": "Subreddit icon as an image URL or a data: URI. Omitted: a plain fallback community icon."
        },
        "name": {
          "type": "string",
          "maxLength": 40,
          "description": "Subreddit shown in the header and post byline, without \"r/\". Default: \"AskReddit\"."
        }
      },
      "description": "The subreddit the thread is posted in."
    }
  }
}
reddit_render_video annotations
{
  "title": "Render a Reddit thread video",
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": false,
  "destructiveHint": false
}
reddit_screenshot — Screenshot a Reddit thread
Advertised by the server during the last successful anonymous check.

Render a single frame of a Reddit thread (script, or post+comments) to a PNG and return it as an image. Defaults to the last frame — pass "frame" to capture an earlier moment (30 fps). Returns the PNG as image content, plus a text line with the PNG's URL (expires after 24 hours). If the thread is invalid, this returns an error result with the validation messages instead of an image. Reddit thread script format (post + nested comments): title: AITA for not going to my sister's wedding? body: First paragraph of the post (optional, one line per paragraph) body: Second paragraph [photo URL] a photo in the post (URL, file path or uploaded photo's number) sleepy_joe: A top-level comment > OP: A reply by the post's author (blue OP label) >> sleepy_joe [2.1K 3h]: A reply to that reply, with its votes and age [91 more replies] collapsed replies under the comment above [pause 2] wait 2 s # comment The video is a screen recording of a thread on mobile reddit.com in iOS Safari: the page is laid out once and the view scrolls from block to block, holding each one long enough to read. The title is on screen from frame 0. Scores and ages left out of the script are generated (decreasing down the thread, never older than the post).

reddit_screenshot input schema
{
  "type": "object",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "properties": {
    "post": {
      "type": "object",
      "properties": {
        "age": {
          "type": "string",
          "maxLength": 20,
          "description": "Post age, e.g. \"5h\". Default: \"5h\"."
        },
        "body": {
          "type": "string",
          "maxLength": 40000,
          "description": "Post body text; each line becomes its own paragraph and gets its own reading beat."
        },
        "flair": {
          "type": "string",
          "maxLength": 60,
          "description": "Post flair, e.g. \"Not the A-hole\"."
        },
        "image": {
          "type": "string",
          "maxLength": 15000000,
          "description": "A photo under the title: image URL or data: URI."
        },
        "score": {
          "type": "string",
          "maxLength": 12,
          "description": "Post votes as shown, e.g. \"14K\". Default: made up, always above the top comment."
        },
        "title": {
          "type": "string",
          "maxLength": 400,
          "minLength": 1,
          "description": "The post title. Required when not using script — this is the hook, on screen from frame 0."
        },
        "author": {
          "type": "string",
          "maxLength": 40,
          "description": "Poster's username. Default: \"throwaway_4412\"."
        },
        "comments": {
          "type": "string",
          "maxLength": 12,
          "description": "Comment count shown on the post, e.g. \"1.2K\". Default: made up."
        },
        "flairColor": {
          "type": "string",
          "maxLength": 30,
          "description": "Flair background color (CSS color). Default: a Reddit blue."
        }
      },
      "description": "The post. Use this OR script, not both."
    },
    "frame": {
      "type": "integer",
      "maximum": 9007199254740991,
      "minimum": 0,
      "description": "Frame index to capture, at 30 fps (e.g. 30 = one second in). Omit to capture the final frame of the thread."
    },
    "speed": {
      "type": "number",
      "maximum": 4,
      "minimum": 0.25,
      "description": "Pacing multiplier; 2 = twice as fast, 0.5 = half speed. Default: 1. Use 1.3-1.5 for a faster reel."
    },
    "theme": {
      "enum": [
        "light",
        "dark"
      ],
      "type": "string",
      "description": "Color theme, \"light\" or \"dark\". Default: light."
    },
    "script": {
      "type": "string",
      "description": "Thread as a plain-text script. Use this OR post/comments, not both.\n\nReddit thread script format (post + nested comments):\n\ntitle: AITA for not going to my sister's wedding?\nbody: First paragraph of the post (optional, one line per paragraph)\nbody: Second paragraph\n[photo URL]                    a photo in the post (URL, file path or uploaded photo's number)\nsleepy_joe: A top-level comment\n> OP: A reply by the post's author (blue OP label)\n>> sleepy_joe [2.1K 3h]: A reply to that reply, with its votes and age\n[91 more replies]              collapsed replies under the comment above\n[pause 2]                      wait 2 s\n# comment\n\nThe video is a screen recording of a thread on mobile reddit.com in iOS Safari: the page is laid out once and the view scrolls from block to block, holding each one long enough to read. The title is on screen from frame 0. Scores and ages left out of the script are generated (decreasing down the thread, never older than the post)."
    },
    "endHold": {
      "type": "number",
      "maximum": 15,
      "minimum": 0,
      "description": "Seconds to hold on the final frame before the video ends. Default: 2."
    },
    "comments": {
      "type": "array",
      "items": {
        "type": "object",
        "propertyNames": {
          "type": "string"
        },
        "additionalProperties": {}
      },
      "description": "Comments as an array of event objects instead of a script string (use this OR script, not both). Each item is one of: {author, text, depth (0 top-level, 1 reply to the nearest depth-0 comment above, 2 reply to that, …), score?, age?, op?, avatar?, moreReplies?, hold?} or {type:\"pause\", seconds}. \"op\" or an author matching the post author gets the blue OP label automatically."
    },
    "statusBar": {
      "type": "object",
      "properties": {
        "time": {
          "type": "string",
          "maxLength": 10,
          "description": "Status bar clock text, e.g. \"9:41\". Default: \"9:41\"."
        },
        "wifi": {
          "type": "boolean",
          "description": "Show the wifi icon in the status bar. Default: true."
        },
        "signal": {
          "type": "integer",
          "maximum": 4,
          "minimum": 0,
          "description": "Cell signal bars, 0-4. Default: 4."
        },
        "battery": {
          "type": "number",
          "maximum": 100,
          "minimum": 0,
          "description": "Battery percentage shown in the status bar. Default: 100."
        },
        "recording": {
          "type": "boolean",
          "description": "Red pill behind the time, as in an iPhone screen recording. Makes the clip read as a real screen recording. Default: false."
        }
      },
      "description": "Phone status bar shown above the Safari toolbar."
    },
    "subreddit": {
      "type": "object",
      "properties": {
        "icon": {
          "type": "string",
          "maxLength": 3000000,
          "description": "Subreddit icon as an image URL or a data: URI. Omitted: a plain fallback community icon."
        },
        "name": {
          "type": "string",
          "maxLength": 40,
          "description": "Subreddit shown in the header and post byline, without \"r/\". Default: \"AskReddit\"."
        }
      },
      "description": "The subreddit the thread is posted in."
    }
  }
}
reddit_screenshot annotations
{
  "title": "Screenshot a Reddit thread",
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": false,
  "destructiveHint": false
}
lockscreen_render_video — Render a lock screen notification video
Advertised by the server during the last successful anonymous check.

Render an iOS lock screen with notifications to an MP4 and wait for it to finish: a wallpaper, the big clock, and cards from imessage, whatsapp, instagram, chatgpt, claude, reddit, mail, phone, facetime, notes, reminders that land one by one at the seconds you set. This queues a render, waits for it, and returns once it is done (or after failing/timing out). A short render (3-5 notifications) typically takes 20-60 seconds. Returns a public link to the MP4; the file is deleted after 24 hours. If txtreel is busy or you hit the rate limit, the result is an error with the message. If the input is invalid, this returns an error result with the validation messages instead of rendering. Lock screen format (an iOS 26 lock screen with notifications): The video starts on a lock screen (wallpaper, big clock, date, padlock). Like the chat videos, the 9:16 video is the iPhone screen cut below the status bar and above the flashlight and camera buttons. Each notification is a Liquid Glass card from one app (imessage, whatsapp, instagram, chatgpt, claude, reddit, mail, phone, facetime, notes, reminders) with a bold title (usually the sender), a body of up to 4 lines, and a see-through time such as "now" or "2m ago". A card lands at its "at" second (seconds from the start of the video), growing out of the bottom of the screen (or out of the card already there) as on iOS 26; at 0 it is already there. While cards are on screen the lower half of the wallpaper darkens, as on the phone. At least one notification is required. The video ends "endHold" seconds (default 2) after the last card has settled. Wallpaper and avatars are https image URLs or data: URIs (local file paths are not available). "wallpaper" also takes one of Apple's iOS 26 wallpapers by name ("sky", "shadow", "halo", "dusk", "yellow", "pink", "azure", "purple"); without one, "sky" (the iOS 26 default) is drawn, in its light or dark version with "theme". "avatar" on a notification shows the contact's photo with the app icon as a small badge. "display": "stack" folds the cards like iOS 26 does (the newest card, the next one or two as one-line rows under it, the rest counted as "+3 from WhatsApp and Messages"); "list" shows every card.

lockscreen_render_video input schema
{
  "type": "object",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "required": [
    "notifications"
  ],
  "properties": {
    "dim": {
      "type": "number",
      "maximum": 1,
      "minimum": 0,
      "description": "Black overlay on the wallpaper, 0 to 1. Default: 0."
    },
    "date": {
      "type": "string",
      "maxLength": 40,
      "description": "The line above the clock, written as iOS 26 does, e.g. \"Tue Oct 6\". Default: \"Tue Oct 6\"."
    },
    "time": {
      "type": "string",
      "maxLength": 10,
      "description": "The big clock, without AM/PM, e.g. \"9:41\". Default: \"9:41\"."
    },
    "theme": {
      "enum": [
        "light",
        "dark"
      ],
      "type": "string",
      "description": "The phone's appearance: \"light\" = frosted glass, \"dark\" = smoked glass, white text on both as on iOS 26; Apple's wallpapers follow it. Default: light."
    },
    "camera": {
      "type": "boolean",
      "description": "Camera button bottom right. Default: true."
    },
    "sounds": {
      "type": "boolean",
      "description": "The iOS notification tone when each card lands (video only). Default: true."
    },
    "carrier": {
      "type": "string",
      "maxLength": 30,
      "description": "Network name on the left of the status bar. Default: none."
    },
    "display": {
      "enum": [
        "list",
        "stack"
      ],
      "type": "string",
      "description": "\"list\" shows every card, newest on top; \"stack\" is iOS 26's default fold: the newest card, the next one or two as one-line rows, the rest counted. Default: list."
    },
    "endHold": {
      "type": "number",
      "maximum": 15,
      "minimum": 0,
      "description": "Seconds to hold on the last frame after the last card has settled. Default: 2."
    },
    "padlock": {
      "type": "boolean",
      "description": "Small locked padlock above the date. Default: true."
    },
    "clockSize": {
      "type": "number",
      "maximum": 200,
      "minimum": 40,
      "description": "Clock size in points on a 402 pt wide iPhone screen. Default: 100."
    },
    "statusBar": {
      "type": "object",
      "properties": {
        "wifi": {
          "type": "boolean",
          "description": "Show the wifi icon. Default: true."
        },
        "signal": {
          "type": "integer",
          "maximum": 4,
          "minimum": 0,
          "description": "Cell signal bars, 0-4. Default: 4."
        },
        "battery": {
          "type": "number",
          "maximum": 100,
          "minimum": 0,
          "description": "Battery percentage. Default: 100."
        },
        "recording": {
          "type": "boolean",
          "description": "Red pill behind the status bar clock, as in an iPhone screen recording. Default: false."
        }
      },
      "description": "Right side of the status bar. (The clock there is not shown on a lock screen.)"
    },
    "wallpaper": {
      "type": "string",
      "maxLength": 15000000,
      "description": "One of Apple's iOS 26 wallpapers by name (\"sky\", \"shadow\", \"halo\", \"dusk\", \"yellow\", \"pink\", \"azure\", \"purple\"), or a full-bleed image: URL or data: URI. Omitted: \"sky\", the iOS 26 default."
    },
    "clockColor": {
      "type": "string",
      "maxLength": 40,
      "description": "Clock colour (CSS colour). Default: \"#FFFFFF\"."
    },
    "flashlight": {
      "type": "boolean",
      "description": "Flashlight button bottom left. Default: true."
    },
    "wallpaperX": {
      "type": "number",
      "maximum": 100,
      "minimum": 0,
      "description": "Where the crop is centred horizontally when the image is not the screen's shape, in % (50 = middle). Default: 50."
    },
    "wallpaperY": {
      "type": "number",
      "maximum": 100,
      "minimum": 0,
      "description": "Where the crop is centred vertically, in % (50 = middle). Default: 50."
    },
    "clockWeight": {
      "type": "number",
      "maximum": 900,
      "minimum": 100,
      "description": "Clock numeral weight, 100-900. Default: 760."
    },
    "clockOpacity": {
      "type": "number",
      "maximum": 1,
      "minimum": 0,
      "description": "How much of the colour fills the Liquid Glass numerals, 0-1; the wallpaper shows through the rest. Default: 0.45."
    },
    "clockStretch": {
      "type": "number",
      "maximum": 1.6,
      "minimum": 0.8,
      "description": "Vertical stretch of the clock numerals. Default: 1.16."
    },
    "notifications": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "at": {
            "type": "number",
            "maximum": 300,
            "minimum": 0,
            "description": "Seconds into the video when this card lands. 0 = already on screen at the first frame. Default: 0."
          },
          "app": {
            "enum": [
              "imessage",
              "whatsapp",
              "instagram",
              "chatgpt",
              "claude",
              "reddit",
              "mail",
              "phone",
              "facetime",
              "notes",
              "reminders"
            ],
            "type": "string",
            "description": "Which app sent it, so the card wears its icon: \"imessage\", \"whatsapp\", \"instagram\", \"chatgpt\", \"claude\", \"reddit\", \"mail\", \"phone\", \"facetime\", \"notes\", \"reminders\". Default: imessage."
          },
          "body": {
            "type": "string",
            "maxLength": 1000,
            "description": "The message text. Up to 4 lines are shown, then an ellipsis."
          },
          "when": {
            "type": "string",
            "maxLength": 20,
            "description": "Grey text on the right, e.g. \"now\", \"2m ago\", \"9:41 AM\". Default: \"now\"."
          },
          "title": {
            "type": "string",
            "maxLength": 80,
            "description": "Bold first line: the contact's name (emoji stay in colour) or the app's headline."
          },
          "avatar": {
            "type": "string",
            "maxLength": 3000000,
            "description": "The contact's photo as an image URL or a data: URI; the app icon shrinks to a badge on it. Omitted: the app icon is drawn."
          }
        }
      },
      "maxItems": 20,
      "minItems": 1,
      "description": "The notifications, 1 to 20, in any order (they land in order of \"at\", ties keep the written order, and the newest sits on top).\n\nLock screen format (an iOS 26 lock screen with notifications):\n\nThe video starts on a lock screen (wallpaper, big clock, date, padlock). Like the chat videos, the 9:16 video is the iPhone screen cut below the status bar and above the flashlight and camera buttons. Each notification is a Liquid Glass card from one app (imessage, whatsapp, instagram, chatgpt, claude, reddit, mail, phone, facetime, notes, reminders) with a bold title (usually the sender), a body of up to 4 lines, and a see-through time such as \"now\" or \"2m ago\". A card lands at its \"at\" second (seconds from the start of the video), growing out of the bottom of the screen (or out of the card already there) as on iOS 26; at 0 it is already there. While cards are on screen the lower half of the wallpaper darkens, as on the phone. At least one notification is required. The video ends \"endHold\" seconds (default 2) after the last card has settled.\n\nWallpaper and avatars are https image URLs or data: URIs (local file paths are not available). \"wallpaper\" also takes one of Apple's iOS 26 wallpapers by name (\"sky\", \"shadow\", \"halo\", \"dusk\", \"yellow\", \"pink\", \"azure\", \"purple\"); without one, \"sky\" (the iOS 26 default) is drawn, in its light or dark version with \"theme\". \"avatar\" on a notification shows the contact's photo with the app icon as a small badge. \"display\": \"stack\" folds the cards like iOS 26 does (the newest card, the next one or two as one-line rows under it, the rest counted as \"+3 from WhatsApp and Messages\"); \"list\" shows every card."
    },
    "wallpaperZoom": {
      "type": "number",
      "maximum": 4,
      "minimum": 1,
      "description": "1 = cover the screen, 2 = twice as close. Default: 1."
    }
  }
}
lockscreen_render_video annotations
{
  "title": "Render a lock screen notification video",
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": false,
  "destructiveHint": false
}
lockscreen_screenshot — Screenshot a lock screen with notifications
Advertised by the server during the last successful anonymous check.

Render a single frame of an iOS lock screen with notifications to a PNG and return it as an image. Defaults to the last frame — pass "frame" to capture an earlier moment (30 fps). Returns the PNG as image content, plus a text line with the PNG's URL (expires after 24 hours). If the input is invalid, this returns an error result with the validation messages instead of an image. Lock screen format (an iOS 26 lock screen with notifications): The video starts on a lock screen (wallpaper, big clock, date, padlock). Like the chat videos, the 9:16 video is the iPhone screen cut below the status bar and above the flashlight and camera buttons. Each notification is a Liquid Glass card from one app (imessage, whatsapp, instagram, chatgpt, claude, reddit, mail, phone, facetime, notes, reminders) with a bold title (usually the sender), a body of up to 4 lines, and a see-through time such as "now" or "2m ago". A card lands at its "at" second (seconds from the start of the video), growing out of the bottom of the screen (or out of the card already there) as on iOS 26; at 0 it is already there. While cards are on screen the lower half of the wallpaper darkens, as on the phone. At least one notification is required. The video ends "endHold" seconds (default 2) after the last card has settled. Wallpaper and avatars are https image URLs or data: URIs (local file paths are not available). "wallpaper" also takes one of Apple's iOS 26 wallpapers by name ("sky", "shadow", "halo", "dusk", "yellow", "pink", "azure", "purple"); without one, "sky" (the iOS 26 default) is drawn, in its light or dark version with "theme". "avatar" on a notification shows the contact's photo with the app icon as a small badge. "display": "stack" folds the cards like iOS 26 does (the newest card, the next one or two as one-line rows under it, the rest counted as "+3 from WhatsApp and Messages"); "list" shows every card.

lockscreen_screenshot input schema
{
  "type": "object",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "required": [
    "notifications"
  ],
  "properties": {
    "dim": {
      "type": "number",
      "maximum": 1,
      "minimum": 0,
      "description": "Black overlay on the wallpaper, 0 to 1. Default: 0."
    },
    "date": {
      "type": "string",
      "maxLength": 40,
      "description": "The line above the clock, written as iOS 26 does, e.g. \"Tue Oct 6\". Default: \"Tue Oct 6\"."
    },
    "time": {
      "type": "string",
      "maxLength": 10,
      "description": "The big clock, without AM/PM, e.g. \"9:41\". Default: \"9:41\"."
    },
    "frame": {
      "type": "integer",
      "maximum": 9007199254740991,
      "minimum": 0,
      "description": "Frame index to capture, at 30 fps (e.g. 30 = one second in). Omit to capture the final frame, with every notification on screen."
    },
    "theme": {
      "enum": [
        "light",
        "dark"
      ],
      "type": "string",
      "description": "The phone's appearance: \"light\" = frosted glass, \"dark\" = smoked glass, white text on both as on iOS 26; Apple's wallpapers follow it. Default: light."
    },
    "camera": {
      "type": "boolean",
      "description": "Camera button bottom right. Default: true."
    },
    "sounds": {
      "type": "boolean",
      "description": "The iOS notification tone when each card lands (video only). Default: true."
    },
    "carrier": {
      "type": "string",
      "maxLength": 30,
      "description": "Network name on the left of the status bar. Default: none."
    },
    "display": {
      "enum": [
        "list",
        "stack"
      ],
      "type": "string",
      "description": "\"list\" shows every card, newest on top; \"stack\" is iOS 26's default fold: the newest card, the next one or two as one-line rows, the rest counted. Default: list."
    },
    "endHold": {
      "type": "number",
      "maximum": 15,
      "minimum": 0,
      "description": "Seconds to hold on the last frame after the last card has settled. Default: 2."
    },
    "padlock": {
      "type": "boolean",
      "description": "Small locked padlock above the date. Default: true."
    },
    "clockSize": {
      "type": "number",
      "maximum": 200,
      "minimum": 40,
      "description": "Clock size in points on a 402 pt wide iPhone screen. Default: 100."
    },
    "statusBar": {
      "type": "object",
      "properties": {
        "wifi": {
          "type": "boolean",
          "description": "Show the wifi icon. Default: true."
        },
        "signal": {
          "type": "integer",
          "maximum": 4,
          "minimum": 0,
          "description": "Cell signal bars, 0-4. Default: 4."
        },
        "battery": {
          "type": "number",
          "maximum": 100,
          "minimum": 0,
          "description": "Battery percentage. Default: 100."
        },
        "recording": {
          "type": "boolean",
          "description": "Red pill behind the status bar clock, as in an iPhone screen recording. Default: false."
        }
      },
      "description": "Right side of the status bar. (The clock there is not shown on a lock screen.)"
    },
    "wallpaper": {
      "type": "string",
      "maxLength": 15000000,
      "description": "One of Apple's iOS 26 wallpapers by name (\"sky\", \"shadow\", \"halo\", \"dusk\", \"yellow\", \"pink\", \"azure\", \"purple\"), or a full-bleed image: URL or data: URI. Omitted: \"sky\", the iOS 26 default."
    },
    "clockColor": {
      "type": "string",
      "maxLength": 40,
      "description": "Clock colour (CSS colour). Default: \"#FFFFFF\"."
    },
    "flashlight": {
      "type": "boolean",
      "description": "Flashlight button bottom left. Default: true."
    },
    "wallpaperX": {
      "type": "number",
      "maximum": 100,
      "minimum": 0,
      "description": "Where the crop is centred horizontally when the image is not the screen's shape, in % (50 = middle). Default: 50."
    },
    "wallpaperY": {
      "type": "number",
      "maximum": 100,
      "minimum": 0,
      "description": "Where the crop is centred vertically, in % (50 = middle). Default: 50."
    },
    "clockWeight": {
      "type": "number",
      "maximum": 900,
      "minimum": 100,
      "description": "Clock numeral weight, 100-900. Default: 760."
    },
    "clockOpacity": {
      "type": "number",
      "maximum": 1,
      "minimum": 0,
      "description": "How much of the colour fills the Liquid Glass numerals, 0-1; the wallpaper shows through the rest. Default: 0.45."
    },
    "clockStretch": {
      "type": "number",
      "maximum": 1.6,
      "minimum": 0.8,
      "description": "Vertical stretch of the clock numerals. Default: 1.16."
    },
    "notifications": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "at": {
            "type": "number",
            "maximum": 300,
            "minimum": 0,
            "description": "Seconds into the video when this card lands. 0 = already on screen at the first frame. Default: 0."
          },
          "app": {
            "enum": [
              "imessage",
              "whatsapp",
              "instagram",
              "chatgpt",
              "claude",
              "reddit",
              "mail",
              "phone",
              "facetime",
              "notes",
              "reminders"
            ],
            "type": "string",
            "description": "Which app sent it, so the card wears its icon: \"imessage\", \"whatsapp\", \"instagram\", \"chatgpt\", \"claude\", \"reddit\", \"mail\", \"phone\", \"facetime\", \"notes\", \"reminders\". Default: imessage."
          },
          "body": {
            "type": "string",
            "maxLength": 1000,
            "description": "The message text. Up to 4 lines are shown, then an ellipsis."
          },
          "when": {
            "type": "string",
            "maxLength": 20,
            "description": "Grey text on the right, e.g. \"now\", \"2m ago\", \"9:41 AM\". Default: \"now\"."
          },
          "title": {
            "type": "string",
            "maxLength": 80,
            "description": "Bold first line: the contact's name (emoji stay in colour) or the app's headline."
          },
          "avatar": {
            "type": "string",
            "maxLength": 3000000,
            "description": "The contact's photo as an image URL or a data: URI; the app icon shrinks to a badge on it. Omitted: the app icon is drawn."
          }
        }
      },
      "maxItems": 20,
      "minItems": 1,
      "description": "The notifications, 1 to 20, in any order (they land in order of \"at\", ties keep the written order, and the newest sits on top).\n\nLock screen format (an iOS 26 lock screen with notifications):\n\nThe video starts on a lock screen (wallpaper, big clock, date, padlock). Like the chat videos, the 9:16 video is the iPhone screen cut below the status bar and above the flashlight and camera buttons. Each notification is a Liquid Glass card from one app (imessage, whatsapp, instagram, chatgpt, claude, reddit, mail, phone, facetime, notes, reminders) with a bold title (usually the sender), a body of up to 4 lines, and a see-through time such as \"now\" or \"2m ago\". A card lands at its \"at\" second (seconds from the start of the video), growing out of the bottom of the screen (or out of the card already there) as on iOS 26; at 0 it is already there. While cards are on screen the lower half of the wallpaper darkens, as on the phone. At least one notification is required. The video ends \"endHold\" seconds (default 2) after the last card has settled.\n\nWallpaper and avatars are https image URLs or data: URIs (local file paths are not available). \"wallpaper\" also takes one of Apple's iOS 26 wallpapers by name (\"sky\", \"shadow\", \"halo\", \"dusk\", \"yellow\", \"pink\", \"azure\", \"purple\"); without one, \"sky\" (the iOS 26 default) is drawn, in its light or dark version with \"theme\". \"avatar\" on a notification shows the contact's photo with the app icon as a small badge. \"display\": \"stack\" folds the cards like iOS 26 does (the newest card, the next one or two as one-line rows under it, the rest counted as \"+3 from WhatsApp and Messages\"); \"list\" shows every card."
    },
    "wallpaperZoom": {
      "type": "number",
      "maximum": 4,
      "minimum": 1,
      "description": "1 = cover the screen, 2 = twice as close. Default: 1."
    }
  }
}
lockscreen_screenshot annotations
{
  "title": "Screenshot a lock screen with notifications",
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": false,
  "destructiveHint": false
}

Prompts

Not advertised

No prompt definitions available.

Resources

Not advertised

No resource definitions available.

90-day uptime

A lightweight connection check about every six hours, sampled on hosts with many listings. It shows the server answered, not that its tools work.

Too few checks for a percentage yet (1 of 1 answered).

  1. 2026-11-07: 1 of 1 checks answered

30-day availability

Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.

Successful checks
1 of 1 (100%)
Average latency
1044 ms
95th percentile latency
1044 ms

Maintenance history

Public check and source-refresh events. Editorial and ownership records remain private.

  • · probe · passed · 1044 ms · metadata changed · 7 tool(s)

Connect from your client

Each snippet registers the same endpoint; pick the client you use.

This server is operated by the named third party. Listing and endpoint checks are not an endorsement or a security audit. Review the operator's permissions, terms, and data handling before connecting. How listings are checked and corrected.

Claude Code

Run this once in a terminal. It registers the remote server. Use --scope project to write .mcp.json for the repo.
Claude Code configuration
claude mcp add --transport http txtreel-mcp https://txtreel.com/mcp

Codex

Run this once. Codex stores MCP servers in ~/.codex/config.toml (or .codex/config.toml in a trusted project).
Codex configuration
codex mcp add txtreel-mcp --url https://txtreel.com/mcp

Cursor

Merge this server into .cursor/mcp.json in your project, then enable txtreel-mcp in your MCP settings.
Cursor configuration
{
  "mcpServers": {
    "txtreel-mcp": {
      "url": "https://txtreel.com/mcp"
    }
  }
}

Cline

Merge this into cline_mcp_settings.json (Cline → MCP Servers → Configure).
Cline configuration
{
  "mcpServers": {
    "txtreel-mcp": {
      "type": "streamableHttp",
      "url": "https://txtreel.com/mcp",
      "disabled": false
    }
  }
}

VS Code

Merge this server into .vscode/mcp.json. Use MCP: List Servers to start txtreel-mcp and inspect its tools.
VS Code configuration
{
  "servers": {
    "txtreel-mcp": {
      "type": "http",
      "url": "https://txtreel.com/mcp"
    }
  }
}

Gemini CLI

Run this once. It writes .gemini/settings.json in the current project; pass --scope user for ~/.gemini/settings.json.
Gemini CLI configuration
gemini mcp add --transport http txtreel-mcp https://txtreel.com/mcp

Claude Desktop

Open Claude Settings > Connectors > Add custom connector, enter this server URL and enable it for the conversation. This is a manual remote-connector handoff, not a desktop JSON configuration.
Claude Desktop configuration
https://txtreel.com/mcp

Windsurf

Merge this into ~/.codeium/windsurf/mcp_config.json, then refresh MCP in Cascade. Windsurf remote servers use serverUrl, not url.
Windsurf configuration
{
  "mcpServers": {
    "txtreel-mcp": {
      "serverUrl": "https://txtreel.com/mcp"
    }
  }
}

Let your agent find its tools

Find servers like txtreel MCP from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.

Do you run txtreel MCP?

Prove ownership with a DNS record, a well-known file, or your Official MCP Registry entry, and your edits publish immediately. Proofs are re-checked weekly; admins can still hide or remove a listing.

Claim this listing