teams-transcripts 0.5.0__tar.gz → 0.5.2__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.
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/.gitignore +1 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/.gitlab-ci.yml +20 -8
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/PKG-INFO +109 -10
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/README.md +108 -9
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/pyproject.toml +8 -1
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/scripts/verify_distribution.py +4 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/infrastructure/config.py +12 -4
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/mcp_server.py +18 -1
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_component_flags.py +9 -2
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_infrastructure_config.py +17 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_mcp_server.py +18 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/uv.lock +1 -1
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/bin/teams-transcripts +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/bin/teams-transcripts-mcp +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/lefthook.yml +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/legacy/__init__.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/legacy/transcript_download.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/pyrightconfig.json +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/__init__.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/__main__.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/adapters/__init__.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/adapters/cdp/__init__.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/adapters/cdp/browser.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/adapters/cdp/connection.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/adapters/cdp/helpers.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/adapters/cdp/teams.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/adapters/vertex.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/domain/__init__.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/domain/chat.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/domain/html.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/domain/mcps.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/domain/models.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/domain/participants.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/domain/scoring.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/domain/speakers.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/domain/summary.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/domain/timestamps.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/domain/vtt.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/infrastructure/__init__.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/infrastructure/errors.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/infrastructure/logging.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/infrastructure/signals.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/ports/__init__.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/ports/agent.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/ports/browser.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/services/__init__.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/services/downloader.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/services/extraction.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/services/listing.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/services/navigation.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/services/output.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/services/tenant.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/conftest.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/fakes/__init__.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/fakes/agent.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/fakes/browser.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_adapters_cdp_browser.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_adapters_cdp_connection.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_adapters_cdp_helpers.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_adapters_cdp_teams.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_adapters_vertex.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_append_chat.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_build_result_summary.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_cli_validation.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_domain_mcps.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_domain_models.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_domain_participants.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_env_vars.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_error_json.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_force_restart_cli.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_infrastructure_errors.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_infrastructure_logging.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_infrastructure_signals.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_log_and_exit.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_merge_vtt_parts.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_occurrence_matching.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_parse_meeting_times.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_parse_vtt_cues.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_prepend_vtt_metadata.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_regression_iframe_timing.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_resolve_chat_author.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_resolve_config.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_safe_teams_restart.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_search_scoring.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_services_downloader.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_services_extraction.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_services_listing.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_services_navigation.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_services_tenant.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_signal_handling.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_speaker_mapping.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_strip_html.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_tenant_config.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_timestamp_fill_and_discontinuity.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_timestamp_utils.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_verbose_dead_code.py +0 -0
- {teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_verify_output.py +0 -0
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
stages:
|
|
2
2
|
- verify
|
|
3
3
|
- package
|
|
4
|
+
- publish
|
|
5
|
+
|
|
6
|
+
variables:
|
|
7
|
+
UV_CACHE_DIR: "/tmp/uv-cache"
|
|
4
8
|
|
|
5
9
|
default:
|
|
6
10
|
image: ghcr.io/astral-sh/uv:python3.13-bookworm-slim
|
|
7
|
-
|
|
8
|
-
UV_CACHE_DIR: "$CI_PROJECT_DIR/.uv-cache"
|
|
9
|
-
cache:
|
|
10
|
-
key:
|
|
11
|
-
files:
|
|
12
|
-
- uv.lock
|
|
13
|
-
paths:
|
|
14
|
-
- .uv-cache/
|
|
11
|
+
|
|
15
12
|
verify:
|
|
16
13
|
stage: verify
|
|
17
14
|
parallel:
|
|
@@ -43,3 +40,18 @@ package:
|
|
|
43
40
|
artifacts:
|
|
44
41
|
paths:
|
|
45
42
|
- dist/
|
|
43
|
+
|
|
44
|
+
publish:
|
|
45
|
+
stage: publish
|
|
46
|
+
dependencies:
|
|
47
|
+
- package
|
|
48
|
+
id_tokens:
|
|
49
|
+
PYPI_ID_TOKEN:
|
|
50
|
+
aud: pypi
|
|
51
|
+
environment:
|
|
52
|
+
name: pypi
|
|
53
|
+
resource_group: pypi
|
|
54
|
+
rules:
|
|
55
|
+
- if: '$CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+([a-zA-Z0-9.-]+)?$/'
|
|
56
|
+
script:
|
|
57
|
+
- uv publish --trusted-publishing always dist/*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: teams-transcripts
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2
|
|
4
4
|
Summary: Download Microsoft Teams transcripts, chat, and meeting metadata from the macOS desktop app
|
|
5
5
|
Project-URL: Homepage, https://gitlab.com/jamiemills-choreograph/teams-transcripts
|
|
6
6
|
Project-URL: Repository, https://gitlab.com/jamiemills-choreograph/teams-transcripts
|
|
@@ -49,13 +49,20 @@ uvx teams-transcripts \
|
|
|
49
49
|
--gcp-project your-project
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
The MCP server has a different executable name from the PyPI package, so use
|
|
53
|
+
`--from` to tell `uvx` which package provides it:
|
|
53
54
|
|
|
54
55
|
```sh
|
|
55
|
-
uvx --from teams-transcripts teams-transcripts-mcp
|
|
56
|
+
uvx --from 'teams-transcripts==0.5.2' teams-transcripts-mcp
|
|
56
57
|
uvx --from teams-transcripts transcript-download --help
|
|
57
58
|
```
|
|
58
59
|
|
|
60
|
+
Use `teams-transcripts==0.5.2` for a reproducible MCP configuration, or replace
|
|
61
|
+
it with `teams-transcripts` to follow the latest release. Do not run
|
|
62
|
+
`uvx teams-transcripts-mcp`: that asks PyPI for a package named
|
|
63
|
+
`teams-transcripts-mcp`, rather than running the executable supplied by this
|
|
64
|
+
package.
|
|
65
|
+
|
|
59
66
|
`uvx` creates an isolated temporary environment. The Teams desktop app, CDP,
|
|
60
67
|
and authentication prerequisites below still apply.
|
|
61
68
|
|
|
@@ -171,8 +178,12 @@ file.
|
|
|
171
178
|
|
|
172
179
|
### 3. Python dependencies
|
|
173
180
|
|
|
174
|
-
The
|
|
175
|
-
[uv](https://docs.astral.sh/uv/).
|
|
181
|
+
The package requires Python 3.10+ and is managed with
|
|
182
|
+
[uv](https://docs.astral.sh/uv/). When using `uvx`, dependencies are installed
|
|
183
|
+
automatically in an isolated environment; no project checkout or `uv sync` is
|
|
184
|
+
needed.
|
|
185
|
+
|
|
186
|
+
For development from a source checkout, install the locked dependencies:
|
|
176
187
|
|
|
177
188
|
```sh
|
|
178
189
|
uv sync --locked
|
|
@@ -631,6 +642,22 @@ tools directly.
|
|
|
631
642
|
|
|
632
643
|
### Running the MCP server
|
|
633
644
|
|
|
645
|
+
From PyPI, with no repository checkout or persistent installation:
|
|
646
|
+
|
|
647
|
+
```sh
|
|
648
|
+
# Pinned release (recommended for MCP client configuration)
|
|
649
|
+
uvx --from 'teams-transcripts==0.5.2' teams-transcripts-mcp
|
|
650
|
+
|
|
651
|
+
# Latest release
|
|
652
|
+
uvx --from teams-transcripts teams-transcripts-mcp
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
STDIO is the default transport. When run manually, the command waits quietly
|
|
656
|
+
for MCP messages on standard input; normally an MCP client starts and manages
|
|
657
|
+
this process for you.
|
|
658
|
+
|
|
659
|
+
If the package is already installed, run the entry point directly:
|
|
660
|
+
|
|
634
661
|
```sh
|
|
635
662
|
# STDIO transport (default, for subprocess spawning by MCP clients)
|
|
636
663
|
teams-transcripts-mcp
|
|
@@ -658,8 +685,10 @@ MCP transport for local subprocess spawning). When `--port` is specified,
|
|
|
658
685
|
the server runs as an SSE HTTP server on that port, accessible over the
|
|
659
686
|
network.
|
|
660
687
|
|
|
661
|
-
The server inherits environment variables for GCP configuration
|
|
662
|
-
|
|
688
|
+
The server inherits environment variables for GCP configuration, including
|
|
689
|
+
`GOOGLE_CLOUD_PROJECT` and the optional `CLOUD_ML_REGION`, `MODEL_ID`,
|
|
690
|
+
`GOOGLE_ADC`, and `TEAMS_TENANT` settings (see
|
|
691
|
+
[Environment variables](#environment-variables)).
|
|
663
692
|
|
|
664
693
|
### Available tools
|
|
665
694
|
|
|
@@ -712,9 +741,43 @@ Or for a known meeting:
|
|
|
712
741
|
1. download(meeting="Weekly Standup", date="2026-04-29")
|
|
713
742
|
```
|
|
714
743
|
|
|
715
|
-
###
|
|
744
|
+
### MCP client configuration with uvx
|
|
716
745
|
|
|
717
|
-
|
|
746
|
+
Use `uvx` as the MCP command and pass the package plus its MCP executable as
|
|
747
|
+
arguments. This works with clients that accept the standard JSON MCP server
|
|
748
|
+
configuration format, including Claude Desktop:
|
|
749
|
+
|
|
750
|
+
```json
|
|
751
|
+
{
|
|
752
|
+
"mcpServers": {
|
|
753
|
+
"teams-transcripts": {
|
|
754
|
+
"command": "uvx",
|
|
755
|
+
"args": [
|
|
756
|
+
"--from",
|
|
757
|
+
"teams-transcripts==0.5.2",
|
|
758
|
+
"teams-transcripts-mcp"
|
|
759
|
+
],
|
|
760
|
+
"env": {
|
|
761
|
+
"GOOGLE_CLOUD_PROJECT": "your-gcp-project"
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
For Claude Desktop, add that entry to `claude_desktop_config.json` and restart
|
|
769
|
+
Claude Desktop. Other MCP clients use the same command and arguments in their
|
|
770
|
+
local-server configuration. Add any optional environment variables your setup
|
|
771
|
+
needs to the `env` object. The MCP process must run as the same macOS user as
|
|
772
|
+
Teams so it can connect to the desktop app and use that user's credentials.
|
|
773
|
+
|
|
774
|
+
To track the newest PyPI release instead of pinning one, change the package
|
|
775
|
+
argument from `teams-transcripts==0.5.2` to `teams-transcripts`.
|
|
776
|
+
|
|
777
|
+
### Configuration from an installed checkout
|
|
778
|
+
|
|
779
|
+
If the project is already installed in a virtual environment, point the MCP
|
|
780
|
+
client at its entry point:
|
|
718
781
|
|
|
719
782
|
```json
|
|
720
783
|
{
|
|
@@ -729,7 +792,8 @@ Add to your `claude_desktop_config.json`:
|
|
|
729
792
|
}
|
|
730
793
|
```
|
|
731
794
|
|
|
732
|
-
|
|
795
|
+
For a source checkout managed by `uv`, configure the client to run in the
|
|
796
|
+
repository directory:
|
|
733
797
|
|
|
734
798
|
```json
|
|
735
799
|
{
|
|
@@ -1370,6 +1434,41 @@ uv add <package>
|
|
|
1370
1434
|
|
|
1371
1435
|
This updates both `pyproject.toml` and `uv.lock`.
|
|
1372
1436
|
|
|
1437
|
+
### Publishing a release
|
|
1438
|
+
|
|
1439
|
+
Version tags publish the verified wheel and source distribution to PyPI through
|
|
1440
|
+
GitLab CI/CD. The `publish` job runs only for tags such as `v0.6.0`, only after
|
|
1441
|
+
both verification jobs and the package job succeed, and uses PyPI Trusted
|
|
1442
|
+
Publishing with a short-lived GitLab OIDC token. No PyPI password or API token
|
|
1443
|
+
is stored in GitLab.
|
|
1444
|
+
|
|
1445
|
+
Before the first automated release, add a GitLab CI/CD trusted publisher under
|
|
1446
|
+
the PyPI project's **Manage > Publishing** settings with these values:
|
|
1447
|
+
|
|
1448
|
+
| Setting | Value |
|
|
1449
|
+
|---------|-------|
|
|
1450
|
+
| Namespace | `jamiemills-choreograph` |
|
|
1451
|
+
| Project | `teams-transcripts` |
|
|
1452
|
+
| Top-level pipeline file | `.gitlab-ci.yml` |
|
|
1453
|
+
| Environment | `pypi` |
|
|
1454
|
+
|
|
1455
|
+
To release a new version:
|
|
1456
|
+
|
|
1457
|
+
```sh
|
|
1458
|
+
uv version 0.6.0 --no-sync
|
|
1459
|
+
git add pyproject.toml uv.lock
|
|
1460
|
+
git commit -m "Prepare 0.6.0 release"
|
|
1461
|
+
git push origin main
|
|
1462
|
+
|
|
1463
|
+
# After the main pipeline succeeds:
|
|
1464
|
+
git tag -a v0.6.0 -m "teams-transcripts 0.6.0"
|
|
1465
|
+
git push origin v0.6.0
|
|
1466
|
+
```
|
|
1467
|
+
|
|
1468
|
+
The package job verifies that the tag exactly matches the built package version
|
|
1469
|
+
before publishing. PyPI releases are immutable, so never move or reuse a
|
|
1470
|
+
version tag; increment the project version and create a new tag instead.
|
|
1471
|
+
|
|
1373
1472
|
### Running tests
|
|
1374
1473
|
|
|
1375
1474
|
```sh
|
|
@@ -20,13 +20,20 @@ uvx teams-transcripts \
|
|
|
20
20
|
--gcp-project your-project
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
The MCP server has a different executable name from the PyPI package, so use
|
|
24
|
+
`--from` to tell `uvx` which package provides it:
|
|
24
25
|
|
|
25
26
|
```sh
|
|
26
|
-
uvx --from teams-transcripts teams-transcripts-mcp
|
|
27
|
+
uvx --from 'teams-transcripts==0.5.2' teams-transcripts-mcp
|
|
27
28
|
uvx --from teams-transcripts transcript-download --help
|
|
28
29
|
```
|
|
29
30
|
|
|
31
|
+
Use `teams-transcripts==0.5.2` for a reproducible MCP configuration, or replace
|
|
32
|
+
it with `teams-transcripts` to follow the latest release. Do not run
|
|
33
|
+
`uvx teams-transcripts-mcp`: that asks PyPI for a package named
|
|
34
|
+
`teams-transcripts-mcp`, rather than running the executable supplied by this
|
|
35
|
+
package.
|
|
36
|
+
|
|
30
37
|
`uvx` creates an isolated temporary environment. The Teams desktop app, CDP,
|
|
31
38
|
and authentication prerequisites below still apply.
|
|
32
39
|
|
|
@@ -142,8 +149,12 @@ file.
|
|
|
142
149
|
|
|
143
150
|
### 3. Python dependencies
|
|
144
151
|
|
|
145
|
-
The
|
|
146
|
-
[uv](https://docs.astral.sh/uv/).
|
|
152
|
+
The package requires Python 3.10+ and is managed with
|
|
153
|
+
[uv](https://docs.astral.sh/uv/). When using `uvx`, dependencies are installed
|
|
154
|
+
automatically in an isolated environment; no project checkout or `uv sync` is
|
|
155
|
+
needed.
|
|
156
|
+
|
|
157
|
+
For development from a source checkout, install the locked dependencies:
|
|
147
158
|
|
|
148
159
|
```sh
|
|
149
160
|
uv sync --locked
|
|
@@ -602,6 +613,22 @@ tools directly.
|
|
|
602
613
|
|
|
603
614
|
### Running the MCP server
|
|
604
615
|
|
|
616
|
+
From PyPI, with no repository checkout or persistent installation:
|
|
617
|
+
|
|
618
|
+
```sh
|
|
619
|
+
# Pinned release (recommended for MCP client configuration)
|
|
620
|
+
uvx --from 'teams-transcripts==0.5.2' teams-transcripts-mcp
|
|
621
|
+
|
|
622
|
+
# Latest release
|
|
623
|
+
uvx --from teams-transcripts teams-transcripts-mcp
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
STDIO is the default transport. When run manually, the command waits quietly
|
|
627
|
+
for MCP messages on standard input; normally an MCP client starts and manages
|
|
628
|
+
this process for you.
|
|
629
|
+
|
|
630
|
+
If the package is already installed, run the entry point directly:
|
|
631
|
+
|
|
605
632
|
```sh
|
|
606
633
|
# STDIO transport (default, for subprocess spawning by MCP clients)
|
|
607
634
|
teams-transcripts-mcp
|
|
@@ -629,8 +656,10 @@ MCP transport for local subprocess spawning). When `--port` is specified,
|
|
|
629
656
|
the server runs as an SSE HTTP server on that port, accessible over the
|
|
630
657
|
network.
|
|
631
658
|
|
|
632
|
-
The server inherits environment variables for GCP configuration
|
|
633
|
-
|
|
659
|
+
The server inherits environment variables for GCP configuration, including
|
|
660
|
+
`GOOGLE_CLOUD_PROJECT` and the optional `CLOUD_ML_REGION`, `MODEL_ID`,
|
|
661
|
+
`GOOGLE_ADC`, and `TEAMS_TENANT` settings (see
|
|
662
|
+
[Environment variables](#environment-variables)).
|
|
634
663
|
|
|
635
664
|
### Available tools
|
|
636
665
|
|
|
@@ -683,9 +712,43 @@ Or for a known meeting:
|
|
|
683
712
|
1. download(meeting="Weekly Standup", date="2026-04-29")
|
|
684
713
|
```
|
|
685
714
|
|
|
686
|
-
###
|
|
715
|
+
### MCP client configuration with uvx
|
|
687
716
|
|
|
688
|
-
|
|
717
|
+
Use `uvx` as the MCP command and pass the package plus its MCP executable as
|
|
718
|
+
arguments. This works with clients that accept the standard JSON MCP server
|
|
719
|
+
configuration format, including Claude Desktop:
|
|
720
|
+
|
|
721
|
+
```json
|
|
722
|
+
{
|
|
723
|
+
"mcpServers": {
|
|
724
|
+
"teams-transcripts": {
|
|
725
|
+
"command": "uvx",
|
|
726
|
+
"args": [
|
|
727
|
+
"--from",
|
|
728
|
+
"teams-transcripts==0.5.2",
|
|
729
|
+
"teams-transcripts-mcp"
|
|
730
|
+
],
|
|
731
|
+
"env": {
|
|
732
|
+
"GOOGLE_CLOUD_PROJECT": "your-gcp-project"
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
For Claude Desktop, add that entry to `claude_desktop_config.json` and restart
|
|
740
|
+
Claude Desktop. Other MCP clients use the same command and arguments in their
|
|
741
|
+
local-server configuration. Add any optional environment variables your setup
|
|
742
|
+
needs to the `env` object. The MCP process must run as the same macOS user as
|
|
743
|
+
Teams so it can connect to the desktop app and use that user's credentials.
|
|
744
|
+
|
|
745
|
+
To track the newest PyPI release instead of pinning one, change the package
|
|
746
|
+
argument from `teams-transcripts==0.5.2` to `teams-transcripts`.
|
|
747
|
+
|
|
748
|
+
### Configuration from an installed checkout
|
|
749
|
+
|
|
750
|
+
If the project is already installed in a virtual environment, point the MCP
|
|
751
|
+
client at its entry point:
|
|
689
752
|
|
|
690
753
|
```json
|
|
691
754
|
{
|
|
@@ -700,7 +763,8 @@ Add to your `claude_desktop_config.json`:
|
|
|
700
763
|
}
|
|
701
764
|
```
|
|
702
765
|
|
|
703
|
-
|
|
766
|
+
For a source checkout managed by `uv`, configure the client to run in the
|
|
767
|
+
repository directory:
|
|
704
768
|
|
|
705
769
|
```json
|
|
706
770
|
{
|
|
@@ -1341,6 +1405,41 @@ uv add <package>
|
|
|
1341
1405
|
|
|
1342
1406
|
This updates both `pyproject.toml` and `uv.lock`.
|
|
1343
1407
|
|
|
1408
|
+
### Publishing a release
|
|
1409
|
+
|
|
1410
|
+
Version tags publish the verified wheel and source distribution to PyPI through
|
|
1411
|
+
GitLab CI/CD. The `publish` job runs only for tags such as `v0.6.0`, only after
|
|
1412
|
+
both verification jobs and the package job succeed, and uses PyPI Trusted
|
|
1413
|
+
Publishing with a short-lived GitLab OIDC token. No PyPI password or API token
|
|
1414
|
+
is stored in GitLab.
|
|
1415
|
+
|
|
1416
|
+
Before the first automated release, add a GitLab CI/CD trusted publisher under
|
|
1417
|
+
the PyPI project's **Manage > Publishing** settings with these values:
|
|
1418
|
+
|
|
1419
|
+
| Setting | Value |
|
|
1420
|
+
|---------|-------|
|
|
1421
|
+
| Namespace | `jamiemills-choreograph` |
|
|
1422
|
+
| Project | `teams-transcripts` |
|
|
1423
|
+
| Top-level pipeline file | `.gitlab-ci.yml` |
|
|
1424
|
+
| Environment | `pypi` |
|
|
1425
|
+
|
|
1426
|
+
To release a new version:
|
|
1427
|
+
|
|
1428
|
+
```sh
|
|
1429
|
+
uv version 0.6.0 --no-sync
|
|
1430
|
+
git add pyproject.toml uv.lock
|
|
1431
|
+
git commit -m "Prepare 0.6.0 release"
|
|
1432
|
+
git push origin main
|
|
1433
|
+
|
|
1434
|
+
# After the main pipeline succeeds:
|
|
1435
|
+
git tag -a v0.6.0 -m "teams-transcripts 0.6.0"
|
|
1436
|
+
git push origin v0.6.0
|
|
1437
|
+
```
|
|
1438
|
+
|
|
1439
|
+
The package job verifies that the tag exactly matches the built package version
|
|
1440
|
+
before publishing. PyPI releases are immutable, so never move or reuse a
|
|
1441
|
+
version tag; increment the project version and create a new tag instead.
|
|
1442
|
+
|
|
1344
1443
|
### Running tests
|
|
1345
1444
|
|
|
1346
1445
|
```sh
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "teams-transcripts"
|
|
3
|
-
version = "0.5.
|
|
3
|
+
version = "0.5.2"
|
|
4
4
|
description = "Download Microsoft Teams transcripts, chat, and meeting metadata from the macOS desktop app"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -34,6 +34,13 @@ Issues = "https://gitlab.com/jamiemills-choreograph/teams-transcripts/-/issues"
|
|
|
34
34
|
requires = ["hatchling"]
|
|
35
35
|
build-backend = "hatchling.build"
|
|
36
36
|
|
|
37
|
+
[tool.hatch.build]
|
|
38
|
+
exclude = [
|
|
39
|
+
"/.uv-cache",
|
|
40
|
+
"/.venv",
|
|
41
|
+
"/dist",
|
|
42
|
+
]
|
|
43
|
+
|
|
37
44
|
[tool.hatch.build.targets.wheel]
|
|
38
45
|
packages = ["legacy", "teams_transcripts"]
|
|
39
46
|
|
|
@@ -10,6 +10,7 @@ import zipfile
|
|
|
10
10
|
from email.parser import Parser
|
|
11
11
|
|
|
12
12
|
EXPECTED_PACKAGES = ("legacy/", "teams_transcripts/")
|
|
13
|
+
EXCLUDED_SDIST_PATHS = ("/.uv-cache/", "/.venv/", "/dist/")
|
|
13
14
|
EXPECTED_SCRIPTS = {
|
|
14
15
|
"teams-transcripts = teams_transcripts.__main__:main",
|
|
15
16
|
"teams-transcripts-mcp = teams_transcripts.mcp_server:main",
|
|
@@ -51,6 +52,9 @@ def _verify_sdist(path: pathlib.Path) -> None:
|
|
|
51
52
|
for package in EXPECTED_PACKAGES:
|
|
52
53
|
if not any(f"/{package}" in name for name in names):
|
|
53
54
|
raise SystemExit(f"{path.name} does not contain {package}")
|
|
55
|
+
for excluded in EXCLUDED_SDIST_PATHS:
|
|
56
|
+
if any(excluded in f"/{name}/" for name in names):
|
|
57
|
+
raise SystemExit(f"{path.name} unexpectedly contains {excluded.strip('/')}")
|
|
54
58
|
|
|
55
59
|
|
|
56
60
|
def main() -> None:
|
{teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/infrastructure/config.py
RENAMED
|
@@ -94,9 +94,10 @@ def parse_args() -> argparse.Namespace:
|
|
|
94
94
|
" gcloud auth application-default login\n"
|
|
95
95
|
" Or point --adc / GOOGLE_ADC at a service account key file.\n"
|
|
96
96
|
"\n"
|
|
97
|
-
" 3. Python
|
|
98
|
-
"
|
|
99
|
-
"
|
|
97
|
+
" 3. Python 3.10+ and uv are required. uvx installs dependencies in an\n"
|
|
98
|
+
" isolated environment automatically; no checkout or uv sync is needed.\n"
|
|
99
|
+
" For a source checkout, run 'uv sync --locked', then use\n"
|
|
100
|
+
" 'uv run teams-transcripts'.\n"
|
|
100
101
|
"\n"
|
|
101
102
|
"environment variables (used as fallbacks when CLI flags are omitted):\n"
|
|
102
103
|
" GOOGLE_CLOUD_PROJECT GCP project ID for Vertex AI (required if\n"
|
|
@@ -425,7 +426,13 @@ def parse_args() -> argparse.Namespace:
|
|
|
425
426
|
"\n"
|
|
426
427
|
"mcp server:\n"
|
|
427
428
|
" An MCP (Model Context Protocol) server is available for tool-use by\n"
|
|
428
|
-
" AI agents.
|
|
429
|
+
" AI agents. Run from PyPI without installing:\n"
|
|
430
|
+
" uvx --from teams-transcripts teams-transcripts-mcp\n"
|
|
431
|
+
" For a reproducible client config, pin the package version:\n"
|
|
432
|
+
" uvx --from 'teams-transcripts==VERSION' teams-transcripts-mcp\n"
|
|
433
|
+
" MCP client command: uvx\n"
|
|
434
|
+
" MCP client args: --from teams-transcripts teams-transcripts-mcp\n"
|
|
435
|
+
" If installed locally, start with: teams-transcripts-mcp\n"
|
|
429
436
|
" Or on a network port: teams-transcripts-mcp --port 8080\n"
|
|
430
437
|
" Recommended tool: download(meeting_id=UUID) -- the unified entry point.\n"
|
|
431
438
|
" Also available: restart_teams, list_tenants, list_transcripts,\n"
|
|
@@ -434,6 +441,7 @@ def parse_args() -> argparse.Namespace:
|
|
|
434
441
|
" AI agents should prefer MCP tools over direct CLI invocation.\n"
|
|
435
442
|
" STDIO transport (default) for subprocess spawning by MCP clients.\n"
|
|
436
443
|
" SSE transport (--port) for network access.\n"
|
|
444
|
+
" The server inherits GOOGLE_CLOUD_PROJECT and other documented env vars.\n"
|
|
437
445
|
),
|
|
438
446
|
)
|
|
439
447
|
p.add_argument(
|
|
@@ -795,7 +795,24 @@ def main() -> None:
|
|
|
795
795
|
import argparse
|
|
796
796
|
|
|
797
797
|
p = argparse.ArgumentParser(
|
|
798
|
-
description=
|
|
798
|
+
description=(
|
|
799
|
+
"Teams Transcripts MCP server. Uses STDIO by default so an MCP "
|
|
800
|
+
"client can start and manage it as a local subprocess."
|
|
801
|
+
),
|
|
802
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
803
|
+
epilog="""Run directly from PyPI without installing:
|
|
804
|
+
uvx --from teams-transcripts teams-transcripts-mcp
|
|
805
|
+
|
|
806
|
+
Pin a release for a reproducible MCP client configuration:
|
|
807
|
+
uvx --from 'teams-transcripts==VERSION' teams-transcripts-mcp
|
|
808
|
+
|
|
809
|
+
MCP client configuration:
|
|
810
|
+
command: uvx
|
|
811
|
+
args: --from teams-transcripts teams-transcripts-mcp
|
|
812
|
+
|
|
813
|
+
The server inherits GOOGLE_CLOUD_PROJECT and the other environment variables
|
|
814
|
+
documented in the project README. Do not use `uvx teams-transcripts-mcp`;
|
|
815
|
+
the PyPI package name is `teams-transcripts`.""",
|
|
799
816
|
)
|
|
800
817
|
p.add_argument(
|
|
801
818
|
"--port",
|
|
@@ -9,6 +9,7 @@ Covers:
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
11
|
import argparse
|
|
12
|
+
from datetime import datetime, timedelta
|
|
12
13
|
|
|
13
14
|
import pytest
|
|
14
15
|
|
|
@@ -20,6 +21,7 @@ from teams_transcripts.domain.models import (
|
|
|
20
21
|
TranscriptCue,
|
|
21
22
|
)
|
|
22
23
|
from teams_transcripts.domain.summary import build_result_summary
|
|
24
|
+
from teams_transcripts.domain.timestamps import parse_meeting_times
|
|
23
25
|
from teams_transcripts.infrastructure.config import resolve_config
|
|
24
26
|
|
|
25
27
|
# ---------------------------------------------------------------------------
|
|
@@ -331,11 +333,16 @@ class TestComposableJsonOutput:
|
|
|
331
333
|
|
|
332
334
|
def test_chat_message_has_offset(self) -> None:
|
|
333
335
|
"""Chat messages in JSON include offset from meeting start."""
|
|
336
|
+
meeting_time_text = "22 April 2026 11:00 - 12:00"
|
|
337
|
+
meeting_start, _ = parse_meeting_times(meeting_time_text, "2026-04-22")
|
|
338
|
+
message_timestamp = (
|
|
339
|
+
datetime.fromisoformat(meeting_start) + timedelta(minutes=15)
|
|
340
|
+
).isoformat()
|
|
334
341
|
messages = [
|
|
335
|
-
ChatMessage(timestamp=
|
|
342
|
+
ChatMessage(timestamp=message_timestamp, author="Bob", text="Hi"),
|
|
336
343
|
]
|
|
337
344
|
result = build_result_summary(
|
|
338
|
-
_base_config(meeting_time_text=
|
|
345
|
+
_base_config(meeting_time_text=meeting_time_text),
|
|
339
346
|
chat_messages=messages,
|
|
340
347
|
chat_count=1,
|
|
341
348
|
include_transcript=False,
|
|
@@ -18,6 +18,23 @@ from pydantic import ValidationError
|
|
|
18
18
|
from teams_transcripts.domain.models import MeetingConfig
|
|
19
19
|
from teams_transcripts.infrastructure.config import parse_args, resolve_config
|
|
20
20
|
|
|
21
|
+
|
|
22
|
+
def test_main_help_documents_uvx_mcp_configuration(
|
|
23
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
24
|
+
capsys: pytest.CaptureFixture[str],
|
|
25
|
+
) -> None:
|
|
26
|
+
monkeypatch.setattr("sys.argv", ["teams-transcripts", "--help"])
|
|
27
|
+
|
|
28
|
+
with pytest.raises(SystemExit) as exc_info:
|
|
29
|
+
parse_args()
|
|
30
|
+
|
|
31
|
+
assert exc_info.value.code == 0
|
|
32
|
+
help_text = capsys.readouterr().out
|
|
33
|
+
assert "uvx --from teams-transcripts teams-transcripts-mcp" in help_text
|
|
34
|
+
assert "MCP client command: uvx" in help_text
|
|
35
|
+
assert "teams-transcripts==VERSION" in help_text
|
|
36
|
+
|
|
37
|
+
|
|
21
38
|
# ---------------------------------------------------------------------------
|
|
22
39
|
# Helpers
|
|
23
40
|
# ---------------------------------------------------------------------------
|
|
@@ -25,6 +25,7 @@ from teams_transcripts.mcp_server import (
|
|
|
25
25
|
_run_cli,
|
|
26
26
|
_run_cli_list,
|
|
27
27
|
download,
|
|
28
|
+
main,
|
|
28
29
|
mcp,
|
|
29
30
|
)
|
|
30
31
|
|
|
@@ -33,6 +34,23 @@ from teams_transcripts.mcp_server import (
|
|
|
33
34
|
# ---------------------------------------------------------------------------
|
|
34
35
|
|
|
35
36
|
|
|
37
|
+
def test_mcp_help_documents_uvx_configuration(
|
|
38
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
39
|
+
capsys: pytest.CaptureFixture[str],
|
|
40
|
+
) -> None:
|
|
41
|
+
monkeypatch.setattr("sys.argv", ["teams-transcripts-mcp", "--help"])
|
|
42
|
+
|
|
43
|
+
with pytest.raises(SystemExit) as exc_info:
|
|
44
|
+
main()
|
|
45
|
+
|
|
46
|
+
assert exc_info.value.code == 0
|
|
47
|
+
help_text = capsys.readouterr().out
|
|
48
|
+
assert "uvx --from teams-transcripts teams-transcripts-mcp" in help_text
|
|
49
|
+
assert "teams-transcripts==VERSION" in help_text
|
|
50
|
+
assert "GOOGLE_CLOUD_PROJECT" in help_text
|
|
51
|
+
assert "STDIO" in help_text
|
|
52
|
+
|
|
53
|
+
|
|
36
54
|
class TestToolDiscovery:
|
|
37
55
|
"""Verify all MCP tools are registered with correct metadata."""
|
|
38
56
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/adapters/cdp/__init__.py
RENAMED
|
File without changes
|
{teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/adapters/cdp/browser.py
RENAMED
|
File without changes
|
{teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/adapters/cdp/connection.py
RENAMED
|
File without changes
|
{teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/adapters/cdp/helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/domain/participants.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/infrastructure/__init__.py
RENAMED
|
File without changes
|
{teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/infrastructure/errors.py
RENAMED
|
File without changes
|
{teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/infrastructure/logging.py
RENAMED
|
File without changes
|
{teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/infrastructure/signals.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/services/downloader.py
RENAMED
|
File without changes
|
{teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/services/extraction.py
RENAMED
|
File without changes
|
|
File without changes
|
{teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/teams_transcripts/services/navigation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{teams_transcripts-0.5.0 → teams_transcripts-0.5.2}/tests/test_timestamp_fill_and_discontinuity.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|