twelveai 0.1.0 → 0.3.0
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 +34 -0
- package/dist/index.cjs +28 -0
- package/dist/index.d.cts +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.js +28 -0
- package/dist/mcp.cjs +158 -0
- package/dist/mcp.d.cts +86 -0
- package/dist/mcp.d.ts +86 -0
- package/dist/mcp.js +134 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -36,6 +36,18 @@ console.log(res.message) // "Your current balance is NGN 307.05."
|
|
|
36
36
|
|
|
37
37
|
Behind that one call the SDK also completed any **client-fetch hand-offs**: when a tool is configured as *"my app calls it"*, the engine returns the resolved request (method, URL, body - no credentials attached) instead of calling your API. The SDK performs it with your `clientAuth`, resumes the turn, and hands you the final grounded answer. Inspect what ran via `res.executedHandoffs`.
|
|
38
38
|
|
|
39
|
+
## Level 0: classify only (zero commitment)
|
|
40
|
+
|
|
41
|
+
Not ready to hand over the conversation? Keep every flow you have and use
|
|
42
|
+
TwelveAI purely as the router - free, nothing stored:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
const r = await twelve.classify({ message: 'send 5k to 0123456789' })
|
|
46
|
+
// { intent: 'transfer', confidence: 0.8,
|
|
47
|
+
// entities: { amount: 5000, account_number: '0123456789' } }
|
|
48
|
+
if (r.confidence > 0.6) routeToMyTransferFlow(r.entities)
|
|
49
|
+
```
|
|
50
|
+
|
|
39
51
|
## Multi-turn conversations
|
|
40
52
|
|
|
41
53
|
Pass the previous turn's `continuation` to keep context:
|
|
@@ -165,6 +177,28 @@ Approve a `pendingConfirmation` (resends the turn with `confirmed: true`).
|
|
|
165
177
|
| `escalated` / `policy` | The turn was routed for human review, and by which rule. |
|
|
166
178
|
| `usage` / `billing` | Token usage and billing for the turn. |
|
|
167
179
|
|
|
180
|
+
## MCP server (for AI coding agents)
|
|
181
|
+
|
|
182
|
+
The package ships an MCP server, so the agent writing your integration can read
|
|
183
|
+
the docs, inspect your workspace's exact tool-call contract, classify messages,
|
|
184
|
+
and test sandbox chats while it codes:
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"mcpServers": {
|
|
189
|
+
"twelveai": {
|
|
190
|
+
"command": "npx",
|
|
191
|
+
"args": ["-y", "twelveai-mcp"],
|
|
192
|
+
"env": { "TWELVE_API_KEY": "sk_live_..." }
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Tools: `twelveai_docs`, `twelveai_manifest` (per-active-agent tool-call
|
|
199
|
+
contract), `twelveai_classify`, `twelveai_sandbox_chat`. Only `twelveai_docs`
|
|
200
|
+
works without an API key.
|
|
201
|
+
|
|
168
202
|
## Docs
|
|
169
203
|
|
|
170
204
|
Full platform documentation - agents, policies, customer tiers, WhatsApp channel, endpoint auth options, streaming: **https://console.twelveai.app/docs**
|
package/dist/index.cjs
CHANGED
|
@@ -67,6 +67,34 @@ var TwelveAI = class {
|
|
|
67
67
|
async confirm(continuation, input = {}) {
|
|
68
68
|
return this.chat({ message: "yes", ...input, continuation, confirmed: true });
|
|
69
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Level-0 integration: classify a message WITHOUT running the conversation.
|
|
72
|
+
* Returns the intent, a deterministic confidence score, and cheap extracted
|
|
73
|
+
* entities (amount / account number / phone) - you keep your existing flows
|
|
74
|
+
* and make the call yourself. Free: no tools run, nothing is stored.
|
|
75
|
+
*/
|
|
76
|
+
async classify(input) {
|
|
77
|
+
try {
|
|
78
|
+
const res = await this.fetchImpl(`${this.baseUrl}/v1/classify`, {
|
|
79
|
+
method: "POST",
|
|
80
|
+
headers: { "content-type": "application/json", "x-api-key": this.apiKey },
|
|
81
|
+
body: JSON.stringify({ message: input.message })
|
|
82
|
+
});
|
|
83
|
+
const data = await res.json().catch(() => ({}));
|
|
84
|
+
return {
|
|
85
|
+
ok: res.ok && data.ok !== false,
|
|
86
|
+
intent: data.intent ?? null,
|
|
87
|
+
label: data.label ?? null,
|
|
88
|
+
confidence: data.confidence ?? 0,
|
|
89
|
+
alternatives: data.alternatives ?? [],
|
|
90
|
+
entities: data.entities ?? {},
|
|
91
|
+
status: res.status,
|
|
92
|
+
...res.ok ? {} : { error: data.error ?? `Engine returned ${res.status}` }
|
|
93
|
+
};
|
|
94
|
+
} catch (error) {
|
|
95
|
+
return { ok: false, intent: null, label: null, confidence: 0, alternatives: [], entities: {}, status: 0, error: error?.message || "Could not reach the engine." };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
70
98
|
/* ------------------------------ internals ------------------------------ */
|
|
71
99
|
chatBody(input) {
|
|
72
100
|
const body = {};
|
package/dist/index.d.cts
CHANGED
|
@@ -123,6 +123,22 @@ interface ChatResponse {
|
|
|
123
123
|
* `clientAuth` headers and returns `{ ok, data }`.
|
|
124
124
|
*/
|
|
125
125
|
type HandoffExecutor = (call: PendingToolCall) => Promise<unknown>;
|
|
126
|
+
interface ClassifyResponse {
|
|
127
|
+
ok: boolean;
|
|
128
|
+
/** The winning agent intent, or null when nothing matched. */
|
|
129
|
+
intent: string | null;
|
|
130
|
+
label: string | null;
|
|
131
|
+
/** Deterministic confidence in [0, 0.95]; 0 = no match. */
|
|
132
|
+
confidence: number;
|
|
133
|
+
alternatives: Array<{
|
|
134
|
+
intent: string;
|
|
135
|
+
label: string;
|
|
136
|
+
}>;
|
|
137
|
+
/** Cheap extracted entities: amount, account_number, phone (when present). */
|
|
138
|
+
entities: Record<string, unknown>;
|
|
139
|
+
status: number;
|
|
140
|
+
error?: string;
|
|
141
|
+
}
|
|
126
142
|
|
|
127
143
|
/**
|
|
128
144
|
* The TwelveAI client. One call does the whole conversation protocol:
|
|
@@ -161,6 +177,15 @@ declare class TwelveAI {
|
|
|
161
177
|
* `confirmed: true` so the engine proceeds.
|
|
162
178
|
*/
|
|
163
179
|
confirm(continuation: string, input?: Omit<ChatInput, 'continuation' | 'confirmed'>): Promise<ChatResponse>;
|
|
180
|
+
/**
|
|
181
|
+
* Level-0 integration: classify a message WITHOUT running the conversation.
|
|
182
|
+
* Returns the intent, a deterministic confidence score, and cheap extracted
|
|
183
|
+
* entities (amount / account number / phone) - you keep your existing flows
|
|
184
|
+
* and make the call yourself. Free: no tools run, nothing is stored.
|
|
185
|
+
*/
|
|
186
|
+
classify(input: {
|
|
187
|
+
message: string;
|
|
188
|
+
}): Promise<ClassifyResponse>;
|
|
164
189
|
private chatBody;
|
|
165
190
|
private post;
|
|
166
191
|
/**
|
|
@@ -227,4 +252,4 @@ declare function createJwksVerifier(options?: JwksVerifierOptions): (input: JwtR
|
|
|
227
252
|
claims?: Record<string, unknown>;
|
|
228
253
|
}>;
|
|
229
254
|
|
|
230
|
-
export { type Attachment, type ChatInput, type ChatResponse, type HandoffExecutor, type HandoffRequest, type JwksVerifierOptions, type JwtRequestInput, type PendingToolCall, type SignedRequestInput, type ToolCall, type ToolResult, TwelveAI, type TwelveAIOptions, createJwksVerifier, verifySignedRequest, verifyWebhook };
|
|
255
|
+
export { type Attachment, type ChatInput, type ChatResponse, type ClassifyResponse, type HandoffExecutor, type HandoffRequest, type JwksVerifierOptions, type JwtRequestInput, type PendingToolCall, type SignedRequestInput, type ToolCall, type ToolResult, TwelveAI, type TwelveAIOptions, createJwksVerifier, verifySignedRequest, verifyWebhook };
|
package/dist/index.d.ts
CHANGED
|
@@ -123,6 +123,22 @@ interface ChatResponse {
|
|
|
123
123
|
* `clientAuth` headers and returns `{ ok, data }`.
|
|
124
124
|
*/
|
|
125
125
|
type HandoffExecutor = (call: PendingToolCall) => Promise<unknown>;
|
|
126
|
+
interface ClassifyResponse {
|
|
127
|
+
ok: boolean;
|
|
128
|
+
/** The winning agent intent, or null when nothing matched. */
|
|
129
|
+
intent: string | null;
|
|
130
|
+
label: string | null;
|
|
131
|
+
/** Deterministic confidence in [0, 0.95]; 0 = no match. */
|
|
132
|
+
confidence: number;
|
|
133
|
+
alternatives: Array<{
|
|
134
|
+
intent: string;
|
|
135
|
+
label: string;
|
|
136
|
+
}>;
|
|
137
|
+
/** Cheap extracted entities: amount, account_number, phone (when present). */
|
|
138
|
+
entities: Record<string, unknown>;
|
|
139
|
+
status: number;
|
|
140
|
+
error?: string;
|
|
141
|
+
}
|
|
126
142
|
|
|
127
143
|
/**
|
|
128
144
|
* The TwelveAI client. One call does the whole conversation protocol:
|
|
@@ -161,6 +177,15 @@ declare class TwelveAI {
|
|
|
161
177
|
* `confirmed: true` so the engine proceeds.
|
|
162
178
|
*/
|
|
163
179
|
confirm(continuation: string, input?: Omit<ChatInput, 'continuation' | 'confirmed'>): Promise<ChatResponse>;
|
|
180
|
+
/**
|
|
181
|
+
* Level-0 integration: classify a message WITHOUT running the conversation.
|
|
182
|
+
* Returns the intent, a deterministic confidence score, and cheap extracted
|
|
183
|
+
* entities (amount / account number / phone) - you keep your existing flows
|
|
184
|
+
* and make the call yourself. Free: no tools run, nothing is stored.
|
|
185
|
+
*/
|
|
186
|
+
classify(input: {
|
|
187
|
+
message: string;
|
|
188
|
+
}): Promise<ClassifyResponse>;
|
|
164
189
|
private chatBody;
|
|
165
190
|
private post;
|
|
166
191
|
/**
|
|
@@ -227,4 +252,4 @@ declare function createJwksVerifier(options?: JwksVerifierOptions): (input: JwtR
|
|
|
227
252
|
claims?: Record<string, unknown>;
|
|
228
253
|
}>;
|
|
229
254
|
|
|
230
|
-
export { type Attachment, type ChatInput, type ChatResponse, type HandoffExecutor, type HandoffRequest, type JwksVerifierOptions, type JwtRequestInput, type PendingToolCall, type SignedRequestInput, type ToolCall, type ToolResult, TwelveAI, type TwelveAIOptions, createJwksVerifier, verifySignedRequest, verifyWebhook };
|
|
255
|
+
export { type Attachment, type ChatInput, type ChatResponse, type ClassifyResponse, type HandoffExecutor, type HandoffRequest, type JwksVerifierOptions, type JwtRequestInput, type PendingToolCall, type SignedRequestInput, type ToolCall, type ToolResult, TwelveAI, type TwelveAIOptions, createJwksVerifier, verifySignedRequest, verifyWebhook };
|
package/dist/index.js
CHANGED
|
@@ -38,6 +38,34 @@ var TwelveAI = class {
|
|
|
38
38
|
async confirm(continuation, input = {}) {
|
|
39
39
|
return this.chat({ message: "yes", ...input, continuation, confirmed: true });
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Level-0 integration: classify a message WITHOUT running the conversation.
|
|
43
|
+
* Returns the intent, a deterministic confidence score, and cheap extracted
|
|
44
|
+
* entities (amount / account number / phone) - you keep your existing flows
|
|
45
|
+
* and make the call yourself. Free: no tools run, nothing is stored.
|
|
46
|
+
*/
|
|
47
|
+
async classify(input) {
|
|
48
|
+
try {
|
|
49
|
+
const res = await this.fetchImpl(`${this.baseUrl}/v1/classify`, {
|
|
50
|
+
method: "POST",
|
|
51
|
+
headers: { "content-type": "application/json", "x-api-key": this.apiKey },
|
|
52
|
+
body: JSON.stringify({ message: input.message })
|
|
53
|
+
});
|
|
54
|
+
const data = await res.json().catch(() => ({}));
|
|
55
|
+
return {
|
|
56
|
+
ok: res.ok && data.ok !== false,
|
|
57
|
+
intent: data.intent ?? null,
|
|
58
|
+
label: data.label ?? null,
|
|
59
|
+
confidence: data.confidence ?? 0,
|
|
60
|
+
alternatives: data.alternatives ?? [],
|
|
61
|
+
entities: data.entities ?? {},
|
|
62
|
+
status: res.status,
|
|
63
|
+
...res.ok ? {} : { error: data.error ?? `Engine returned ${res.status}` }
|
|
64
|
+
};
|
|
65
|
+
} catch (error) {
|
|
66
|
+
return { ok: false, intent: null, label: null, confidence: 0, alternatives: [], entities: {}, status: 0, error: error?.message || "Could not reach the engine." };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
41
69
|
/* ------------------------------ internals ------------------------------ */
|
|
42
70
|
chatBody(input) {
|
|
43
71
|
const body = {};
|
package/dist/mcp.cjs
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/mcp.ts
|
|
22
|
+
var mcp_exports = {};
|
|
23
|
+
__export(mcp_exports, {
|
|
24
|
+
createServer: () => createServer
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(mcp_exports);
|
|
27
|
+
var import_node_readline = require("readline");
|
|
28
|
+
var PROTOCOL_VERSION = "2024-11-05";
|
|
29
|
+
var SERVER_INFO = { name: "twelveai", version: "0.3.0" };
|
|
30
|
+
var TOOLS = [
|
|
31
|
+
{
|
|
32
|
+
name: "twelveai_docs",
|
|
33
|
+
description: "The TwelveAI integration guide (llms.txt): auth, the classify level, the chat protocol, client-fetch hand-offs, endpoint auth options, and every endpoint. Read this first.",
|
|
34
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false }
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "twelveai_manifest",
|
|
38
|
+
description: "The workspace's tool-call contract, per ACTIVE agent: every tool call the integration might receive, with parameter schemas, side effects, execution mode (hosted vs client), and endpoint bindings. Code against this.",
|
|
39
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false }
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: "twelveai_classify",
|
|
43
|
+
description: "Classify one customer message: returns intent, a confidence score, and extracted entities (amount / account number / phone). Free; nothing stored.",
|
|
44
|
+
inputSchema: {
|
|
45
|
+
type: "object",
|
|
46
|
+
properties: { message: { type: "string", description: "The customer message to classify." } },
|
|
47
|
+
required: ["message"],
|
|
48
|
+
additionalProperties: false
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "twelveai_sandbox_chat",
|
|
53
|
+
description: "Run one chat turn in SANDBOX mode (tools return sample data; nothing real is called or moved). Use it to see real response shapes - including pendingToolCalls hand-offs - while building.",
|
|
54
|
+
inputSchema: {
|
|
55
|
+
type: "object",
|
|
56
|
+
properties: {
|
|
57
|
+
message: { type: "string" },
|
|
58
|
+
customerId: { type: "string", description: 'Your id for the test end-user (default "mcp-test").' },
|
|
59
|
+
continuation: { type: "string", description: "Previous turn continuation, to test multi-turn." }
|
|
60
|
+
},
|
|
61
|
+
required: ["message"],
|
|
62
|
+
additionalProperties: false
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
];
|
|
66
|
+
function createServer(deps = {}) {
|
|
67
|
+
const fetchImpl = deps.fetch ?? globalThis.fetch;
|
|
68
|
+
const env = deps.env ?? process.env;
|
|
69
|
+
const baseUrl = (env.TWELVE_BASE_URL || "https://ai.twelveai.app").replace(/\/+$/, "");
|
|
70
|
+
function apiKey() {
|
|
71
|
+
return env.TWELVE_API_KEY || null;
|
|
72
|
+
}
|
|
73
|
+
async function api(path, body) {
|
|
74
|
+
const key = apiKey();
|
|
75
|
+
if (!key) return "Error: set the TWELVE_API_KEY environment variable (your workspace API key from https://console.twelveai.app) to use this tool.";
|
|
76
|
+
const res = await fetchImpl(`${baseUrl}${path}`, {
|
|
77
|
+
method: body ? "POST" : "GET",
|
|
78
|
+
headers: { "content-type": "application/json", "x-api-key": key },
|
|
79
|
+
...body ? { body: JSON.stringify(body) } : {}
|
|
80
|
+
});
|
|
81
|
+
const text = await res.text();
|
|
82
|
+
if (!res.ok) return `Error ${res.status}: ${text.slice(0, 500)}`;
|
|
83
|
+
try {
|
|
84
|
+
return JSON.stringify(JSON.parse(text), null, 2);
|
|
85
|
+
} catch {
|
|
86
|
+
return text;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async function runTool(name, args) {
|
|
90
|
+
switch (name) {
|
|
91
|
+
case "twelveai_docs": {
|
|
92
|
+
const res = await fetchImpl(`${baseUrl}/llms.txt`);
|
|
93
|
+
return res.ok ? await res.text() : `Error ${res.status}: could not fetch the docs.`;
|
|
94
|
+
}
|
|
95
|
+
case "twelveai_manifest":
|
|
96
|
+
return api("/v1/manifest");
|
|
97
|
+
case "twelveai_classify":
|
|
98
|
+
return api("/v1/classify", { message: String(args?.message ?? "") });
|
|
99
|
+
case "twelveai_sandbox_chat":
|
|
100
|
+
return api("/v1/chat", {
|
|
101
|
+
message: String(args?.message ?? ""),
|
|
102
|
+
customerId: args?.customerId ? String(args.customerId) : "mcp-test",
|
|
103
|
+
...args?.continuation ? { continuation: String(args.continuation) } : {},
|
|
104
|
+
sandbox: true
|
|
105
|
+
});
|
|
106
|
+
default:
|
|
107
|
+
throw Object.assign(new Error(`Unknown tool: ${name}`), { code: -32602 });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
async function handle(msg) {
|
|
111
|
+
if (!msg || msg.jsonrpc !== "2.0" || !msg.method) return null;
|
|
112
|
+
const id = msg.id;
|
|
113
|
+
const reply = (result) => ({ jsonrpc: "2.0", id: id ?? null, result });
|
|
114
|
+
try {
|
|
115
|
+
switch (msg.method) {
|
|
116
|
+
case "initialize":
|
|
117
|
+
return reply({ protocolVersion: PROTOCOL_VERSION, capabilities: { tools: {} }, serverInfo: SERVER_INFO });
|
|
118
|
+
case "ping":
|
|
119
|
+
return reply({});
|
|
120
|
+
case "tools/list":
|
|
121
|
+
return reply({ tools: TOOLS });
|
|
122
|
+
case "tools/call": {
|
|
123
|
+
const text = await runTool(String(msg.params?.name), msg.params?.arguments ?? {});
|
|
124
|
+
return reply({ content: [{ type: "text", text }], isError: text.startsWith("Error") });
|
|
125
|
+
}
|
|
126
|
+
default:
|
|
127
|
+
if (id === void 0) return null;
|
|
128
|
+
return { jsonrpc: "2.0", id, error: { code: -32601, message: `Method not found: ${msg.method}` } };
|
|
129
|
+
}
|
|
130
|
+
} catch (error) {
|
|
131
|
+
if (id === void 0) return null;
|
|
132
|
+
const e = error;
|
|
133
|
+
return { jsonrpc: "2.0", id, error: { code: e.code ?? -32603, message: e.message || "Internal error" } };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return { handle, tools: TOOLS };
|
|
137
|
+
}
|
|
138
|
+
var isMain = process.argv[1] && /mcp\.(js|cjs|ts)$/.test(process.argv[1]);
|
|
139
|
+
if (isMain) {
|
|
140
|
+
const server = createServer();
|
|
141
|
+
const rl = (0, import_node_readline.createInterface)({ input: process.stdin, crlfDelay: Infinity });
|
|
142
|
+
rl.on("line", async (line) => {
|
|
143
|
+
const trimmed = line.trim();
|
|
144
|
+
if (!trimmed) return;
|
|
145
|
+
let msg;
|
|
146
|
+
try {
|
|
147
|
+
msg = JSON.parse(trimmed);
|
|
148
|
+
} catch {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const res = await server.handle(msg);
|
|
152
|
+
if (res) process.stdout.write(JSON.stringify(res) + "\n");
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
156
|
+
0 && (module.exports = {
|
|
157
|
+
createServer
|
|
158
|
+
});
|
package/dist/mcp.d.cts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* The TwelveAI MCP server - plug TwelveAI into any MCP-capable coding agent
|
|
4
|
+
* (Claude Code, Cursor, etc.) so it can read the docs, inspect your workspace's
|
|
5
|
+
* tool-call contract, classify messages, and test chats in the sandbox while it
|
|
6
|
+
* writes your integration.
|
|
7
|
+
*
|
|
8
|
+
* npx twelveai-mcp # stdio transport
|
|
9
|
+
*
|
|
10
|
+
* Config (env):
|
|
11
|
+
* TWELVE_API_KEY your workspace key - required for manifest/classify/chat
|
|
12
|
+
* TWELVE_BASE_URL engine base (default https://ai.twelveai.app)
|
|
13
|
+
*
|
|
14
|
+
* Zero dependencies: a minimal JSON-RPC 2.0 loop over stdio implementing the
|
|
15
|
+
* MCP handshake, tools/list, and tools/call.
|
|
16
|
+
*/
|
|
17
|
+
interface Deps {
|
|
18
|
+
fetch?: typeof globalThis.fetch;
|
|
19
|
+
env?: Record<string, string | undefined>;
|
|
20
|
+
}
|
|
21
|
+
interface RpcMessage {
|
|
22
|
+
jsonrpc: '2.0';
|
|
23
|
+
id?: number | string | null;
|
|
24
|
+
method?: string;
|
|
25
|
+
params?: any;
|
|
26
|
+
result?: any;
|
|
27
|
+
error?: any;
|
|
28
|
+
}
|
|
29
|
+
/** Build the message handler; injectable deps keep it unit-testable. */
|
|
30
|
+
declare function createServer(deps?: Deps): {
|
|
31
|
+
handle: (msg: RpcMessage) => Promise<RpcMessage | null>;
|
|
32
|
+
tools: ({
|
|
33
|
+
name: string;
|
|
34
|
+
description: string;
|
|
35
|
+
inputSchema: {
|
|
36
|
+
type: string;
|
|
37
|
+
properties: {
|
|
38
|
+
message?: undefined;
|
|
39
|
+
customerId?: undefined;
|
|
40
|
+
continuation?: undefined;
|
|
41
|
+
};
|
|
42
|
+
additionalProperties: boolean;
|
|
43
|
+
required?: undefined;
|
|
44
|
+
};
|
|
45
|
+
} | {
|
|
46
|
+
name: string;
|
|
47
|
+
description: string;
|
|
48
|
+
inputSchema: {
|
|
49
|
+
type: string;
|
|
50
|
+
properties: {
|
|
51
|
+
message: {
|
|
52
|
+
type: string;
|
|
53
|
+
description: string;
|
|
54
|
+
};
|
|
55
|
+
customerId?: undefined;
|
|
56
|
+
continuation?: undefined;
|
|
57
|
+
};
|
|
58
|
+
required: string[];
|
|
59
|
+
additionalProperties: boolean;
|
|
60
|
+
};
|
|
61
|
+
} | {
|
|
62
|
+
name: string;
|
|
63
|
+
description: string;
|
|
64
|
+
inputSchema: {
|
|
65
|
+
type: string;
|
|
66
|
+
properties: {
|
|
67
|
+
message: {
|
|
68
|
+
type: string;
|
|
69
|
+
description?: undefined;
|
|
70
|
+
};
|
|
71
|
+
customerId: {
|
|
72
|
+
type: string;
|
|
73
|
+
description: string;
|
|
74
|
+
};
|
|
75
|
+
continuation: {
|
|
76
|
+
type: string;
|
|
77
|
+
description: string;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
required: string[];
|
|
81
|
+
additionalProperties: boolean;
|
|
82
|
+
};
|
|
83
|
+
})[];
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export { createServer };
|
package/dist/mcp.d.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* The TwelveAI MCP server - plug TwelveAI into any MCP-capable coding agent
|
|
4
|
+
* (Claude Code, Cursor, etc.) so it can read the docs, inspect your workspace's
|
|
5
|
+
* tool-call contract, classify messages, and test chats in the sandbox while it
|
|
6
|
+
* writes your integration.
|
|
7
|
+
*
|
|
8
|
+
* npx twelveai-mcp # stdio transport
|
|
9
|
+
*
|
|
10
|
+
* Config (env):
|
|
11
|
+
* TWELVE_API_KEY your workspace key - required for manifest/classify/chat
|
|
12
|
+
* TWELVE_BASE_URL engine base (default https://ai.twelveai.app)
|
|
13
|
+
*
|
|
14
|
+
* Zero dependencies: a minimal JSON-RPC 2.0 loop over stdio implementing the
|
|
15
|
+
* MCP handshake, tools/list, and tools/call.
|
|
16
|
+
*/
|
|
17
|
+
interface Deps {
|
|
18
|
+
fetch?: typeof globalThis.fetch;
|
|
19
|
+
env?: Record<string, string | undefined>;
|
|
20
|
+
}
|
|
21
|
+
interface RpcMessage {
|
|
22
|
+
jsonrpc: '2.0';
|
|
23
|
+
id?: number | string | null;
|
|
24
|
+
method?: string;
|
|
25
|
+
params?: any;
|
|
26
|
+
result?: any;
|
|
27
|
+
error?: any;
|
|
28
|
+
}
|
|
29
|
+
/** Build the message handler; injectable deps keep it unit-testable. */
|
|
30
|
+
declare function createServer(deps?: Deps): {
|
|
31
|
+
handle: (msg: RpcMessage) => Promise<RpcMessage | null>;
|
|
32
|
+
tools: ({
|
|
33
|
+
name: string;
|
|
34
|
+
description: string;
|
|
35
|
+
inputSchema: {
|
|
36
|
+
type: string;
|
|
37
|
+
properties: {
|
|
38
|
+
message?: undefined;
|
|
39
|
+
customerId?: undefined;
|
|
40
|
+
continuation?: undefined;
|
|
41
|
+
};
|
|
42
|
+
additionalProperties: boolean;
|
|
43
|
+
required?: undefined;
|
|
44
|
+
};
|
|
45
|
+
} | {
|
|
46
|
+
name: string;
|
|
47
|
+
description: string;
|
|
48
|
+
inputSchema: {
|
|
49
|
+
type: string;
|
|
50
|
+
properties: {
|
|
51
|
+
message: {
|
|
52
|
+
type: string;
|
|
53
|
+
description: string;
|
|
54
|
+
};
|
|
55
|
+
customerId?: undefined;
|
|
56
|
+
continuation?: undefined;
|
|
57
|
+
};
|
|
58
|
+
required: string[];
|
|
59
|
+
additionalProperties: boolean;
|
|
60
|
+
};
|
|
61
|
+
} | {
|
|
62
|
+
name: string;
|
|
63
|
+
description: string;
|
|
64
|
+
inputSchema: {
|
|
65
|
+
type: string;
|
|
66
|
+
properties: {
|
|
67
|
+
message: {
|
|
68
|
+
type: string;
|
|
69
|
+
description?: undefined;
|
|
70
|
+
};
|
|
71
|
+
customerId: {
|
|
72
|
+
type: string;
|
|
73
|
+
description: string;
|
|
74
|
+
};
|
|
75
|
+
continuation: {
|
|
76
|
+
type: string;
|
|
77
|
+
description: string;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
required: string[];
|
|
81
|
+
additionalProperties: boolean;
|
|
82
|
+
};
|
|
83
|
+
})[];
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export { createServer };
|
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/mcp.ts
|
|
4
|
+
import { createInterface } from "readline";
|
|
5
|
+
var PROTOCOL_VERSION = "2024-11-05";
|
|
6
|
+
var SERVER_INFO = { name: "twelveai", version: "0.3.0" };
|
|
7
|
+
var TOOLS = [
|
|
8
|
+
{
|
|
9
|
+
name: "twelveai_docs",
|
|
10
|
+
description: "The TwelveAI integration guide (llms.txt): auth, the classify level, the chat protocol, client-fetch hand-offs, endpoint auth options, and every endpoint. Read this first.",
|
|
11
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false }
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: "twelveai_manifest",
|
|
15
|
+
description: "The workspace's tool-call contract, per ACTIVE agent: every tool call the integration might receive, with parameter schemas, side effects, execution mode (hosted vs client), and endpoint bindings. Code against this.",
|
|
16
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false }
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: "twelveai_classify",
|
|
20
|
+
description: "Classify one customer message: returns intent, a confidence score, and extracted entities (amount / account number / phone). Free; nothing stored.",
|
|
21
|
+
inputSchema: {
|
|
22
|
+
type: "object",
|
|
23
|
+
properties: { message: { type: "string", description: "The customer message to classify." } },
|
|
24
|
+
required: ["message"],
|
|
25
|
+
additionalProperties: false
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: "twelveai_sandbox_chat",
|
|
30
|
+
description: "Run one chat turn in SANDBOX mode (tools return sample data; nothing real is called or moved). Use it to see real response shapes - including pendingToolCalls hand-offs - while building.",
|
|
31
|
+
inputSchema: {
|
|
32
|
+
type: "object",
|
|
33
|
+
properties: {
|
|
34
|
+
message: { type: "string" },
|
|
35
|
+
customerId: { type: "string", description: 'Your id for the test end-user (default "mcp-test").' },
|
|
36
|
+
continuation: { type: "string", description: "Previous turn continuation, to test multi-turn." }
|
|
37
|
+
},
|
|
38
|
+
required: ["message"],
|
|
39
|
+
additionalProperties: false
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
];
|
|
43
|
+
function createServer(deps = {}) {
|
|
44
|
+
const fetchImpl = deps.fetch ?? globalThis.fetch;
|
|
45
|
+
const env = deps.env ?? process.env;
|
|
46
|
+
const baseUrl = (env.TWELVE_BASE_URL || "https://ai.twelveai.app").replace(/\/+$/, "");
|
|
47
|
+
function apiKey() {
|
|
48
|
+
return env.TWELVE_API_KEY || null;
|
|
49
|
+
}
|
|
50
|
+
async function api(path, body) {
|
|
51
|
+
const key = apiKey();
|
|
52
|
+
if (!key) return "Error: set the TWELVE_API_KEY environment variable (your workspace API key from https://console.twelveai.app) to use this tool.";
|
|
53
|
+
const res = await fetchImpl(`${baseUrl}${path}`, {
|
|
54
|
+
method: body ? "POST" : "GET",
|
|
55
|
+
headers: { "content-type": "application/json", "x-api-key": key },
|
|
56
|
+
...body ? { body: JSON.stringify(body) } : {}
|
|
57
|
+
});
|
|
58
|
+
const text = await res.text();
|
|
59
|
+
if (!res.ok) return `Error ${res.status}: ${text.slice(0, 500)}`;
|
|
60
|
+
try {
|
|
61
|
+
return JSON.stringify(JSON.parse(text), null, 2);
|
|
62
|
+
} catch {
|
|
63
|
+
return text;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function runTool(name, args) {
|
|
67
|
+
switch (name) {
|
|
68
|
+
case "twelveai_docs": {
|
|
69
|
+
const res = await fetchImpl(`${baseUrl}/llms.txt`);
|
|
70
|
+
return res.ok ? await res.text() : `Error ${res.status}: could not fetch the docs.`;
|
|
71
|
+
}
|
|
72
|
+
case "twelveai_manifest":
|
|
73
|
+
return api("/v1/manifest");
|
|
74
|
+
case "twelveai_classify":
|
|
75
|
+
return api("/v1/classify", { message: String(args?.message ?? "") });
|
|
76
|
+
case "twelveai_sandbox_chat":
|
|
77
|
+
return api("/v1/chat", {
|
|
78
|
+
message: String(args?.message ?? ""),
|
|
79
|
+
customerId: args?.customerId ? String(args.customerId) : "mcp-test",
|
|
80
|
+
...args?.continuation ? { continuation: String(args.continuation) } : {},
|
|
81
|
+
sandbox: true
|
|
82
|
+
});
|
|
83
|
+
default:
|
|
84
|
+
throw Object.assign(new Error(`Unknown tool: ${name}`), { code: -32602 });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function handle(msg) {
|
|
88
|
+
if (!msg || msg.jsonrpc !== "2.0" || !msg.method) return null;
|
|
89
|
+
const id = msg.id;
|
|
90
|
+
const reply = (result) => ({ jsonrpc: "2.0", id: id ?? null, result });
|
|
91
|
+
try {
|
|
92
|
+
switch (msg.method) {
|
|
93
|
+
case "initialize":
|
|
94
|
+
return reply({ protocolVersion: PROTOCOL_VERSION, capabilities: { tools: {} }, serverInfo: SERVER_INFO });
|
|
95
|
+
case "ping":
|
|
96
|
+
return reply({});
|
|
97
|
+
case "tools/list":
|
|
98
|
+
return reply({ tools: TOOLS });
|
|
99
|
+
case "tools/call": {
|
|
100
|
+
const text = await runTool(String(msg.params?.name), msg.params?.arguments ?? {});
|
|
101
|
+
return reply({ content: [{ type: "text", text }], isError: text.startsWith("Error") });
|
|
102
|
+
}
|
|
103
|
+
default:
|
|
104
|
+
if (id === void 0) return null;
|
|
105
|
+
return { jsonrpc: "2.0", id, error: { code: -32601, message: `Method not found: ${msg.method}` } };
|
|
106
|
+
}
|
|
107
|
+
} catch (error) {
|
|
108
|
+
if (id === void 0) return null;
|
|
109
|
+
const e = error;
|
|
110
|
+
return { jsonrpc: "2.0", id, error: { code: e.code ?? -32603, message: e.message || "Internal error" } };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return { handle, tools: TOOLS };
|
|
114
|
+
}
|
|
115
|
+
var isMain = process.argv[1] && /mcp\.(js|cjs|ts)$/.test(process.argv[1]);
|
|
116
|
+
if (isMain) {
|
|
117
|
+
const server = createServer();
|
|
118
|
+
const rl = createInterface({ input: process.stdin, crlfDelay: Infinity });
|
|
119
|
+
rl.on("line", async (line) => {
|
|
120
|
+
const trimmed = line.trim();
|
|
121
|
+
if (!trimmed) return;
|
|
122
|
+
let msg;
|
|
123
|
+
try {
|
|
124
|
+
msg = JSON.parse(trimmed);
|
|
125
|
+
} catch {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const res = await server.handle(msg);
|
|
129
|
+
if (res) process.stdout.write(JSON.stringify(res) + "\n");
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
export {
|
|
133
|
+
createServer
|
|
134
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "twelveai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Official SDK for TwelveAI - AI infrastructure for conversational banking. One call runs the whole chat protocol: routing, grounded tool calls, client-fetch hand-offs, confirmations, and request verification.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "TwelveAI",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"node": ">=18"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
|
-
"build": "tsup src/index.ts --format esm,cjs --dts --clean",
|
|
41
|
+
"build": "tsup src/index.ts src/mcp.ts --format esm,cjs --dts --clean",
|
|
42
42
|
"test": "vitest run",
|
|
43
43
|
"typecheck": "tsc --noEmit",
|
|
44
44
|
"prepublishOnly": "npm run typecheck && npm run test && npm run build"
|
|
@@ -54,5 +54,8 @@
|
|
|
54
54
|
},
|
|
55
55
|
"publishConfig": {
|
|
56
56
|
"access": "public"
|
|
57
|
+
},
|
|
58
|
+
"bin": {
|
|
59
|
+
"twelveai-mcp": "./dist/mcp.js"
|
|
57
60
|
}
|
|
58
61
|
}
|