grade

Public TV and radio catalogue with measured stream health, folders and M3U feeds for VLC.

¿Debería usar esto?

Calidad y seguridad

B
Calidad de la descripción
76%
Integridad del esquema
74%
Calidad de los nombres
91%
Riesgo de envenenamiento
80%
Coincidencia de permisos
100%
Cumplimiento del protocolo
100%

Hallazgos (24)

  • HIGHTool poisoning patterns detected
  • MEDIUMTool description contains URL to non-standard domainen legacy_stream
  • LOWTool 'api_index' description lacks action verben api_index
  • LOWTool 'health' description lacks action verben health
  • LOWTool 'geo' description lacks action verben geo
  • LOWTool 'list_tags' description lacks action verben list_tags
  • LOWTool 'list_languages' description lacks action verben list_languages
  • LOWTool 'list_networks' description lacks action verben list_networks
  • LOWTool 'list_subdivisions' description lacks action verben list_subdivisions
  • LOWTool 'list_cities' description lacks action verben list_cities

Basado en el análisis automatizado de las definiciones de herramientas y el cumplimiento del protocolo.

Costo de contexto

~4,828Tokens (definiciones de herramientas)
~603 BTamaño de respuesta típico
Impacto significativo en la atención (3.77% del contexto de 128k)

Este es el número aproximado de tokens que se consumen cada vez que las herramientas del servidor se cargan en el contexto de un modelo. Los recuentos más altos reducen la atención disponible para otras tareas.

Instalar

Instalación con un clic

Agrega esto a tu archivo `claude_desktop_config.json`:

{
  "mcpServers": {
    "grade": {
      "url": "https://gradetv.net/mcp"
    }
  }
}

Puntos de conexión remotos

https://gradetv.net/mcpstreamable-http

Qué puede hacer

Inventario de herramientas

Herramientas (40)

🟢 Solo lectura🟡 Escritura🔴 Eliminación⚪ Desconocido
⚪api_index

Índice auto-descrito da API inteira, com os idiomas e as páginas HTML de cada um.

Esquema de entrada

{
  "type": "object",
  "properties": {}
}
⚪health

Liveness e o commit publicado agora — é como o smoke espera o próprio deploy.

Esquema de entrada

{
  "type": "object",
  "properties": {}
}
⚪geo

País e idioma sugeridos para quem está chamando. Sem país detectado pela borda, sugere BR com `source: "fallback"`; `detected` diz o que a borda viu.

Esquema de entrada

{
  "type": "object",
  "properties": {}
}
🟢list_countries(kind)

Países do catálogo com contagem playable e URL da bandeira (kind=radio conta estações).

Esquema de entrada

{
  "type": "object",
  "properties": {
    "kind": {
      "type": "string",
      "enum": [
        "tv",
        "radio",
        "all"
      ],
      "description": "`tv` (padrão) conta canais de TV; `radio` conta estações de rádio; `all` junta os dois.",
      "default": "tv"
    }
  }
}
🟢list_tags(country, limit)

Tags das estações de rádio tocáveis, com contagem; opcionalmente por país.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "country": {
      "type": "string",
      "description": "Restringe às estações de um país, ISO 3166-1 alpha-2."
    },
    "limit": {
      "type": "string",
      "description": "Quantas tags devolver (teto 100).",
      "default": 40
    }
  }
}
🟢search_channels(q, country, category, language, network, ...)

