mcp-loom 1.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,28 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - uses: jdx/mise-action@v2
15
+
16
+ - name: Install dependencies
17
+ run: mise run install
18
+
19
+ - name: Lint
20
+ run: mise run lint
21
+
22
+ - name: Format check
23
+ run: mise run format-check
24
+
25
+ - name: Test
26
+ run: mise run test
27
+ env:
28
+ LOOM_COOKIE: test=dummy
@@ -0,0 +1,27 @@
1
+ name: Publish
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ environment: pypi
12
+ permissions:
13
+ contents: read
14
+ id-token: write
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - uses: jdx/mise-action@v2
19
+
20
+ - name: Install dependencies
21
+ run: mise run install
22
+
23
+ - name: Build
24
+ run: uv build
25
+
26
+ - name: Publish to PyPI
27
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,6 @@
1
+ __pycache__/
2
+ .venv/
3
+ auth.json
4
+ .env
5
+ *.har
6
+ .temp/
@@ -0,0 +1,46 @@
1
+ [tools]
2
+ python = "3.13"
3
+ uv = "latest"
4
+
5
+ [env]
6
+ UV_PROJECT_ENVIRONMENT = "{{cwd}}/.venv"
7
+
8
+ [tasks.install]
9
+ description = "Install dependencies"
10
+ run = "uv sync --all-extras"
11
+
12
+ [tasks.test]
13
+ description = "Run tests"
14
+ run = "uv run --with pytest pytest tests/ -v"
15
+
16
+ [tasks.lint]
17
+ description = "Run ruff linter"
18
+ run = "uv run --with ruff ruff check ."
19
+
20
+ [tasks.format]
21
+ description = "Run ruff formatter"
22
+ run = "uv run --with ruff ruff format ."
23
+
24
+ [tasks.format-check]
25
+ description = "Check formatting without writing"
26
+ run = "uv run --with ruff ruff format --check ."
27
+
28
+ [tasks.check]
29
+ description = "Run lint and format check"
30
+ depends = ["lint", "format-check"]
31
+
32
+ [tasks.install-hooks]
33
+ description = "Install git pre-commit hook"
34
+ run = """
35
+ cat > .git/hooks/pre-commit << 'EOF'
36
+ #!/bin/sh
37
+ mise run format
38
+ git add -u
39
+ mise run lint && mise run test
40
+ EOF
41
+ chmod +x .git/hooks/pre-commit
42
+ echo "pre-commit hook installed"
43
+ """
44
+
45
+ [hooks]
46
+ enter = "[ -f .git/hooks/pre-commit ] || mise run install-hooks"
@@ -0,0 +1,88 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [1.2.0] - 2026-03-17
6
+
7
+ ### Added
8
+
9
+ - GitHub Actions CI workflow (lint, format, test) triggered on push and PRs
10
+ - PyPI publish workflow using OIDC trusted publishing, triggered on version tags
11
+ - mise tasks: `install`, `lint`, `format`, `test`, `build`
12
+ - `install-hooks` mise task and `mise enter` hook for automatic pre-commit setup
13
+
14
+ ### Changed
15
+
16
+ - Renamed PyPI package from `loom-mcp` to `mcp-loom`
17
+ - Pre-commit hook now auto-formats and re-stages files
18
+
19
+ ### Fixed
20
+
21
+ - Publish workflow permissions include `contents: read` so `actions/checkout` works alongside `id-token: write`
22
+
23
+ ## [1.1.0] - 2026-02-17
24
+
25
+ ### Added
26
+
27
+ - Optional `save_dir` parameter on all per-video read tools (`get_video`, `get_transcript`, `get_captions`, `get_summary`, `get_chapters`, `get_description`, `get_key_takeaways`, `get_comments`, `get_tasks`, `get_reactions`, `get_tags`, `get_backlinks`, `get_video_details`)
28
+ - When `save_dir` is provided, tool output is saved to `{save_dir}/{video_id}/` with appropriate filenames (e.g. `transcript.txt`, `captions.vtt`, `metadata.json`)
29
+ - `get_video_details` saves each piece individually plus a combined `details.md`
30
+ - Saved file path is returned alongside the content
31
+
32
+ ## [1.0.2] - 2026-02-10
33
+
34
+ ### Added
35
+
36
+ - `get_space` tool — get details of a space by ID (parallels `get_folder`)
37
+ - `limit` parameter on `search_videos` (default 50, max 200)
38
+
39
+ ### Changed
40
+
41
+ - `search_videos` now uses the paginated `SearchVideos` endpoint instead of the semantic `Search` endpoint — faster and no longer capped at 10 results
42
+ - Remove unused `fetch_videos_by_id` and `get_all_videos` from client
43
+
44
+ ## [1.0.1] - 2026-02-10
45
+
46
+ ### Fixed
47
+
48
+ - 401 error message now says to refresh the browser cookie instead of referencing removed `login.js`
49
+ - `.env` and `auth.json` path resolution no longer breaks when installed via `uvx` (gracefully skipped when no local `pyproject.toml` is found)
50
+ - Clear error message when neither `LOOM_COOKIE` nor `LOOM_AUTH_FILE` is set
51
+
52
+ ### Changed
53
+
54
+ - `get_video_details` now fetches transcript, chapters, summary, comments, and tasks concurrently via `asyncio.gather` instead of sequentially
55
+ - Rewrite README with per-client install instructions, badges, and improved auth docs
56
+ - Add MIT license, CONTRIBUTING.md
57
+
58
+ ## [1.0.0] - 2026-02-10
59
+
60
+ ### Changed
61
+
62
+ - Restructure to `src/` layout (`src/loom_mcp/server.py`, `src/loom_mcp/client.py`)
63
+ - Move tests to `tests/` directory
64
+ - Add hatchling build-system for proper packaging
65
+ - Resolve `.env`/`auth.json` paths by walking up to `pyproject.toml`
66
+ - Lower `requires-python` from 3.14 to 3.11
67
+ - Remove `.python-version` (redundant with `requires-python`)
68
+ - Simplify README auth docs, drop parent repo references
69
+ - Rename package to `loom-mcp` with console script entry point
70
+ - Switch to `@lifespan` decorator
71
+ - Add `read`/`write` tags and `timeout=30.0` to all tools
72
+ - Use `uvx` entry point in `fastmcp.json`
73
+
74
+ ## [0.1.0] - 2026-02-08
75
+
76
+ ### Added
77
+
78
+ - FastMCP server exposing 58 tools (29 read, 29 write) for Loom's internal GraphQL API
79
+ - Async Python client using httpx with concurrency limiter (5 concurrent requests)
80
+ - Auth via `LOOM_COOKIE` env var, `LOOM_AUTH_FILE` path, or `auth.json`
81
+ - Auto-load `.env` file for auth config
82
+ - Tool annotations (`readOnlyHint`, `destructiveHint`, `idempotentHint`) on all tools
83
+ - Input ID validation via `_id()`/`_ids()` helpers to reject injection-style inputs
84
+ - `LoomAPIError` and `ToolError` for actionable error messages (401, 403, connection errors)
85
+ - Lifespan-managed httpx client with proper cleanup
86
+ - 31 tests covering tool registration, annotations, ID validation, happy paths, and error paths
87
+ - `fastmcp.json` for FastMCP run support
88
+ - Ruff T20 lint rule to ban `print()` in stdio server code
@@ -0,0 +1,13 @@
1
+ # Project Instructions
2
+
3
+ ## Testing
4
+
5
+ ```sh
6
+ uv run --with pytest pytest tests/ -v
7
+ ```
8
+
9
+ ## Releasing
10
+
11
+ - Tag versions with annotated tags: `git tag -a v1.2.3 -m "v1.2.3"`
12
+ - Do NOT create GitHub releases — tags + CHANGELOG.md is sufficient
13
+ - Bump version in both `pyproject.toml` and `src/loom_mcp/server.py`
@@ -0,0 +1,31 @@
1
+ # Contributing
2
+
3
+ Thanks for your interest in contributing to loom-mcp!
4
+
5
+ ## Getting started
6
+
7
+ ```sh
8
+ git clone git@github.com:karbassi/loom-mcp.git
9
+ cd loom-mcp
10
+ uv sync
11
+ ```
12
+
13
+ ## Running tests
14
+
15
+ ```sh
16
+ uv run --with pytest --with anyio pytest tests/ -q
17
+ ```
18
+
19
+ ## Linting and formatting
20
+
21
+ ```sh
22
+ uv run --with ruff ruff check src tests
23
+ uv run --with ruff ruff format src tests
24
+ ```
25
+
26
+ ## Submitting changes
27
+
28
+ 1. Fork the repo and create a branch from `main`
29
+ 2. Make your changes
30
+ 3. Ensure tests pass and code is formatted
31
+ 4. Open a pull request
mcp_loom-1.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ali Karbassi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.4
2
+ Name: mcp-loom
3
+ Version: 1.2.0
4
+ Summary: MCP server for Loom's internal GraphQL API
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.11
8
+ Requires-Dist: fastmcp>=3.0.0b2
9
+ Requires-Dist: httpx>=0.28.1
@@ -0,0 +1,253 @@
1
+ # Loom MCP Server
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/mcp-loom.svg)](https://pypi.org/project/mcp-loom/)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
6
+
7
+ [MCP](https://modelcontextprotocol.io) server exposing 59 tools for Loom's internal GraphQL API. Works with Claude, Cursor, or any MCP-compatible client.
8
+
9
+ ## Features
10
+
11
+ - List, search, and get detailed metadata for Loom videos
12
+ - Read transcripts, captions, AI summaries, chapters, and key takeaways
13
+ - Save any fetched content to disk on demand via `save_dir` parameter
14
+ - Manage comments, tasks, reactions, and tags
15
+ - Organize with folders, spaces, and watch lists
16
+ - Update video settings, share to spaces, and more
17
+
18
+ ## Prerequisites
19
+
20
+ - Python 3.11+
21
+ - [uv](https://docs.astral.sh/uv/) package manager
22
+
23
+ ## Setup
24
+
25
+ ### Authentication
26
+
27
+ > [!IMPORTANT]
28
+ > 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.
29
+
30
+ 1. Open [loom.com](https://www.loom.com) in your browser
31
+ 2. Open DevTools (F12) → Application → Cookies → `https://www.loom.com`
32
+ 3. Copy the **value** of the `connect.sid` cookie (starts with `s%3A...`)
33
+ 4. Paste it as `LOOM_COOKIE` in your MCP client config below, prefixed with `connect.sid=`
34
+
35
+ The cookie lasts about 30 days. See [Troubleshooting](#troubleshooting) if you get auth errors.
36
+
37
+ ### Installation
38
+
39
+ Pick your MCP client below for the appropriate config. Each uses `uvx` to install and run from PyPI — no clone needed.
40
+
41
+ <details>
42
+ <summary><b>Claude Desktop</b></summary>
43
+
44
+ Add to your `claude_desktop_config.json`:
45
+
46
+ ```json
47
+ {
48
+ "mcpServers": {
49
+ "loom": {
50
+ "type": "stdio",
51
+ "command": "uvx",
52
+ "args": ["--from", "git+https://github.com/karbassi/loom-mcp.git", "loom-mcp"],
53
+ "env": {
54
+ "LOOM_COOKIE": "connect.sid=s%3A..."
55
+ }
56
+ }
57
+ }
58
+ }
59
+ ```
60
+
61
+ </details>
62
+
63
+ <details>
64
+ <summary><b>Claude Code</b></summary>
65
+
66
+ ```sh
67
+ claude mcp add loom -- uvx --from mcp-loom loom-mcp
68
+ ```
69
+
70
+ Then set the env var in your shell or `.env`:
71
+
72
+ ```sh
73
+ export LOOM_COOKIE="connect.sid=s%3A..."
74
+ ```
75
+
76
+ </details>
77
+
78
+ <details>
79
+ <summary><b>Cursor</b></summary>
80
+
81
+ Add to your Cursor MCP settings (`.cursor/mcp.json`):
82
+
83
+ ```json
84
+ {
85
+ "mcpServers": {
86
+ "loom": {
87
+ "type": "stdio",
88
+ "command": "uvx",
89
+ "args": ["--from", "git+https://github.com/karbassi/loom-mcp.git", "loom-mcp"],
90
+ "env": {
91
+ "LOOM_COOKIE": "connect.sid=s%3A..."
92
+ }
93
+ }
94
+ }
95
+ }
96
+ ```
97
+
98
+ </details>
99
+
100
+ <details>
101
+ <summary><b>VS Code</b></summary>
102
+
103
+ Add to your VS Code settings (`.vscode/mcp.json`):
104
+
105
+ ```json
106
+ {
107
+ "mcpServers": {
108
+ "loom": {
109
+ "type": "stdio",
110
+ "command": "uvx",
111
+ "args": ["--from", "git+https://github.com/karbassi/loom-mcp.git", "loom-mcp"],
112
+ "env": {
113
+ "LOOM_COOKIE": "connect.sid=s%3A..."
114
+ }
115
+ }
116
+ }
117
+ }
118
+ ```
119
+
120
+ </details>
121
+
122
+ <details>
123
+ <summary><b>Local clone</b></summary>
124
+
125
+ ```sh
126
+ git clone git@github.com:karbassi/loom-mcp.git
127
+ cd loom-mcp
128
+ uv sync
129
+ uv run loom-mcp
130
+ ```
131
+
132
+ Or reference it from any MCP client:
133
+
134
+ ```json
135
+ {
136
+ "mcpServers": {
137
+ "loom": {
138
+ "type": "stdio",
139
+ "command": "uv",
140
+ "args": ["run", "--directory", "/path/to/loom-mcp", "loom-mcp"],
141
+ "env": {
142
+ "LOOM_COOKIE": "connect.sid=s%3A..."
143
+ }
144
+ }
145
+ }
146
+ }
147
+ ```
148
+
149
+ </details>
150
+
151
+ ## Tools
152
+
153
+ ### Read (30 tools)
154
+
155
+ Tools marked with **Save** accept an optional `save_dir` parameter — see [Saving to disk](#saving-to-disk).
156
+
157
+ | Tool | Description | Save |
158
+ |---|---|:---:|
159
+ | `list_videos` | List your videos, sorted by most recent | |
160
+ | `search_videos` | AI-powered semantic search | |
161
+ | `get_video` | Video metadata (name, duration, owner, views) | `metadata.json` |
162
+ | `get_transcript` | Full transcript with timestamps and speakers | `transcript.txt` |
163
+ | `get_captions` | WebVTT captions with start+end timestamps per cue | `captions.vtt` |
164
+ | `get_summary` | AI-generated summary | `summary.txt` |
165
+ | `get_chapters` | AI-generated chapters | `chapters.txt` |
166
+ | `get_description` | AI-generated detailed description with timestamped sections | `description.txt` |
167
+ | `get_key_takeaways` | AI-generated key takeaways | `takeaways.txt` |
168
+ | `get_comments` | Comments and replies | `comments.txt` |
169
+ | `get_tasks` | AI-generated action items | `tasks.txt` |
170
+ | `get_reactions` | Emoji reactions | `reactions.txt` |
171
+ | `get_tags` | Video tags | `tags.txt` |
172
+ | `get_backlinks` | External references (where the video is shared/embedded) | `backlinks.txt` |
173
+ | `get_meeting_notes` | Confluence meeting notes URL | |
174
+ | `get_confluence_pages` | Linked Confluence pages | |
175
+ | `get_download_url` | Signed MP4 download URL | |
176
+ | `get_video_details` | All-in-one: metadata + transcript + chapters + summary + comments + tasks | `details.md` + all above |
177
+ | `list_folders` | List your folders | |
178
+ | `list_spaces` | List your workspaces | |
179
+ | `get_space` | Space details (name, privacy, primary) | |
180
+ | `search_folders` | Search folders by name | |
181
+ | `get_folder` | Folder details | |
182
+ | `get_last_watch_time` | Last timestamp where you stopped watching | |
183
+ | `get_watch_later_count` | Number of videos in your Watch Later list | |
184
+ | `get_total_videos_count` | Total videos created by a user | |
185
+ | `get_frequent_reactions` | Your most-used emoji reaction types | |
186
+ | `get_comment_reactions` | Emoji reactions on a specific comment | |
187
+ | `get_user` | User profile by ID (name, email, company, avatar) | |
188
+ | `search_workspace_tags` | Search tags in your workspace | |
189
+
190
+ ### Write (29 tools)
191
+
192
+ > [!NOTE]
193
+ > Write tools modify your Loom data. Destructive actions (delete, archive, move) cannot always be undone.
194
+
195
+ | Tool | Description |
196
+ |---|---|
197
+ | `update_video_name` | Rename a video |
198
+ | `update_video_description` | Update video description |
199
+ | `update_video_settings` | Update video settings (downloads, comments, etc.) |
200
+ | `create_comment` | Post a comment (with optional timestamp) |
201
+ | `edit_comment` | Edit an existing comment |
202
+ | `delete_comment` | Delete a comment |
203
+ | `create_task` | Create an action item on a video |
204
+ | `update_task` | Update the content of an action item |
205
+ | `delete_task` | Delete an action item |
206
+ | `approve_task` | Mark a task as approved |
207
+ | `respond_to_task` | Respond to a task |
208
+ | `add_reaction` | Add an emoji reaction at a timestamp |
209
+ | `delete_reaction` | Delete a reaction |
210
+ | `add_comment_reaction` | React to a comment with an emoji |
211
+ | `toggle_following` | Follow/unfollow a video |
212
+ | `toggle_following_tag` | Follow/unfollow a workspace tag |
213
+ | `archive_videos` | Archive or unarchive videos |
214
+ | `duplicate_video` | Duplicate a video |
215
+ | `delete_video` | Permanently delete a video |
216
+ | `recover_video` | Recover a deleted video from trash |
217
+ | `pin_video` | Pin or unpin a video in your library |
218
+ | `add_to_watch_later` | Add to Watch Later list |
219
+ | `remove_from_watch_later` | Remove from Watch Later list |
220
+ | `create_folder` | Create a new folder |
221
+ | `rename_folder` | Rename a folder |
222
+ | `delete_folders` | Delete folders |
223
+ | `move_videos` | Move videos to a different folder |
224
+ | `move_folders` | Move folders into a different parent folder |
225
+ | `share_videos_to_spaces` | Share videos to one or more spaces |
226
+
227
+ ## Saving to disk
228
+
229
+ 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.
230
+
231
+ Just ask naturally — *"get the details from my last meeting and save them to the `ask` directory"* — and the LLM will pass `save_dir="ask"` to the tool.
232
+
233
+ `get_video_details` saves each piece individually (`metadata.json`, `transcript.txt`, `summary.txt`, etc.) plus a combined `details.md`.
234
+
235
+ ## Troubleshooting
236
+
237
+ ### Auth errors
238
+
239
+ 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).
240
+
241
+ ### Debugging with MCP Inspector
242
+
243
+ ```sh
244
+ npx @modelcontextprotocol/inspector uvx --from mcp-loom loom-mcp
245
+ ```
246
+
247
+ ## Contributing
248
+
249
+ Issues and pull requests are welcome on [GitHub](https://github.com/karbassi/loom-mcp).
250
+
251
+ ## License
252
+
253
+ [MIT](LICENSE)
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "Loom",
3
+ "description": "Access Loom videos, transcripts, summaries, and comments via Loom's internal GraphQL API.",
4
+ "mcpServers": {
5
+ "loom": {
6
+ "type": "stdio",
7
+ "command": "uvx",
8
+ "args": ["--from", "git+https://github.com/karbassi/loom-mcp.git", "loom-mcp"]
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,23 @@
1
+ [project]
2
+ name = "mcp-loom"
3
+ version = "1.2.0"
4
+ description = "MCP server for Loom's internal GraphQL API"
5
+ license = "MIT"
6
+ requires-python = ">=3.11"
7
+ dependencies = [
8
+ "fastmcp>=3.0.0b2",
9
+ "httpx>=0.28.1",
10
+ ]
11
+
12
+ [project.scripts]
13
+ loom-mcp = "loom_mcp.server:main"
14
+
15
+ [tool.hatch.build.targets.wheel]
16
+ packages = ["src/loom_mcp"]
17
+
18
+ [build-system]
19
+ requires = ["hatchling"]
20
+ build-backend = "hatchling.build"
21
+
22
+ [tool.ruff.lint]
23
+ select = ["T20"]
File without changes