{"name":"ai.weftly/weftly","slug":"weftly","title":"Weftly","description":"Find & cut horizontal and vertical video clips (Shorts/Reels), transcribe & summarize. Pay per job.","url":"https://mcp.market/server/weftly","rating":null,"grade":"C","score":57,"certified":false,"status":"active","category":"media","tags":["media"],"presence":{"score":8,"stars":null,"forks":null,"downloads_week":null,"last_push_at":null,"license":null},"uptime":{"percent":100,"checks":6,"ok":6,"last_checked_at":"2026-09-20T19:40:43.532Z","last_ok_at":"2026-09-20T19:40:43.532Z","latency_ms":334},"claimed":false,"transport":"remote","callable_via_gateway":true,"default_price_micros":0,"repository":"https://github.com/woven-record-media/weftly-monorepo","website":"https://weftly.ai","version":"0.30.10","remotes":[{"type":"streamable-http","url":"https://api.weftly.ai/mcp"}],"packages":[],"tools":[{"name":"clips_horizontal","description":"Batch-cut a set of your own chosen horizontal 16:9 clips from a prior find_clips job in one purchase, delivered as SEPARATE files. Two-call flow: (1) call with `source_job_id` (a find_clips job id) and `clips` (1-5 objects `{start, end, title?}` in source seconds — your own picks from that job's `clip-candidates` output; nothing is auto-selected) to receive {job_id, payment_challenge}; (2) pay by credit card via the returned `payment_url` or Tempo USDC via mppx, then call again with `job_id` + `payment_credential` to start processing. Poll get_job_status(job_id); outputs are roles `clip-1-video` through `clip-N-video` (one .mp4 per requested clip, source framing and resolution preserved) plus `clips-manifest` (JSON) recording each clip's timing, output role, and any per-clip failure — a batch delivering fewer than N clips is not refunded, only one delivering zero is. Price: N × $0.50 per clip, charged once for the whole batch (Stripe quantity) rather than once per clip — see /.well-known/mpp.json for the Tempo USDC rate. No `profile` or `subject` parameters: horizontal keeps the source framing, so there is no crop to aim. **Choose deliberately between this and extract_clip**: extract_clip with several `segments` concatenates them into ONE highlight reel at the single-clip price, while this cuts N SEPARATE files at N × that price. Use this when each moment stands alone; use extract_clip when you want one file, need per-clip transcripts, or need more than 5 moments from one source. Use clips_vertical instead for 9:16 social output. Source must still be in storage — check `expires_at` on the find_clips parent via get_job_status. Retrying with `job_id` alone recovers the current state.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"source_job_id":{"description":"Job ID of a prior find_clips job. Possessing this id is the capability — clips_horizontal is not session-bound, so a user can come back from a different session within the parent's TTL and still cut. Required on the first call.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"clips":{"description":"Your own chosen clips (1-5) from source_job_id's clip-candidates output — nothing is auto-selected. Each is cut as a SEPARATE horizontal file; the batch is charged N × the per-clip price as one payment covering the whole set, not one charge per clip. Total duration across all clips is capped. Required on the first call.","minItems":1,"maxItems":5,"type":"array","items":{"type":"object","properties":{"start":{"type":"number","minimum":0,"description":"Source-relative start time in seconds."},"end":{"type":"number","minimum":0,"description":"Source-relative end time in seconds. Must be > start."},"title":{"description":"Optional title for this clip; surfaces in the clips-manifest output and download filenames.","type":"string","maxLength":200}},"required":["start","end"],"additionalProperties":false}},"job_id":{"description":"Job ID returned from a previous clips_horizontal call. Include along with payment_credential to confirm payment and trigger processing. Also include alone to recover the current state.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"payment_credential":{"description":"MPP payment credential (full Authorization header value, e.g. \"Payment eyJ...\"). clips_horizontal accepts Tempo USDC and Stripe SPT — see the challenge's WWW-Authenticate header or /.well-known/mpp.json for the supported methods. Include with job_id after paying the challenge to start processing.","type":"string"}},"additionalProperties":false}},{"name":"clips_vertical","description":"Batch-cut a set of your own chosen vertical 9:16 clips from a prior find_clips job in one purchase. Two-call flow: (1) call with `source_job_id` (a find_clips job id) and `clips` (1-5 objects `{start, end, title?}` in source seconds — your own picks from that job's `clip-candidates` output; nothing is auto-selected) to receive {job_id, payment_challenge}; (2) pay by credit card via the returned `payment_url` or Tempo USDC via mppx, then call again with `job_id` + `payment_credential` to start processing. Poll get_job_status(job_id); outputs are roles `clip-1-video` through `clip-N-video` (one 1080×1920 .mp4 per requested clip, loudness-normalized to -14 LUFS / -1.5 dBTP) plus `clips-manifest` (JSON) recording each clip's timing, output role, and any per-clip failure — a batch delivering fewer than N clips is not refunded, only one delivering zero is. Price: N × $0.50 per clip, charged once for the whole batch (Stripe quantity) rather than once per clip — see /.well-known/mpp.json for the Tempo USDC rate. Optional `profile` (default `tiktok-primary`; also `tiktok-primary-720p`, `instagram-reels`, `instagram-stories`) and `subject` (default `follow`, switches crop between active speakers; `auto` and `center` are opt-outs) apply to every clip in the batch. Use this once find_clips has surfaced candidates you like, to cut several in one purchase instead of one extract_vertical_clip call per clip; use extract_vertical_clip directly instead when you need per-clip framing control or more than 5 clips from one source. Source must still be in storage — check `expires_at` on the find_clips parent via get_job_status. Retrying with `job_id` alone recovers the current state.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"source_job_id":{"description":"Job ID of a prior find_clips job. Possessing this id is the capability — clips_vertical is not session-bound, so a user can come back from a different session within the parent's TTL and still cut. Required on the first call.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"clips":{"description":"Your own chosen clips (1-5) from source_job_id's clip-candidates output — nothing is auto-selected. Each is cut as a separate vertical clip; the batch is charged N × the per-clip price as one payment covering the whole set, not one charge per clip. Total duration across all clips is capped. Required on the first call.","minItems":1,"maxItems":5,"type":"array","items":{"type":"object","properties":{"start":{"type":"number","minimum":0,"description":"Source-relative start time in seconds."},"end":{"type":"number","minimum":0,"description":"Source-relative end time in seconds. Must be > start."},"title":{"description":"Optional title for this clip; surfaces in the clips-manifest output and download filenames.","type":"string","maxLength":200}},"required":["start","end"],"additionalProperties":false}},"profile":{"description":"Optional encoding profile applied to every clip in the batch. Default: tiktok-primary (1080×1920 H.264 fast preset, CRF 22, 6 Mbps cap). tiktok-primary-720p: 720×1280, CBR 3 Mbps. instagram-reels: 1080×1920 H.264 slow preset, CBR 4 Mbps. instagram-stories: same encode shape as instagram-reels. Also sets each clip's max duration (240s/180s/60s). All four apply loudness normalization to -14 LUFS / -1.5 dBTP.","type":"string","enum":["tiktok-primary","tiktok-primary-720p","instagram-reels","instagram-stories"]},"subject":{"description":"Optional reframing strategy applied to every clip in the batch. Default: \"follow\" — cuts between active-speaker framings across each clip, the mode that makes multi-speaker interview clips usable. \"auto\": one detected framing for the whole clip. \"center\": fixed centre crop, no detection. \"subject_id\" and \"manual\" are not supported here — they need per-clip data a batch call cannot supply; use extract_vertical_clip directly for those.","type":"string","enum":["follow","auto","center"]},"job_id":{"description":"Job ID returned from a previous clips_vertical call. Include along with payment_credential to confirm payment and trigger processing. Also include alone to recover the current state.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"payment_credential":{"description":"MPP payment credential (full Authorization header value, e.g. \"Payment eyJ...\"). clips_vertical accepts Tempo USDC and Stripe SPT — see the challenge's WWW-Authenticate header or /.well-known/mpp.json for the supported methods. Include with job_id after paying the challenge to start processing.","type":"string"}},"additionalProperties":false}},{"name":"complete_upload","description":"Confirm that the file has been uploaded (via HTTP PUT to the upload_url from transcribe or summarize) and start processing. Verifies that the file is present in storage and that the job has been paid. Returns status \"processing\". Poll get_job_status to track progress and retrieve download URLs when done.","write_action":true,"price_micros":0,"input_schema":{"type":"object","properties":{"job_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The job_id returned from a previous transcribe or summarize call."}},"required":["job_id"],"additionalProperties":false}},{"name":"extract_clip","description":"Cut and assemble a clip from any prior video job (find_clips, summarize, or video transcribe). Operates on a parent job — possessing the parent `source_job_id` is the capability, no upload step. Pass one segment for a simple cut, or multiple non-contiguous segments to compose a single mp4 highlight reel — same flat $0.50 either way. Two-call flow: (1) call with `source_job_id` + `segments` (ordered array of `{start, end, label?}` in source seconds, total duration capped at 30 minutes) to receive {job_id, payment_challenge}; (2) pay via MPP and call with `job_id` + `payment_credential` to start processing. No upload step. Poll get_job_status(job_id) for completion; outputs are role `clip-video` (the assembled .mp4, frame-accurate boundaries with 15ms audio fades at segment joins; audio loudness-normalized to -14 LUFS / -1.5 dBTP for clean, consistent playback) and — when `include_transcript: true` (default) — roles `clip-srt` + `clip-words` (transcripts stitched and time-shifted to match the assembled video). Set `include_transcript: false` to skip transcript outputs. Payment: pay by credit card via the Stripe Checkout link (open the returned `payment_url` in any browser) or Tempo USDC via mppx; the challenge's WWW-Authenticate header and /.well-known/mpp.json are authoritative for which methods are offered. Source must still be in storage (72h TTL for find_clips parents, 24h elsewhere — check `expires_at` from get_job_status on the parent). Multiple extract_clip calls against one parent are independent paid jobs. Failed jobs auto-refund.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"source_job_id":{"description":"Job ID of any prior video job (find_clips, summarize, or video transcribe). Possessing this id is the capability — extract_clip is not session-bound, so a user can come back from a different session within the parent's TTL and still extract. Required on the first call.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"segments":{"description":"Ordered array of source-relative segments to cut and concatenate into the output. Single segment for a simple cut; multiple segments compose a single mp4 from non-contiguous moments — same flat $0.50 either way. Total summed duration capped at 30 minutes per call. Required on the first call.","minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"start":{"type":"number","minimum":0,"description":"Source-relative start time in seconds."},"end":{"type":"number","minimum":0,"description":"Source-relative end time in seconds. Must be > start."},"label":{"description":"Optional label for this segment, surfaces in agent dialog.","type":"string","maxLength":200}},"required":["start","end"],"additionalProperties":false}},"include_transcript":{"description":"Default true. When true, the pipeline writes clip-srt + clip-words outputs stitched to match the assembled video. Set false to skip and just receive the .mp4.","type":"boolean"},"title":{"description":"Optional title for the assembled clip. Surfaces in get_job_status and download filenames; doesn't affect the cut itself.","type":"string","maxLength":200},"job_id":{"description":"Job ID returned from a previous extract_clip call. Include along with payment_credential to confirm payment and trigger processing. Also include alone to recover the current state.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"payment_credential":{"description":"MPP payment credential (full Authorization header value, e.g. \"Payment eyJ...\"). extract_clip accepts Tempo USDC and Stripe SPT — see the challenge's WWW-Authenticate header or /.well-known/mpp.json for the supported methods. Include with job_id after paying the challenge to start processing.","type":"string"}},"additionalProperties":false}},{"name":"extract_vertical_clip","description":"Cut a 9:16 vertical clip from any prior video job (find_clips, summarize, or video transcribe), suitable for direct upload to TikTok, Instagram Reels, or YouTube Shorts. Default output is 1080×1920 H.264 / AAC `.mp4` with center-cropped framing; audio loudness-normalized to -14 LUFS / -1.5 dBTP for short-form social. Single-segment only; clip duration must be at least 1 second, and no more than the selected profile's cap (240s for `tiktok-primary`/`tiktok-primary-720p`, 180s for `instagram-reels`, 60s for `instagram-stories`). Operates on a parent job — possessing the parent `source_job_id` is the capability, no upload step. Two-call flow: (1) call with `source_job_id` + `start` + `end` (in source seconds) to receive {job_id, payment_challenge}; (2) pay via MPP and call with `job_id` + `payment_credential` to start processing. Poll get_job_status(job_id) for completion; output is role `clip-vertical-video` (the `.mp4`). Flat price: $0.50 per clip. Payment: pay by credit card via the Stripe Checkout link (open the returned `payment_url` in any browser) or Tempo USDC via mppx. Optional `profile` parameter selects the encoding profile (default `tiktok-primary`). Allowed values: `tiktok-primary` (1080×1920, fast preset, CRF 22), `tiktok-primary-720p` (720×1280, CBR 3 Mbps — half-resolution mobile-optimized, ~40% faster wall time), `instagram-reels` (1080×1920, slow preset, CBR 4 Mbps), `instagram-stories` (same encode shape as instagram-reels). All four profiles loudness-normalize identically. Optional `subject` parameter controls reframing (default `center`, preserves today's behavior): `auto` locks onto the longest-tracked face from the parent's subjects-sidecar (or runs inline detection if the parent has none); `subject_id` (with `subject_id` param naming a face_N from the sidecar) locks onto a specific subject; `follow` switches crop between active speakers across the clip using the sidecar's active_speaker_timeline; `manual` accepts caller-supplied framing via `subject_box: {x, y, w, h}` (source pixels) or `subject_x_offset` (direct crop x). Sidecar shape at /.well-known/weftly-subjects-v1.schema.json. auto/subject_id/follow fall back to center if detection or sidecar resolution fails — the paid job always delivers a clip. Source must be a horizontal video (wider than 9:16) — already-vertical or square sources are rejected. Source must still be in storage (72h TTL for find_clips parents, 24h elsewhere — check `expires_at` from get_job_status on the parent). Pair with `find_clips` ($2.00/video) to pick a moment first, then call this to get a download-ready vertical mp4 in under 5 minutes. Multiple extract_vertical_clip calls against one parent are independent paid jobs. Failed jobs auto-refund.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"source_job_id":{"description":"Job ID of any prior video job (find_clips, summarize, or video transcribe). Possessing this id is the capability — extract_vertical_clip is not session-bound, so a user can come back from a different session within the parent's TTL and still extract. Required on the first call.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"start":{"description":"Source-relative start time in seconds. Required on the first call.","type":"number","minimum":0},"end":{"description":"Source-relative end time in seconds (must be > start; end - start must be at least 1s, and no more than the selected profile's cap — 240s for tiktok-primary/tiktok-primary-720p, 180s for instagram-reels, 60s for instagram-stories). Required on the first call.","type":"number","minimum":0},"title":{"description":"Optional title for the assembled clip. Surfaces in get_job_status and download filenames; doesn't affect the cut itself.","type":"string","maxLength":200},"profile":{"description":"Optional encoding profile. Default: tiktok-primary (1080×1920 H.264 fast preset, CRF 22, 6 Mbps cap). tiktok-primary-720p: 720×1280, CBR 3 Mbps — half-resolution mobile-optimized, ~40% faster wall time. instagram-reels: 1080×1920 H.264 slow preset, CBR 4 Mbps. instagram-stories: same encode shape as instagram-reels. All four apply loudness normalization to -14 LUFS / -1.5 dBTP.","type":"string","enum":["tiktok-primary","tiktok-primary-720p","instagram-reels","instagram-stories"]},"subject":{"description":"Optional reframing strategy. Default: \"center\" (hardcoded center crop, today's behavior). \"auto\": lock onto the longest-tracked face from the parent find_clips job's subjects-sidecar (or run inline detection if no sidecar). \"subject_id\": lock onto a specific face named in the sidecar (pass subject_id). \"follow\": switch crop between active speakers across the clip using the sidecar's active_speaker_timeline (per-segment encode + concat). \"manual\": caller specifies the subject (pass subject_box or subject_x_offset). See /.well-known/weftly-subjects-v1.schema.json. auto/subject_id/follow fall back to center if detection fails — the paid job always delivers a clip.","type":"string","enum":["center","manual","auto","subject_id","follow"]},"subject_id":{"description":"Required when subject=\"subject_id\". Subject id from the parent's subjects-sidecar (e.g. \"face_0\").","type":"string","maxLength":64},"subject_box":{"description":"For subject=\"manual\" — bounding box of the subject in source pixels. Crop centers on the box center.","type":"object","properties":{"x":{"type":"number","minimum":0},"y":{"type":"number","minimum":0},"w":{"type":"number","exclusiveMinimum":0},"h":{"type":"number","exclusiveMinimum":0}},"required":["x","y","w","h"],"additionalProperties":false},"t_ref":{"description":"For subject=\"manual\" with subject_box — source-seconds timestamp the box applies to. Informational in v1.","type":"number","minimum":0},"subject_x_offset":{"description":"For subject=\"manual\" — direct crop x-offset in source pixels (alternative to subject_box).","type":"number","minimum":0},"job_id":{"description":"Job ID returned from a previous extract_vertical_clip call. Include along with payment_credential to confirm payment and trigger processing. Also include alone to recover the current state.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"payment_credential":{"description":"MPP payment credential (full Authorization header value, e.g. \"Payment eyJ...\"). extract_vertical_clip accepts Tempo USDC and Stripe SPT — see the challenge's WWW-Authenticate header or /.well-known/mpp.json for the supported methods. Include with job_id after paying the challenge to start processing.","type":"string"}},"additionalProperties":false}},{"name":"find_clips","description":"START HERE for any clip workflow on a video — `find_clips` is the canonical entry point and includes a full transcription as a free byproduct. **Do not call `transcribe` first**: doing so doubles the upload, doubles the spend, and produces the same transcript. Identify ranked candidate clips in a video — what to cut for highlights, social, or testimonials. Three-call flow: (1) call with `filename` (and optional `query`) to receive {job_id, payment_challenge}; (2) pay via MPP, then call with `job_id` + `payment_credential` to receive {upload_url} (presigned PUT, 1h expiry); (3) PUT the bytes, then complete_upload(job_id), then poll get_job_status(job_id). On completion, get_job_status returns three outputs: role `clip-candidates` (JSON matching /.well-known/weftly-clips-v1.schema.json — includes `source_job_id` and `source_expires_at`), role `transcript` (SRT, free byproduct), role `transcript-words` (JSON matching /.well-known/weftly-transcript-v2.schema.json, free byproduct). Each candidate carries `transcript_text` — the full text of what's in the clip — so callers can preview content before paying for extract_clip. Optional `query` parameter switches to query mode (e.g., \"they discuss pricing\", \"the part about hiring\") with the same output shape; the `mode` field in clip-candidates.json indicates which mode produced the result. Flat price: $2.00 video — see /.well-known/mpp.json. **Source-reuse contract:** the source video stays in storage for 72h after find_clips completes. Hand the find_clips `job_id` (also returned as `source_job_id` in the candidates JSON) to `extract_clip` or `extract_vertical_clip` as their `source_job_id` — within those 72h they cut directly from the stored source: no re-upload, no re-transcribe, just $0.50 per cut. Pass the same `source_job_id` to as many extract calls as you need. Use for interviews, podcasts, sales calls, all-hands recordings. Retrying with `job_id` alone returns current state. Failed jobs auto-refund.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"filename":{"description":"Filename with extension (e.g. \"podcast.mp3\"). Required on the first call — used to infer media type (audio vs video) and label outputs. Supported extensions: mp3, wav, m4a, ogg, flac, mp4, mov, webm, mkv.","type":"string"},"job_id":{"description":"Job ID returned from a previous call. Include along with payment_credential to confirm payment and receive the presigned upload URL. Also include alone to recover the current challenge/state if the original response was lost.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"payment_credential":{"description":"MPP payment credential (full Authorization header value, e.g. \"Payment eyJ...\") obtained by paying the challenge returned from the first call. Include with job_id to verify payment and receive the upload URL.","type":"string"},"query":{"description":"Optional. Switches the analyzer from \"best clips\" discovery mode to query mode — finds segments matching this content (e.g., \"they discuss pricing\", \"the part about hiring\"). Same output shape either way; the `mode` field in clip-candidates.json tells consumers how to interpret per-candidate scoring.","type":"string","minLength":1,"maxLength":500}},"additionalProperties":false}},{"name":"get_job_status","description":"Check the status of a transcribe or summarize job. Returns the current state and, when completed, an `outputs` array. Each output has either `content` (returned inline) or a presigned, time-limited (1 hour) `download_url`. Small text outputs (e.g. `transcript` SRT, `clip-candidates`, `summary`) come inline as `content`; larger outputs — `transcript-words` JSON for any non-trivial recording, plus video outputs like `clip-video` / `clip-vertical-video` — come as a `download_url` to fetch when needed. Optionally pass `format` (srt, txt, vtt, json, words) to get the transcript content inline in the top-level `transcript` field — `txt` and `vtt` are derived from the stored SRT; `json` is v1 (segments only); `words` is v2 (segments + per-word timestamps matching /.well-known/weftly-transcript-v2.schema.json). Poll this periodically after calling complete_upload — wait at least 60 seconds between checks. For files under 10 minutes, jobs usually complete within 1-2 minutes. For long files (1hr+), expect 10-30 minutes. Derivative jobs (`extract_clip`, `extract_vertical_clip`, `publish_youtube`, `clips_vertical`, `clips_horizontal`) have no upload step and no `awaiting_upload`/`awaiting_complete_upload` states — a paid derivative job reports `processing` once its parent source is confirmed still in storage, or a terminal `source_expired` if the parent source has expired: stop polling, and re-run the parent job named in `source_job_id` to get a fresh source before retrying.\n\nAlso use this to recover from lost state: if the original challenge was lost, call get_job_status(job_id) to retrieve a fresh challenge (status \"awaiting_payment\") or the upload URL (status \"awaiting_upload\").","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"job_id":{"type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$","description":"The job_id returned from a previous transcribe or summarize call."},"format":{"description":"When the job is completed, return the transcript inline in this format instead of only a download URL. Options: \"srt\" (SubRip with timestamps), \"txt\" (plain text — no timestamps), \"vtt\" (WebVTT), \"json\" (v1, segments only), \"words\" (v2, segments + per-word timestamps matching /.well-known/weftly-transcript-v2.schema.json). Omit for download URLs only.","type":"string","enum":["srt","txt","vtt","json","words"]}},"required":["job_id"],"additionalProperties":false}},{"name":"get_youtube_publish_status","description":"Check the status of a YouTube publish job. Poll periodically after trigger_youtube_publish — the upload takes 1-10 minutes depending on video size. Returns status (pending, publishing, completed, failed) and the youtube_video_url once complete.","write_action":true,"price_micros":0,"input_schema":{"type":"object","properties":{"session_token":{"type":"string","description":"Session token"},"job_id":{"type":"string","description":"Job ID returned by publish_to_youtube"}},"required":["session_token","job_id"],"additionalProperties":false}},{"name":"mpp_smoke_test","description":"Smoke-test the MPP payment plumbing end-to-end via this MCP server, for $0.01 USDC. Two-call flow: (1) call with no arguments to receive an MPP `payment_challenge`; (2) pay via MPP and call again with `payment_credential` set to the resulting Authorization header value (e.g. \"Payment eyJ...\") to receive {paid: true, timestamp, receipt_ref, payment_method}. Uses the exact same `createPayToAddress` + `createMppHandler` verification path as paid product tools (transcribe, summarize), so a green run here means real paid calls will work too. Stateless — no job is created, no database row written. Use this whenever you want to confirm a wallet, the MCP transport, the worker, and the production payment middleware are all healthy without paying a transcribe price. Cost: $0.01 USDC per attempt.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"payment_credential":{"description":"MPP payment credential (full Authorization header value, e.g. \"Payment eyJ...\") obtained by paying the challenge returned from the first call. Include to verify payment and receive {paid: true}. Omit on the first call.","type":"string"}},"additionalProperties":false}},{"name":"publish_to_youtube","description":"Publish an existing video from a transcribe or summarize job to YouTube. Creates a paid publish job (flat $1.75 price) and stores the OAuth token. Captions are auto-generated from the session transcript if available. Workflow: create_job → pay → trigger_youtube_publish → poll get_youtube_publish_status. Requires a YouTube OAuth2 access token obtained independently via Google OAuth (scope: youtube.upload).","write_action":true,"price_micros":0,"input_schema":{"type":"object","properties":{"session_token":{"type":"string","description":"Session token from create_session, create_transcript, or create_summary"},"access_token":{"type":"string","description":"YouTube OAuth2 access token — the caller is responsible for obtaining this via Google OAuth"},"refresh_token":{"description":"YouTube OAuth2 refresh token — if provided, the Workflow will refresh the access token automatically before uploading","type":"string"},"source_job_id":{"type":"string","description":"Job ID of an existing transcribe or summarize job in this session whose video to publish. The Workflow will auto-generate captions if no transcript is found."},"title":{"type":"string","maxLength":100,"description":"YouTube video title (max 100 characters)"},"visibility":{"default":"private","description":"YouTube video visibility: \"private\" (default), \"unlisted\", or \"public\"","type":"string","enum":["private","unlisted","public"]},"description":{"description":"YouTube video description (max 5000 characters)","type":"string","maxLength":5000}},"required":["session_token","access_token","source_job_id","title","visibility"],"additionalProperties":false}},{"name":"summarize","description":"Summarize an audio or video file — returns both a text summary AND the full transcript (with per-word timestamps). Do not also call transcribe on the same file. Three-call flow: (1) call with `filename` to receive {job_id, payment_challenge}; (2) pay via MPP, then call with `job_id` + `payment_credential` to receive {upload_url} (presigned PUT, 1h expiry); (3) PUT the bytes, then complete_upload(job_id), then poll get_job_status(job_id). On completion, get_job_status returns three outputs: role `summary` (plain text), role `transcript` (SRT), and role `transcript-words` (JSON matching /.well-known/weftly-transcript-v2.schema.json, with segment-level and per-word timestamps). For other formats, pass `format=srt|txt|vtt|json|words` to get_job_status to receive transcript content inline — `txt` and `vtt` are derived from SRT, `json` is v1 (segments only), `words` is v2 (segments + words). Flat price: audio $0.75, video $1.25 — see /.well-known/mpp.json for the authoritative table. Use for meetings, long-form interviews, lectures, and podcast episodes; the `words` output additionally supports creating clips, multicamera edits, or edit-video-from-transcript. Retrying any call with `job_id` alone returns current state (idempotent). Failed jobs auto-refund.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"filename":{"description":"Filename with extension (e.g. \"podcast.mp3\"). Required on the first call — used to infer media type (audio vs video) and label outputs. Supported extensions: mp3, wav, m4a, ogg, flac, mp4, mov, webm, mkv.","type":"string"},"job_id":{"description":"Job ID returned from a previous call. Include along with payment_credential to confirm payment and receive the presigned upload URL. Also include alone to recover the current challenge/state if the original response was lost.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"payment_credential":{"description":"MPP payment credential (full Authorization header value, e.g. \"Payment eyJ...\") obtained by paying the challenge returned from the first call. Include with job_id to verify payment and receive the upload URL.","type":"string"}},"additionalProperties":false}},{"name":"transcribe","description":"Transcribe audio or video to text, including per-word timestamps for precise editing. Three-call flow: (1) call with `filename` to receive {job_id, payment_challenge}; (2) pay via MPP, then call with `job_id` + `payment_credential` to receive {upload_url} (presigned PUT, 1h expiry); (3) PUT the bytes, then complete_upload(job_id), then poll get_job_status(job_id). On completion, get_job_status returns two outputs: role `transcript` (SRT) and role `transcript-words` (JSON matching /.well-known/weftly-transcript-v2.schema.json, with segment-level and per-word timestamps). For other formats, pass `format=srt|txt|vtt|json|words` to get_job_status to receive content inline — `txt` and `vtt` are derived from SRT, `json` is v1 (segments only), `words` is v2 (segments + words). Flat price: audio $0.50, video $1.00 — see /.well-known/mpp.json for the authoritative table. Use for podcasts, interviews, meetings, lectures, and especially for creating clips, multicamera edits, or edit-video-from-transcript where word boundaries matter. Retrying any call with `job_id` alone returns current state (idempotent). Failed jobs auto-refund.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"filename":{"description":"Filename with extension (e.g. \"podcast.mp3\"). Required on the first call — used to infer media type (audio vs video) and label outputs. Supported extensions: mp3, wav, m4a, ogg, flac, mp4, mov, webm, mkv.","type":"string"},"job_id":{"description":"Job ID returned from a previous call. Include along with payment_credential to confirm payment and receive the presigned upload URL. Also include alone to recover the current challenge/state if the original response was lost.","type":"string","format":"uuid","pattern":"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"},"payment_credential":{"description":"MPP payment credential (full Authorization header value, e.g. \"Payment eyJ...\") obtained by paying the challenge returned from the first call. Include with job_id to verify payment and receive the upload URL.","type":"string"}},"additionalProperties":false}},{"name":"trigger_youtube_publish","description":"Start the YouTube upload after payment is confirmed. Call this after publish_to_youtube once payment_status is \"paid\". Returns immediately — the upload runs as a durable Workflow in the background. Poll get_youtube_publish_status to track progress.","write_action":true,"price_micros":0,"input_schema":{"type":"object","properties":{"session_token":{"type":"string","description":"Session token"},"job_id":{"type":"string","description":"Job ID returned by publish_to_youtube"}},"required":["session_token","job_id"],"additionalProperties":false}}],"scan":{"score":57,"grade":"C","scanned_at":"2026-09-20T11:37:48.850Z","report":{"scannerVersion":"0.1.9","scannedAt":"2026-09-20T11:37:48.761Z","components":{"code":{"score":-1,"max":25,"notes":["remote-only server, no package to scan"]},"reliability":{"score":20,"max":20,"notes":["remote reachable in 138ms"]},"poisoning":{"score":13,"max":15,"notes":["13 tool descriptions checked"]},"auth":{"score":3,"max":15,"notes":["open endpoint exposes 4 write-action tools with no auth"]},"maintenance":{"score":3,"max":15,"notes":["repository not readable: repo not found"]},"identity":{"score":4,"max":10,"notes":["verified namespace with website, no repo"]}},"findings":[{"id":"auth.open-write","severity":"high","component":"auth","title":"Write-action tools reachable without authentication"},{"id":"poison.long-description","severity":"low","component":"poisoning","title":"Unusually long tool description (over 2,000 characters)","evidence":"tool extract_vertical_clip: …Cut a 9:16 vertical clip from any prior video job (find_clips, summarize, or video transcribe), suitable for direct upload to TikTok, Instagram Reels, or YouTube Shorts. Default output is 1080×1920 H.264 / AAC `.mp4` with center-cropped framing; audio loudness-normalized to -14 LUFS / -1.5 dBTP for short-form social. Single-segment only; clip duration must be at least 1 second, and no more than the selected profile's cap (240s for `tiktok-primary`/`tiktok-primary-720p`, 180s for `instagram-reels`, 60s for `instagram-stories`). Operates on a parent job — possessing the parent `source_job_id` is the capability, no upload step. Two-call flow: (1) call with `source_job_id` + `start` + `end` (in source seconds) to receive {job_id, payment_challenge}; (2) pay via MPP and call with `job_id` + `payment_credential` to start processing. Poll get_job_status(job_id) for completion; output is role `clip-vertical-video` (the `.mp4`). Flat price: $0.50 per clip. Payment: pay by credit card via the Stripe Checkout link (open the returned `payment_url` in any browser) or Tempo USDC via mppx. Optional `profile` parameter selects the encoding profile (default `tiktok-primary`). Allowed values: `tiktok-primary` (1080×1920, fast preset, CRF 22), `tiktok-primary-720p` (720×1280, CBR 3 Mbps — half-resolution mobile-optimized, ~40% faster wall time), `instagram-reels` (1080×1920, slow preset, CBR 4 Mbps), `instagram-stories` (same encode shape as instagram-reels). All four profiles loudness-normalize identically. Optional `subject` parameter controls reframing (default `center`, preserves today's behavior): `auto` locks onto the longest-tracked face from the parent's subjects-sidecar (or runs inline detection if the parent has none); `subject_id` (with `subject_id` param naming a face_N from the sidecar) locks onto a specific subject; `follow` switches crop between active speakers across the clip using the sidecar's active_speaker_timeline; `manual` accepts caller-supplied framing via `subject_box: {x, y, w, h}` (source pixels) or `subject_x_offset` (direct crop x). Sidecar shape at /.well-known/weftly-subjects-v1.schema.json. auto/subject_id/follow fall back to center if detection or sidecar resolution fails — the paid job always delivers a clip. Source must be a horizontal video (wider than 9:16) — already-vertical or square sources are rejected. Source must still be in storage (72h TTL for find_clips parents, 24h elsewhere — check `expires_at` from get_job_status on the parent). Pair with `find_clips` ($2.00/video) to pick a moment first, then call this to get a download-ready vertical mp4 in under 5 minutes. Multiple extract_vertical_clip calls against one parent are independent paid jobs. Failed jobs auto-refund.…"}],"inputs":{"probes":[{"url":"https://api.weftly.ai/mcp","reachable":true,"authRequired":false,"latencyMs":138,"serverInfo":{"name":"weftly","version":"0.30.10"}}],"packages":[],"repo":{"found":false,"owner":"woven-record-media","repo":"weftly-monorepo","error":"repo not found"},"icon":{"url":"https://weftly.ai/icon-192.png","source":"registry","width":192,"height":183},"presence":{"stars":null,"forks":null,"downloadsWeek":null,"license":null,"lastPushAt":null,"score":8}}}},"grade_history":[],"reviews":[]}