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 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.1.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
  }