@three-ws/assistant-mcp
v0.1.1
Published
Generate a paste-ready three.ws assistant widget from any AI agent. build_assistant_widget turns a config into an embeddable <script> tag, a frame URL, and a ThreeAssistant.init() snippet for a floating 3D avatar chatbot on any website; list_assistant_opt
Maintainers
Readme
A Model Context Protocol server that lets any AI assistant build a paste-ready embed for the three.ws assistant widget over stdio. Describe how you want the assistant to look and behave and
build_assistant_widgetreturns a ready-to-paste<script>tag, a standalone frame URL for<iframe>embedding, and an equivalentThreeAssistant.init({...})JavaScript-API snippet, a floating 3D avatar chatbot that lives in the corner of any site.list_assistant_optionsenumerates every built-in avatar, background preset, interaction mode, chat lane, anddata-*attribute you can set.
Both tools are pure and offline: building an embed is deterministic string logic over local validators. Every field is validated and clamped, so a bad or hostile value falls back to a safe default and the generated HTML is always well-formed. No API key, no signer, no payment, no network call. Point THREE_WS_BASE at a deployment (or leave the default) and go.
Install
npm install @three-ws/assistant-mcpOr run with npx (no install):
npx -y @three-ws/assistant-mcpQuick start
Claude Code, one line:
claude mcp add assistant -- npx -y @three-ws/assistant-mcpClaude Desktop / Cursor (claude_desktop_config.json or mcp.json):
{
"mcpServers": {
"assistant": {
"command": "npx",
"args": ["-y", "@three-ws/assistant-mcp"]
}
}
}Inspect the surface with the MCP Inspector:
npx -y @modelcontextprotocol/inspector npx @three-ws/assistant-mcpTools
| Tool | Type | What it does |
| ------------------------ | --------- | -------------------------------------------------------------------------------------------------------------------- |
| build_assistant_widget | read-only | A config → a paste-ready <script> embed (non-default settings as data-*), a frame URL, a ThreeAssistant.init() snippet, and the normalized config. |
| list_assistant_options | read-only | Enumerate every built-in avatar, background preset and grammar, interaction mode, chat lane, and the full data-* attribute reference. |
build_assistant_widget is a pure function: the same config always yields the same embed, and it never touches the network. The generated <script> tag carries only the settings that differ from their defaults, so a minimal config yields a minimal tag. The frame_url carries every resolved value (including defaults) so the standalone frame renders deterministically when dropped into an <iframe>.
Input parameters
build_assistant_widget, all optional:
| Field | Type / values | Default |
| ------------ | ------------------------------------------------------------------------------------------ | ------------- |
| avatar | avatar id, /avatars/*.glb path, or GLB URL | mannequin |
| agent | a three.ws agent id (alternative to avatar) | (none) |
| background | transparent | #hex | ember/ocean/violet/forest/dusk/slate | gradient:#a,#b[,angle] | transparent |
| mode | chat | speak | both | both |
| name | string (≤ 60) | (none) |
| greeting | string (≤ 200) | (none) |
| context | string (≤ 500), what the assistant should know about the site | (none) |
| accent | #hex | #f97316 |
| position | right | left | right |
| voice | boolean, start with voice on/off | on |
| badge | boolean, show the three.ws attribution badge | on |
Any value that fails validation (a bad hex, an unknown background, an over-long string) falls back to its default rather than being rejected, so you always get a working embed.
list_assistant_options, filter (optional: avatars | backgrounds | modes | chat_lanes | attributes). Omit to get everything.
Example
// build_assistant_widget
> {
"avatar": "/avatars/selfie-girl.glb",
"background": "ocean",
"mode": "both",
"name": "Aria",
"greeting": "Hi! Ask me anything about the site.",
"accent": "#00c2ff",
"position": "left"
}
{
"ok": true,
"snippet": "<script src=\"https://three.ws/assistant/v1.js\" async data-avatar=\"/avatars/selfie-girl.glb\" data-bg=\"ocean\" data-name=\"Aria\" data-greeting=\"Hi! Ask me anything about the site.\" data-accent=\"#00c2ff\" data-position=\"left\"></script>",
"frame_url": "https://three.ws/assistant-frame?avatar=%2Favatars%2Fselfie-girl.glb&bg=ocean&mode=both&name=Aria&greeting=Hi%21+Ask+me+anything+about+the+site.&accent=%2300c2ff",
"js_api": "ThreeAssistant.init({\n \"bg\": \"ocean\",\n \"mode\": \"both\",\n \"accent\": \"#00c2ff\",\n \"position\": \"left\",\n \"avatar\": \"/avatars/selfie-girl.glb\",\n \"name\": \"Aria\",\n \"greeting\": \"Hi! Ask me anything about the site.\"\n});",
"builder_url": "https://three.ws/assistant",
"config": { "bg": "ocean", "mode": "both", "accent": "#00c2ff", "position": "left", "avatar": "/avatars/selfie-girl.glb", "name": "Aria", "greeting": "Hi! Ask me anything about the site." },
"notes": "Chat runs on the free three.ws LLM chain by default (no key). Visitors can paste their own Groq or OpenRouter key in the widget settings for a private lane; that key stays in their browser."
}Paste the snippet before </body> on any page, or drop the frame_url into an <iframe>. Design it visually first at three.ws/assistant.
Requirements
- Node.js >= 20.
- No network access needed.
THREE_WS_BASEonly sets the origin baked into the generated URLs.
Environment variables
| Variable | Required | Default |
| --------------- | -------- | ------------------ |
| THREE_WS_BASE | no | https://three.ws |
Programmatic use
The package entry point exports TOOLS (every tool definition: name, title,
description, inputSchema, annotations, handler) and buildServer(), which returns a
fully-registered McpServer with no transport attached. Both tools are pure and offline, so you
can call them directly as a widget-builder library with no MCP client involved.
// run from a checkout: node this-file.mjs
import { TOOLS, buildServer } from '@three-ws/assistant-mcp';
const build = TOOLS.find((t) => t.name === 'build_assistant_widget');
const { snippet } = await build.handler({ avatar: '/avatars/michelle.glb', background: 'dusk' });
console.log(snippet);
// Or hand the whole registered server to your own MCP transport.
buildServer();<script src="https://three.ws/assistant/v1.js" async data-avatar="/avatars/michelle.glb" data-bg="dusk"></script>Links
- Builder: https://three.ws/assistant
- Docs: https://three.ws/docs/assistant-widget
- Homepage: https://three.ws
- Changelog: https://three.ws/changelog
- Issues: https://github.com/nirholas/three.ws/issues
- License: proprietary, see LICENSE
