Whyzit MCP connector

The connector's contract, as the server at app.whyzit.com enforces it.

Whyzit turns a question into a short narrated video lesson, about one minute long. This connector lets an agent list a person's lessons, read one, and find the lessons that already answer a question. An agent can also start a new lesson on the person's behalf, and start buying a plan. It is a Model Context Protocol server with five tools, behind OAuth 2.1.

Endpoint

MCP endpoint https://app.whyzit.com/mcp
Transport Streamable HTTP: one POST carrying JSON-RPC 2.0
Protocol version 2025-06-18
Protected resource metadata https://app.whyzit.com/.well-known/oauth-protected-resource
Authorization server metadata https://app.whyzit.com/.well-known/oauth-authorization-server

A call with no token, or an expired one, answers 401 with a WWW-Authenticate: Bearer resource_metadata="…" header. That header is how a client discovers where to send the person to sign in.

Authorization

The server is an OAuth 2.1 authorization server as well as the resource.

The person signs in once, at connect time, on https://app.whyzit.com/connect, with Google or with email and password, and approves the client by name. After that the client holds a token and calls without asking again.

Scopes

Scope What the person sees Tools it unlocks
films:read See the films you have made list_films, get_film, find_films
films:make Make new films on your account make_film, start_plan

films:make spends the person's own film allowance. It is shown to them as a separate line on the consent screen, and it can be refused on its own. A token that lacks a tool's scope is not offered that tool in tools/list. A call to it anyway answers a JSON-RPC error with code -32002, naming the missing scope.

Tools

Every tool answers with MCP content blocks. A result that carries data also carries structuredContent with the same object. A refusal has isError: true and one sentence of plain text meant to be read to the person.

list_films

Lists the lessons this person has made, newest first, up to 200. Films in state finished and rendering are listed; a film whose render stopped early is not.

Input: {}

Result, when there are films:

{
  "films": [
    {
      "id": "b5395bc6-5b05-4f73-8f95-147147254ce5",
      "question": "Why is the sky blue?",
      "style": "photoreal",
      "state": "finished",
      "createdAt": "2026-09-21T23:39:38.000Z",
      "finishedAt": "2026-09-21T23:40:41.000Z",
      "threadId": "b5395bc6-5b05-4f73-8f95-147147254ce5",
      "title": "Why the Sky Is Blue",
      "publishedAt": "2026-09-22T08:12:04.611Z",
      "public": true,
      "views": 14,
      "likes": 3,
      "opener": "/api/session/b5395bc6-5b05-4f73-8f95-147147254ce5/beat/0.mp4",
      "url": "https://app.whyzit.com/f/b5395bc6-5b05-4f73-8f95-147147254ce5"
    }
  ]
}

public says whether the person published the film on Explore, and publishedAt is when they first did. views counts one viewer once a day, and likes counts the people who pressed like. opener is the film's opening clip, a path under https://app.whyzit.com that a page plays as the film's picture.

When there are none, the text This person has not made any films yet.

get_film

Reads one lesson. Only the person's own films are readable through the connector. A film belonging to someone else is refused, even though its public address plays for anyone holding the link.

Input:

{ "id": "b5395bc6-5b05-4f73-8f95-147147254ce5" }

id is required and is a UUID.

Result:

{
  "id": "b5395bc6-5b05-4f73-8f95-147147254ce5",
  "question": "Why is the sky blue?",
  "state": "finished",
  "style": "photoreal",
  "threadId": "b5395bc6-5b05-4f73-8f95-147147254ce5",
  "title": "Why the Sky Is Blue",
  "seconds": 59.4,
  "public": true,
  "views": 14,
  "likes": 3,
  "url": "https://app.whyzit.com/f/b5395bc6-5b05-4f73-8f95-147147254ce5"
}

threadId is the id of the first film in the thread this film belongs to. A film that started its own thread carries its own id. title and seconds are null until the film has rendered something. state is one of created, rendering, finished, gone. public, views and likes mean what they mean in list_films.

Refusals: get_film needs the film id., There is no film at that id., That film belongs to somebody else.

find_films

Finds the lessons that already answer a question: this person's own films, and the public films other people published. Call it before make_film, because watching a film that exists costs the person nothing and a new film spends one of their films.