Busca canais públicos (filtros: q, country, category, language, network, quality, playable; kind=radio para estações de rádio, tag para tag de rádio; sort=score ordena pela saúde medida por terceiro, sort=votes pelos votos da rádio; online=1 só quem foi visto online nas últimas 48 h). É a porta de entrada do produto. A resposta varia por navegador, sistema e país de quem pede — cada canal traz `social.your_fails`, o recorte do SEU ambiente — por isso ela é `Cache-Control: private`.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "q": {
      "type": "string",
      "description": "Texto livre no nome e nos apelidos do canal (busca full-text)."
    },
    "country": {
      "type": "string",
      "description": "País do canal, ISO 3166-1 alpha-2."
    },
    "category": {
      "type": "string",
      "description": "ID de categoria do catálogo."
    },
    "language": {
      "type": "string",
      "description": "Idioma do canal, ISO 639-3."
    },
    "network": {
      "type": "string",
      "description": "Nome exato da rede/emissora."
    },
    "quality": {
      "type": "string",
      "description": "Qualidade exata do stream."
    },
    "guide": {
      "type": "string",
      "description": "`1` traz só canal com grade de programação (EPG).",
      "enum": [
        "0",
        "1"
      ],
      "default": "0"
    },
    "subdivision": {
      "type": "string",
      "description": "Estado/província, código do catálogo."
    },
    "city": {
      "type": "string",
      "description": "Cidade, código do catálogo."
    },
    "playable": {
      "type": "string",
      "description": "`0` inclui canal sem stream utilizável conhecido.",
      "enum": [
        "0",
        "1"
      ],
      "default": "1"
    },
    "sort": {
      "type": "string",
      "enum": [
        "name",
        "score",
        "votes"
      ],
      "description": "`score` ordena pela saúde medida por terceiro, melhor primeiro; canal não medido vai para o fim. `votes` ordena pelos votos registrados para a estação (rádio). `name` é a ordem alfabética.",
      "default": "name"
    },
    "online": {
      "type": "string",
      "description": "`1` traz só canal visto online pela fonte nas 48 h anteriores à última recarga do catálogo (`health_ext.online`); com o catálogo parado há mais de 48 h o filtro não devolve ninguém.",
      "enum": [
        "0",
        "1"
      ],
      "default": "0"
    },
    "kind": {
      "type": "string",
      "enum": [
        "tv",
        "radio",
        "all"
      ],
      "description": "`tv` (padrão) é o catálogo de TV; `radio` são as estações de rádio; `all` junta os dois. Sem `kind`, rádio nunca aparece.",
      "default": "tv"
    },
    "tag": {
      "type": "string",
      "description": "Tag da estação de rádio (vocabulário livre, ex. `mpb`, `news`); veja `GET /api/tags`."
    }
  }
}
🟢list_languages

Idiomas que têm canal tocável, com a contagem de cada um.

Esquema de entrada

{
  "type": "object",
  "properties": {}
}
🟢list_networks

Redes e emissoras que têm canal tocável, com a contagem.

Esquema de entrada

{
  "type": "object",
  "properties": {}
}
🟢list_qualities

Qualidades distintas encontradas nos streams do catálogo (em rádio, codec e bitrate).

Esquema de entrada

{
  "type": "object",
  "properties": {}
}
🟢list_subdivisions(country)

Estados e províncias que têm canal tocável.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "country": {
      "type": "string",
      "description": "Restringe a um país, ISO 3166-1 alpha-2."
    }
  }
}
🟢list_cities(country, subdivision)

Cidades que têm canal tocável, filtráveis por país e por estado.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "country": {
      "type": "string",
      "description": "Restringe a um país, ISO 3166-1 alpha-2."
    },
    "subdivision": {
      "type": "string",
      "description": "Restringe a um estado/província."
    }
  }
}
🟢get_channel(id)

Ficha completa de um canal, com os streams já apontando para o nosso hop.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "ID do canal no catálogo, ex. `GloboNews.br`."
    }
  },
  "required": [
    "id"
  ]
}
🟢get_channel_guide(id)

Programação de hoje do canal (grabada por nós): agora, a seguir e a lista. Disponível nos canais com guia (`guide=1`), com validade de até dois dias. Confira a data da resposta; a ficha traz o resumo em `guide_now`.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "ID do canal no catálogo."
    }
  },
  "required": [
    "id"
  ]
}
🟡create_guest

Cria guest ipt_… Não pede e-mail nem nada. Guarde o token: perdeu o token, perdeu a biblioteca — a não ser que a conta já o tenha reivindicado (`POST /api/auth/claim`), e aí o que ele guardou está na conta.

Esquema de entrada

{
  "type": "object",
  "properties": {}
}
🟢get_library

A galeria inteira do dono: pastas, sub-abas, canais e as URLs de feed de cada nível.

Esquema de entrada

{
  "type": "object",
  "properties": {}
}
🟡create_category(name)

Cria tab/categoria pessoal. Teto de 8 pastas por dono. A resposta traz a biblioteca inteira já atualizada — não precisa recarregar `GET /api/library` depois.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "Nome da pasta, até 40 caracteres."
    }
  },
  "required": [
    "name"
  ]
}
🟡create_group(category_id, name)

