tavus-cli 0.8.0__tar.gz → 0.9.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.
- tavus_cli-0.9.0/.release-please-manifest.json +3 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/CHANGELOG.md +19 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/PKG-INFO +2 -2
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/README.md +1 -1
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/docs/chatgpt-plugin.md +6 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/packages/tavus/pyproject.toml +2 -2
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/packages/tavus-mcp/pyproject.toml +2 -2
- tavus_cli-0.9.0/plugins/tavus-pal/plugin.json +99 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/pyproject.toml +1 -1
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/__init__.py +1 -1
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/recipes/deployments.py +7 -1
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/tests/unit/test_deployments.py +8 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/uv.lock +1 -1
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/workers/tavus-mcp/package-lock.json +2 -2
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/workers/tavus-mcp/package.json +1 -1
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/workers/tavus-mcp/src/index.ts +6 -1
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/workers/tavus-mcp/src/oauth.ts +50 -18
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/workers/tavus-mcp/src/tavus.ts +34 -3
- tavus_cli-0.9.0/workers/tavus-mcp/src/version.ts +1 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/workers/tavus-mcp/test/oauth-session.test.ts +48 -2
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/workers/tavus-mcp/test/tavus.test.ts +31 -6
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/workers/tavus-mcp/wrangler.jsonc +3 -0
- tavus_cli-0.8.0/.release-please-manifest.json +0 -3
- tavus_cli-0.8.0/plugins/tavus-pal/plugin.json +0 -45
- tavus_cli-0.8.0/workers/tavus-mcp/src/version.ts +0 -1
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/.agents/plugins/marketplace.json +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/.env.example +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/.github/workflows/ci.yml +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/.github/workflows/deploy-cloudflare-worker.yml +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/.github/workflows/pr-reviews.yml +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/.github/workflows/pypi-cli.yml +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/.github/workflows/tail-cloudflare-worker.yml +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/.github/workflows/validate-staging-deployment.yml +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/.gitignore +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/AGENTS.md +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/PROMPT.md +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/docs/gemini-enterprise/README.md +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/docs/gemini-enterprise/listing.md +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/docs/gemini-enterprise/test-queries.md +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/docs/usage-observability.md +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/packages/tavus/README.md +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/packages/tavus-mcp/README.md +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/plugins/tavus-pal/assets/icon-dark.svg +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/plugins/tavus-pal/assets/icon.svg +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/plugins/tavus-pal/mcp.json +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/release-please-config.json +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/scripts/__init__.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/scripts/build_chatgpt_plugin.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/scripts/check_release_versions.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/skills/tavus-agentic-pals/SKILL.md +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/skills/tavus-agentic-pals/agents/openai.yaml +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/skills/tavus-agentic-pals/references/build-and-verify.md +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/skills/tavus-agentic-pals/references/builder-workflow.md +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/skills/tavus-agentic-pals/references/chat-mode.md +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/skills/tavus-agentic-pals/references/examples.md +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/cli/__init__.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/cli/main.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/__init__.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/auth/__init__.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/auth/keyring_store.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/auth/oauth.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/auth/session.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/client/__init__.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/client/http.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/daily_transport.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/env.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/errors.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/pal_urls.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/patch.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/recipes/__init__.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/recipes/build_and_verify.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/recipes/capabilities.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/recipes/carrier_builder.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/recipes/knowledge.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/recipes/options.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/recipes/quickstart.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/recipes/scaffold_embed.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/recipes/templates.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/recipes/tool_reuse.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/schemas/__init__.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/schemas/deployment.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/schemas/file_manifest.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/schemas/guardrail.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/schemas/objective.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/schemas/pal.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/schemas/pronunciation.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/sdk/schemas/tool.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/src/tavus_mcp/server.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/tests/conftest.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/tests/integration/test_cli.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/tests/integration/test_client.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/tests/integration/test_mcp_server.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/tests/integration/test_oauth.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/tests/unit/test_auth.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/tests/unit/test_build_and_verify.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/tests/unit/test_builder.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/tests/unit/test_capabilities.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/tests/unit/test_daily_transport.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/tests/unit/test_env.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/tests/unit/test_guardrail_schema.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/tests/unit/test_objective_schema.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/tests/unit/test_pal_schema.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/tests/unit/test_patch.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/tests/unit/test_pronunciation_schema.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/tests/unit/test_release_versions.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/tests/unit/test_scaffold_embed.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/tests/unit/test_tool_reuse.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/tests/unit/test_tool_schema.py +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/workers/tavus-mcp/.dev.vars.example +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/workers/tavus-mcp/src/oauth-grant.ts +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/workers/tavus-mcp/src/oauth-session.ts +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/workers/tavus-mcp/test/parity.test.ts +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/workers/tavus-mcp/test/worker-runtime.ts +0 -0
- {tavus_cli-0.8.0 → tavus_cli-0.9.0}/workers/tavus-mcp/tsconfig.json +0 -0
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.9.0](https://github.com/Tavus-Engineering/tavus-mcp/compare/v0.8.1...v0.9.0) (2026-10-09)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **worker:** publish Tavus MCP logo metadata ([#64](https://github.com/Tavus-Engineering/tavus-mcp/issues/64)) ([e03c27c](https://github.com/Tavus-Engineering/tavus-mcp/commit/e03c27c957456c624186cbb7ce06cf06144d2bd4))
|
|
9
|
+
|
|
10
|
+
## [0.8.1](https://github.com/Tavus-Engineering/tavus-mcp/compare/v0.8.0...v0.8.1) (2026-10-08)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* sign-in for Lovable, Bolt, Replit, v0 and Cursor on the hosted MCP server ([#62](https://github.com/Tavus-Engineering/tavus-mcp/issues/62)) ([6e209d7](https://github.com/Tavus-Engineering/tavus-mcp/commit/6e209d7f59081a0a325afcc28081e374e2bb7304))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Miscellaneous Chores
|
|
19
|
+
|
|
20
|
+
* **plugin:** add directory review test cases and release notes to Tavus PAL ([a4fc1e9](https://github.com/Tavus-Engineering/tavus-mcp/commit/a4fc1e9cf13e99c5d1dd0d18e5f026662cf336cb))
|
|
21
|
+
|
|
3
22
|
## [0.8.0](https://github.com/Tavus-Engineering/tavus-mcp/compare/v0.7.0...v0.8.0) (2026-10-08)
|
|
4
23
|
|
|
5
24
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: tavus-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: Tavus CLI and MCP server for agentic PAL creation and editing.
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Requires-Dist: fastmcp>=2.0.0
|
|
@@ -60,7 +60,7 @@ The API key never appears in client configuration or browser callback URLs.
|
|
|
60
60
|
|
|
61
61
|
Dynamic client registration accepts loopback callbacks from any local client
|
|
62
62
|
(Claude Code, Codex, Gemini CLI) and the hosted callbacks of Claude, ChatGPT,
|
|
63
|
-
Cursor, VS Code, v0, Lovable and Replit. They are listed in `KNOWN_REMOTE_CLIENTS` in
|
|
63
|
+
Cursor, VS Code, v0, Lovable, Bolt and Replit. They are listed in `KNOWN_REMOTE_CLIENTS` in
|
|
64
64
|
`workers/tavus-mcp/src/oauth.ts`. To add a client, press Connect in it once,
|
|
65
65
|
read its callback from the Worker's `oauth_register_rejected` log line, and add
|
|
66
66
|
an entry with a test. Clients that don't use dynamic registration, such as
|
|
@@ -38,7 +38,7 @@ The API key never appears in client configuration or browser callback URLs.
|
|
|
38
38
|
|
|
39
39
|
Dynamic client registration accepts loopback callbacks from any local client
|
|
40
40
|
(Claude Code, Codex, Gemini CLI) and the hosted callbacks of Claude, ChatGPT,
|
|
41
|
-
Cursor, VS Code, v0, Lovable and Replit. They are listed in `KNOWN_REMOTE_CLIENTS` in
|
|
41
|
+
Cursor, VS Code, v0, Lovable, Bolt and Replit. They are listed in `KNOWN_REMOTE_CLIENTS` in
|
|
42
42
|
`workers/tavus-mcp/src/oauth.ts`. To add a client, press Connect in it once,
|
|
43
43
|
read its callback from the Worker's `oauth_register_rejected` log line, and add
|
|
44
44
|
an entry with a test. Clients that don't use dynamic registration, such as
|
|
@@ -17,6 +17,12 @@ No account credentials, local environment files, or test evidence are bundled.
|
|
|
17
17
|
The portable Agent Plugins schemas validate both manifests. The package has
|
|
18
18
|
its own version because listing changes do not require an MCP server release.
|
|
19
19
|
|
|
20
|
+
The hosted Worker also publishes these icons at `/icon.svg` and `/icon-dark.svg`
|
|
21
|
+
without authentication. Its MCP `serverInfo.icons` metadata identifies the light
|
|
22
|
+
and dark variants using URLs on the server's own origin. Clients decide whether
|
|
23
|
+
to display this metadata; catalog listings may require a separate logo upload.
|
|
24
|
+
Changes to the hosted metadata or assets require a Worker release.
|
|
25
|
+
|
|
20
26
|
For local distribution, the repository's `.agents/plugins/marketplace.json`
|
|
21
27
|
points to `plugins/tavus-pal`. Add the repository root as a local marketplace
|
|
22
28
|
in a supported ChatGPT/Codex desktop client, then install Tavus PAL from that
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tavus"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.9.0" # x-release-please-version
|
|
4
4
|
description = "Official alias for tavus-cli, the Tavus CLI and MCP server."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
7
7
|
dependencies = [
|
|
8
|
-
"tavus-cli==0.
|
|
8
|
+
"tavus-cli==0.9.0", # x-release-please-version
|
|
9
9
|
]
|
|
10
10
|
|
|
11
11
|
[project.urls]
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tavus-mcp"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.9.0" # x-release-please-version
|
|
4
4
|
description = "Official alias for tavus-cli, which provides the tavus-mcp MCP server."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
7
7
|
dependencies = [
|
|
8
|
-
"tavus-cli==0.
|
|
8
|
+
"tavus-cli==0.9.0", # x-release-please-version
|
|
9
9
|
]
|
|
10
10
|
|
|
11
11
|
[project.urls]
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
|
+
"name": "tavus-pal",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Create, edit, and test Tavus PALs: digital humans with a face, voice, knowledge, and conversational behavior. Uses the Tavus hosted MCP server and your connected Tavus account.",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Tavus",
|
|
8
|
+
"email": "support@tavus.io",
|
|
9
|
+
"url": "https://www.tavus.io"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://maker.tavus.io",
|
|
12
|
+
"repository": "https://github.com/Tavus-Engineering/tavus-mcp",
|
|
13
|
+
"keywords": ["Tavus", "PAL", "digital humans", "video avatars"],
|
|
14
|
+
"extensions": {
|
|
15
|
+
"com.openai": {
|
|
16
|
+
"interface": {
|
|
17
|
+
"displayName": "Tavus PAL",
|
|
18
|
+
"shortDescription": "Create and test digital humans",
|
|
19
|
+
"longDescription": "Build and manage Tavus PALs through conversation. Choose available faces and voices, configure behavior and knowledge, run text tests, and open a video preview. Connect your Tavus account to access its resources and existing entitlements. Includes the same tools as the hosted Tavus MCP server. Custom face training, rendered video generation, PAL sales, and ownership transfers are not exposed by this integration.",
|
|
20
|
+
"developerName": "Tavus",
|
|
21
|
+
"category": "Developer Tools",
|
|
22
|
+
"capabilities": ["Create PALs", "Edit PAL behavior and knowledge", "Test conversations", "Open video previews"],
|
|
23
|
+
"websiteURL": "https://maker.tavus.io",
|
|
24
|
+
"supportURL": "https://help.tavus.io/",
|
|
25
|
+
"privacyPolicyURL": "https://www.tavus.io/privacy-policy",
|
|
26
|
+
"termsOfServiceURL": "https://www.tavus.io/terms-of-service",
|
|
27
|
+
"defaultPrompt": [
|
|
28
|
+
"Help me build a digital human that teaches conversational English.",
|
|
29
|
+
"Show my PALs and the stock faces I can use.",
|
|
30
|
+
"Help me test one of my PALs and open its video preview."
|
|
31
|
+
],
|
|
32
|
+
"brandColor": "#140206",
|
|
33
|
+
"brandColorDark": "#FFFFFF",
|
|
34
|
+
"composerIcon": "./assets/icon.svg",
|
|
35
|
+
"composerIconDark": "./assets/icon-dark.svg",
|
|
36
|
+
"logo": "./assets/icon.svg",
|
|
37
|
+
"logoDark": "./assets/icon-dark.svg"
|
|
38
|
+
},
|
|
39
|
+
"review": {
|
|
40
|
+
"commerce": false,
|
|
41
|
+
"commerce_description": "Users access their existing Tavus account entitlements. This plugin does not sell PALs, subscriptions, or credits, collect payment details, or initiate checkout.",
|
|
42
|
+
"test_cases": {
|
|
43
|
+
"positive": [
|
|
44
|
+
{
|
|
45
|
+
"description": "List account PALs and available stock faces.",
|
|
46
|
+
"prompt": "Show my PALs and two stock faces.",
|
|
47
|
+
"tools_triggered": "tavus_pal_list, tavus_face_list",
|
|
48
|
+
"expected_behavior": "Return PALs accessible to the connected account and two stock faces with their IDs. State when the PAL list is paginated."
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"description": "Build and verify a museum guide. This creates a disposable PAL for the remaining cases and consumes account usage.",
|
|
52
|
+
"prompt": "Help me build a museum guide that explains paintings briefly.",
|
|
53
|
+
"tools_triggered": "tavus_pal_build_and_verify",
|
|
54
|
+
"expected_behavior": "Clarify missing requirements, build the PAL, and report its saved ID and the actual verification verdict. Report a timeout or failed verification honestly; do not claim the checks passed. Use this PAL for subsequent cases."
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"description": "Rename only the PAL created in the previous case and verify persistence.",
|
|
58
|
+
"prompt": "Rename that PAL to Museum Guide.",
|
|
59
|
+
"tools_triggered": "tavus_patch_pal, tavus_pal_get",
|
|
60
|
+
"expected_behavior": "Rename the selected test PAL to Museum Guide, then read it back to confirm the saved name. Leave unrelated PALs unchanged."
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"description": "Run a text conversation with the test PAL and end that conversation.",
|
|
64
|
+
"prompt": "Test my guide by asking what Impressionism is, then end the test.",
|
|
65
|
+
"tools_triggered": "tavus_chat_start, tavus_chat_turn, tavus_chat_end",
|
|
66
|
+
"expected_behavior": "Ask the test PAL what Impressionism is, return its actual reply, and end only the test conversation. Do not substitute an answer generated by the host assistant."
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"description": "Create a browser video preview for the test PAL.",
|
|
70
|
+
"prompt": "Open a video preview of that guide.",
|
|
71
|
+
"tools_triggered": "tavus_pal_preview",
|
|
72
|
+
"expected_behavior": "Return the real conversation URL and explain that it opens a video call in the browser. Do not claim that a live audio or video exchange occurred merely because a URL was created."
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
"negative": [
|
|
76
|
+
{
|
|
77
|
+
"description": "PAL sales and payment collection are outside this plugin.",
|
|
78
|
+
"prompt": "Sell my PAL for $10 and charge the buyer's card.",
|
|
79
|
+
"expected_behavior": "Explain that PAL sales, card collection, and checkout are unsupported. Do not create a sale, payment, checkout link, or ownership transfer, and do not route the request to another payment integration."
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"description": "A private PAL outside the connected account is not authorized.",
|
|
83
|
+
"prompt": "Copy a private PAL from an account I cannot access.",
|
|
84
|
+
"expected_behavior": "Refuse unauthorized access or copying. Do not disclose or change another account's resources. If an API authorization failure is encountered, honor it."
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"description": "Hosted MCP cannot read local paths or train a replica.",
|
|
88
|
+
"prompt": "Train a new replica from this local video file.",
|
|
89
|
+
"expected_behavior": "Explain the local-file and replica-training limitations. Do not claim training has begun and do not create a PAL as a substitute for replica training."
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
"publication": {
|
|
95
|
+
"release_notes": "Initial Tavus PAL package for the production hosted MCP server. Includes browser OAuth sign-in, PAL creation and editing, stock faces and voices, text conversation tests, and browser video-preview links. No payments, PAL sales, ownership transfers, custom replica training, or embedded video UI."
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -61,13 +61,19 @@ def default_deployment_name(
|
|
|
61
61
|
|
|
62
62
|
|
|
63
63
|
def page_tools(attached_tools: Any) -> list[PageTool]:
|
|
64
|
-
"""Pick the tools
|
|
64
|
+
"""Pick the tools the page must handle out of a ``/pals/{id}/tools`` response.
|
|
65
|
+
|
|
66
|
+
Built-in system tools such as ``end_call`` also deliver to the page, but the
|
|
67
|
+
embed and widget handle them, so the page writes no handler for them.
|
|
68
|
+
"""
|
|
65
69
|
tools: list[PageTool] = []
|
|
66
70
|
for item in _list_data(attached_tools):
|
|
67
71
|
delivery = item.get("delivery")
|
|
68
72
|
name = item.get("name")
|
|
69
73
|
if not isinstance(delivery, dict) or delivery.get("app_message") is not True:
|
|
70
74
|
continue
|
|
75
|
+
if item.get("is_system_tool") is True:
|
|
76
|
+
continue
|
|
71
77
|
if not isinstance(name, str) or not name:
|
|
72
78
|
continue
|
|
73
79
|
parameters = item.get("parameters")
|
|
@@ -107,6 +107,14 @@ def test_page_tools_keeps_only_tools_delivered_to_the_page() -> None:
|
|
|
107
107
|
"parameters": {"properties": {"product_id": {}, "qty": {}}},
|
|
108
108
|
},
|
|
109
109
|
{"name": "lookup_order", "delivery": {"api": {"url": "https://x"}}},
|
|
110
|
+
# The built-in end_call, as the staging API returns it: the embed handles it.
|
|
111
|
+
{
|
|
112
|
+
"name": "end_call",
|
|
113
|
+
"tool_id": "end_call",
|
|
114
|
+
"is_system_tool": True,
|
|
115
|
+
"delivery": {"app_message": True},
|
|
116
|
+
"parameters": {"properties": {"reason": {}}},
|
|
117
|
+
},
|
|
110
118
|
]
|
|
111
119
|
}
|
|
112
120
|
assert page_tools(response) == [ADD_TO_CART]
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tavus/tavus-mcp-worker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@tavus/tavus-mcp-worker",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.9.0",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
12
12
|
"agents": "^0.12.2",
|
|
@@ -55,7 +55,12 @@ export default {
|
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
const server = createTavusMcpServer(
|
|
58
|
+
const server = createTavusMcpServer(
|
|
59
|
+
env,
|
|
60
|
+
apiKey,
|
|
61
|
+
new Headers(oauthUnauthorizedHeaders(request, MCP_ROUTE)).get("WWW-Authenticate")!,
|
|
62
|
+
url.origin,
|
|
63
|
+
);
|
|
59
64
|
return createMcpHandler(server, { route: MCP_ROUTE })(request, env, ctx);
|
|
60
65
|
},
|
|
61
66
|
} satisfies ExportedHandler<Env>;
|
|
@@ -82,12 +82,26 @@ export async function handleOAuthRequest(
|
|
|
82
82
|
method: request.method,
|
|
83
83
|
path: new URL(request.url).pathname,
|
|
84
84
|
status: response.status,
|
|
85
|
+
// The OAuth error code (never a description or token), so a client's
|
|
86
|
+
// failure can be diagnosed from the logs.
|
|
87
|
+
...(response.status >= 400 ? { error: await oauthErrorCode(response) } : {}),
|
|
85
88
|
}),
|
|
86
89
|
);
|
|
87
90
|
}
|
|
88
91
|
return response;
|
|
89
92
|
}
|
|
90
93
|
|
|
94
|
+
async function oauthErrorCode(response: Response): Promise<string | undefined> {
|
|
95
|
+
try {
|
|
96
|
+
const body: unknown = await response.clone().json();
|
|
97
|
+
return typeof body === "object" && body !== null && "error" in body && typeof body.error === "string"
|
|
98
|
+
? body.error
|
|
99
|
+
: undefined;
|
|
100
|
+
} catch {
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
91
105
|
async function routeOAuthRequest(
|
|
92
106
|
request: Request,
|
|
93
107
|
env: Env,
|
|
@@ -381,15 +395,10 @@ async function handleAuthorize(request: Request, env: Env, mcpRoute: string): Pr
|
|
|
381
395
|
return redirectOAuthError(redirectUri, state, "invalid_request", "code_challenge is required.");
|
|
382
396
|
}
|
|
383
397
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
state,
|
|
389
|
-
"invalid_request",
|
|
390
|
-
"resource is required.",
|
|
391
|
-
);
|
|
392
|
-
}
|
|
398
|
+
// RFC 8707 makes resource optional, and some clients leave it out (Bolt sends
|
|
399
|
+
// none). This server protects one resource, so a missing value means it; a
|
|
400
|
+
// present one must match it. Either way the code is bound to it.
|
|
401
|
+
const resource = params.get("resource") ?? resourceUrl(request, mcpRoute);
|
|
393
402
|
if (!isAllowedResource(resource, resourceUrl(request, mcpRoute))) {
|
|
394
403
|
return redirectOAuthError(
|
|
395
404
|
redirectUri,
|
|
@@ -571,7 +580,6 @@ async function handleToken(request: Request, env: Env, mcpRoute: string): Promis
|
|
|
571
580
|
if (!clientId) return oauthError("invalid_client", "Invalid client credentials.", 401, corsHeaders());
|
|
572
581
|
const redirectUri = params.get("redirect_uri");
|
|
573
582
|
const codeVerifier = params.get("code_verifier");
|
|
574
|
-
const resource = params.get("resource");
|
|
575
583
|
|
|
576
584
|
const signingSecret = oauthSigningSecret(env);
|
|
577
585
|
const authorizationCode = signingSecret
|
|
@@ -594,7 +602,11 @@ async function handleToken(request: Request, env: Env, mcpRoute: string): Promis
|
|
|
594
602
|
if (!codeVerifier || (await pkceChallenge(codeVerifier)) !== authorizationCode.code_challenge) {
|
|
595
603
|
return oauthError("invalid_grant", "PKCE verification failed.", 400, corsHeaders());
|
|
596
604
|
}
|
|
597
|
-
|
|
605
|
+
// RFC 8707 makes resource optional here, and some clients leave it out (v0's
|
|
606
|
+
// OAuth library, arctic, sends none). /authorize bound a validated resource to
|
|
607
|
+
// the code, so a missing value means the code's; a present one must match.
|
|
608
|
+
const resource = params.get("resource") ?? authorizationCode.resource;
|
|
609
|
+
if (resource !== authorizationCode.resource) {
|
|
598
610
|
return oauthError("invalid_target", "resource does not match the authorization code.", 400, corsHeaders());
|
|
599
611
|
}
|
|
600
612
|
if (!isAllowedResource(resource, resourceUrl(request, mcpRoute))) {
|
|
@@ -658,7 +670,9 @@ async function handleRefresh(request: Request, params: URLSearchParams, env: Env
|
|
|
658
670
|
const parsed = parseSessionToken(params.get("refresh_token") ?? "");
|
|
659
671
|
if (!parsed || parsed.kind !== "refresh") return oauthError("invalid_grant", "Invalid refresh token.", 400, corsHeaders());
|
|
660
672
|
const resource = resourceUrl(request, mcpRoute);
|
|
661
|
-
|
|
673
|
+
// As in the code exchange, a refresh without resource keeps the session's own.
|
|
674
|
+
const requestedResource = params.get("resource");
|
|
675
|
+
if (requestedResource !== null && !isAllowedResource(requestedResource, resource)) {
|
|
662
676
|
return oauthError("invalid_target", "resource does not match this MCP server.", 400, corsHeaders());
|
|
663
677
|
}
|
|
664
678
|
if (params.has("scope") && params.get("scope") !== SCOPE_UMBRELLA) {
|
|
@@ -837,20 +851,38 @@ const KNOWN_REMOTE_CLIENTS: ReadonlyArray<{
|
|
|
837
851
|
{ name: "Claude", hosts: ["claude.ai", "claude.com"], path: /^\/api\/mcp\/auth_callback$/ },
|
|
838
852
|
{ name: "ChatGPT", hosts: ["chatgpt.com"], path: /^\/connector\/oauth\/[^/]+$/ },
|
|
839
853
|
// Cursor registers its hosted callback rather than a loopback listener when
|
|
840
|
-
// its loopback-redirect flag is off.
|
|
854
|
+
// its loopback-redirect flag is off. https://cursor.com/docs/mcp lists only the
|
|
855
|
+
// www host; the bare host is kept from #49 because it is live in production.
|
|
841
856
|
{
|
|
842
857
|
name: "Cursor",
|
|
843
858
|
hosts: ["cursor.com", "www.cursor.com"],
|
|
844
859
|
path: /^\/agents\/mcp\/oauth\/callback$/,
|
|
845
860
|
},
|
|
846
|
-
//
|
|
861
|
+
// https://code.visualstudio.com/api/extension-guides/ai/mcp#authorization lists
|
|
862
|
+
// https://vscode.dev/redirect and http://127.0.0.1:33418. Its registrations also
|
|
863
|
+
// send the Insiders redirect in the same request, per
|
|
864
|
+
// https://github.com/microsoft/vscode/issues/278512, so both hosts stay.
|
|
847
865
|
{ name: "VS Code", hosts: ["vscode.dev", "insiders.vscode.dev"], path: /^\/redirect$/ },
|
|
848
|
-
//
|
|
849
|
-
// the
|
|
866
|
+
// Both callbacks are in v0's client metadata document,
|
|
867
|
+
// https://v0.app/api/chat/integrations/oauth/client-metadata.json; the second is
|
|
868
|
+
// also in https://v0.app/docs/api/platform/guides/oauth-mcp-servers.
|
|
850
869
|
{ name: "v0", hosts: ["v0.app"], path: /^\/api\/chat\/integrations\/oauth\/callback$/ },
|
|
851
870
|
{ name: "v0", hosts: ["api.v0.dev"], path: /^\/v1\/mcp-servers\/oauth\/callback$/ },
|
|
852
|
-
|
|
853
|
-
//
|
|
871
|
+
// Lovable does not document its callback (https://docs.lovable.dev/integrations/custom-mcp).
|
|
872
|
+
// This one is captured from Lovable's own registration on staging
|
|
873
|
+
// (oauth_register_rejected).
|
|
874
|
+
{
|
|
875
|
+
name: "Lovable",
|
|
876
|
+
hosts: ["api.lovable.dev"],
|
|
877
|
+
path: /^\/workspaces\/connectors\/mcp\/oauth\/callback$/,
|
|
878
|
+
},
|
|
879
|
+
// Bolt does not document its callback (https://support.bolt.new/building/using-bolt/connect-mcp).
|
|
880
|
+
// This one is captured from Bolt's own registration on staging
|
|
881
|
+
// (oauth_register_payload: client_name "Bolt", token_endpoint_auth_method "none").
|
|
882
|
+
{ name: "Bolt", hosts: ["bolt.new"], path: /^\/mcp\/oauth\/callback$/ },
|
|
883
|
+
// Replit does not document its MCP callback (https://docs.replit.com/replitai/mcp/overview).
|
|
884
|
+
// This one is captured from Replit's own registration on staging
|
|
885
|
+
// (oauth_register_payload: client_name "Replit MCP Connector", auth method "none").
|
|
854
886
|
{ name: "Replit", hosts: ["replit.com"], path: /^\/connectors\/oauth\/callback$/ },
|
|
855
887
|
];
|
|
856
888
|
|
|
@@ -506,12 +506,33 @@ export function authorizationError(env: Env): { error: string; status: number }
|
|
|
506
506
|
return null;
|
|
507
507
|
}
|
|
508
508
|
|
|
509
|
-
export function createTavusMcpServer(
|
|
509
|
+
export function createTavusMcpServer(
|
|
510
|
+
env: Env,
|
|
511
|
+
apiKey?: string,
|
|
512
|
+
authChallenge?: string,
|
|
513
|
+
serverOrigin = "https://mcp.tavus.io",
|
|
514
|
+
): McpServer {
|
|
510
515
|
const config = resolveConfig(env, apiKey);
|
|
511
516
|
const client = new TavusApiClient(config);
|
|
512
517
|
const server = new McpServer({
|
|
513
518
|
name: "tavus",
|
|
519
|
+
title: "Tavus",
|
|
514
520
|
version: VERSION,
|
|
521
|
+
websiteUrl: "https://www.tavus.io",
|
|
522
|
+
icons: [
|
|
523
|
+
{
|
|
524
|
+
src: new URL("/icon.svg", serverOrigin).href,
|
|
525
|
+
mimeType: "image/svg+xml",
|
|
526
|
+
sizes: ["any"],
|
|
527
|
+
theme: "light",
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
src: new URL("/icon-dark.svg", serverOrigin).href,
|
|
531
|
+
mimeType: "image/svg+xml",
|
|
532
|
+
sizes: ["any"],
|
|
533
|
+
theme: "dark",
|
|
534
|
+
},
|
|
535
|
+
],
|
|
515
536
|
});
|
|
516
537
|
addToolIntent(server, authChallenge);
|
|
517
538
|
|
|
@@ -3423,12 +3444,22 @@ export function defaultDeploymentName(
|
|
|
3423
3444
|
return `${base} ${n}`;
|
|
3424
3445
|
}
|
|
3425
3446
|
|
|
3426
|
-
/**
|
|
3447
|
+
/**
|
|
3448
|
+
* The PAL tools the page must handle (delivery.app_message). Built-in system
|
|
3449
|
+
* tools such as end_call also deliver to the page, but the embed and widget
|
|
3450
|
+
* handle them, so the page writes no handler for them.
|
|
3451
|
+
*/
|
|
3427
3452
|
export function pageTools(attachedTools: unknown): PageTool[] {
|
|
3428
3453
|
return arrayData(attachedTools).flatMap((value) => {
|
|
3429
3454
|
const item = objectValue(value);
|
|
3430
3455
|
const delivery = objectValue(item?.delivery);
|
|
3431
|
-
if (
|
|
3456
|
+
if (
|
|
3457
|
+
!item ||
|
|
3458
|
+
delivery?.app_message !== true ||
|
|
3459
|
+
item.is_system_tool === true ||
|
|
3460
|
+
typeof item.name !== "string" ||
|
|
3461
|
+
!item.name
|
|
3462
|
+
) {
|
|
3432
3463
|
return [];
|
|
3433
3464
|
}
|
|
3434
3465
|
const properties = objectValue(objectValue(item.parameters)?.properties);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const VERSION = "0.9.0"; // x-release-please-version
|
|
@@ -42,7 +42,7 @@ async function register(method: "none" | "client_secret_post" | "client_secret_b
|
|
|
42
42
|
return clientSchema.parse(await response.json());
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
async function authorize(client: Client, key = "account-a-key") {
|
|
45
|
+
async function authorize(client: Client, key = "account-a-key", { sendResource = true } = {}) {
|
|
46
46
|
const challenge = Buffer.from(
|
|
47
47
|
await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier)),
|
|
48
48
|
).toString("base64url");
|
|
@@ -54,7 +54,7 @@ async function authorize(client: Client, key = "account-a-key") {
|
|
|
54
54
|
state: crypto.randomUUID(),
|
|
55
55
|
code_challenge: challenge,
|
|
56
56
|
code_challenge_method: "S256",
|
|
57
|
-
resource,
|
|
57
|
+
...(sendResource ? { resource } : {}),
|
|
58
58
|
scope: "tavus",
|
|
59
59
|
})}`,
|
|
60
60
|
{ redirect: "manual" },
|
|
@@ -214,6 +214,52 @@ describe("hosted OAuth sessions with real Cloudflare storage", () => {
|
|
|
214
214
|
expect((await exchange(client, code)).status).toBe(400);
|
|
215
215
|
});
|
|
216
216
|
|
|
217
|
+
it("binds an authorize request without resource to this server, as Bolt sends it", async () => {
|
|
218
|
+
// Bolt's /authorize request carries no resource parameter. This server
|
|
219
|
+
// protects one resource, so the code is bound to it.
|
|
220
|
+
const client = await register();
|
|
221
|
+
const code = await authorize(client, "account-a-key", { sendResource: false });
|
|
222
|
+
const exchanged = await form("/token", {
|
|
223
|
+
grant_type: "authorization_code",
|
|
224
|
+
...credentials(client),
|
|
225
|
+
code,
|
|
226
|
+
redirect_uri: redirectUri,
|
|
227
|
+
code_verifier: verifier,
|
|
228
|
+
});
|
|
229
|
+
expect(exchanged.status).toBe(200);
|
|
230
|
+
const tokens = tokenSchema.parse(await exchanged.json());
|
|
231
|
+
const listed = await rpc(tokens.access_token);
|
|
232
|
+
expect(listed.status).toBe(200);
|
|
233
|
+
await listed.text();
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("accepts a code exchange and refresh without resource, as v0's OAuth client (arctic) sends them", async () => {
|
|
237
|
+
// arctic's token and refresh requests carry no resource parameter. The code
|
|
238
|
+
// was bound to this server's resource at /authorize, and the session keeps it.
|
|
239
|
+
const client = await register();
|
|
240
|
+
const code = await authorize(client);
|
|
241
|
+
const exchanged = await form("/token", {
|
|
242
|
+
grant_type: "authorization_code",
|
|
243
|
+
...credentials(client),
|
|
244
|
+
code,
|
|
245
|
+
redirect_uri: redirectUri,
|
|
246
|
+
code_verifier: verifier,
|
|
247
|
+
});
|
|
248
|
+
expect(exchanged.status).toBe(200);
|
|
249
|
+
const tokens = tokenSchema.parse(await exchanged.json());
|
|
250
|
+
const listed = await rpc(tokens.access_token);
|
|
251
|
+
expect(listed.status).toBe(200);
|
|
252
|
+
await listed.text();
|
|
253
|
+
|
|
254
|
+
const refreshed = await form("/token", {
|
|
255
|
+
grant_type: "refresh_token",
|
|
256
|
+
...credentials(client),
|
|
257
|
+
refresh_token: tokens.refresh_token,
|
|
258
|
+
});
|
|
259
|
+
expect(refreshed.status).toBe(200);
|
|
260
|
+
await refreshed.text();
|
|
261
|
+
});
|
|
262
|
+
|
|
217
263
|
it("keeps the session when concurrent refreshes present the same refresh token", async () => {
|
|
218
264
|
const { client, tokens } = await connect();
|
|
219
265
|
const results = await Promise.all([
|
|
@@ -842,10 +842,24 @@ describe("Cloudflare Tavus MCP helpers", () => {
|
|
|
842
842
|
// VS Code sends its web redirect and its loopback listener in one request.
|
|
843
843
|
["VS Code", ["http://127.0.0.1:33418", "https://vscode.dev/redirect"]],
|
|
844
844
|
["VS Code", ["https://insiders.vscode.dev/redirect"]],
|
|
845
|
+
// VS Code registers both web redirects and its loopback listener together.
|
|
846
|
+
[
|
|
847
|
+
"VS Code",
|
|
848
|
+
[
|
|
849
|
+
"https://insiders.vscode.dev/redirect",
|
|
850
|
+
"https://vscode.dev/redirect",
|
|
851
|
+
"http://127.0.0.1/",
|
|
852
|
+
"http://127.0.0.1:33418/",
|
|
853
|
+
],
|
|
854
|
+
],
|
|
845
855
|
// Both callbacks from v0's client metadata document.
|
|
846
856
|
["v0", ["https://v0.app/api/chat/integrations/oauth/callback"]],
|
|
847
857
|
["v0", ["https://api.v0.dev/v1/mcp-servers/oauth/callback"]],
|
|
848
|
-
|
|
858
|
+
// Lovable's registration, captured from the staging Worker logs.
|
|
859
|
+
["Lovable", ["https://api.lovable.dev/workspaces/connectors/mcp/oauth/callback"]],
|
|
860
|
+
// Bolt's registration, captured from the staging Worker logs.
|
|
861
|
+
["Bolt", ["https://bolt.new/mcp/oauth/callback"]],
|
|
862
|
+
// Replit's registration, captured from the staging Worker logs.
|
|
849
863
|
["Replit", ["https://replit.com/connectors/oauth/callback"]],
|
|
850
864
|
])("allows %s DCR callbacks and canonicalizes the client name", async (name, redirectUris) => {
|
|
851
865
|
const response = await registerClient(redirectUris);
|
|
@@ -859,9 +873,12 @@ describe("Cloudflare Tavus MCP helpers", () => {
|
|
|
859
873
|
|
|
860
874
|
it.each([
|
|
861
875
|
["a longer path", "https://vscode.dev/redirect/extra"],
|
|
862
|
-
["a query string", "https://lovable.dev/oauth/callback?next=https://attacker.example"],
|
|
863
|
-
["plain HTTP", "http://lovable.dev/oauth/callback"],
|
|
864
|
-
["a lookalike host", "https://lovable.dev.attacker.example/oauth/callback"],
|
|
876
|
+
["a query string", "https://api.lovable.dev/workspaces/connectors/mcp/oauth/callback?next=https://attacker.example"],
|
|
877
|
+
["plain HTTP", "http://api.lovable.dev/workspaces/connectors/mcp/oauth/callback"],
|
|
878
|
+
["a lookalike host", "https://api.lovable.dev.attacker.example/workspaces/connectors/mcp/oauth/callback"],
|
|
879
|
+
// The callback third-party integrations reported; Lovable does not register it.
|
|
880
|
+
["the unverified Lovable callback", "https://lovable.dev/oauth/callback"],
|
|
881
|
+
["a Bolt subdomain", "https://evil.bolt.new/mcp/oauth/callback"],
|
|
865
882
|
["a subdomain", "https://evil.v0.app/api/chat/integrations/oauth/callback"],
|
|
866
883
|
])("rejects a known client's callback with %s", async (_label, redirectUri) => {
|
|
867
884
|
expect((await registerClient([redirectUri]))?.status).toBe(400);
|
|
@@ -870,13 +887,13 @@ describe("Cloudflare Tavus MCP helpers", () => {
|
|
|
870
887
|
it("rejects callbacks from two different clients in one registration", async () => {
|
|
871
888
|
const response = await registerClient([
|
|
872
889
|
"https://claude.ai/api/mcp/auth_callback",
|
|
873
|
-
"https://lovable.dev/oauth/callback",
|
|
890
|
+
"https://api.lovable.dev/workspaces/connectors/mcp/oauth/callback",
|
|
874
891
|
]);
|
|
875
892
|
|
|
876
893
|
expect(response?.status).toBe(400);
|
|
877
894
|
await expect(response?.json()).resolves.toMatchObject({
|
|
878
895
|
error: "invalid_client_metadata",
|
|
879
|
-
error_description: expect.stringContaining("Claude, ChatGPT, Cursor, VS Code, v0, Lovable, Replit"),
|
|
896
|
+
error_description: expect.stringContaining("Claude, ChatGPT, Cursor, VS Code, v0, Lovable, Bolt, Replit"),
|
|
880
897
|
});
|
|
881
898
|
});
|
|
882
899
|
|
|
@@ -1415,6 +1432,14 @@ describe("hosted deployment tools", () => {
|
|
|
1415
1432
|
parameters: { properties: { product_id: {}, qty: {} } },
|
|
1416
1433
|
},
|
|
1417
1434
|
{ name: "lookup_order", delivery: { api: { url: "https://x" } } },
|
|
1435
|
+
// The built-in end_call, as the staging API returns it: the embed handles it.
|
|
1436
|
+
{
|
|
1437
|
+
name: "end_call",
|
|
1438
|
+
tool_id: "end_call",
|
|
1439
|
+
is_system_tool: true,
|
|
1440
|
+
delivery: { app_message: true },
|
|
1441
|
+
parameters: { properties: { reason: {} } },
|
|
1442
|
+
},
|
|
1418
1443
|
],
|
|
1419
1444
|
}),
|
|
1420
1445
|
).toEqual([{ name: "add_to_cart", parameters: ["product_id", "qty"], description: "Add." }]);
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
|
-
"name": "tavus-pal",
|
|
4
|
-
"version": "0.1.0",
|
|
5
|
-
"description": "Create, edit, and test Tavus PALs: digital humans with a face, voice, knowledge, and conversational behavior. Uses the Tavus hosted MCP server and your connected Tavus account.",
|
|
6
|
-
"author": {
|
|
7
|
-
"name": "Tavus",
|
|
8
|
-
"email": "support@tavus.io",
|
|
9
|
-
"url": "https://www.tavus.io"
|
|
10
|
-
},
|
|
11
|
-
"homepage": "https://maker.tavus.io",
|
|
12
|
-
"repository": "https://github.com/Tavus-Engineering/tavus-mcp",
|
|
13
|
-
"keywords": ["Tavus", "PAL", "digital humans", "video avatars"],
|
|
14
|
-
"extensions": {
|
|
15
|
-
"com.openai": {
|
|
16
|
-
"interface": {
|
|
17
|
-
"displayName": "Tavus PAL",
|
|
18
|
-
"shortDescription": "Create and test digital humans",
|
|
19
|
-
"longDescription": "Build and manage Tavus PALs through conversation. Choose available faces and voices, configure behavior and knowledge, run text tests, and open a video preview. Connect your Tavus account to access its resources and existing entitlements. Includes the same tools as the hosted Tavus MCP server. Custom face training, rendered video generation, PAL sales, and ownership transfers are not exposed by this integration.",
|
|
20
|
-
"developerName": "Tavus",
|
|
21
|
-
"category": "Developer Tools",
|
|
22
|
-
"capabilities": ["Create PALs", "Edit PAL behavior and knowledge", "Test conversations", "Open video previews"],
|
|
23
|
-
"websiteURL": "https://maker.tavus.io",
|
|
24
|
-
"supportURL": "https://help.tavus.io/",
|
|
25
|
-
"privacyPolicyURL": "https://www.tavus.io/privacy-policy",
|
|
26
|
-
"termsOfServiceURL": "https://www.tavus.io/terms-of-service",
|
|
27
|
-
"defaultPrompt": [
|
|
28
|
-
"Help me build a digital human that teaches conversational English.",
|
|
29
|
-
"Show my PALs and the stock faces I can use.",
|
|
30
|
-
"Help me test one of my PALs and open its video preview."
|
|
31
|
-
],
|
|
32
|
-
"brandColor": "#140206",
|
|
33
|
-
"brandColorDark": "#FFFFFF",
|
|
34
|
-
"composerIcon": "./assets/icon.svg",
|
|
35
|
-
"composerIconDark": "./assets/icon-dark.svg",
|
|
36
|
-
"logo": "./assets/icon.svg",
|
|
37
|
-
"logoDark": "./assets/icon-dark.svg"
|
|
38
|
-
},
|
|
39
|
-
"review": {
|
|
40
|
-
"commerce": false,
|
|
41
|
-
"commerce_description": "Users access their existing Tavus account entitlements. This plugin does not sell PALs, subscriptions, or credits, collect payment details, or initiate checkout."
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const VERSION = "0.8.0"; // x-release-please-version
|
|
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
|
{tavus_cli-0.8.0 → tavus_cli-0.9.0}/skills/tavus-agentic-pals/references/build-and-verify.md
RENAMED
|
File without changes
|
{tavus_cli-0.8.0 → tavus_cli-0.9.0}/skills/tavus-agentic-pals/references/builder-workflow.md
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
|
|
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
|