@assemble-inc/chat-widget 0.1.4 → 0.1.6
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.
- package/README.md +20 -5
- package/dist/embed.iife.js +379 -0
- package/dist/index.js +2256 -2232
- package/dist/server.cjs +65 -18
- package/dist/server.d.cts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +65 -18
- package/package.json +3 -1
package/dist/server.cjs
CHANGED
|
@@ -28,6 +28,31 @@ var import_ai = require("ai");
|
|
|
28
28
|
var import_streamableHttp = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
29
29
|
var import_client = require("@modelcontextprotocol/sdk/client/index.js");
|
|
30
30
|
var DEFAULT_SYSTEM_PROMPT = `You are a helpful assistant. You must ONLY answer questions using information returned by the tools available to you \u2014 never draw on your own training knowledge. If the tools do not return relevant information, respond with: "I don't have that information available." Do not guess or speculate.`;
|
|
31
|
+
var askMultipleChoiceTool = (0, import_ai.tool)({
|
|
32
|
+
description: "Present the user with a multiple-choice question and wait for them to select an option. Use this whenever you need the user to pick from a discrete set of options.",
|
|
33
|
+
inputSchema: (0, import_ai.jsonSchema)({
|
|
34
|
+
type: "object",
|
|
35
|
+
properties: {
|
|
36
|
+
question: {
|
|
37
|
+
type: "string",
|
|
38
|
+
description: "The question or prompt to display to the user."
|
|
39
|
+
},
|
|
40
|
+
choices: {
|
|
41
|
+
type: "array",
|
|
42
|
+
description: "The list of choices for the user to select from.",
|
|
43
|
+
items: {
|
|
44
|
+
type: "object",
|
|
45
|
+
properties: {
|
|
46
|
+
id: { type: "string" },
|
|
47
|
+
label: { type: "string" }
|
|
48
|
+
},
|
|
49
|
+
required: ["id", "label"]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
required: ["question", "choices"]
|
|
54
|
+
})
|
|
55
|
+
});
|
|
31
56
|
function createChatHandler(config) {
|
|
32
57
|
const anthropicClient = (0, import_anthropic.createAnthropic)({ apiKey: config.apiKey });
|
|
33
58
|
const defaultModel = "claude-sonnet-4-5";
|
|
@@ -43,8 +68,8 @@ function createChatHandler(config) {
|
|
|
43
68
|
} = req.body;
|
|
44
69
|
const messages = await (0, import_ai.convertToModelMessages)(uiMessages);
|
|
45
70
|
const allowlist = config.mcpCredentials ?? {};
|
|
46
|
-
const resolvedUrl = requestedUrl && (requestedUrl === config.mcpServerUrl || Object.prototype.hasOwnProperty.call(allowlist, requestedUrl)) ? requestedUrl : config.mcpServerUrl;
|
|
47
|
-
const resolvedToken = resolvedUrl === config.mcpServerUrl ? config.mcpBearerToken : allowlist[resolvedUrl];
|
|
71
|
+
const resolvedUrl = config.mcpServerUrl ? requestedUrl && (requestedUrl === config.mcpServerUrl || Object.prototype.hasOwnProperty.call(allowlist, requestedUrl)) ? requestedUrl : config.mcpServerUrl : void 0;
|
|
72
|
+
const resolvedToken = resolvedUrl ? resolvedUrl === config.mcpServerUrl ? config.mcpBearerToken : allowlist[resolvedUrl] : void 0;
|
|
48
73
|
const base = config.systemPrompt ?? widgetSystemPrompt ?? DEFAULT_SYSTEM_PROMPT;
|
|
49
74
|
const userContext = [
|
|
50
75
|
context,
|
|
@@ -56,6 +81,25 @@ function createChatHandler(config) {
|
|
|
56
81
|
${userContext}` : base;
|
|
57
82
|
const resolvedModel = config.model ?? widgetModel ?? defaultModel;
|
|
58
83
|
const temperature = config.temperature ?? widgetTemperature;
|
|
84
|
+
if (!resolvedUrl) {
|
|
85
|
+
try {
|
|
86
|
+
const result = (0, import_ai.streamText)({
|
|
87
|
+
model: anthropicClient(resolvedModel),
|
|
88
|
+
system,
|
|
89
|
+
messages,
|
|
90
|
+
tools: { ask_multiple_choice: askMultipleChoiceTool },
|
|
91
|
+
temperature,
|
|
92
|
+
stopWhen: (0, import_ai.stepCountIs)(10)
|
|
93
|
+
});
|
|
94
|
+
result.pipeUIMessageStreamToResponse(res);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
console.error("[chat handler] error:", err);
|
|
97
|
+
if (!res.headersSent) {
|
|
98
|
+
res.status(500).json({ error: "Failed to generate response." });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
59
103
|
const transportOptions = resolvedToken ? { requestInit: { headers: { Authorization: `Bearer ${resolvedToken}` } } } : {};
|
|
60
104
|
const mcpClient = new import_client.Client({ name: "@assemble-inc/chat-widget", version: "0.1.0" });
|
|
61
105
|
try {
|
|
@@ -63,22 +107,25 @@ ${userContext}` : base;
|
|
|
63
107
|
new import_streamableHttp.StreamableHTTPClientTransport(new URL(resolvedUrl), transportOptions)
|
|
64
108
|
);
|
|
65
109
|
const { tools: mcpTools } = await mcpClient.listTools();
|
|
66
|
-
const tools =
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
110
|
+
const tools = {
|
|
111
|
+
ask_multiple_choice: askMultipleChoiceTool,
|
|
112
|
+
...Object.fromEntries(
|
|
113
|
+
mcpTools.map((t) => [
|
|
114
|
+
t.name,
|
|
115
|
+
(0, import_ai.tool)({
|
|
116
|
+
description: t.description,
|
|
117
|
+
inputSchema: (0, import_ai.jsonSchema)(t.inputSchema),
|
|
118
|
+
execute: async (input) => {
|
|
119
|
+
const result2 = await mcpClient.callTool({
|
|
120
|
+
name: t.name,
|
|
121
|
+
arguments: input
|
|
122
|
+
});
|
|
123
|
+
return result2.content;
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
])
|
|
127
|
+
)
|
|
128
|
+
};
|
|
82
129
|
const result = (0, import_ai.streamText)({
|
|
83
130
|
model: anthropicClient(resolvedModel),
|
|
84
131
|
system,
|
package/dist/server.d.cts
CHANGED
|
@@ -3,8 +3,8 @@ import { Request, Response } from 'express';
|
|
|
3
3
|
interface ChatHandlerConfig {
|
|
4
4
|
/** Anthropic API key */
|
|
5
5
|
apiKey: string;
|
|
6
|
-
/** Default MCP server URL used when no per-embed URL is provided */
|
|
7
|
-
mcpServerUrl
|
|
6
|
+
/** Default MCP server URL used when no per-embed URL is provided. Omit to run without MCP tools. */
|
|
7
|
+
mcpServerUrl?: string;
|
|
8
8
|
/** Bearer token for the default mcpServerUrl */
|
|
9
9
|
mcpBearerToken?: string;
|
|
10
10
|
/**
|
package/dist/server.d.ts
CHANGED
|
@@ -3,8 +3,8 @@ import { Request, Response } from 'express';
|
|
|
3
3
|
interface ChatHandlerConfig {
|
|
4
4
|
/** Anthropic API key */
|
|
5
5
|
apiKey: string;
|
|
6
|
-
/** Default MCP server URL used when no per-embed URL is provided */
|
|
7
|
-
mcpServerUrl
|
|
6
|
+
/** Default MCP server URL used when no per-embed URL is provided. Omit to run without MCP tools. */
|
|
7
|
+
mcpServerUrl?: string;
|
|
8
8
|
/** Bearer token for the default mcpServerUrl */
|
|
9
9
|
mcpBearerToken?: string;
|
|
10
10
|
/**
|
package/dist/server.js
CHANGED
|
@@ -4,6 +4,31 @@ import { streamText, tool, jsonSchema, stepCountIs, convertToModelMessages } fro
|
|
|
4
4
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
5
5
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
6
6
|
var DEFAULT_SYSTEM_PROMPT = `You are a helpful assistant. You must ONLY answer questions using information returned by the tools available to you \u2014 never draw on your own training knowledge. If the tools do not return relevant information, respond with: "I don't have that information available." Do not guess or speculate.`;
|
|
7
|
+
var askMultipleChoiceTool = tool({
|
|
8
|
+
description: "Present the user with a multiple-choice question and wait for them to select an option. Use this whenever you need the user to pick from a discrete set of options.",
|
|
9
|
+
inputSchema: jsonSchema({
|
|
10
|
+
type: "object",
|
|
11
|
+
properties: {
|
|
12
|
+
question: {
|
|
13
|
+
type: "string",
|
|
14
|
+
description: "The question or prompt to display to the user."
|
|
15
|
+
},
|
|
16
|
+
choices: {
|
|
17
|
+
type: "array",
|
|
18
|
+
description: "The list of choices for the user to select from.",
|
|
19
|
+
items: {
|
|
20
|
+
type: "object",
|
|
21
|
+
properties: {
|
|
22
|
+
id: { type: "string" },
|
|
23
|
+
label: { type: "string" }
|
|
24
|
+
},
|
|
25
|
+
required: ["id", "label"]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
required: ["question", "choices"]
|
|
30
|
+
})
|
|
31
|
+
});
|
|
7
32
|
function createChatHandler(config) {
|
|
8
33
|
const anthropicClient = createAnthropic({ apiKey: config.apiKey });
|
|
9
34
|
const defaultModel = "claude-sonnet-4-5";
|
|
@@ -19,8 +44,8 @@ function createChatHandler(config) {
|
|
|
19
44
|
} = req.body;
|
|
20
45
|
const messages = await convertToModelMessages(uiMessages);
|
|
21
46
|
const allowlist = config.mcpCredentials ?? {};
|
|
22
|
-
const resolvedUrl = requestedUrl && (requestedUrl === config.mcpServerUrl || Object.prototype.hasOwnProperty.call(allowlist, requestedUrl)) ? requestedUrl : config.mcpServerUrl;
|
|
23
|
-
const resolvedToken = resolvedUrl === config.mcpServerUrl ? config.mcpBearerToken : allowlist[resolvedUrl];
|
|
47
|
+
const resolvedUrl = config.mcpServerUrl ? requestedUrl && (requestedUrl === config.mcpServerUrl || Object.prototype.hasOwnProperty.call(allowlist, requestedUrl)) ? requestedUrl : config.mcpServerUrl : void 0;
|
|
48
|
+
const resolvedToken = resolvedUrl ? resolvedUrl === config.mcpServerUrl ? config.mcpBearerToken : allowlist[resolvedUrl] : void 0;
|
|
24
49
|
const base = config.systemPrompt ?? widgetSystemPrompt ?? DEFAULT_SYSTEM_PROMPT;
|
|
25
50
|
const userContext = [
|
|
26
51
|
context,
|
|
@@ -32,6 +57,25 @@ function createChatHandler(config) {
|
|
|
32
57
|
${userContext}` : base;
|
|
33
58
|
const resolvedModel = config.model ?? widgetModel ?? defaultModel;
|
|
34
59
|
const temperature = config.temperature ?? widgetTemperature;
|
|
60
|
+
if (!resolvedUrl) {
|
|
61
|
+
try {
|
|
62
|
+
const result = streamText({
|
|
63
|
+
model: anthropicClient(resolvedModel),
|
|
64
|
+
system,
|
|
65
|
+
messages,
|
|
66
|
+
tools: { ask_multiple_choice: askMultipleChoiceTool },
|
|
67
|
+
temperature,
|
|
68
|
+
stopWhen: stepCountIs(10)
|
|
69
|
+
});
|
|
70
|
+
result.pipeUIMessageStreamToResponse(res);
|
|
71
|
+
} catch (err) {
|
|
72
|
+
console.error("[chat handler] error:", err);
|
|
73
|
+
if (!res.headersSent) {
|
|
74
|
+
res.status(500).json({ error: "Failed to generate response." });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
35
79
|
const transportOptions = resolvedToken ? { requestInit: { headers: { Authorization: `Bearer ${resolvedToken}` } } } : {};
|
|
36
80
|
const mcpClient = new Client({ name: "@assemble-inc/chat-widget", version: "0.1.0" });
|
|
37
81
|
try {
|
|
@@ -39,22 +83,25 @@ ${userContext}` : base;
|
|
|
39
83
|
new StreamableHTTPClientTransport(new URL(resolvedUrl), transportOptions)
|
|
40
84
|
);
|
|
41
85
|
const { tools: mcpTools } = await mcpClient.listTools();
|
|
42
|
-
const tools =
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
86
|
+
const tools = {
|
|
87
|
+
ask_multiple_choice: askMultipleChoiceTool,
|
|
88
|
+
...Object.fromEntries(
|
|
89
|
+
mcpTools.map((t) => [
|
|
90
|
+
t.name,
|
|
91
|
+
tool({
|
|
92
|
+
description: t.description,
|
|
93
|
+
inputSchema: jsonSchema(t.inputSchema),
|
|
94
|
+
execute: async (input) => {
|
|
95
|
+
const result2 = await mcpClient.callTool({
|
|
96
|
+
name: t.name,
|
|
97
|
+
arguments: input
|
|
98
|
+
});
|
|
99
|
+
return result2.content;
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
])
|
|
103
|
+
)
|
|
104
|
+
};
|
|
58
105
|
const result = streamText({
|
|
59
106
|
model: anthropicClient(resolvedModel),
|
|
60
107
|
system,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assemble-inc/chat-widget",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Embeddable AI chat widget powered by Anthropic and MCP",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chat",
|
|
@@ -52,12 +52,14 @@
|
|
|
52
52
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
53
53
|
"ai": "^6.0.176",
|
|
54
54
|
"class-variance-authority": "^0.7.1",
|
|
55
|
+
"cors": "^2.8.6",
|
|
55
56
|
"express": "^4.21.2",
|
|
56
57
|
"react-markdown": "^9.0.1"
|
|
57
58
|
},
|
|
58
59
|
"devDependencies": {
|
|
59
60
|
"@tailwindcss/postcss": "^4.2.4",
|
|
60
61
|
"@tailwindcss/vite": "^4.2.4",
|
|
62
|
+
"@types/cors": "^2.8.19",
|
|
61
63
|
"@types/express": "^5.0.1",
|
|
62
64
|
"@types/node": "22.15.20",
|
|
63
65
|
"@types/react": "19.1.4",
|