Cria sub-aba numa categoria. Teto de 12 sub-abas por pasta. A resposta traz a biblioteca inteira já atualizada — não precisa recarregar `GET /api/library` depois.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "category_id": {
      "type": "string",
      "description": "Pasta que vai receber a sub-aba, `cat_…`."
    },
    "name": {
      "type": "string",
      "description": "Nome da sub-aba, até 40 caracteres."
    }
  },
  "required": [
    "category_id",
    "name"
  ]
}
🟡add_item(group_id, channel_id)

Adiciona canal à sub-aba. Teto de 40 canais por sub-aba. A resposta diz em que pasta e sub-aba o canal caiu, para a tela abrir no lugar certo. A resposta traz a biblioteca inteira já atualizada — não precisa recarregar `GET /api/library` depois.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "group_id": {
      "type": "string",
      "description": "Sub-aba que recebe o canal, `grp_…`."
    },
    "channel_id": {
      "type": "string",
      "description": "ID do canal no catálogo, ex. `GloboNews.br`."
    }
  },
  "required": [
    "group_id",
    "channel_id"
  ]
}
🟢get_history(limit, offset)

Histórico de canais assistidos pelo dono. Exibe somente canais disponíveis no catálogo.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "limit": {
      "type": "string",
      "description": "Itens por página. Acima de 50 é silenciosamente reduzido a 50.",
      "default": 20
    },
    "offset": {
      "type": "string",
      "description": "Quantos itens pular. Use `next_offset` da resposta anterior.",
      "default": 0
    }
  }
}
⚪record_watch(channel_id)

Registra um canal assistido no histórico do dono.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "channel_id": {
      "type": "string",
      "description": "Canal assistido, ex. `GloboNews.br`."
    }
  },
  "required": [
    "channel_id"
  ]
}
⚪forget_watch(channel_id)

Tira um canal do histórico do dono.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "channel_id": {
      "type": "string",
      "description": "Canal a remover do histórico, ex. `GloboNews.br`."
    }
  },
  "required": [
    "channel_id"
  ]
}
🔴clear_history

Limpa o histórico inteiro do dono, de uma vez.

Esquema de entrada

{
  "type": "object",
  "properties": {}
}
🔴legacy_stream(id)

Metadados públicos: site oficial do canal, URL da transmissão para copiar em outro player e player legado HTTP. O botão legacy_url abre http://legacy.gradetv.net:8080/legacy?stream=ID. A página isolada aceita lang=pt/en/es/fr/de e theme=light/dark. Playlists continuam pelo hop HTTPS e mídia direto da origem. Não remove recusa de acesso nem exigência CORS. Uma leitura indexada; não busca origem nem grava no D1.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "ID público do stream, incluindo compatibilidade de IDs antigos."
    }
  },
  "required": [
    "id"
  ]
}
🟡producer_services(lang)

Oferta de transmissão autorizada sob consulta: informações e contatos para produtores. Não ativa streams. Somente informações e captação de interesse. Não provisiona, não cobra e não ativa transmissão. Use contact.form_url no browser, contact.email por e-mail ou POST /api/contact para apresentar o projeto. O envio é livre — sem captcha nem pagamento —, uma mensagem a cada 10 s por rede.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "lang": {
      "type": "string",
      "enum": [
        "pt",
        "en",
        "es",
        "fr",
        "de"
      ],
      "description": "Idioma da oferta: pt, en, es, fr ou de; ausente ou desconhecido volta a pt.",
      "default": "pt"
    }
  }
}
⚪report_play(channel_id, ok, code)

