# Loom MCP Server [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/) [MCP](https://modelcontextprotocol.io) server exposing 59 tools for Loom's internal GraphQL API. Works with Claude, Cursor, or any MCP-compatible client. ## Features - List, search, and get detailed metadata for Loom videos - Read transcripts, captions, AI summaries, chapters, and key takeaways - Save any fetched content to disk on demand via `save_dir` parameter - Manage comments, tasks, reactions, and tags - Organize with folders, spaces, and watch lists - Update video settings, share to spaces, and more ## Prerequisites - Python 3.11+ - [uv](https://docs.astral.sh/uv/) package manager ## Setup ### Authentication > [!IMPORTANT] > This server uses Loom's internal GraphQL API via a browser session cookie. There is no official API key — you must grab the cookie from your browser. 1. Open [loom.com](https://www.loom.com) in your browser 2. Open DevTools (F12) → Application → Cookies → `https://www.loom.com` 3. Copy the **value** of the `connect.sid` cookie (starts with `s%3A...`) 4. Paste it as `LOOM_COOKIE` in your MCP client config below, prefixed with `connect.sid=` The cookie lasts about 30 days. See [Troubleshooting](#troubleshooting) if you get auth errors. ### Installation Pick your MCP client below for the appropriate config. Each uses `uvx` to install and run directly from GitHub — no clone needed.
Claude Desktop Add to your `claude_desktop_config.json`: ```json { "mcpServers": { "loom": { "type": "stdio", "command": "uvx", "args": ["--from", "git+https://github.com/karbassi/loom-mcp.git", "loom-mcp"], "env": { "LOOM_COOKIE": "connect.sid=s%3A..." } } } } ```
Claude Code ```sh claude mcp add loom -- uvx --from git+https://github.com/karbassi/loom-mcp.git loom-mcp ``` Then set the env var in your shell or `.env`: ```sh export LOOM_COOKIE="connect.sid=s%3A..." ```
Cursor Add to your Cursor MCP settings (`.cursor/mcp.json`): ```json { "mcpServers": { "loom": { "type": "stdio", "command": "uvx", "args": ["--from", "git+https://github.com/karbassi/loom-mcp.git", "loom-mcp"], "env": { "LOOM_COOKIE": "connect.sid=s%3A..." } } } } ```
VS Code Add to your VS Code settings (`.vscode/mcp.json`): ```json { "mcpServers": { "loom": { "type": "stdio", "command": "uvx", "args": ["--from", "git+https://github.com/karbassi/loom-mcp.git", "loom-mcp"], "env": { "LOOM_COOKIE": "connect.sid=s%3A..." } } } } ```
Local clone ```sh git clone git@github.com:karbassi/loom-mcp.git cd loom-mcp uv sync uv run loom-mcp ``` Or reference it from any MCP client: ```json { "mcpServers": { "loom": { "type": "stdio", "command": "uv", "args": ["run", "--directory", "/path/to/loom-mcp", "loom-mcp"], "env": { "LOOM_COOKIE": "connect.sid=s%3A..." } } } } ```
### Shared HTTP server By default every client starts its own stdio process. To run one long-lived server that many clients share: ```bash LOOM_COOKIE="connect.sid=s%3A..." LOOM_MCP_TRANSPORT=http LOOM_MCP_PORT=8505 loom-mcp ``` ```json { "mcpServers": { "loom": { "type": "http", "url": "http://127.0.0.1:8505/mcp" } } } ``` The endpoint has **no authentication**: anything that can connect to it can use every tool with your Loom session, including deleting videos. So: - The server only binds to loopback (`LOOM_MCP_HOST` must be `127.0.0.1`, `localhost`, or `::1`). Any local process can still connect, so only use HTTP mode on a machine where every local user and process is trusted with your Loom account. - It always turns on FastMCP's Host/Origin protection, which FastMCP leaves off by default. That check **only** stops DNS-rebinding attacks from web pages. It does not authenticate anyone. - To reach it from another machine, put it behind a reverse proxy that **controls who can connect**: a bearer token, mTLS, an IP allowlist on a private network, or similar. Then add the proxy's hostname to `FASTMCP_HTTP_ALLOWED_HOSTS`, e.g. `FASTMCP_HTTP_ALLOWED_HOSTS='["mcp.example.com"]'`. If a browser-based client will call it, also add that client's origin to `FASTMCP_HTTP_ALLOWED_ORIGINS`, e.g. `'["https://mcp.example.com"]'`. Never expose the port or the proxy publicly without access control. File-writing tools (`save_dir` on the read tools, `download_media`) write on **the server's machine**, not the client's, and need absolute paths. See [Saving to disk](#saving-to-disk). ## Tools ### Read (31 tools) Tools marked with **Save** accept an optional `save_dir` parameter — see [Saving to disk](#saving-to-disk). | Tool | Description | Save | |---|---|:---:| | `list_videos` | List your videos, sorted by most recent | | | `search_videos` | AI-powered semantic search | | | `get_video` | Video metadata (name, duration, owner, views) | `metadata.json` | | `get_transcript` | Full transcript with timestamps and speakers | `transcript.txt` | | `get_captions` | WebVTT captions with start+end timestamps per cue | `captions.vtt` | | `get_summary` | AI-generated summary | `summary.txt` | | `get_chapters` | AI-generated chapters | `chapters.txt` | | `get_description` | AI-generated detailed description with timestamped sections | `description.txt` | | `get_key_takeaways` | AI-generated key takeaways | `takeaways.txt` | | `get_comments` | Comments and replies | `comments.txt` | | `get_tasks` | AI-generated action items | `tasks.txt` | | `get_reactions` | Emoji reactions | `reactions.txt` | | `get_tags` | Video tags | `tags.txt` | | `get_backlinks` | External references (where the video is shared/embedded) | `backlinks.txt` | | `get_meeting_notes` | Confluence meeting notes URL | | | `get_confluence_pages` | Linked Confluence pages | | | `get_download_url` | Signed MP4 download URL | | | `download_media` | Download audio or video to a local file (works when MP4 export is disabled; optional `start`/`end` trim; needs `ffmpeg`) | `out_path` | | `get_video_details` | All-in-one: metadata + transcript + chapters + summary + comments + tasks | `details.md` + all above | | `list_folders` | List your folders | | | `list_spaces` | List your workspaces | | | `get_space` | Space details (name, privacy, primary) | | | `search_folders` | Search folders by name | | | `get_folder` | Folder details | | | `get_last_watch_time` | Last timestamp where you stopped watching | | | `get_watch_later_count` | Number of videos in your Watch Later list | | | `get_total_videos_count` | Total videos created by a user | | | `get_frequent_reactions` | Your most-used emoji reaction types | | | `get_comment_reactions` | Emoji reactions on a specific comment | | | `get_user` | User profile by ID (name, email, company, avatar) | | | `search_workspace_tags` | Search tags in your workspace | | ### Write (29 tools) > [!NOTE] > Write tools modify your Loom data. Destructive actions (delete, archive, move) cannot always be undone. | Tool | Description | |---|---| | `update_video_name` | Rename a video | | `update_video_description` | Update video description | | `update_video_settings` | Update video settings (downloads, comments, etc.) | | `create_comment` | Post a comment (with optional timestamp) | | `edit_comment` | Edit an existing comment | | `delete_comment` | Delete a comment | | `create_task` | Create an action item on a video | | `update_task` | Update the content of an action item | | `delete_task` | Delete an action item | | `approve_task` | Mark a task as approved | | `respond_to_task` | Respond to a task | | `add_reaction` | Add an emoji reaction at a timestamp | | `delete_reaction` | Delete a reaction | | `add_comment_reaction` | React to a comment with an emoji | | `toggle_following` | Follow/unfollow a video | | `toggle_following_tag` | Follow/unfollow a workspace tag | | `archive_videos` | Archive or unarchive videos | | `duplicate_video` | Duplicate a video | | `delete_video` | Permanently delete a video | | `recover_video` | Recover a deleted video from trash | | `pin_video` | Pin or unpin a video in your library | | `add_to_watch_later` | Add to Watch Later list | | `remove_from_watch_later` | Remove from Watch Later list | | `create_folder` | Create a new folder | | `rename_folder` | Rename a folder | | `delete_folders` | Delete folders | | `move_videos` | Move videos to a different folder | | `move_folders` | Move folders into a different parent folder | | `share_videos_to_spaces` | Share videos to one or more spaces | ## Saving to disk Most per-video read tools accept an optional `save_dir` parameter. When provided, the tool saves its output to `{save_dir}/{video_id}/` and returns the file path alongside the content. When omitted, nothing is saved. `save_dir` (and `download_media`'s `out_path`) must be an absolute path; `~` is expanded. Relative paths are rejected because they would resolve against the server process's working directory, which means nothing for a shared or launchd-managed server. Just ask naturally — *"get the details from my last meeting and save them to `~/Downloads/loom`"* — and the LLM will pass `save_dir="~/Downloads/loom"` to the tool. `get_video_details` saves each piece individually (`metadata.json`, `transcript.txt`, `summary.txt`, etc.) plus a combined `details.md`. ## Troubleshooting ### Auth errors If you get auth errors, your `connect.sid` cookie has expired (~30 days). Grab a fresh one from your browser using the steps in [Authentication](#authentication). ### Debugging with MCP Inspector ```sh npx @modelcontextprotocol/inspector uvx --from git+https://github.com/karbassi/loom-mcp.git loom-mcp ``` ## Contributing Issues and pull requests are welcome on [GitHub](https://github.com/karbassi/loom-mcp). ## License [MIT](LICENSE)