
Libtv Skill
Wire your coding agent to LibTV agent-im OpenAPI to create and query IM sessions and open canvas projects from project IDs.
Overview
LibTV skill is an agent skill for the Build phase that connects agents to LibTV agent-im OpenAPI for Bearer-authenticated session calls and canvas project URLs.
Install
npx skills add https://github.com/libtv-labs/libtv-skills --skill libtv-skillWhat is this skill?
- POST helper for agent-im OpenAPI with Authorization: Bearer access key
- Session create and query flows against configurable IM_BASE / OPENAPI_IM_BASE
- Project canvas deep links via https://www.liblib.tv/canvas?projectId=
- stdlib urllib client with 30s timeout and structured API error reporting
- Requires LIBTV_ACCESS_KEY environment variable before any call
- 30-second HTTP timeout on OpenAPI POST helper
- Canvas links use fixed projectId query parameter on liblib.tv
Adoption & trust: 3.7k installs on skills.sh; 736 GitHub stars; 1/3 security scanners passed (skills.sh audits).
What problem does it solve?
You want LibTV IM sessions from an agent run but only have raw endpoints and no shared, env-based client in the repo.
Who is it for?
Solo builders shipping agent workflows that must create LibTV IM sessions and return canvas links after automated project creation.
Skip if: Teams that need a complete LibTV SDK, GUI-only workflows, or integrations that must not hold long-lived API keys in the agent environment.
When should I use this skill?
You need authenticated LibTV agent-im session operations or canvas URLs inside an agent-driven build workflow.
What do I get? / Deliverables
Reusable POST helpers and project URL builders let agents create or query sessions and link users to the LibTV canvas without duplicating auth headers each run.
- Authenticated API POST calls to agent-im paths
- Project canvas URLs derived from projectUuid
- Stderr-documented API and network error exits
Recommended Skills
Journey fit
Canonical shelf is Build because the skill is an external API client you embed while composing agent workflows, not a launch or ops playbook. integrations fits LibTV Bearer-authenticated HTTP calls and session lifecycle hooks against im.liblib.tv.
How it compares
Use as a focused OpenAPI client snippet instead of a generic REST MCP server when LibTV is the only external IM surface.
Common Questions / FAQ
Who is libtv-skill for?
Indie builders and small teams wiring Claude Code, Cursor, or Codex to LibTV’s agent-im API for session automation and canvas handoffs.
When should I use libtv-skill?
During Build integrations when you are adding LibTV session create/query steps to an agent pipeline, or when you need stable canvas URLs from projectUuid values after API responses.
Is libtv-skill safe to install?
It expects a Bearer access key in the environment and performs outbound HTTPS calls—review the Security Audits panel on this page and rotate keys if the repo is untrusted.
SKILL.md
READMESKILL.md - Libtv Skill
"""agent-im OpenAPI 公共模块:创建会话、查询会话(鉴权为 Authorization: Bearer <access_key>)""" import json import os import sys import urllib.request import urllib.error # 默认 im 环境 IM_BASE = os.environ.get("OPENAPI_IM_BASE", os.environ.get("IM_BASE_URL", "https://im.liblib.tv")) ACCESS_KEY = os.environ.get("LIBTV_ACCESS_KEY", "") # 项目画布地址前缀,拼上 projectId 即项目地址 PROJECT_CANVAS_BASE = "https://www.liblib.tv/canvas?projectId=" def build_project_url(project_id: str) -> str: """根据 projectId(即 projectUuid)拼接项目画布地址""" if not project_id: return "" return PROJECT_CANVAS_BASE + project_id.strip() if not ACCESS_KEY: print("错误:请设置 LIBTV_ACCESS_KEY 环境变量", file=sys.stderr) sys.exit(1) def _headers(): return { "Authorization": f"Bearer {ACCESS_KEY}", "Content-Type": "application/json", } def api_post(path: str, body: dict) -> dict: """POST 请求 agent-im OpenAPI""" url = f"{IM_BASE.rstrip('/')}{path}" data = json.dumps(body).encode("utf-8") req = urllib.request.Request( url, data=data, method="POST", headers=_headers(), ) try: with urllib.request.urlopen(req, timeout=30) as resp: return json.loads(resp.read().decode("utf-8")) except urllib.error.HTTPError as e: err_body = e.read().decode("utf-8") if e.fp else "" print(f"API 错误 {e.code}: {err_body}", file=sys.stderr) sys.exit(1) except urllib.error.URLError as e: print(f"网络错误: {e.reason}", file=sys.stderr) sys.exit(1) def api_get(path: str) -> dict: """GET 请求 agent-im OpenAPI""" url = f"{IM_BASE.rstrip('/')}{path}" req = urllib.request.Request(url, method="GET", headers=_headers()) try: with urllib.request.urlopen(req, timeout=30) as resp: return json.loads(resp.read().decode("utf-8")) except urllib.error.HTTPError as e: err_body = e.read().decode("utf-8") if e.fp else "" print(f"API 错误 {e.code}: {err_body}", file=sys.stderr) sys.exit(1) except urllib.error.URLError as e: print(f"网络错误: {e.reason}", file=sys.stderr) sys.exit(1) def create_session(session_id: str = "", message: str = "") -> dict: """ 创建会话或向已有会话发消息。 返回 data: { projectUuid, sessionId }。 """ body = {} if session_id: body["sessionId"] = session_id if message: body["message"] = message resp = api_post("/openapi/session", body) return resp.get("data", {}) def query_session(session_id: str, after_seq: int = 0) -> dict: """ 查询会话消息列表。 返回 data: { messages: [...] }。 """ path = f"/openapi/session/{session_id}" if after_seq > 0: path += f"?afterSeq={after_seq}" resp = api_get(path) return resp.get("data", {}) def change_project() -> dict: """ 切换当前 accessKey 绑定的项目(调用 libtv 切换项目,后续 create_session 将使用新项目)。 返回 data: { projectUuid }。 """ resp = api_post("/openapi/session/change-project", {}) return resp.get("data", {}) #!/usr/bin/env python3 """切换当前 accessKey 绑定的项目:POST /openapi/session/change-project""" import json import sys import os sys.path.insert(0, os.path.dirname(__file__)) from _common import change_project, build_project_url def main(): data = change_project() project_uuid = data.get("projectUuid", "") if not project_uuid: print("错误:未返回 projectUuid", file=sys.stderr) sys.exit(1) project_url = build_project_url(project_uuid) out = { "projectUuid": project_uuid, "projectUrl": project_url, } print(json.dumps(out, ensure_ascii=False, indent=2)) if __name__ == "__main__": main() #!/usr/bin/env python3 """创建会话 / 向会话发送消息(生图、生视频等):POST /openapi/session""" import argparse import json import sys import os sys.path.insert(0, os.path.dirname(__file__)) from _common import create_session, build_project_url def main(): parser = argparse.ArgumentParser( description="创建会话或向已有会话发送消息(用于生图、生视频)",