Relata se um canal tocou (ok=true) ou não (ok=false + code). Navegador, SO e país saem do pedido, não do corpo. Conta uma vez por dono, por canal, por dia e por resultado; relatar de novo devolve 200 com `reason: ja_relatado_hoje`, e não é erro. Navegador e sistema saem do User-Agent e o país da borda — mandar isso no corpo não muda nada. Sem relato, o catálogo não aprende: é assim que `GET /api/channels/:id/health` sabe distinguir canal fora do ar de canal bloqueado para você.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "channel_id": {
      "type": "string",
      "description": "Canal que você tentou assistir."
    },
    "ok": {
      "type": "boolean",
      "description": "`true` se tocou, `false` se falhou."
    },
    "code": {
      "type": "string",
      "description": "Por que falhou; só quando `ok` é `false`.",
      "enum": [
        "cors",
        "geo",
        "sumiu",
        "codec",
        "playlist",
        "sem_resposta",
        "protocolo",
        "sem_stream",
        "outro"
      ]
    }
  },
  "required": [
    "channel_id",
    "ok"
  ]
}
⚪play_reports(channel_id, ok, limit)

Relatos crus de um canal, com endereço — só operador (METRICS_TOKEN). Use para investigar um canal específico. Existe separado do painel público justamente porque traz endereço. A linha some depois do prazo em `retention_days`, e `/api/channels/:id/health` nunca devolve IP.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "channel_id": {
      "type": "string",
      "description": "Restringe a um canal."
    },
    "ok": {
      "type": "string",
      "description": "`0` traz só as falhas — é o recorte que interessa numa investigação.",
      "enum": [
        "0"
      ]
    },
    "limit": {
      "type": "string",
      "description": "Itens por página. Acima de 50 é silenciosamente reduzido a 50.",
      "default": 20
    }
  }
}
⚪channel_health(id)

Saúde comunitária do canal: taxa de sucesso, motivos de falha e ambientes afetados. É o que separa 'o canal está fora do ar' de 'o canal está bloqueado no seu país'. `geo` diz se é restrição regional ou falha geral (com os países), `regions[]` traz sucesso/falha por país, `latency[]` a velocidade de abertura medida no hop por país, e `pra_voce` resume tudo para o país de quem chama. Sem relato da comunidade (`POST /api/play-report`) o painel fica vazio.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "ID do canal no catálogo."
    }
  },
  "required": [
    "id"
  ]
}
🟢list_favorites

Favoritos do dono. Exibe somente canais disponíveis no catálogo.

Esquema de entrada

{
  "type": "object",
  "properties": {}
}
🟡add_favorite(channel_id)

Favorita um canal. Repetir não soma: o contador público conta pessoas, não cliques.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "channel_id": {
      "type": "string",
      "description": "Canal a favoritar, ex. `GloboNews.br`."
    }
  },
  "required": [
    "channel_id"
  ]
}
🔴remove_favorite(channel_id)

Desfavorita o canal e devolve o ponto ao contador público.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "channel_id": {
      "type": "string",
      "description": "Canal a desfavoritar, ex. `GloboNews.br`."
    }
  },
  "required": [
    "channel_id"
  ]
}
🟢list_comments(id)

Comentários da comunidade sobre um canal. Com credencial na chamada, cada comentário seu vem com `mine: true` — é assim que a interface sabe o que dá para apagar.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "ID do canal no catálogo."
    }
  },
  "required": [
    "id"
  ]
}
🟡post_comment(id, body, author)

Comenta num canal (teto de 20/hora por dono). Sem `author`, o apelido é gerado e fica estável para o mesmo dono — a pessoa não vira um nome diferente a cada mensagem.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "ID do canal no catálogo."
    },
    "body": {
      "type": "string",
      "description": "O texto do comentário; o teto vem em `max_length` da listagem."
    },
    "author": {
      "type": "string",
      "description": "Apelido a usar; sem ele o servidor gera um estável."
    }
  },
  "required": [
    "id",
    "body"
  ]
}
🔴delete_comment(id)

Apaga um comentário do próprio dono. O 404 é de propósito: a API não confirma que existe um comentário com aquele id se ele não é seu.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "ID do comentário, vindo de `Comentario.id`."
    }
  },
  "required": [
    "id"
  ]
}
⚪chat_history(channel_id)

Últimas mensagens da sala de um canal (sem WebSocket).

Esquema de entrada

{
  "type": "object",
  "properties": {
    "channel_id": {
      "type": "string",
      "description": "ID do canal no catálogo."
    }
  },
  "required": [
    "channel_id"
  ]
}
🟡chat_send(channel_id, body, author)

