trueconf-server-mcp 0.1.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.
- trueconf_server_mcp-0.1.0/.env.example +39 -0
- trueconf_server_mcp-0.1.0/.github/workflows/release-test.yml +51 -0
- trueconf_server_mcp-0.1.0/.github/workflows/release.yml +44 -0
- trueconf_server_mcp-0.1.0/.gitignore +193 -0
- trueconf_server_mcp-0.1.0/.python-version +1 -0
- trueconf_server_mcp-0.1.0/AGENTS.md +309 -0
- trueconf_server_mcp-0.1.0/LICENSE +32 -0
- trueconf_server_mcp-0.1.0/PKG-INFO +476 -0
- trueconf_server_mcp-0.1.0/README-ru.md +442 -0
- trueconf_server_mcp-0.1.0/README.md +444 -0
- trueconf_server_mcp-0.1.0/app/__init__.py +0 -0
- trueconf_server_mcp-0.1.0/app/_version.py +24 -0
- trueconf_server_mcp-0.1.0/app/config.py +168 -0
- trueconf_server_mcp-0.1.0/app/mcp/__init__.py +311 -0
- trueconf_server_mcp-0.1.0/app/mcp/auth.py +276 -0
- trueconf_server_mcp-0.1.0/app/mcp/code_mode.py +73 -0
- trueconf_server_mcp-0.1.0/app/mcp/errors.py +29 -0
- trueconf_server_mcp-0.1.0/app/mcp/i18n.py +104 -0
- trueconf_server_mcp-0.1.0/app/mcp/instructions.py +192 -0
- trueconf_server_mcp-0.1.0/app/mcp/logging_utils.py +11 -0
- trueconf_server_mcp-0.1.0/app/mcp/pages.py +105 -0
- trueconf_server_mcp-0.1.0/app/mcp/prompts.py +27 -0
- trueconf_server_mcp-0.1.0/app/mcp/routes.py +488 -0
- trueconf_server_mcp-0.1.0/app/mcp/token_store.py +323 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/__init__.py +0 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/__init__.py +45 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/add_invitation.py +31 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/calculate_conferences.py +27 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/create_conference.py +130 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/delete_conference.py +13 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/export_chat_messages.py +46 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/get_chat_messages.py +13 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/get_conference.py +13 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/get_conference_calendars.py +13 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/get_conference_ics.py +24 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/get_conference_me.py +13 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/get_conference_owner.py +13 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/get_conference_participants.py +36 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/get_conference_translations.py +13 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/get_deeplinks.py +24 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/get_invitation.py +19 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/get_recording.py +19 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/get_shared_links.py +13 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/invite_participants.py +21 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/join_conference.py +13 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/list_conferences.py +58 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/list_invitations.py +13 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/list_recordings.py +42 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/notify_conference.py +25 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/pause_recording.py +13 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/register_for_conference.py +28 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/remove_invitation.py +19 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/run_conference.py +13 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/start_recording.py +13 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/stop_conference.py +13 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/stop_recording.py +13 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/update_conference.py +112 -0
- trueconf_server_mcp-0.1.0/app/mcp/tools/conferences/update_invitation.py +25 -0
- trueconf_server_mcp-0.1.0/app/tls.py +241 -0
- trueconf_server_mcp-0.1.0/app/trueconf_api/__init__.py +0 -0
- trueconf_server_mcp-0.1.0/app/trueconf_api/mode_utils.py +76 -0
- trueconf_server_mcp-0.1.0/app/trueconf_api/models.py +149 -0
- trueconf_server_mcp-0.1.0/app/web/assets/favicon.ico +0 -0
- trueconf_server_mcp-0.1.0/app/web/assets/logo.png +0 -0
- trueconf_server_mcp-0.1.0/app/web/locales/common.en.yml +10 -0
- trueconf_server_mcp-0.1.0/app/web/locales/common.ru.yml +10 -0
- trueconf_server_mcp-0.1.0/app/web/locales/error.en.yml +14 -0
- trueconf_server_mcp-0.1.0/app/web/locales/error.ru.yml +14 -0
- trueconf_server_mcp-0.1.0/app/web/locales/login.en.yml +12 -0
- trueconf_server_mcp-0.1.0/app/web/locales/login.ru.yml +12 -0
- trueconf_server_mcp-0.1.0/app/web/locales/success.en.yml +26 -0
- trueconf_server_mcp-0.1.0/app/web/locales/success.ru.yml +27 -0
- trueconf_server_mcp-0.1.0/app/web/static/app.css +196 -0
- trueconf_server_mcp-0.1.0/app/web/static/app.js +82 -0
- trueconf_server_mcp-0.1.0/app/web/templates/error.html +341 -0
- trueconf_server_mcp-0.1.0/app/web/templates/login.html +344 -0
- trueconf_server_mcp-0.1.0/app/web/templates/success.html +468 -0
- trueconf_server_mcp-0.1.0/assets/login_en.png +0 -0
- trueconf_server_mcp-0.1.0/assets/login_ru.png +0 -0
- trueconf_server_mcp-0.1.0/assets/poster-en.png +0 -0
- trueconf_server_mcp-0.1.0/assets/poster-ru.png +0 -0
- trueconf_server_mcp-0.1.0/assets/success_en.png +0 -0
- trueconf_server_mcp-0.1.0/assets/success_ru.png +0 -0
- trueconf_server_mcp-0.1.0/main.py +295 -0
- trueconf_server_mcp-0.1.0/pyproject.toml +89 -0
- trueconf_server_mcp-0.1.0/setup.cfg +4 -0
- trueconf_server_mcp-0.1.0/tests/__init__.py +6 -0
- trueconf_server_mcp-0.1.0/tests/conftest.py +162 -0
- trueconf_server_mcp-0.1.0/tests/test_auth.py +226 -0
- trueconf_server_mcp-0.1.0/tests/test_code_mode.py +52 -0
- trueconf_server_mcp-0.1.0/tests/test_config.py +189 -0
- trueconf_server_mcp-0.1.0/tests/test_conftest.py +39 -0
- trueconf_server_mcp-0.1.0/tests/test_instructions.py +34 -0
- trueconf_server_mcp-0.1.0/tests/test_logging.py +97 -0
- trueconf_server_mcp-0.1.0/tests/test_mode_utils.py +47 -0
- trueconf_server_mcp-0.1.0/tests/test_request.py +219 -0
- trueconf_server_mcp-0.1.0/tests/test_request_file.py +164 -0
- trueconf_server_mcp-0.1.0/tests/test_routes.py +65 -0
- trueconf_server_mcp-0.1.0/tests/test_routes_auth.py +540 -0
- trueconf_server_mcp-0.1.0/tests/test_tls.py +211 -0
- trueconf_server_mcp-0.1.0/tests/test_token_store.py +355 -0
- trueconf_server_mcp-0.1.0/tests/test_tools.py +182 -0
- trueconf_server_mcp-0.1.0/trueconf_server_mcp.egg-info/PKG-INFO +476 -0
- trueconf_server_mcp-0.1.0/trueconf_server_mcp.egg-info/SOURCES.txt +109 -0
- trueconf_server_mcp-0.1.0/trueconf_server_mcp.egg-info/dependency_links.txt +1 -0
- trueconf_server_mcp-0.1.0/trueconf_server_mcp.egg-info/entry_points.txt +2 -0
- trueconf_server_mcp-0.1.0/trueconf_server_mcp.egg-info/requires.txt +7 -0
- trueconf_server_mcp-0.1.0/trueconf_server_mcp.egg-info/scm_file_list.json +104 -0
- trueconf_server_mcp-0.1.0/trueconf_server_mcp.egg-info/scm_version.json +8 -0
- trueconf_server_mcp-0.1.0/trueconf_server_mcp.egg-info/top_level.txt +2 -0
- trueconf_server_mcp-0.1.0/uv.lock +1494 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# TrueConf Server MCP — example configuration.
|
|
2
|
+
# Copy to .env and edit. .env is git-ignored.
|
|
3
|
+
#
|
|
4
|
+
# Priority (highest → lowest):
|
|
5
|
+
# 1. CLI flags: trueconf-server-mcp --server 10.0.0.1 --port 9000
|
|
6
|
+
# 2. Environment variables (export TRUECONF_SERVER=...)
|
|
7
|
+
# 3. This .env file (loaded via python-dotenv)
|
|
8
|
+
# 4. Built-in defaults
|
|
9
|
+
#
|
|
10
|
+
# Required:
|
|
11
|
+
TRUECONF_SERVER=10.140.1.255
|
|
12
|
+
TRUECONF_CLIENT_ID=your_client_id
|
|
13
|
+
TRUECONF_SECRET=your_secret
|
|
14
|
+
#
|
|
15
|
+
# Optional:
|
|
16
|
+
TRUECONF_VERIFY_SSL=false
|
|
17
|
+
MCP_BASE_URL=https://localhost
|
|
18
|
+
# MCP_BASE_URL must be reachable by MCP clients from outside — 127.0.0.1 does NOT work.
|
|
19
|
+
# Default: https://localhost (port 443 omitted); http://localhost:<port> when MCP_NO_TLS=true.
|
|
20
|
+
# For LAN access use https://<your-LAN-IP>, e.g. https://10.140.1.177
|
|
21
|
+
TRUECONF_MCP_PORT=443
|
|
22
|
+
# Default: 443 (HTTPS) or 80 when MCP_NO_TLS=true. Omit to use the default.
|
|
23
|
+
AUTH_MODE=token
|
|
24
|
+
# AUTH_MODE: oauth (OAuthProxy + DCR) or token (manual long-lived token)
|
|
25
|
+
DISCOVERY_MODE=static
|
|
26
|
+
# DISCOVERY_MODE: static (all 33 tools) | bm25 (search gateway) | code (CodeMode sandbox)
|
|
27
|
+
API_TOKEN_TTL=86400
|
|
28
|
+
# Legacy: CODE_MODE_EXPERIMENTAL=true forces DISCOVERY_MODE=code (overrides DISCOVERY_MODE)
|
|
29
|
+
HTTP_TIMEOUT=30
|
|
30
|
+
# HTTP_TIMEOUT: timeout (seconds) for TrueConf API HTTP requests. Default: 30.
|
|
31
|
+
#
|
|
32
|
+
# TLS:
|
|
33
|
+
MCP_NO_TLS=false
|
|
34
|
+
# MCP_NO_TLS=true — disable TLS (plain HTTP, default port 80).
|
|
35
|
+
MCP_TLS_CERT=
|
|
36
|
+
MCP_TLS_KEY=
|
|
37
|
+
# MCP_TLS_CERT / MCP_TLS_KEY — paths to a custom PEM cert+key. Both required if either is set.
|
|
38
|
+
# If omitted and TLS is enabled, a self-signed cert is generated and persisted
|
|
39
|
+
# in ~/Library/Application Support/fastmcp/tls/ (SAN from MCP_BASE_URL host).
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: Release to TestPyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
tag:
|
|
7
|
+
description: "Tag to release (e.g. v1.2.3 or v1.2.3rc1)"
|
|
8
|
+
required: true
|
|
9
|
+
type: string
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
id-token: write
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
publish-to-testpypi:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
environment: testpypi
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
with:
|
|
22
|
+
fetch-depth: 0
|
|
23
|
+
fetch-tags: true
|
|
24
|
+
ref: ${{ github.event.inputs.tag }}
|
|
25
|
+
|
|
26
|
+
- name: Set version from tag
|
|
27
|
+
run: |
|
|
28
|
+
TAG="${{ github.event.inputs.tag }}"
|
|
29
|
+
VERSION="${TAG#v}"
|
|
30
|
+
sed -i "s/^version = .*/version = \"${VERSION}\"/" pyproject.toml
|
|
31
|
+
|
|
32
|
+
- name: Set up Python
|
|
33
|
+
uses: actions/setup-python@v5
|
|
34
|
+
with:
|
|
35
|
+
python-version: "3.12"
|
|
36
|
+
|
|
37
|
+
- name: Build package
|
|
38
|
+
run: |
|
|
39
|
+
pip install build
|
|
40
|
+
python -m build
|
|
41
|
+
|
|
42
|
+
- name: Check dist metadata
|
|
43
|
+
run: |
|
|
44
|
+
pip install twine
|
|
45
|
+
twine check dist/*
|
|
46
|
+
|
|
47
|
+
- name: Publish package to TestPyPI
|
|
48
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
49
|
+
with:
|
|
50
|
+
repository-url: https://test.pypi.org/legacy/
|
|
51
|
+
verbose: true
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: Release to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
id-token: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build-and-publish:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
environment: pypi
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0
|
|
19
|
+
ref: ${{ github.event.release.tag_name }}
|
|
20
|
+
|
|
21
|
+
- name: Set version from tag
|
|
22
|
+
run: |
|
|
23
|
+
TAG="${{ github.event.release.tag_name }}"
|
|
24
|
+
VERSION="${TAG#v}"
|
|
25
|
+
sed -i "s/^version = .*/version = \"${VERSION}\"/" pyproject.toml
|
|
26
|
+
|
|
27
|
+
- name: Set up Python
|
|
28
|
+
uses: actions/setup-python@v5
|
|
29
|
+
with:
|
|
30
|
+
python-version: "3.12"
|
|
31
|
+
|
|
32
|
+
- name: Build package
|
|
33
|
+
run: |
|
|
34
|
+
pip install build
|
|
35
|
+
python -m build
|
|
36
|
+
|
|
37
|
+
- name: Upload artifacts to Actions
|
|
38
|
+
uses: actions/upload-artifact@v4
|
|
39
|
+
with:
|
|
40
|
+
name: dist
|
|
41
|
+
path: dist/*
|
|
42
|
+
|
|
43
|
+
- name: Publish to PyPI
|
|
44
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
app/_version.py
|
|
26
|
+
.installed.cfg
|
|
27
|
+
*.egg
|
|
28
|
+
MANIFEST
|
|
29
|
+
|
|
30
|
+
# PyInstaller
|
|
31
|
+
# Usually these files are written by a python script from a template
|
|
32
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
33
|
+
*.manifest
|
|
34
|
+
*.spec
|
|
35
|
+
|
|
36
|
+
# Installer logs
|
|
37
|
+
pip-log.txt
|
|
38
|
+
pip-delete-this-directory.txt
|
|
39
|
+
|
|
40
|
+
# Unit test / coverage reports
|
|
41
|
+
htmlcov/
|
|
42
|
+
.tox/
|
|
43
|
+
.nox/
|
|
44
|
+
.coverage
|
|
45
|
+
.coverage.*
|
|
46
|
+
.cache
|
|
47
|
+
nosetests.xml
|
|
48
|
+
coverage.xml
|
|
49
|
+
*.cover
|
|
50
|
+
*.py,cover
|
|
51
|
+
.hypothesis/
|
|
52
|
+
.pytest_cache/
|
|
53
|
+
cover/
|
|
54
|
+
|
|
55
|
+
# Translations
|
|
56
|
+
*.mo
|
|
57
|
+
*.pot
|
|
58
|
+
|
|
59
|
+
# Django stuff:
|
|
60
|
+
*.log
|
|
61
|
+
local_settings.py
|
|
62
|
+
db.sqlite3
|
|
63
|
+
db.sqlite3-journal
|
|
64
|
+
|
|
65
|
+
# Flask stuff:
|
|
66
|
+
instance/
|
|
67
|
+
.webassets-cache
|
|
68
|
+
|
|
69
|
+
# Scrapy stuff:
|
|
70
|
+
.scrapy
|
|
71
|
+
|
|
72
|
+
# Sphinx documentation
|
|
73
|
+
docs/_build/
|
|
74
|
+
|
|
75
|
+
# PyBuilder
|
|
76
|
+
.pybuilder/
|
|
77
|
+
target/
|
|
78
|
+
|
|
79
|
+
# Jupyter Notebook
|
|
80
|
+
.ipynb_checkpoints
|
|
81
|
+
|
|
82
|
+
# IPython
|
|
83
|
+
profile_default/
|
|
84
|
+
ipython_config.py
|
|
85
|
+
|
|
86
|
+
# pyenv
|
|
87
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
88
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
89
|
+
# .python-version
|
|
90
|
+
|
|
91
|
+
# pipenv
|
|
92
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
93
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
94
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
95
|
+
# install all needed dependencies.
|
|
96
|
+
#Pipfile.lock
|
|
97
|
+
|
|
98
|
+
# UV
|
|
99
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
100
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
101
|
+
# commonly ignored for libraries.
|
|
102
|
+
#uv.lock
|
|
103
|
+
|
|
104
|
+
# poetry
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
106
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
107
|
+
# commonly ignored for libraries.
|
|
108
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
109
|
+
#poetry.lock
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
#pdm.lock
|
|
114
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
115
|
+
# in version control.
|
|
116
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
117
|
+
.pdm.toml
|
|
118
|
+
.pdm-python
|
|
119
|
+
.pdm-build/
|
|
120
|
+
|
|
121
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
122
|
+
__pypackages__/
|
|
123
|
+
|
|
124
|
+
# Celery stuff
|
|
125
|
+
celerybeat-schedule
|
|
126
|
+
celerybeat.pid
|
|
127
|
+
|
|
128
|
+
# SageMath parsed files
|
|
129
|
+
*.sage.py
|
|
130
|
+
|
|
131
|
+
# Environments
|
|
132
|
+
.venv
|
|
133
|
+
env/
|
|
134
|
+
venv/
|
|
135
|
+
ENV/
|
|
136
|
+
env.bak/
|
|
137
|
+
venv.bak/
|
|
138
|
+
# dotenv: real config (keep .env.example tracked)
|
|
139
|
+
.env
|
|
140
|
+
.env.local
|
|
141
|
+
.env.*.local
|
|
142
|
+
|
|
143
|
+
# Spyder project settings
|
|
144
|
+
.spyderproject
|
|
145
|
+
.spyproject
|
|
146
|
+
|
|
147
|
+
# Rope project settings
|
|
148
|
+
.ropeproject
|
|
149
|
+
|
|
150
|
+
# mkdocs documentation
|
|
151
|
+
/site
|
|
152
|
+
|
|
153
|
+
# mypy
|
|
154
|
+
.mypy_cache/
|
|
155
|
+
.dmypy.json
|
|
156
|
+
dmypy.json
|
|
157
|
+
|
|
158
|
+
# Pyre type checker
|
|
159
|
+
.pyre/
|
|
160
|
+
|
|
161
|
+
# pytype static type analyzer
|
|
162
|
+
.pytype/
|
|
163
|
+
|
|
164
|
+
# Cython debug symbols
|
|
165
|
+
cython_debug/
|
|
166
|
+
|
|
167
|
+
# PyCharm
|
|
168
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
169
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
170
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
171
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
172
|
+
#.idea/
|
|
173
|
+
|
|
174
|
+
# PyPI configuration file
|
|
175
|
+
.pypirc
|
|
176
|
+
|
|
177
|
+
#VS Code folder
|
|
178
|
+
.vscode
|
|
179
|
+
|
|
180
|
+
.idea
|
|
181
|
+
|
|
182
|
+
*.DS_Store
|
|
183
|
+
|
|
184
|
+
.venv*
|
|
185
|
+
venv*
|
|
186
|
+
/gitignore/
|
|
187
|
+
|
|
188
|
+
docs
|
|
189
|
+
graphify-out
|
|
190
|
+
|
|
191
|
+
# Local-only files (not part of the project)
|
|
192
|
+
openapi.yaml
|
|
193
|
+
skills-lock.json
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# TrueConf Server MCP
|
|
2
|
+
|
|
3
|
+
## Запуск
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# Typer CLI (entry point после uv sync):
|
|
7
|
+
trueconf-server-mcp serve --server 10.0.0.1 --port 9000
|
|
8
|
+
|
|
9
|
+
# или через python main.py (тот же Typer app):
|
|
10
|
+
uv run python main.py serve --server 10.0.0.1
|
|
11
|
+
|
|
12
|
+
# без подкоманды — работает (serve — единственная команда):
|
|
13
|
+
uv run python main.py --server 10.0.0.1
|
|
14
|
+
|
|
15
|
+
# с .env файлом (см. ниже):
|
|
16
|
+
uv run python main.py
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Python 3.12, пакеты через `uv`. Тесты: `uv run pytest` (pytest + pytest-asyncio, `asyncio_mode = auto`, 15 файлов в `tests/`). Линтер: `uv run ruff check .`.
|
|
20
|
+
|
|
21
|
+
### Приоритет конфигурации
|
|
22
|
+
|
|
23
|
+
**CLI flags > env vars > `.env` > дефолты кода.**
|
|
24
|
+
|
|
25
|
+
1. **CLI flags** — `--server`, `--port`, `--discovery-mode {static|bm25|code}`, ... (см. `trueconf-server-mcp serve --help`).
|
|
26
|
+
2. **Environment variables** — `TRUECONF_SERVER`, `TRUECONF_MCP_PORT`, ... (Typer `envvar=` читает их автоматически).
|
|
27
|
+
3. **`.env` файл** — загружается через `python-dotenv` (`load_dotenv()` в начале `main.py`). Пример: `.env.example`. Реальный `.env` в `.gitignore`.
|
|
28
|
+
4. **Дефолты** — в сигнатурах Typer-опций и `app/config.py::Config`.
|
|
29
|
+
|
|
30
|
+
`run_server(config)` (в `main.py`) принимает готовый `Config` dataclass и запускает сервер. `Config.from_env()` — fallback для программного использования без Typer.
|
|
31
|
+
|
|
32
|
+
## Конфигурация
|
|
33
|
+
|
|
34
|
+
| Параметр CLI | Env var | По умолчанию | Описание |
|
|
35
|
+
|---|---|---|---|
|
|
36
|
+
| `--server` | `TRUECONF_SERVER` | (обязательный) | Хост TrueConf Server |
|
|
37
|
+
| `--client-id` | `TRUECONF_CLIENT_ID` | (обязательный) | OAuth client_id |
|
|
38
|
+
| `--secret` | `TRUECONF_SECRET` | (обязательный) | OAuth client_secret |
|
|
39
|
+
| `--verify-ssl/--no-verify-ssl` | `TRUECONF_VERIFY_SSL` | `true` | Проверка SSL-сертификата |
|
|
40
|
+
| `--base-url` | `MCP_BASE_URL` | `https://localhost` | Публичный URL сервера. По умолчанию `https://localhost` (порт 443 опускается); `http://localhost:<port>` при `--no-tls`. |
|
|
41
|
+
| `--port` | `TRUECONF_MCP_PORT` | `443` (`80` при `--no-tls`) | Порт. HTTPS по умолчанию 443, plain HTTP при `--no-tls` — 80. |
|
|
42
|
+
| `--no-tls` | `MCP_NO_TLS` | `false` | Отключить TLS — plain HTTP. Дефолт порта становится 80, самоподписанный сертификат не генерируется. |
|
|
43
|
+
| `--tls-cert` | `MCP_TLS_CERT` | (none → авто-генерация) | Путь к PEM-сертификату. Требует `--tls-key`. Если не указан и TLS включён — генерируется self-signed. |
|
|
44
|
+
| `--tls-key` | `MCP_TLS_KEY` | (none → авто-генерация) | Путь к PEM-ключу. Требует `--tls-cert`. |
|
|
45
|
+
| `--discovery-mode` | `DISCOVERY_MODE` | `static` | `static` (все 32 инструмента), `bm25` (search gateway), `code` (CodeMode sandbox) |
|
|
46
|
+
| — | `CODE_MODE_EXPERIMENTAL` | `false` | **Deprecated** — legacy-альяс: `true` → `discovery-mode=code` (только если `--discovery-mode`/`DISCOVERY_MODE` не заданы явно) |
|
|
47
|
+
| `--auth-mode` | `AUTH_MODE` | `token` | `token` = ручной токен через TokenStore (единственный активный режим). `oauth` = OAuthProxy + DCR (**отключён**: `_TrueConfTokenVerifier` принимал любой токен — auth bypass; код оставлен как dead code для будущего re-enable после реализации настоящей верификации). `--auth-mode oauth` rejected Typer. |
|
|
48
|
+
| `--api-token-ttl` | `API_TOKEN_TTL` | `86400` | TTL нашего токена (сек) |
|
|
49
|
+
| `--http-timeout` | `HTTP_TIMEOUT` | `30.0` | Timeout (сек) для HTTP-запросов к TrueConf API |
|
|
50
|
+
|
|
51
|
+
`run.sh` / `run.bat` содержат хардкод-креденшалы — не коммитьте изменения в них. Для локальной разработки скопируйте `.env.example` → `.env` и отредактируйте.
|
|
52
|
+
|
|
53
|
+
## TLS
|
|
54
|
+
|
|
55
|
+
По умолчанию сервер запускается на `https://0.0.0.0:443` с **автоматически сгенерированным self-signed сертификатом**. SAN сертификата берётся из host-части `MCP_BASE_URL`:
|
|
56
|
+
- `https://localhost` (дефолт) → SAN `[localhost, 127.0.0.1]`
|
|
57
|
+
- `https://10.100.2.108` → SAN `[10.100.2.108]`
|
|
58
|
+
- `https://conf.local` → SAN `[conf.local]`
|
|
59
|
+
|
|
60
|
+
Сертификат **персистится** в `~/Library/Application Support/fastmcp/tls/{cert.pem,key.pem}` (права `0600` на ключ) и переиспользуется между рестартами. Регенерация — при отсутствии файлов, истечении срока (<30 дней), **или несовпадении SAN с текущим `MCP_BASE_URL`**.
|
|
61
|
+
|
|
62
|
+
**Кастомный сертификат**: `--tls-cert /path/cert.pem --tls-key /path/key.pem` (оба флага обязательны, если указан хотя бы один). Используется для валидных сертификатов (Let's Encrypt и т.п.).
|
|
63
|
+
|
|
64
|
+
**Plain HTTP**: `--no-tls` — отключает TLS, дефолт порта становится 80. Bearer-токены передаются в cleartext — только для случаев, когда TLS-клиент не работает (например, LM Studio с self-signed).
|
|
65
|
+
|
|
66
|
+
### Привилегированные порты (<1024)
|
|
67
|
+
|
|
68
|
+
- **macOS** (Mojave+): бинд `0.0.0.0:443` работает **без root**. Бинд на конкретный интерфейс (`127.0.0.1:443`) требует root.
|
|
69
|
+
- **Linux**: требует `sudo setcap cap_net_bind_service+ep $(which python)` (один раз при установке) либо запуск под `sudo`. Без прав — exit с понятной ошибкой и подсказкой про `setcap`/`sudo`/`--port 8443`.
|
|
70
|
+
- **Windows**: запуск от администратора (UAC) либо `--port 8443`.
|
|
71
|
+
|
|
72
|
+
### Перерегистрация OAuth redirect_uri
|
|
73
|
+
|
|
74
|
+
Смена дефолта `MCP_BASE_URL` с `http://localhost:8000` на `https://localhost` меняет `redirect_uri`, который регистрируется на TrueConf Server при OAuth-настройке. Существующие регистрации нужно обновить. Проект не релизнут — миграции нет.
|
|
75
|
+
|
|
76
|
+
**Переименование `/login/callback` → `/auth/callback`** также меняет `redirect_uri` (теперь `.../auth/callback`). Для **обоих** auth-режимов (`oauth` и `token`) существующие регистрации на TrueConf Server нужно обновить — старый путь `/login/callback` больше не обслуживается.
|
|
77
|
+
|
|
78
|
+
## Архитектура
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
main.py # Typer CLI (app + serve + DiscoveryMode), run_server(config),
|
|
82
|
+
# _serve() (uvicorn+cleanup_task), import-time side effects
|
|
83
|
+
# (load_dotenv, logging.basicConfig, init_i18n, регистрация
|
|
84
|
+
# tools/prompts/routes через side-effect imports)
|
|
85
|
+
app/
|
|
86
|
+
__init__.py # маркер пакета
|
|
87
|
+
config.py # Config dataclass + get_config()/set_config() — единый источник конфигурации
|
|
88
|
+
tls.py # TLS: extract_san_names, generate/ensure_self_signed_cert,
|
|
89
|
+
# resolve_tls_files(config), bind_error_help(port)
|
|
90
|
+
trueconf_api/ # слой 1: чистый домен TrueConf (без зависимостей от MCP/fastmcp)
|
|
91
|
+
__init__.py # маркер пакета
|
|
92
|
+
models.py # Pydantic-модели из OpenAPI schemas
|
|
93
|
+
mode_utils.py # _resolve_mode() + _MODE_MAP / _ACCESS_MAP
|
|
94
|
+
mcp/ # слой 2: MCP-инфра (зависит от trueconf_api + fastmcp)
|
|
95
|
+
__init__.py # mcp (FastMCP), _request(), init_http_client()/close_http_client()
|
|
96
|
+
# _token_store + get_token_store()/set_token_store() — общие утилиты
|
|
97
|
+
auth.py # ApiTokenAuth — наш UUID → TrueConf токен + авто-refresh,
|
|
98
|
+
# create_oauth_auth() — OAuthProxy с DCR,
|
|
99
|
+
# init_auth(config, token_store) — всегда ApiTokenAuth (OAuth path disabled)
|
|
100
|
+
token_store.py # TokenStore — зашифрованное файловое хранилище токенов,
|
|
101
|
+
# init_token_store(config) — фабрика (derive keys + FileTreeStore + Fernet),
|
|
102
|
+
# periodic_cleanup() — часовая фоновая задача (вызывает cleanup_expired)
|
|
103
|
+
instructions.py # STATIC/BM25/CODE_MODE_INSTRUCTIONS — per-mode server instructions,
|
|
104
|
+
# apply_discovery_mode(config) — выбор transform + mcp.instructions
|
|
105
|
+
code_mode.py # CodeMode: guide + create_code_mode_transform()
|
|
106
|
+
pages.py # HTML-страницы (login/success/error) + путь к templates/
|
|
107
|
+
prompts.py # MCP-prompts (conference_help) — side-effect регистрация через import
|
|
108
|
+
routes.py # HTTP UI-роуты (/, /success, /error, /auth/callback, /api/health,
|
|
109
|
+
# /static/*, /favicon.ico, /logo.png) + _cors() +
|
|
110
|
+
# register_login_callback() — всегда (OAuth path disabled)
|
|
111
|
+
tools/
|
|
112
|
+
__init__.py # маркер (агрегатор при масштабировании: транскрипции и т.д.)
|
|
113
|
+
conferences/ # 32 MCP-инструмента, один файл на инструмент
|
|
114
|
+
__init__.py # импорт всех 32 модулей → триггер @mcp.tool регистрации
|
|
115
|
+
# Core CRUD
|
|
116
|
+
list_conferences.py
|
|
117
|
+
get_conference.py
|
|
118
|
+
create_conference.py
|
|
119
|
+
update_conference.py
|
|
120
|
+
delete_conference.py
|
|
121
|
+
# Lifecycle
|
|
122
|
+
run_conference.py
|
|
123
|
+
stop_conference.py
|
|
124
|
+
join_conference.py
|
|
125
|
+
# Invitations
|
|
126
|
+
list_invitations.py
|
|
127
|
+
add_invitation.py
|
|
128
|
+
remove_invitation.py
|
|
129
|
+
get_invitation.py
|
|
130
|
+
update_invitation.py
|
|
131
|
+
invite_participants.py
|
|
132
|
+
# Participants & Roles
|
|
133
|
+
get_conference_participants.py
|
|
134
|
+
get_conference_owner.py
|
|
135
|
+
get_conference_me.py
|
|
136
|
+
# Recordings
|
|
137
|
+
list_recordings.py
|
|
138
|
+
get_recording.py
|
|
139
|
+
start_recording.py
|
|
140
|
+
stop_recording.py
|
|
141
|
+
pause_recording.py
|
|
142
|
+
download_recording.py
|
|
143
|
+
# Chat
|
|
144
|
+
get_chat_messages.py
|
|
145
|
+
export_chat_messages.py
|
|
146
|
+
# Links & Calendar
|
|
147
|
+
get_deeplinks.py
|
|
148
|
+
get_shared_links.py
|
|
149
|
+
get_conference_ics.py
|
|
150
|
+
get_conference_calendars.py
|
|
151
|
+
# Notifications & Registration
|
|
152
|
+
notify_conference.py
|
|
153
|
+
register_for_conference.py
|
|
154
|
+
# Translations
|
|
155
|
+
get_conference_translations.py
|
|
156
|
+
# Admin
|
|
157
|
+
calculate_conferences.py
|
|
158
|
+
# (масштабируется: tools/transcriptions/, tools/users/ и т.д.)
|
|
159
|
+
web/ # веб-ассеты и шаблоны (раньше были в корне как static/ + templates/)
|
|
160
|
+
assets/ # favicon.ico, logo.png
|
|
161
|
+
static/
|
|
162
|
+
app.css # Общий chrome (topbar, footer, lang-switch, status-badge) — login/success/error
|
|
163
|
+
app.js # Language switcher dropdown + TrueConf Server health check
|
|
164
|
+
templates/
|
|
165
|
+
login.html # Полная страница входа (shared topbar + two-column main + footer)
|
|
166
|
+
success.html # Полная страница после авторизации (токен + конфиги MCP-клиентов)
|
|
167
|
+
error.html # Полная страница ошибки OAuth (красный акцент, retry-карточка)
|
|
168
|
+
login_body.html # Legacy body-фрагмент (не используется)
|
|
169
|
+
success_body.html # Legacy body-фрагмент (не используется)
|
|
170
|
+
openapi.yaml # TrueConf Server API v4 (277 эндпоинтов)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Критично: Токен-флоу
|
|
174
|
+
|
|
175
|
+
Цепочка авторизации — самая важная концепция в проекте:
|
|
176
|
+
|
|
177
|
+
**Режим `oauth` (отключён):** OAuthProxy + DCR путь оставлен как dead code (`create_oauth_auth` / `_TrueConfTokenVerifier` в `auth.py`). `init_auth` всегда возвращает `ApiTokenAuth`. Причина отключения: `_TrueConfTokenVerifier.verify_token` принимал ЛЮБУЮ строку как валидный токен — полный обход авторизации. Re-enable только после реализации настоящей валидации opaque-токенов против TrueConf Server.
|
|
178
|
+
|
|
179
|
+
**Режим `token` (единственный активный):**
|
|
180
|
+
1. Пользователь заходит на `/` → OAuth2 редирект на TrueConf Server
|
|
181
|
+
2. `/auth/callback` обменивает code на TrueConf tokens, создаёт наш длинный токен, редиректит на `/success` (токен доставляется через `mcp_token` httpOnly cookie, не через query param)
|
|
182
|
+
3. Наш токен хранится зашифрованно в `~/Library/Application Support/fastmcp/oauth-proxy/<fingerprint>/mcp-api-tokens/`
|
|
183
|
+
4. MCP-клиент шлёт `Authorization: Bearer <our_token>`:
|
|
184
|
+
- `ApiTokenAuth.verify_token()` ищет наш токен в TokenStore
|
|
185
|
+
- Если TrueConf токен протух — прозрачный refresh
|
|
186
|
+
- Возвращает `AccessToken(token=<TrueConf_token>)` — `.token` это TrueConf токен, не наш UUID
|
|
187
|
+
5. Инструменты вызывают `get_access_token()` → `.token` = TrueConf токен, `.client_id` = user_id
|
|
188
|
+
6. `_call_trueconf()` использует общий httpx-клиент (`init_http_client()`), добавляет `Authorization: Bearer <TrueConf_token>` в headers per-request
|
|
189
|
+
|
|
190
|
+
**Не путайте наш UUID-токен с TrueConf access_token.** `ApiTokenAuth` делает маппинг.
|
|
191
|
+
|
|
192
|
+
**CORS для cookie-флоу.** TrueConf Server выполняет `/oauth2/authorize` через `fetch()` из JS — `/auth/callback` приходит как **credentialed cross-site CORS request**. Для таких запросов браузер требует: (1) `Access-Control-Allow-Origin` = конкретный Origin (не `*`), (2) `Access-Control-Allow-Credentials: true`. Без обоих браузер блокирует ответ и **дропает `Set-Cookie`** → `/success` не видит cookie → login loop. `_cors()` в `routes.py` эхит Origin из request + ставит `Allow-Credentials: true` + `Vary: Origin`. `SameSite=None` + `Secure` на cookie необходимы, но **недостаточны** без правильных CORS headers.
|
|
193
|
+
|
|
194
|
+
**Неаутентифицированные запросы (pass-through).** `RequireAuthMiddleware` пропатчен (`_patch_auth_middleware_optional` в `auth.py`) так, что запросы без Bearer-токена проходят через middleware к MCP-обработчику. Инструменты сами проверяют `get_access_token()` через `_request` и, если токена нет, возвращают `{"error": "authorization_required", "login_url": ..., "message": ..., "how_to": ...}` dict — LLM объясняет юзеру как авторизоваться. Жёсткий 401-ответ с JSON-инструкциями **никогда не срабатывает** в обоих auth-режимах (token и oauth). Pass-through патч обязателен для code_mode/bm25 — иначе `initialize` падает на 401 и discovery недоступен.
|
|
195
|
+
|
|
196
|
+
## Добавление новых инструментов
|
|
197
|
+
|
|
198
|
+
1. Если нужны новые модели — добавить в `app/trueconf_api/models.py`
|
|
199
|
+
2. Создать файл в соответствующем пакете (`app/mcp/tools/conferences/`, `app/mcp/tools/transcriptions/` и т.д.)
|
|
200
|
+
3. Декоратор `@mcp.tool(tags={"tag1", "tag2"})` — `mcp` импортируется из `app.mcp`
|
|
201
|
+
4. API-запросы через `await _request("METHOD", "path", json=..., params=...)` (`_request` — из `app.mcp`)
|
|
202
|
+
5. `_request` сам обрабатывает auth, логирование, парсинг ошибок и учёт использования
|
|
203
|
+
6. Импорт нового модуля в `__init__.py` пакета (например `app/mcp/tools/conferences/__init__.py`) автоматически регистрирует инструменты. Не забыть `import app.mcp.tools.<domain>` в `main.py` (или в общем `app/mcp/tools/__init__.py`)
|
|
204
|
+
|
|
205
|
+
`mcp.instructions` (server-level контекст для MCP-клиента) задаётся в `apply_discovery_mode()` (`app/mcp/instructions.py`) per-режим (`STATIC_INSTRUCTIONS` / `BM25_INSTRUCTIONS` / `CODE_MODE_INSTRUCTIONS`). Вызывается из `run_server()` в `main.py`. При изменении домена (новые типы объектов) обновлять все три константы.
|
|
206
|
+
|
|
207
|
+
## Деплой
|
|
208
|
+
|
|
209
|
+
- По умолчанию сервер сам терминирует TLS на 443 с self-signed сертификатом (см. раздел [TLS](#tls))
|
|
210
|
+
- Caddy (`Caddyfile`) — опционально, для валидных сертификатов (Let's Encrypt); проксирует на `localhost:443` (или `--port 8443` при коллизии)
|
|
211
|
+
- Альтернатива: ngrok (`ngrok http 443`) — туннелирует HTTPS с валидным сертификатом
|
|
212
|
+
- Путь хранения токенов зависит от `TRUECONF_SECRET` — смена = все токены станут нечитаемыми
|
|
213
|
+
- Фоновая задача `periodic_cleanup()` стартует с одного sweep при запуске, затем каждые час чистит протухшие токены без refresh_token
|
|
214
|
+
- **⚠️ Single-worker only:** `asyncio.Lock` в `TokenStore._index_lock` сериализует только в одном процессе. Multi-worker деплой (uvicorn `--workers N`) с общим filesystem — гонки на индексе (read-modify-write → потерянные токены, двойные удаления). Запускать с одним worker.
|
|
215
|
+
|
|
216
|
+
## Подключение MCP-клиентов (LM Studio, Cursor и т.д.)
|
|
217
|
+
|
|
218
|
+
### MCP_BASE_URL — критически важно
|
|
219
|
+
|
|
220
|
+
`MCP_BASE_URL` определяет URL-адреса в OAuth metadata (`/.well-known/oauth-authorization-server`).
|
|
221
|
+
MCP-клиент получает эти URL и обращается к ним при DCR и OAuth flow.
|
|
222
|
+
|
|
223
|
+
**Правило:** `MCP_BASE_URL` должен быть тем URL, по которому клиент может достучаться до сервера извне.
|
|
224
|
+
|
|
225
|
+
| Сценарий | `MCP_BASE_URL` |
|
|
226
|
+
|----------|----------------|
|
|
227
|
+
| Локальный (только localhost) | `https://localhost` (дефолт, порт 443 опущен) |
|
|
228
|
+
| LAN (другие устройства в сети) | `https://<LAN-IP>` (порт 443 опущен) |
|
|
229
|
+
| Через Caddy | `https://<domain-or-ip>` |
|
|
230
|
+
| Через ngrok | `https://<ngrok-url>` |
|
|
231
|
+
|
|
232
|
+
**Частая ошибка:** `MCP_BASE_URL=https://127.0.0.1` — metadata возвращает `127.0.0.1`, клиент не может достучаться → 401 → "Plugin process exited".
|
|
233
|
+
|
|
234
|
+
### CIMD (Client ID Metadata Document)
|
|
235
|
+
|
|
236
|
+
FastMCP OAuthProxy по умолчанию включает CIMD (`enable_cimd=True`). В metadata появляется `client_id_metadata_document_supported: true`.
|
|
237
|
+
|
|
238
|
+
**Проблема:** LM Studio интерпретирует это как "сервер поддерживает только CIMD, а не стандартный DCR" → показывает "This server does not support Dynamic Client Registration".
|
|
239
|
+
|
|
240
|
+
**Решение:** Отключить CIMD в `create_oauth_auth()` (`auth.py`):
|
|
241
|
+
```python
|
|
242
|
+
auth = OAuthProxy(
|
|
243
|
+
...,
|
|
244
|
+
enable_cimd=False,
|
|
245
|
+
)
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### LM Studio + OAuth
|
|
249
|
+
|
|
250
|
+
LM Studio (начиная с 0.3.17) поддерживает MCP-серверы через `mcp.json`. Поддерживает DCR (Dynamic Client Registration) для OAuth flow. **Не поддерживает CIMD.**
|
|
251
|
+
|
|
252
|
+
Пример конфигурации в LM Studio:
|
|
253
|
+
```json
|
|
254
|
+
{
|
|
255
|
+
"mcpServers": {
|
|
256
|
+
"trueconf": {
|
|
257
|
+
"url": "https://<MCP_BASE_URL>/mcp"
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Caddy + Node.js (LM Studio)
|
|
264
|
+
|
|
265
|
+
Node.js (используется внутри LM Studio) **не доверяет самоподписанным сертификатам** — ни Caddy, ни нашему авто-сгенерированному self-signed из раздела [TLS](#tls). Не использует macOS Keychain.
|
|
266
|
+
|
|
267
|
+
Caddy пишет `root certificate is already trusted by system`, но Node.js это игнорирует. `NODE_EXTRA_CA_CERTS` не помогает — LM Studio не прокидывает эту переменную в свой Node.js процесс.
|
|
268
|
+
|
|
269
|
+
**Решения:**
|
|
270
|
+
- **ngrok** — туннелирует HTTPS с валидным сертификатом (free tier: URL меняется при каждом перезапуске)
|
|
271
|
+
- **Домен + Let's Encrypt** — Caddy автоматически получит валидный сертификат
|
|
272
|
+
|
|
273
|
+
### NODE_TLS_REJECT_UNAUTHORIZED workaround (self-signed cert)
|
|
274
|
+
|
|
275
|
+
Если LM Studio вылетает с ошибкой:
|
|
276
|
+
|
|
277
|
+
> TypeError: fetch failed: self-signed certificate; if the root CA is installed locally, try running Node.js with --use-system-ca
|
|
278
|
+
|
|
279
|
+
Запустите LM Studio с отключённой валидацией TLS-сертификатов:
|
|
280
|
+
|
|
281
|
+
**macOS:**
|
|
282
|
+
```bash
|
|
283
|
+
NODE_TLS_REJECT_UNAUTHORIZED=0 open "/Applications/LM Studio.app"
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
**Windows (CMD):**
|
|
287
|
+
```cmd
|
|
288
|
+
set NODE_TLS_REJECT_UNAUTHORIZED=0
|
|
289
|
+
start "" "C:\Program Files\LM Studio\LM Studio.exe"
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
**Linux:**
|
|
293
|
+
```bash
|
|
294
|
+
NODE_TLS_REJECT_UNAUTHORIZED=0 lm-studio
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**Важно:**
|
|
298
|
+
- Env var действует только для текущей сессии — перезапуск из Dock/Start Menu/Spotlight требует повторного запуска из терминала
|
|
299
|
+
- Отключает валидацию сертификатов для ВСЕХ HTTPS-запросов Node.js в процессе LM Studio — только для локальной разработки
|
|
300
|
+
- Проверено: работает с auth mode `token`
|
|
301
|
+
|
|
302
|
+
## Известные проблемы
|
|
303
|
+
|
|
304
|
+
- `download_recording` удалён — грузил весь видеофайл в base64 (memory bomb), LLM не мог осмысленно использовать видео-блоб в MCP-контексте. Скачивание записей — через `download_url` из `get_recording`/`list_recordings`
|
|
305
|
+
- LM Studio не поддерживает CIMD — если в metadata есть `client_id_metadata_document_supported: true`, LM Studio показывает "DCR not supported". Решение: `enable_cimd=False` в OAuthProxy
|
|
306
|
+
- `MCP_BASE_URL` с `127.0.0.1` не работает для удалённых клиентов — metadata возвращает localhost URL
|
|
307
|
+
- Node.js (LM Studio) не доверяет самоподписанным сертификатам — ни Caddy, ни авто-сгенерированному из [TLS](#tls). Workaround: `NODE_TLS_REJECT_UNAUTHORIZED=0` при запуске LM Studio (см. раздел выше). Для production — ngrok или Let's Encrypt.
|
|
308
|
+
- ~~**CORS login loop (fixed).**~~ Исторически `_cors()` в `routes.py` возвращал `Access-Control-Allow-Origin: *` без `Access-Control-Allow-Credentials: true`. Для credentialed cross-site `fetch()` (которым является `/auth/callback` из TrueConf Server JS) браузер блокирует такой ответ и **дропает `Set-Cookie`** → `/success` не видит cookie → login loop. Фикс: `_cors()` эхит Origin из request + ставит `Allow-Credentials: true` + `Vary: Origin` (когда Origin есть); без Origin — `*` как и раньше.
|
|
309
|
+
- **Login CSRF (session fixation, insider-only).** TrueConf Server не поддерживает `state` и PKCE в OAuth flow, поэтому классическая OAuth state-binding недоступна. Атакующий — легитимный пользователь TrueConf Server — может прогнать `/` flow своими кредами, перехватить `code` до consumed и доставить жертве `https://<mcp>/auth/callback?code=<attacker_code>` в течение ~60с (TTL кода). В результате MCP-клиент жертвы работает в аккаунте атакующего (видит его конференции, записи, чаты). Это session fixation, не credential theft: creds жертвы не утекают, work product жертвы оседает в аккаунте атакующего. Митигации: `mcp_token` cookie `max_age=60`, single-use, `httponly`, `samesite=none` + `secure` (None обязателен: TrueConf Server выполняет `/oauth2/authorize` через `fetch()` из JS — callback приходит как cross-site cors request, и SameSite=Lax было бы drop'нуто браузером; cookie вообще не сохранялось → /success не видел токен → login loop). Re-confirmation step (страница «Вы авторизуетесь как X. Подтвердить?» + POST с CSRF-токеном) отклонён по cost/benefit — friction на каждый легитимный логин ради узкого insider-сценария. Остаточный риск принят.
|