Input:

{ "query": "why is the sky blue" }

query is required. The search keeps the words of three letters or more and drops the common ones, such as why, how and the.

Result:

{
  "films": [
    {
      "id": "b5395bc6-5b05-4f73-8f95-147147254ce5",
      "question": "Why is the sky blue?",
      "title": "Why the Sky Is Blue",
      "style": "photoreal",
      "seconds": 59.4,
      "url": "https://app.whyzit.com/f/b5395bc6-5b05-4f73-8f95-147147254ce5",
      "public": true,
      "mine": false,
      "views": 14,
      "likes": 3,
      "match": 1
    }
  ],
  "suggested": ["b5395bc6-5b05-4f73-8f95-147147254ce5"]
}

Up to ten films come back, the closest first. match is the share of the query's kept words that the film's question and title hold, from 0 to 1. mine is true on this person's own film, and those are found while they are private and while they are still rendering. Every other film is public and finished.

suggested holds at most three ids, each with match of 0.6 or more, in the same order. Offer those to the person before making a new film. Every id in suggested has its film in films.

When nothing matches, the text No film answers that question yet.

Refusals: find_films needs some words to look for. for an empty query, and Every word in that query is too short or too common to search for.

make_film

Starts a new lesson answering a question. This spends one of the person's films. Ask them before calling it.

Input:

{ "question": "Why do cats purr?", "style": "cartoon" }

Result:

{
  "id": "9596f069-8a50-47dc-9e4e-556ca479e035",
  "question": "Why do cats purr?",
  "style": "cartoon",
  "url": "https://app.whyzit.com/f/9596f069-8a50-47dc-9e4e-556ca479e035",
  "note": "The film is being made now and is shown to the person, where it updates by itself. Do not call another tool to check on it."
}

The tool writes the film, starts making it, and returns its address. The film starts playing within seconds, and the rest of it is made while it plays, over about a minute. Give the person the url. In an app that draws the Whyzit player, such as ChatGPT or Claude, the film also plays in the conversation.

Refusals, each one sentence: a question is required, that question is too long, unknown style, and when the allowance is spent, This account has used its 2 free films. A film that failed does not count. Explorer gives 12 films a month and Polymath gives 40. A subscriber who has used the month's films reads a different sentence. It names the plan and the date the count resets. In ChatGPT the sentence says only that the films are used up.

start_plan

Starts buying a monthly plan, or changes the plan a subscriber is on. This call charges nothing. It returns a Stripe Checkout address where the person pays. The plan then renews every month until the person cancels it under Manage billing on the Whyzit settings screen. Tell the person the price and the monthly renewal, and ask them, before calling it.

Input:

{ "plan": "explorer" }

plan is required and is explorer (12 films a month, $7.99) or polymath (40 films a month, $19.99).

ChatGPT is not offered this tool, following OpenAI's rule for apps in ChatGPT on purchases. There, a call to it is refused, and no answer from any tool names a plan, a price or a place to buy.

Result, for a new subscriber:

{
  "plan": "explorer",
  "url": "https://checkout.stripe.com/c/pay/cs_live_…",
  "note": "Open the url to pay for Explorer, $7.99 a month, 12 films a month. It renews every month until cancelled under Manage billing on the Whyzit settings screen."
}

For a subscriber on the other plan the url is the Stripe billing portal's confirm page. The note says Stripe shows the prorated amount there.

Refusals, each one sentence: Unknown plan. The plans are Explorer and Polymath., This account is already on Explorer., This account has its own film allowance and needs no plan.

The Checkout page is Stripe's and needs no Whyzit sign-in. An agent may hand the address to the person. An agent that pays with Stripe Link may complete it itself, after the person approves the total. Whyzit accepts Link.

Access requirements

Errors

Situation Answer
No or bad bearer token HTTP 401, WWW-Authenticate challenge, JSON-RPC error -32002
Unknown method JSON-RPC error -32601
Unknown tool JSON-RPC error -32602
Tool the token's scopes do not cover JSON-RPC error -32002, naming the scope
A tool refusing its input result.isError: true with one sentence

Support

help@whyzit.com