Manda mensagem na sala de um canal por HTTP. Ler é grátis; escrever custa $0.10 por 30 dias. Sem passe válido a resposta é 402 com `accepts[]` — pague e repita a mesma chamada. Quem fala é o convidado (`X-Guest-Token`), o mesmo que o WebSocket reconhece; sem convidado, a sessão da conta.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "channel_id": {
      "type": "string",
      "description": "ID do canal no catálogo."
    },
    "body": {
      "type": "string",
      "description": "O texto da mensagem, dentro de `max_length`."
    },
    "author": {
      "type": "string",
      "description": "Apelido a usar; sem ele o servidor gera um estável."
    }
  },
  "required": [
    "channel_id",
    "body"
  ]
}
⚪chat_pass

Passe mensal do chat ($0.10 / 30 dias) do convidado que fala no chat. Sem pagamento → 402. Passe já válido devolve 200 sem cobrar de novo — dá para chamar antes de escrever, sem risco de pagar duas vezes. O passe é de quem fala no chat: o convidado de `X-Guest-Token` (é ele que o WebSocket reconhece no `hello`); sem convidado, a sessão da conta, que fala só por HTTP. Fica no registro global de compras (`direito`).

Esquema de entrada

{
  "type": "object",
  "properties": {}
}
⚪me

Conta da sessão (cookie repassado pelo cliente MCP): e-mail e tamanho da biblioteca da conta. A conta é a da biblioteca de conta; `user.id` é o id da conta, e é ele o dono da galeria com sessão.

Esquema de entrada

{
  "type": "object",
  "properties": {}
}
⚪billing

Preços e tetos. É o número EM VIGOR: leia daqui antes de gastar chamada, em vez de assumir o preço da documentação. Com credencial, também diz se o passe de chat de quem fala no chat (o convidado; sem ele, a conta) está ativo. `prices.abuso_24h_usd` é o preço da porta de UA vazio/curl, hoje desligada.

Esquema de entrada

{
  "type": "object",
  "properties": {}
}
⚪contact(name, email, message, tipo, empresa, ...)

Fale com quem faz o produto: dúvida, ou proposta de patrocínio/parceria/anúncio. Grátis, sem captcha nem pagamento; uma mensagem a cada 10 s por rede (a que chega antes espera a vez). Uma rota para dúvida e para proposta de patrocínio, parceria ou anúncio (`tipo`, com os espaços de `GET /api/partners`). Sem captcha, sem conta, sem pagamento. Uma mensagem a cada 10 segundos por rede: a que chega antes espera a vez e sai — sem erro. A mensagem chega à equipe por e-mail, com o `email` como endereço de resposta.

Esquema de entrada

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "Como chamar quem escreve (alias `nome`)."
    },
    "email": {
      "type": "string",
      "description": "Para onde responder."
    },
    "message": {
      "type": "string",
      "description": "O que você quer dizer (alias `mensagem`)."
    },
    "tipo": {
      "type": "string",
      "enum": [
        "patrocinio",
        "parceria",
        "anuncio"
      ],
      "description": "Proposta: `patrocinio`, `parceria` ou `anuncio`. Liga os campos abaixo."
    },
    "empresa": {
      "type": "string",
      "description": "Quem propõe, quando é empresa."
    },
    "site": {
      "type": "string",
      "description": "Site de quem propõe."
    },
    "orcamento": {
      "type": "string",
      "enum": [
        "ate_100",
        "100_500",
        "500_2000",
        "2000_mais",
        "a_combinar"
      ],
      "description": "`ate_100`, `100_500`, `500_2000`, `2000_mais` ou `a_combinar`."
    },
    "espaco": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Ids de placement de `GET /api/partners`, até 6."
    },
    "duracao": {
      "type": "string",
      "enum": [
        "30",
        "90",
        "365"
      ],
      "description": "Dias de exposição: `30`, `90` ou `365`."
    },
    "pagamento": {
      "type": "string",
      "enum": [
        "usdc",
        "deposito",
        "a_combinar"
      ],
      "description": "`usdc`, `deposito` ou `a_combinar`."
    }
  },
  "required": [
    "name",
    "email",
    "message"
  ]
}
⚪pricing

Current public prices and free allowances; no charge.

Esquema de entrada

{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}

Comunidad

Califica este servidor

Evidencia

Observaciones recientes

verificadoversión no registrada40 herramientas
verificadoversión no registrada41 herramientas