tavus-cli 0.9.2__tar.gz → 0.10.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.10.0/.release-please-manifest.json +3 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/CHANGELOG.md +14 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/PKG-INFO +1 -1
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/PROMPT.md +1 -1
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/docs/builder-guidance.md +25 -5
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/packages/tavus/pyproject.toml +2 -2
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/packages/tavus-mcp/pyproject.toml +2 -2
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/plugins/tavus-pal/plugin.json +2 -1
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/pyproject.toml +1 -1
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/skills/tavus-agentic-pals/SKILL.md +6 -4
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/skills/tavus-agentic-pals/references/build-and-verify.md +30 -2
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/__init__.py +1 -1
- tavus_cli-0.10.0/src/tavus_mcp/builder_guidance.json +25 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/uv.lock +1 -1
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/workers/tavus-mcp/package-lock.json +2 -2
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/workers/tavus-mcp/package.json +1 -1
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/workers/tavus-mcp/src/oauth.ts +46 -3
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/workers/tavus-mcp/src/tavus.ts +104 -17
- tavus_cli-0.10.0/workers/tavus-mcp/src/version.ts +1 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/workers/tavus-mcp/test/builder-guidance.test.ts +10 -1
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/workers/tavus-mcp/test/tavus.test.ts +338 -0
- tavus_cli-0.9.2/.release-please-manifest.json +0 -3
- tavus_cli-0.9.2/src/tavus_mcp/builder_guidance.json +0 -21
- tavus_cli-0.9.2/workers/tavus-mcp/src/version.ts +0 -1
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/.agents/plugins/marketplace.json +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/.env.example +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/.github/workflows/ci.yml +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/.github/workflows/deploy-cloudflare-worker.yml +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/.github/workflows/pr-reviews.yml +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/.github/workflows/pypi-cli.yml +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/.github/workflows/tail-cloudflare-worker.yml +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/.github/workflows/validate-staging-deployment.yml +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/.gitignore +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/AGENTS.md +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/README.md +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/docs/chatgpt-plugin.md +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/docs/gemini-enterprise/README.md +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/docs/gemini-enterprise/listing.md +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/docs/gemini-enterprise/test-queries.md +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/docs/usage-observability.md +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/packages/tavus/README.md +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/packages/tavus-mcp/README.md +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/plugins/tavus-pal/assets/icon-dark.svg +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/plugins/tavus-pal/assets/icon.svg +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/plugins/tavus-pal/mcp.json +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/release-please-config.json +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/scripts/__init__.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/scripts/build_chatgpt_plugin.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/scripts/check_release_versions.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/skills/tavus-agentic-pals/agents/openai.yaml +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/skills/tavus-agentic-pals/references/builder-workflow.md +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/skills/tavus-agentic-pals/references/chat-mode.md +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/skills/tavus-agentic-pals/references/examples.md +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/builder_guidance.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/cli/__init__.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/cli/main.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/__init__.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/auth/__init__.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/auth/keyring_store.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/auth/oauth.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/auth/session.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/client/__init__.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/client/http.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/daily_transport.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/env.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/errors.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/pal_urls.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/patch.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/recipes/__init__.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/recipes/build_and_verify.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/recipes/capabilities.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/recipes/carrier_builder.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/recipes/deployments.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/recipes/knowledge.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/recipes/options.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/recipes/quickstart.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/recipes/scaffold_embed.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/recipes/templates.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/recipes/tool_reuse.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/schemas/__init__.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/schemas/deployment.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/schemas/file_manifest.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/schemas/guardrail.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/schemas/objective.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/schemas/pal.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/schemas/pronunciation.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/sdk/schemas/tool.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/src/tavus_mcp/server.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/tests/conftest.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/tests/integration/test_cli.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/tests/integration/test_client.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/tests/integration/test_mcp_server.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/tests/integration/test_oauth.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/tests/unit/test_auth.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/tests/unit/test_build_and_verify.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/tests/unit/test_builder.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/tests/unit/test_capabilities.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/tests/unit/test_daily_transport.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/tests/unit/test_deployments.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/tests/unit/test_env.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/tests/unit/test_guardrail_schema.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/tests/unit/test_objective_schema.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/tests/unit/test_pal_schema.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/tests/unit/test_patch.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/tests/unit/test_pronunciation_schema.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/tests/unit/test_release_versions.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/tests/unit/test_scaffold_embed.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/tests/unit/test_tool_reuse.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/tests/unit/test_tool_schema.py +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/workers/tavus-mcp/.dev.vars.example +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/workers/tavus-mcp/src/index.ts +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/workers/tavus-mcp/src/oauth-grant.ts +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/workers/tavus-mcp/src/oauth-session.ts +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/workers/tavus-mcp/test/oauth-session.test.ts +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/workers/tavus-mcp/test/parity.test.ts +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/workers/tavus-mcp/test/worker-runtime.ts +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/workers/tavus-mcp/tsconfig.json +0 -0
- {tavus_cli-0.9.2 → tavus_cli-0.10.0}/workers/tavus-mcp/wrangler.jsonc +0 -0
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.10.0](https://github.com/Tavus-Engineering/tavus-mcp/compare/v0.9.3...v0.10.0) (2026-10-14)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* route agents to build_and_verify and let it change existing PALs ([#72](https://github.com/Tavus-Engineering/tavus-mcp/issues/72)) ([0a993f9](https://github.com/Tavus-Engineering/tavus-mcp/commit/0a993f96219dde3cc6c27405d6741adb8fc69523))
|
|
9
|
+
|
|
10
|
+
## [0.9.3](https://github.com/Tavus-Engineering/tavus-mcp/compare/v0.9.2...v0.9.3) (2026-10-13)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **worker:** accept any loopback port and legacy MCP client IDs at sign-in ([#70](https://github.com/Tavus-Engineering/tavus-mcp/issues/70)) ([231a42c](https://github.com/Tavus-Engineering/tavus-mcp/commit/231a42c39806646c21ae07af3330927dbba5ea4a))
|
|
16
|
+
|
|
3
17
|
## [0.9.2](https://github.com/Tavus-Engineering/tavus-mcp/compare/v0.9.1...v0.9.2) (2026-10-10)
|
|
4
18
|
|
|
5
19
|
|
|
@@ -37,7 +37,7 @@ How to do the common things properly
|
|
|
37
37
|
These are the contracts the server keeps. Exact parameters and response shapes are in the reference below; the order of operations is here, because that is what tends to get invented.
|
|
38
38
|
|
|
39
39
|
- Building a new PAL. Use `tavus_pal_build_and_verify`; it is the standard path. You run the interview first: ask me plain questions, up to 12, about who the PAL talks to, what a good conversation achieves, the tone, what it must never say or do, what it should know (links to documents), and what it should do in my app. Ask only what my description leaves open. Pass my description as `prompt`, the questions and my answers as `interview`, and your summary of each part as `briefs`. The call takes one to three minutes. It drafts the personality, objectives, guardrails and greeting, picks a face unless I asked for one (`face_id`), publishes the PAL, runs test conversations and judges them. Read `verdict.overall` (`pass`, `partial` or `fail`), then the summary and any failing evidence, before you tell me it works. Tools can only be attached once the PAL exists, so the build's test turns run without them and tool checks in the verdict fail; attach the tools, then rerun those turns with chat mode before you tell me the tools work. Give me the `pal_url` it returns; that opens the PAL in the PAL Maker. Use `tavus_pal_create` or `tavus_quickstart` only when I hand you a finished system prompt and ask you to skip the build.
|
|
40
|
-
- Changing an existing PAL.
|
|
40
|
+
- Changing an existing PAL. For a change to what the PAL does or shows, such as a new step, an on-screen scorecard or quiz, web search, or memory, call `tavus_pal_build_and_verify` with its `pal_id`, my request as `prompt`, and only the `briefs` for the parts that should change. Put built-in capabilities in `briefs.skills`. The same PAL Maker drafters edit the PAL and leave the rest alone, then it publishes and tests the PAL. Read `drafted` and `builtin_changes` in the result to see what changed. For an exact edit, such as a new greeting, call `tavus_pal_get` and `tavus_describe_pal_options` first, and take ids and patch paths from what they return. Make the smallest change with `tavus_patch_pal` (JSON Patch operations such as `replace` on `/greeting`), or use the dedicated tools for capabilities, Knowledge and tools. A change applies to every conversation the PAL starts from then on, including on my live site, so tell me what will change and get a yes before changing a PAL that is already in use.
|
|
41
41
|
- Giving the PAL a tool. Check `tavus_tool_list` first and attach an existing tool when its name, parameters and delivery fit. When you create a new one that overlaps an existing tool, tell me which tool you saw and why it does not fit. The tool's `description` is what tells the PAL when to call it, so write it as an instruction. `delivery` decides where the call goes: `{ "app_message": true }` (the default) sends it to the page running the conversation, and `delivery.api` makes Tavus call a backend URL. Then write the handler in this repo with the exact tool name and parameter schema, so the two halves match. On a page that uses `<tavus-embed>` or `<tavus-widget>`, the call arrives as `tavus:tool-call` `{ name, arguments, tool_call_id }` with `arguments` as a JSON string, and the page replies with `sendMessage({ event_type: "conversation.tool_result", properties: { tool_call_id, output, status: "success" } })`; the host communication page below has the full contract. A tool that is not attached to the PAL never fires, however the code is wired.
|
|
42
42
|
- Giving the PAL knowledge. Create a Knowledge document from a URL anyone can reach with `tavus_document_create`, then attach it with `tavus_pal_knowledge_add`. The hosted server cannot read files on my machine; for a local file, ask me to upload it in the PAL Maker's Knowledge section or to host it at a URL. Before you tell me what a document says, read what Tavus indexed with `tavus_document_chunks`.
|
|
43
43
|
- Testing by text. `tavus_chat_start` starts a text-only conversation with the PAL, `tavus_chat_turn` sends one message and waits for the reply, and `tavus_chat_end` ends it. Always end the conversations you start. The first turn is slower while the conversation starts up. Replies are not deterministic, so judge behaviour (did it ask for the order number, did it refuse the pricing promise) rather than exact wording.
|
|
@@ -17,12 +17,32 @@ Portal-only tool names, UI instructions, or capabilities this server cannot
|
|
|
17
17
|
configure. In particular, the hosted attach tool takes `capability`, while the
|
|
18
18
|
local tool takes `capability_id`.
|
|
19
19
|
|
|
20
|
+
## Instruction order and length
|
|
21
|
+
|
|
22
|
+
Some clients keep only the first 4,096 characters of the server instructions.
|
|
23
|
+
Claude Code is one of them, and it also defers MCP tools, so an agent often
|
|
24
|
+
picks a tool from the instructions and tool names alone. Keep the routing rules
|
|
25
|
+
first and the whole set under 4,096 characters; the Worker guidance test fails
|
|
26
|
+
when either rule breaks.
|
|
27
|
+
|
|
28
|
+
- `instructions[0]` routes new builds to `tavus_pal_build_and_verify`. Both
|
|
29
|
+
servers send it first.
|
|
30
|
+
- `hosted_instructions` routes changes to existing PALs through
|
|
31
|
+
`tavus_pal_build_and_verify` with `pal_id`. Only the hosted Worker has that
|
|
32
|
+
parameter, so it inserts these right after `instructions[0]`.
|
|
33
|
+
- The remaining `instructions` are the capability rules.
|
|
34
|
+
|
|
20
35
|
## Behavior checks
|
|
21
36
|
|
|
22
|
-
Use these prompts to evaluate
|
|
37
|
+
Use these prompts to evaluate tool selection with an MCP client:
|
|
23
38
|
|
|
24
39
|
| Request | Expected behavior |
|
|
25
40
|
| --- | --- |
|
|
41
|
+
| Build a PAL that shows trainees a scorecard after each roleplay. | Call `tavus_pal_build_and_verify`, with the scorecard in `briefs.skills` on the hosted connector. Do not call `tavus_pal_create`. |
|
|
42
|
+
| Here is the system prompt I drafted; set it up so trainees see a scorecard. | Same as above. A drafted prompt is a brief, not a request to skip the build. |
|
|
43
|
+
| Add an on-screen scorecard to my PAL p123. | Hosted: `tavus_pal_build_and_verify` with `pal_id` and `briefs.skills`. Local: attach Magic Canvas. |
|
|
44
|
+
| Change the greeting of my PAL p123 to "Hi". | `tavus_patch_pal` on `/greeting`. |
|
|
45
|
+
| Create this PAL from my finished prompt and skip the build. | `tavus_pal_create`. |
|
|
26
46
|
| Show a training scorecard after each exercise. | Attach Magic Canvas with `text` and/or `chart` and usage guidance in the same call. Do not create `record_training_scorecard`. |
|
|
27
47
|
| Collect an email address on screen. We have no backend yet. | Attach Magic Canvas with `input`. Explain that delivery can be connected later. Do not invent an endpoint. |
|
|
28
48
|
| Show the score and save it to our existing API. | Configure Magic Canvas for display and separately reuse or create a tool for the verified API. |
|
|
@@ -33,9 +53,9 @@ Use these prompts to evaluate capability selection with an MCP client:
|
|
|
33
53
|
| Remember returning visitors. | Attach Memory and add a first-visit fallback in the prompt and greeting. |
|
|
34
54
|
| Do not show anything on screen. | Respect the decline; do not attach Magic Canvas. |
|
|
35
55
|
|
|
36
|
-
The automated tests verify that clients receive the
|
|
37
|
-
the scorecard configuration travels through the MCP tools
|
|
38
|
-
back, using a mocked API. They do not evaluate a model's
|
|
39
|
-
live rendering. For a live evaluation, record the model's tool calls, read back
|
|
56
|
+
The automated tests verify that clients receive the guidance in the right order
|
|
57
|
+
and length, and that the scorecard configuration travels through the MCP tools
|
|
58
|
+
to the skills API and back, using a mocked API. They do not evaluate a model's
|
|
59
|
+
tool choice or prove live rendering. For a live evaluation, record the model's tool calls, read back
|
|
40
60
|
the saved capability, and inspect the canvas during a call before claiming the
|
|
41
61
|
scorecard displayed.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tavus"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.10.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.10.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.10.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.10.0", # x-release-please-version
|
|
9
9
|
]
|
|
10
10
|
|
|
11
11
|
[project.urls]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "tavus-pal",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.1",
|
|
5
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
6
|
"author": {
|
|
7
7
|
"name": "Tavus",
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"logoDark": "./assets/icon-dark.svg"
|
|
38
38
|
},
|
|
39
39
|
"review": {
|
|
40
|
+
"demo_recording_url": "https://tavus-mcp-review.pages.dev/tavus-openai-review-demo-2026-09-10-19f4128cd7a5cbc4.mp4",
|
|
40
41
|
"commerce": false,
|
|
41
42
|
"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
|
"test_cases": {
|
|
@@ -220,10 +220,12 @@ Autonomous build + verify:
|
|
|
220
220
|
persona, joins its Daily room, executes its tool calls, publishes, selects a
|
|
221
221
|
face, smoke-tests the PAL over Daily, and returns a judge verdict.
|
|
222
222
|
- Hosted connector: `tavus_pal_build_and_verify(prompt, interview?, briefs?,
|
|
223
|
-
face_id?, pal_name?, probes?)` requires the calling agent to
|
|
224
|
-
creator first. It records that transcript, runs the portal's
|
|
225
|
-
drafters, publishes, selects a face, smoke-tests through the Tavus
|
|
226
|
-
bridge, and returns a judge verdict.
|
|
223
|
+
face_id?, pal_name?, probes?, pal_id?)` requires the calling agent to
|
|
224
|
+
interview the creator first. It records that transcript, runs the portal's
|
|
225
|
+
artifact drafters, publishes, selects a face, smoke-tests through the Tavus
|
|
226
|
+
HTTP bridge, and returns a judge verdict. With `pal_id`, it changes that PAL
|
|
227
|
+
instead: only the briefed drafters run, and the PAL keeps its face. Put
|
|
228
|
+
built-in capabilities such as an on-screen scorecard in `briefs.skills`.
|
|
227
229
|
|
|
228
230
|
Pass `probes` on either surface to pin exact validation prompts; otherwise Tavus
|
|
229
231
|
generates them from the PAL spec. Neither path needs a browser or a client-side
|
{tavus_cli-0.9.2 → tavus_cli-0.10.0}/skills/tavus-agentic-pals/references/build-and-verify.md
RENAMED
|
@@ -43,9 +43,28 @@ tavus_pal_build_and_verify(
|
|
|
43
43
|
face_id?, # optional override; RQH selects one when omitted
|
|
44
44
|
pal_name?, # optional explicit name
|
|
45
45
|
probes?, # optional exact validation prompts; generated when omitted
|
|
46
|
+
pal_id?, # optional existing PAL to change instead of building one
|
|
46
47
|
)
|
|
47
48
|
```
|
|
48
49
|
|
|
50
|
+
### Changing an existing PAL (hosted connector)
|
|
51
|
+
|
|
52
|
+
Pass `pal_id` to run the same drafters on an existing PAL. Put the requested
|
|
53
|
+
change in `prompt` and fill only the `briefs` for the parts that should
|
|
54
|
+
change; at least one brief is required. The other parts of the PAL are left
|
|
55
|
+
as they are, and the PAL keeps its face unless `face_id` is given.
|
|
56
|
+
|
|
57
|
+
Use `briefs.skills` for built-in capabilities: on-screen scorecards, quizzes,
|
|
58
|
+
forms, and charts (Magic Canvas), web search, memory, and slide decks. The
|
|
59
|
+
skills drafter attaches and configures them. Use `briefs.tools` only for an
|
|
60
|
+
external action such as a webhook or API call.
|
|
61
|
+
|
|
62
|
+
When the PAL has been used outside PAL Maker (a deployment, a conferencing
|
|
63
|
+
identity, or a real conversation), RQH opens the session on the PAL's private
|
|
64
|
+
PAL Maker draft. The edit lands there and publish copies it onto the live PAL.
|
|
65
|
+
Publishing also publishes any unpublished PAL Maker edits on that PAL, so get
|
|
66
|
+
the creator's agreement before changing a PAL they are editing in PAL Maker.
|
|
67
|
+
|
|
49
68
|
The lower-level Python recipe retains `model` for backwards compatibility but
|
|
50
69
|
ignores it. The authenticated RQH boundary always selects
|
|
51
70
|
`tavus-pal-maker-gemma-4`; GPT-OSS, the old Gemma thinking model, and inline
|
|
@@ -66,6 +85,9 @@ tool payloads cannot override it.
|
|
|
66
85
|
summary,
|
|
67
86
|
},
|
|
68
87
|
validated,
|
|
88
|
+
drafted?, # hosted path: drafters that returned a change
|
|
89
|
+
builtin_changes?, # hosted path: built-ins attached or detached on the
|
|
90
|
+
# tools drafter's recommendation
|
|
69
91
|
build_transcript?, # local path
|
|
70
92
|
refine_rounds_used?, # local path
|
|
71
93
|
}
|
|
@@ -75,8 +97,14 @@ tool payloads cannot override it.
|
|
|
75
97
|
|
|
76
98
|
- A missing or inaccessible builder persona fails the local path; MCP never
|
|
77
99
|
falls back to a synthetic prompt.
|
|
78
|
-
-
|
|
79
|
-
agent supplies only a sparse set of briefs.
|
|
100
|
+
- A new PAL on the hosted path always drafts the four core artifacts, even
|
|
101
|
+
when the calling agent supplies only a sparse set of briefs. A `pal_id`
|
|
102
|
+
change runs only the briefed drafters.
|
|
103
|
+
- The tools drafter only recommends built-in capabilities. The hosted path
|
|
104
|
+
attaches a recommended Magic Canvas, Web Search, or Memory with default
|
|
105
|
+
settings when the skills drafter has not already attached it. It does not
|
|
106
|
+
attach Slide Presenter, which needs a deck's document ids; that case is
|
|
107
|
+
reported in `builtin_changes`.
|
|
80
108
|
- Each path ends the CVI conversations it starts, including failed smoke tests.
|
|
81
109
|
- Deployment tools are not run by the build-only recipe.
|
|
82
110
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"instructions": [
|
|
3
|
+
"Build a new PAL with tavus_pal_build_and_verify. It runs the PAL Maker pipeline that Charlie uses, then publishes and tests the PAL. Use tavus_pal_create or tavus_quickstart only when the creator hands over a finished system prompt and asks to skip the build.",
|
|
4
|
+
"For an existing PAL, read tavus_pal_capabilities_list first and preserve unrelated settings and attachments. Configure built-ins with tavus_pal_capability_attach, tavus_pal_capability_patch, and tavus_pal_capability_detach.",
|
|
5
|
+
"Magic Canvas (magic_canvas) shows scorecards, running scores, charts, quizzes, forms, contact capture, text, and links during a call. A request to show a training scorecard means enabling Magic Canvas, not creating a tool such as record_training_scorecard. The PAL's own model drives the canvas; a saved tool or API response cannot draw on it. Model-generated quiz questions, scores, dice rolls, and random picks need no custom tool. Saving results to a backend is a separate step; recording data does not display it.",
|
|
6
|
+
"When the creator asks for on-screen interaction, attach magic_canvas with the needed magic_canvas_components and magic_canvas_usage_guidance in the same call: text and/or chart for a scorecard, input for forms, question for quizzes. Put one or two lines of when-to-show choreography in magic_canvas_usage_guidance, not in the system prompt. On attach, omitted components are disabled. If the use case only suggests a canvas, offer it once; a decline stays declined. Without a backend, say collection works on screen and delivery can be connected later; do not create a placeholder delivery tool.",
|
|
7
|
+
"Magic Canvas calendar collects date and time preferences and books nothing. Scheduling needs the creator's real HTTPS calendly.com booking link in scheduling_url, not a placeholder or scheduling_embed. Image and map output are not available.",
|
|
8
|
+
"Use slide_presenter to present an uploaded deck: display-ready document_ids, with slides_trigger=walk_the_deck for automatic narration or on_demand when the PAL should wait. For a presentation or sales use case, offer a deck once; Magic Canvas does not replace Slide Presenter.",
|
|
9
|
+
"Use web_search for current information the PAL needs during calls, such as weather, news, prices, and scores, and add when to look things up to the PAL's instructions; for weather, ask for the location first. Do not create get_weather or invent an endpoint. Research for building the PAL does not need Web Search attached.",
|
|
10
|
+
"Use perception for camera and screen awareness, not an invented view_screen tool. Use memory for returning participants, with a prompt and greeting that fall back gracefully for first-time visitors. Do not invent a custom tool for a missing integration or unsupported capability.",
|
|
11
|
+
"Create a saved tool only for a real external action, data lookup, or application event that built-ins do not cover. Check tavus_tool_list or tavus_describe_pal_options first and reuse a matching tool; if a new tool overlaps one, say why the existing one does not fit. Use an endpoint the creator supplied or that exists in their code, or an app-message handler their application implements. Never invent URLs or claim an unimplemented handler works. Attach tools with tavus_pal_tools_attach; inline layers.*.tools writes through tavus_patch_pal are rejected.",
|
|
12
|
+
"Knowledge uses document_ids, document_tags, and layers.knowledge_base. Use tavus_document_* for the library and tavus_pal_knowledge_* to attach. Reading a document does not attach it.",
|
|
13
|
+
"After a capability write, read tavus_pal_capabilities_list to confirm the saved config, and report only what the tools confirm. A build or text-test verdict does not prove a canvas displayed; do not claim that without a live visual check."
|
|
14
|
+
],
|
|
15
|
+
"hosted_instructions": [
|
|
16
|
+
"To change what an existing PAL does or shows, including adding a built-in such as an on-screen scorecard, call tavus_pal_build_and_verify with its pal_id and only the briefs that should change. Put built-ins in briefs.skills and external actions in briefs.tools. Use tavus_patch_pal or the capability tools only for an exact setting the creator names."
|
|
17
|
+
],
|
|
18
|
+
"tool_descriptions": {
|
|
19
|
+
"tavus_tool_create": "Create a saved tool for a real external action or application handler. Check built-in capabilities first: scorecards, quizzes, forms, charts, and on-screen text use tavus_pal_capability_attach with magic_canvas. A saved tool cannot render Magic Canvas. Backend recording is separate from on-screen display. Use web_search for weather or current information. Never invent an endpoint or placeholder handler.",
|
|
20
|
+
"tavus_pal_capability_attach": "Enable a built-in capability by intent. Use magic_canvas for scorecards, quizzes, forms, charts, and on-screen text; pass magic_canvas_components and magic_canvas_usage_guidance in this call. Use slide_presenter for decks, web_search for current information, perception for screen/camera awareness, and memory for returning participants. Read tavus_pal_capabilities_list afterward to verify the saved config.",
|
|
21
|
+
"tavus_pal_capability_patch": "Update an enabled built-in capability. For Magic Canvas, keep display choreography in magic_canvas_usage_guidance. The patch merges top-level config fields but replaces config.components when supplied. Read the saved config and preserve unchanged component settings, including any booking link, when changing components. Read tavus_pal_capabilities_list afterward to verify the saved config."
|
|
22
|
+
},
|
|
23
|
+
"magic_canvas_description": "Show scorecards, running scores, quizzes, forms, charts, text, and links during a call. The PAL's model drives the canvas; saved tools and API responses cannot render it. Use text or chart for a training scorecard, question for quizzes, and input for contact capture. Calendar collects preferences but books nothing. Scheduling needs a real HTTPS Calendly booking link. Image and map output are not available.",
|
|
24
|
+
"magic_canvas_usage_guidance": "One or two lines describing when to show each element, with one fixed variant per step. Keep this display choreography here rather than in the PAL's system prompt."
|
|
25
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tavus/tavus-mcp-worker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.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.10.0",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
12
12
|
"agents": "^0.12.2",
|
|
@@ -18,6 +18,13 @@ const REGISTER_ROUTE = "/register";
|
|
|
18
18
|
const CALLBACK_ROUTE = "/oauth/callback";
|
|
19
19
|
const AUTHORIZATION_REQUEST_ROUTE = "/oauth/authorization-request";
|
|
20
20
|
const DYNAMIC_CLIENT_PREFIX = "tavus-mcp-dcr";
|
|
21
|
+
// Client IDs that /register issued before 2026-08-06 (#21). The Worker never
|
|
22
|
+
// stored them, and /authorize accepted any client ID whose redirect was a
|
|
23
|
+
// loopback URL. Clients such as Claude Code keep replaying the ID they cached
|
|
24
|
+
// instead of registering again (anthropics/claude-code#84614), and the
|
|
25
|
+
// rejection they get is a browser page they never see. Keeping the old rule
|
|
26
|
+
// for these IDs, loopback only, lets them sign in without any stored state.
|
|
27
|
+
const LEGACY_DYNAMIC_CLIENT_ID = /^tavus-mcp-[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/;
|
|
21
28
|
const AUTHORIZATION_STATE_TTL_SECONDS = 10 * 60;
|
|
22
29
|
const AUTHORIZATION_CODE_TTL_SECONDS = 5 * 60;
|
|
23
30
|
|
|
@@ -191,10 +198,37 @@ function findRegisteredRedirect(
|
|
|
191
198
|
redirectUri: string,
|
|
192
199
|
): RegisteredOAuthClient | undefined {
|
|
193
200
|
return clients.find(
|
|
194
|
-
(c) =>
|
|
201
|
+
(c) =>
|
|
202
|
+
c.client_id === clientId &&
|
|
203
|
+
c.redirect_uris.some((registered) => redirectUriMatches(registered, redirectUri)),
|
|
195
204
|
);
|
|
196
205
|
}
|
|
197
206
|
|
|
207
|
+
// RFC 8252 section 7.3: a native client takes an ephemeral port when it starts
|
|
208
|
+
// the flow, so the authorization server MUST accept any port on a loopback
|
|
209
|
+
// redirect URI. Everything else about the URI still has to match. HTTPS
|
|
210
|
+
// callbacks need an exact match.
|
|
211
|
+
function redirectUriMatches(registered: string, requested: string): boolean {
|
|
212
|
+
if (registered === requested) {
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
if (!isLoopbackUrl(registered) || !isLoopbackUrl(requested)) {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
const a = new URL(registered);
|
|
219
|
+
const b = new URL(requested);
|
|
220
|
+
return (
|
|
221
|
+
a.protocol === b.protocol &&
|
|
222
|
+
a.hostname === b.hostname &&
|
|
223
|
+
a.pathname === b.pathname &&
|
|
224
|
+
a.search === b.search
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function isLegacyDynamicClient(clientId: string): boolean {
|
|
229
|
+
return LEGACY_DYNAMIC_CLIENT_ID.test(clientId);
|
|
230
|
+
}
|
|
231
|
+
|
|
198
232
|
async function resolveRegisteredRedirect(
|
|
199
233
|
env: Env,
|
|
200
234
|
clientId: string,
|
|
@@ -208,7 +242,7 @@ async function resolveRegisteredRedirect(
|
|
|
208
242
|
const signingSecret = oauthSigningSecret(env);
|
|
209
243
|
if (signingSecret) {
|
|
210
244
|
const dynamic = await verifyClientPayload(clientId, signingSecret);
|
|
211
|
-
if (dynamic?.redirect_uris.
|
|
245
|
+
if (dynamic?.redirect_uris.some((registered) => redirectUriMatches(registered, redirectUri))) {
|
|
212
246
|
return {
|
|
213
247
|
client_id: clientId,
|
|
214
248
|
redirect_uris: dynamic.redirect_uris,
|
|
@@ -217,6 +251,10 @@ async function resolveRegisteredRedirect(
|
|
|
217
251
|
}
|
|
218
252
|
}
|
|
219
253
|
|
|
254
|
+
if (isLegacyDynamicClient(clientId) && isLoopbackUrl(redirectUri)) {
|
|
255
|
+
return { client_id: clientId, redirect_uris: [redirectUri], name: "Tavus MCP Client" };
|
|
256
|
+
}
|
|
257
|
+
|
|
220
258
|
return undefined;
|
|
221
259
|
}
|
|
222
260
|
|
|
@@ -378,9 +416,10 @@ async function handleAuthorize(request: Request, env: Env, mcpRoute: string): Pr
|
|
|
378
416
|
|
|
379
417
|
const registeredRedirect = await resolveRegisteredRedirect(env, clientId, redirectUri);
|
|
380
418
|
if (!registeredRedirect) {
|
|
419
|
+
// This page is all the person sees; the MCP client never learns about it.
|
|
381
420
|
return oauthError(
|
|
382
421
|
"invalid_request",
|
|
383
|
-
"redirect_uri is not registered for this client.",
|
|
422
|
+
"redirect_uri is not registered for this client. Remove and re-add the Tavus MCP server in your client so it registers again.",
|
|
384
423
|
400,
|
|
385
424
|
);
|
|
386
425
|
}
|
|
@@ -654,6 +693,10 @@ async function authenticateClient(request: Request, params: URLSearchParams, env
|
|
|
654
693
|
}
|
|
655
694
|
return secret ? null : clientId;
|
|
656
695
|
}
|
|
696
|
+
// Legacy IDs were always public clients; see LEGACY_DYNAMIC_CLIENT_ID.
|
|
697
|
+
if (isLegacyDynamicClient(clientId)) {
|
|
698
|
+
return !secret && !basic ? clientId : null;
|
|
699
|
+
}
|
|
657
700
|
const signingSecret = oauthSigningSecret(env);
|
|
658
701
|
const dynamic = signingSecret ? await verifyClientPayload(clientId, signingSecret) : null;
|
|
659
702
|
if (!dynamic || !signingSecret) return null;
|
|
@@ -347,6 +347,13 @@ const CAPABILITY_ALIASES: Record<string, CapabilityId> = {
|
|
|
347
347
|
"skill:memory": "memory",
|
|
348
348
|
};
|
|
349
349
|
|
|
350
|
+
// Some clients keep only the first 4,096 characters of server instructions,
|
|
351
|
+
// so the routing rules lead and the whole set must stay under that limit.
|
|
352
|
+
export function hostedInstructions(): string {
|
|
353
|
+
const [buildRouting, ...capabilityRules] = builderGuidance.instructions;
|
|
354
|
+
return [buildRouting, ...builderGuidance.hosted_instructions, ...capabilityRules].join("\n\n");
|
|
355
|
+
}
|
|
356
|
+
|
|
350
357
|
const KNOWLEDGE_BASE_SETTING_KEYS = new Set([
|
|
351
358
|
"rag_score_threshold",
|
|
352
359
|
"rag_n_chunks",
|
|
@@ -534,7 +541,7 @@ export function createTavusMcpServer(
|
|
|
534
541
|
theme: "dark",
|
|
535
542
|
},
|
|
536
543
|
],
|
|
537
|
-
}, { instructions:
|
|
544
|
+
}, { instructions: hostedInstructions() });
|
|
538
545
|
addToolIntent(server, authChallenge);
|
|
539
546
|
|
|
540
547
|
server.tool(
|
|
@@ -608,7 +615,7 @@ export function createTavusMcpServer(
|
|
|
608
615
|
|
|
609
616
|
server.tool(
|
|
610
617
|
"tavus_pal_create",
|
|
611
|
-
"Create a PAL directly from supplied configuration and return its PAL Maker editor URL. This low-level operation does not draft or verify the PAL through the guided PAL Maker pipeline. Full-pipeline PALs default to tavus-gemma-4 unless layers.llm.model is supplied. Tools are first-class records referenced by top-level tool_ids; inline layers.llm.tools configuration is unsupported.",
|
|
618
|
+
"Create a PAL directly from supplied configuration and return its PAL Maker editor URL. This low-level operation does not draft or verify the PAL through the guided PAL Maker pipeline; for a new PAL, use tavus_pal_build_and_verify unless the creator hands over a finished system prompt and asks to skip the build. Full-pipeline PALs default to tavus-gemma-4 unless layers.llm.model is supplied. Tools are first-class records referenced by top-level tool_ids; inline layers.llm.tools configuration is unsupported.",
|
|
612
619
|
{
|
|
613
620
|
system_prompt: z.string().min(1).optional(),
|
|
614
621
|
pal_name: z.string().default("Agentic Tavus PAL"),
|
|
@@ -666,7 +673,7 @@ export function createTavusMcpServer(
|
|
|
666
673
|
|
|
667
674
|
server.tool(
|
|
668
675
|
"tavus_patch_pal",
|
|
669
|
-
"Patch a PAL with validated JSON Patch operations. Attach first-class tools through top-level tool_ids or tavus_pal_tools_attach; legacy layers.llm.tools writes are rejected.",
|
|
676
|
+
"Patch a PAL with validated JSON Patch operations for an exact field edit. To change what a PAL does or shows, such as adding an on-screen scorecard, use tavus_pal_build_and_verify with pal_id instead. Attach first-class tools through top-level tool_ids or tavus_pal_tools_attach; legacy layers.llm.tools writes are rejected.",
|
|
670
677
|
{
|
|
671
678
|
pal_id: z.string().min(1),
|
|
672
679
|
ops: z.array(JsonPatchOperationSchema).min(1),
|
|
@@ -1062,7 +1069,8 @@ export function createTavusMcpServer(
|
|
|
1062
1069
|
|
|
1063
1070
|
server.tool(
|
|
1064
1071
|
"tavus_tool_create",
|
|
1065
|
-
builderGuidance.tool_descriptions.tavus_tool_create +
|
|
1072
|
+
builderGuidance.tool_descriptions.tavus_tool_create +
|
|
1073
|
+
" Attach the returned tool_id to a PAL with tavus_pal_tools_attach or top-level tool_ids. When the tool is part of building or changing a PAL, tavus_pal_build_and_verify (with pal_id for an existing PAL) drafts saved tools and built-ins together through briefs.tools and briefs.skills.",
|
|
1066
1074
|
toolWriteFields,
|
|
1067
1075
|
writeAnnotations("Create tool"),
|
|
1068
1076
|
async (body) => toolResult(await client.create("tools", clean(body))),
|
|
@@ -1302,7 +1310,8 @@ export function createTavusMcpServer(
|
|
|
1302
1310
|
|
|
1303
1311
|
server.tool(
|
|
1304
1312
|
"tavus_pal_capability_attach",
|
|
1305
|
-
builderGuidance.tool_descriptions.tavus_pal_capability_attach
|
|
1313
|
+
builderGuidance.tool_descriptions.tavus_pal_capability_attach +
|
|
1314
|
+
" When the creator describes a change to an existing PAL rather than an exact setting, use tavus_pal_build_and_verify with pal_id and briefs.skills instead.",
|
|
1306
1315
|
capabilityWriteSchema,
|
|
1307
1316
|
{
|
|
1308
1317
|
title: "Attach PAL capability",
|
|
@@ -1759,7 +1768,7 @@ export function createTavusMcpServer(
|
|
|
1759
1768
|
|
|
1760
1769
|
server.tool(
|
|
1761
1770
|
"tavus_quickstart",
|
|
1762
|
-
"Create a PAL from a complete system prompt, select a stock face when none is supplied, and start a conversation. Returns the PAL, conversation, and selected face. This quick setup does not gather an interview or run the guided PAL Maker build and verification pipeline.",
|
|
1771
|
+
"Create a PAL from a complete system prompt, select a stock face when none is supplied, and start a conversation. Returns the PAL, conversation, and selected face. This quick setup does not gather an interview or run the guided PAL Maker build and verification pipeline; use it only when the creator hands over a finished system prompt and asks to skip the build, and otherwise use tavus_pal_build_and_verify.",
|
|
1763
1772
|
{
|
|
1764
1773
|
system_prompt: z.string().min(1),
|
|
1765
1774
|
pal_name: z.string().default("Agentic Tavus PAL"),
|
|
@@ -1835,8 +1844,13 @@ export function createTavusMcpServer(
|
|
|
1835
1844
|
|
|
1836
1845
|
server.tool(
|
|
1837
1846
|
"tavus_pal_build_and_verify",
|
|
1838
|
-
"
|
|
1847
|
+
"The standard way to build a PAL. Builds and publishes a PAL through the guided PAL Maker pipeline that Charlie uses, from a completed brief and optional interview Q&A. Drafts the personality, objectives, guardrails, and greeting, selects a face, publishes the PAL, runs conversation probes, and returns the PAL ID, editor URL, and judged verification result. The briefs fields describe each artifact, including knowledge, tools, skills, perception, and face preferences. Connected tools or knowledge sources may access external services during building or testing. Usually takes one to three minutes.\n\nTo change an existing PAL, pass its pal_id, put the requested change in prompt, and fill only the briefs for the parts that should change. The same PAL Maker drafters edit the PAL and leave the other parts alone. Put built-in capabilities in briefs.skills: on-screen scorecards, quizzes, forms, and charts (Magic Canvas), web search, memory, and slide decks. Use briefs.tools only for an external action such as a webhook or API call. The PAL keeps its face unless face_id is given. The change is published, together with any unpublished PAL Maker edits on that PAL.",
|
|
1839
1848
|
{
|
|
1849
|
+
pal_id: z
|
|
1850
|
+
.string()
|
|
1851
|
+
.min(1)
|
|
1852
|
+
.optional()
|
|
1853
|
+
.describe("An existing PAL to change instead of building a new one."),
|
|
1840
1854
|
prompt: z.string().min(1),
|
|
1841
1855
|
interview: z
|
|
1842
1856
|
.array(z.object({ question: z.string(), answer: z.string() }))
|
|
@@ -1860,9 +1874,10 @@ export function createTavusMcpServer(
|
|
|
1860
1874
|
probes: z.array(z.string()).max(5).optional(),
|
|
1861
1875
|
},
|
|
1862
1876
|
writeAnnotations("Build and verify PAL with PAL Maker", { destructiveHint: true, openWorldHint: true }),
|
|
1863
|
-
async ({ prompt, interview, briefs, face_id, pal_name, probes }) =>
|
|
1877
|
+
async ({ pal_id, prompt, interview, briefs, face_id, pal_name, probes }) =>
|
|
1864
1878
|
toolResult(
|
|
1865
1879
|
await runGuidedBuild(client, config.palMakerUrl, {
|
|
1880
|
+
pal_id,
|
|
1866
1881
|
prompt,
|
|
1867
1882
|
interview,
|
|
1868
1883
|
briefs,
|
|
@@ -2756,6 +2771,7 @@ export interface InterviewEntry {
|
|
|
2756
2771
|
}
|
|
2757
2772
|
|
|
2758
2773
|
export interface GuidedBuildOptions {
|
|
2774
|
+
pal_id?: string;
|
|
2759
2775
|
prompt: string;
|
|
2760
2776
|
interview?: InterviewEntry[];
|
|
2761
2777
|
briefs?: Partial<Record<DraftBriefKey, string>>;
|
|
@@ -2784,12 +2800,16 @@ export function interviewMessages(
|
|
|
2784
2800
|
|
|
2785
2801
|
export function draftBriefs(
|
|
2786
2802
|
briefs: Partial<Record<DraftBriefKey, string>> | undefined,
|
|
2803
|
+
editing = false,
|
|
2787
2804
|
): Record<string, string> {
|
|
2788
|
-
//
|
|
2789
|
-
// brief set cannot produce a PAL missing objectives or guardrails.
|
|
2805
|
+
// A new PAL always drafts the four core artifacts so a sparse agent-supplied
|
|
2806
|
+
// brief set cannot produce a PAL missing objectives or guardrails. An edit
|
|
2807
|
+
// runs only the briefed drafters, so the rest of the PAL stays as it is.
|
|
2790
2808
|
const mapped: Record<string, string> = {};
|
|
2791
|
-
|
|
2792
|
-
|
|
2809
|
+
if (!editing) {
|
|
2810
|
+
for (const key of DRAFT_CORE_ARTIFACTS) {
|
|
2811
|
+
mapped[key] = DRAFT_FALLBACK_BRIEF;
|
|
2812
|
+
}
|
|
2793
2813
|
}
|
|
2794
2814
|
for (const key of DRAFT_BRIEF_KEYS) {
|
|
2795
2815
|
const value = briefs?.[key];
|
|
@@ -3015,8 +3035,24 @@ async function runGuidedBuild(
|
|
|
3015
3035
|
}
|
|
3016
3036
|
}
|
|
3017
3037
|
|
|
3018
|
-
const
|
|
3019
|
-
const
|
|
3038
|
+
const editPalId = options.pal_id?.trim();
|
|
3039
|
+
const briefs = draftBriefs(options.briefs, Boolean(editPalId));
|
|
3040
|
+
if (editPalId && !Object.keys(briefs).length) {
|
|
3041
|
+
throw new Error(
|
|
3042
|
+
"Changing an existing PAL needs at least one brief naming what to change, " +
|
|
3043
|
+
"for example briefs.skills for an on-screen scorecard or briefs.personality for prompt wording.",
|
|
3044
|
+
);
|
|
3045
|
+
}
|
|
3046
|
+
let sessionName = options.pal_name?.trim();
|
|
3047
|
+
if (editPalId && !sessionName) {
|
|
3048
|
+
const existing = expectObject(await client.get("pals", editPalId));
|
|
3049
|
+
sessionName = String(existing.pal_name ?? existing.persona_name ?? "").trim();
|
|
3050
|
+
}
|
|
3051
|
+
// RQH opens the session on the PAL's private PAL Maker draft when it has
|
|
3052
|
+
// one; that row takes the edits and publish copies them onto the live PAL.
|
|
3053
|
+
const session = expectObject(
|
|
3054
|
+
await client.post("builder", clean({ name: sessionName || derivePalName(prompt), persona_id: editPalId })),
|
|
3055
|
+
);
|
|
3020
3056
|
const builderId = String(session.builder_id ?? "");
|
|
3021
3057
|
let palId = String(session.persona_id ?? "");
|
|
3022
3058
|
if (!builderId || !palId) {
|
|
@@ -3030,7 +3066,7 @@ async function runGuidedBuild(
|
|
|
3030
3066
|
const current = expectObject(await client.get("pals", palId, { include_settings: "true" }));
|
|
3031
3067
|
const terminal = await client.postNdjson(`builder/${builderId}/draft-fanout`, {
|
|
3032
3068
|
message: prompt,
|
|
3033
|
-
briefs
|
|
3069
|
+
briefs,
|
|
3034
3070
|
current: {
|
|
3035
3071
|
system_prompt: current.system_prompt ?? "",
|
|
3036
3072
|
greeting: current.greeting ?? "",
|
|
@@ -3045,8 +3081,10 @@ async function runGuidedBuild(
|
|
|
3045
3081
|
if (operations.length) {
|
|
3046
3082
|
await client.patch("pals", palId, operations);
|
|
3047
3083
|
}
|
|
3084
|
+
const builtinChanges = await applyBuiltinRecommendations(client, palId, content.tools);
|
|
3048
3085
|
|
|
3049
|
-
await client.post(`builder/${builderId}/publish`, {});
|
|
3086
|
+
const published = expectObject(await client.post(`builder/${builderId}/publish`, {}));
|
|
3087
|
+
palId = String(published.persona_id ?? session.live_persona_id ?? palId);
|
|
3050
3088
|
|
|
3051
3089
|
let faceSelection: Record<string, unknown> | null = null;
|
|
3052
3090
|
let faceId = options.face_id;
|
|
@@ -3054,7 +3092,7 @@ async function runGuidedBuild(
|
|
|
3054
3092
|
await client.patch("pals", palId, [
|
|
3055
3093
|
{ op: "replace", path: "/default_face_id", value: faceId },
|
|
3056
3094
|
]);
|
|
3057
|
-
} else {
|
|
3095
|
+
} else if (!editPalId) {
|
|
3058
3096
|
faceSelection = expectObject(
|
|
3059
3097
|
await client.post("persona-builder/select-replica", { persona_id: palId }),
|
|
3060
3098
|
);
|
|
@@ -3111,6 +3149,11 @@ async function runGuidedBuild(
|
|
|
3111
3149
|
face_id: faceId ?? spec.default_replica_id,
|
|
3112
3150
|
face_selection: faceSelection,
|
|
3113
3151
|
pal_spec: spec,
|
|
3152
|
+
drafted: Object.keys(content).filter((kind) => {
|
|
3153
|
+
const value = content[kind];
|
|
3154
|
+
return value != null && !(isRecord(value) && (value.error || value.status === "superseded"));
|
|
3155
|
+
}),
|
|
3156
|
+
builtin_changes: builtinChanges,
|
|
3114
3157
|
probes,
|
|
3115
3158
|
smoke_transcript: smokeTranscript,
|
|
3116
3159
|
verdict,
|
|
@@ -3118,6 +3161,50 @@ async function runGuidedBuild(
|
|
|
3118
3161
|
};
|
|
3119
3162
|
}
|
|
3120
3163
|
|
|
3164
|
+
// RQH's tools drafter only recommends built-in capabilities: the client that
|
|
3165
|
+
// asked for the draft attaches them, as the portal does for Charlie.
|
|
3166
|
+
async function applyBuiltinRecommendations(
|
|
3167
|
+
client: TavusApiClient,
|
|
3168
|
+
palId: string,
|
|
3169
|
+
tools: unknown,
|
|
3170
|
+
): Promise<string[]> {
|
|
3171
|
+
if (!isRecord(tools) || tools.error || tools.status === "superseded") {
|
|
3172
|
+
return [];
|
|
3173
|
+
}
|
|
3174
|
+
const attach = stringArray(tools.attach_builtins);
|
|
3175
|
+
const detach = stringArray(tools.detach_builtins);
|
|
3176
|
+
if (!attach.length && !detach.length) {
|
|
3177
|
+
return [];
|
|
3178
|
+
}
|
|
3179
|
+
// Read after the fan-out: the skills drafter may have attached and
|
|
3180
|
+
// configured the same capability, and an empty attach would reset it.
|
|
3181
|
+
const attached = skillMap(await client.getPath(`pals/${palId}/skills/`));
|
|
3182
|
+
const changes: string[] = [];
|
|
3183
|
+
for (const [builtin, attaching] of [
|
|
3184
|
+
...attach.map((id) => [id, true] as const),
|
|
3185
|
+
...detach.map((id) => [id, false] as const),
|
|
3186
|
+
]) {
|
|
3187
|
+
const capability = CAPABILITY_ALIASES[builtin];
|
|
3188
|
+
const skillId = capability ? CAPABILITY_CATALOG[capability].rqh_skill_id : null;
|
|
3189
|
+
if (!capability || !skillId || attaching === (skillId in attached)) {
|
|
3190
|
+
continue;
|
|
3191
|
+
}
|
|
3192
|
+
if (attaching && capability === "slide_presenter") {
|
|
3193
|
+
changes.push(
|
|
3194
|
+
"slide_presenter recommended but not attached: attach it with tavus_pal_capability_attach and the deck's document_ids.",
|
|
3195
|
+
);
|
|
3196
|
+
continue;
|
|
3197
|
+
}
|
|
3198
|
+
if (attaching) {
|
|
3199
|
+
await client.put(`pals/${palId}/skills/${skillId}`, { config: {} });
|
|
3200
|
+
} else {
|
|
3201
|
+
await client.deletePath(`pals/${palId}/skills/${skillId}`);
|
|
3202
|
+
}
|
|
3203
|
+
changes.push(`${attaching ? "attached" : "detached"} ${capability}`);
|
|
3204
|
+
}
|
|
3205
|
+
return changes;
|
|
3206
|
+
}
|
|
3207
|
+
|
|
3121
3208
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
3122
3209
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3123
3210
|
}
|