@tangle-network/tcloud 0.4.13 → 0.4.14

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  TCloudClient
3
- } from "./chunk-U4VOGRVW.js";
3
+ } from "./chunk-LUJXMUH5.js";
4
4
 
5
5
  // src/shielded.ts
6
6
  import { privateKeyToAccount } from "viem/accounts";
@@ -0,0 +1,16 @@
1
+ // src/version.ts
2
+ import * as fs from "fs";
3
+ function packageVersion() {
4
+ try {
5
+ const packageJson = JSON.parse(
6
+ fs.readFileSync(new URL("../package.json", import.meta.url), "utf-8")
7
+ );
8
+ if (typeof packageJson.version === "string") return packageJson.version;
9
+ } catch {
10
+ }
11
+ return "0.0.0";
12
+ }
13
+
14
+ export {
15
+ packageVersion
16
+ };
@@ -998,6 +998,26 @@ var TCloudClient = class _TCloudClient {
998
998
  })
999
999
  });
1000
1000
  }
1001
+ /** Run a multi-step deep-research task through Tangle Router billing and
1002
+ * provider routing (POST /v1/research). Slower and costlier than `search` —
1003
+ * the provider synthesizes an answer over many fetches. Pick depth with
1004
+ * `effort` (provider-specific vocabulary; see {@link ResearchOptions.effort}). */
1005
+ async research(options) {
1006
+ return this._request(`${this.baseURL}/research`, {
1007
+ method: "POST",
1008
+ body: JSON.stringify({
1009
+ query: options.query,
1010
+ provider: options.provider,
1011
+ model: options.model,
1012
+ effort: options.effort,
1013
+ maxResults: options.maxResults,
1014
+ searchRecency: options.searchRecency,
1015
+ includeDomains: options.includeDomains,
1016
+ excludeDomains: options.excludeDomains,
1017
+ outputSchema: options.outputSchema
1018
+ })
1019
+ });
1020
+ }
1001
1021
  /** Text-to-speech */
1002
1022
  async speech(options) {
1003
1023
  const res = await this._requestRaw(`${this.baseURL}/audio/speech`, {
@@ -4,10 +4,10 @@ import {
4
4
  import {
5
5
  createShieldedClient,
6
6
  generateWallet
7
- } from "./chunk-THOMQXU5.js";
7
+ } from "./chunk-26M4HGWE.js";
8
8
  import {
9
9
  TCloudClient
10
- } from "./chunk-U4VOGRVW.js";
10
+ } from "./chunk-LUJXMUH5.js";
11
11
 
12
12
  // src/index.ts
13
13
  import {
package/dist/cli.cjs CHANGED
@@ -6,6 +6,13 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
9
16
  var __copyProps = (to, from, except, desc) => {
10
17
  if (from && typeof from === "object" || typeof from === "function") {
11
18
  for (let key of __getOwnPropNames(from))
@@ -23,6 +30,238 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
30
  mod
24
31
  ));
25
32
 
33
+ // src/version.ts
34
+ function packageVersion() {
35
+ try {
36
+ const packageJson = JSON.parse(
37
+ fs.readFileSync(new URL("../package.json", import_meta.url), "utf-8")
38
+ );
39
+ if (typeof packageJson.version === "string") return packageJson.version;
40
+ } catch {
41
+ }
42
+ return "0.0.0";
43
+ }
44
+ var fs, import_meta;
45
+ var init_version = __esm({
46
+ "src/version.ts"() {
47
+ "use strict";
48
+ fs = __toESM(require("fs"), 1);
49
+ import_meta = {};
50
+ }
51
+ });
52
+
53
+ // src/mcp.ts
54
+ var mcp_exports = {};
55
+ __export(mcp_exports, {
56
+ runMcpServer: () => runMcpServer
57
+ });
58
+ function optionalMaxResults(value) {
59
+ if (value == null) return void 0;
60
+ const n = Number(value);
61
+ return Number.isFinite(n) && n >= 0 ? n : void 0;
62
+ }
63
+ function requireEnum(value, allowed, label) {
64
+ if (value == null) return void 0;
65
+ if (typeof value !== "string" || !allowed.has(value)) {
66
+ throw new InvalidParams(`invalid ${label}: ${String(value)}`);
67
+ }
68
+ return value;
69
+ }
70
+ function optionalDomains(value) {
71
+ if (!Array.isArray(value)) return void 0;
72
+ const domains = value.filter((d) => typeof d === "string");
73
+ return domains.length ? domains : void 0;
74
+ }
75
+ async function runMcpServer(client, opts = {}) {
76
+ const input = opts.input ?? process.stdin;
77
+ const output = opts.output ?? process.stdout;
78
+ const rl = readline.createInterface({ input });
79
+ const send = (msg) => {
80
+ output.write(`${JSON.stringify(msg)}
81
+ `);
82
+ };
83
+ const ok = (id, result) => send({ jsonrpc: "2.0", id, result });
84
+ const fail = (id, code, message) => send({ jsonrpc: "2.0", id, error: { code, message } });
85
+ const toolError = (id, message) => ok(id, { content: [{ type: "text", text: message }], isError: true });
86
+ process.stderr.write("tcloud mcp: web_search + deep_research server ready (stdio)\n");
87
+ async function handleRequest(req) {
88
+ const { id, method, params } = req;
89
+ try {
90
+ if (method === "initialize") {
91
+ const requested = params?.protocolVersion ?? DEFAULT_PROTOCOL_VERSION;
92
+ const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.has(requested) ? requested : DEFAULT_PROTOCOL_VERSION;
93
+ ok(id, {
94
+ protocolVersion,
95
+ capabilities: { tools: {} },
96
+ serverInfo: { name: "tangle-tcloud", version: packageVersion() }
97
+ });
98
+ } else if (method === "tools/list") {
99
+ ok(id, { tools: TOOLS });
100
+ } else if (method === "tools/call") {
101
+ const name = params?.name;
102
+ if (name === "web_search") {
103
+ await handleWebSearch(id, params?.arguments ?? {});
104
+ } else if (name === "deep_research") {
105
+ await handleDeepResearch(id, params?.arguments ?? {});
106
+ } else {
107
+ fail(id, -32602, `unknown tool: ${String(name)}`);
108
+ }
109
+ } else {
110
+ fail(id, -32601, `method not found: ${String(method)}`);
111
+ }
112
+ } catch (e) {
113
+ if (e instanceof InvalidParams) {
114
+ fail(id, -32602, e.message);
115
+ } else {
116
+ fail(id, -32603, e instanceof Error ? e.message : String(e));
117
+ }
118
+ }
119
+ }
120
+ async function handleWebSearch(id, args) {
121
+ const query = String(args.query ?? "").trim();
122
+ if (!query) {
123
+ fail(id, -32602, 'web_search requires a non-empty "query"');
124
+ return;
125
+ }
126
+ const provider = requireEnum(args.provider, SEARCH_PROVIDERS, "provider");
127
+ const searchRecency = requireEnum(args.recency, RECENCY_WINDOWS, "recency");
128
+ const maxResults = optionalMaxResults(args.maxResults);
129
+ const includeDomains = optionalDomains(args.includeDomains);
130
+ const excludeDomains = optionalDomains(args.excludeDomains);
131
+ try {
132
+ const resp = await client.search({
133
+ query,
134
+ ...provider ? { provider } : {},
135
+ ...maxResults != null ? { maxResults } : {},
136
+ ...searchRecency ? { searchRecency } : {},
137
+ ...includeDomains ? { includeDomains } : {},
138
+ ...excludeDomains ? { excludeDomains } : {}
139
+ });
140
+ const hits = resp.data ?? [];
141
+ const text = hits.length ? hits.map((h, i) => `${i + 1}. ${h.title}
142
+ ${h.url}${h.snippet ? `
143
+ ${h.snippet}` : ""}`).join("\n\n") : `No results for "${resp.query ?? query}".`;
144
+ ok(id, { content: [{ type: "text", text }] });
145
+ } catch (e) {
146
+ toolError(id, e instanceof Error ? e.message : String(e));
147
+ }
148
+ }
149
+ async function handleDeepResearch(id, args) {
150
+ const query = String(args.query ?? "").trim();
151
+ if (!query) {
152
+ fail(id, -32602, 'deep_research requires a non-empty "query"');
153
+ return;
154
+ }
155
+ const provider = requireEnum(args.provider, RESEARCH_PROVIDERS, "provider");
156
+ const searchRecency = requireEnum(args.searchRecency, RECENCY_WINDOWS, "searchRecency");
157
+ const maxResults = optionalMaxResults(args.maxResults);
158
+ const includeDomains = optionalDomains(args.includeDomains);
159
+ const excludeDomains = optionalDomains(args.excludeDomains);
160
+ try {
161
+ const resp = await client.research({
162
+ query,
163
+ ...provider ? { provider } : {},
164
+ ...typeof args.effort === "string" ? { effort: args.effort } : {},
165
+ ...maxResults != null ? { maxResults } : {},
166
+ ...searchRecency ? { searchRecency } : {},
167
+ ...includeDomains ? { includeDomains } : {},
168
+ ...excludeDomains ? { excludeDomains } : {},
169
+ ...args.outputSchema !== void 0 ? { outputSchema: args.outputSchema } : {}
170
+ });
171
+ const sources = resp.results ?? [];
172
+ const sourceList = sources.length ? `
173
+
174
+ Sources:
175
+ ${sources.map((h, i) => `${i + 1}. ${h.title}
176
+ ${h.url}`).join("\n")}` : "";
177
+ const text = `${resp.answer ?? ""}${sourceList}`.trim() || `No research result for "${resp.query ?? query}".`;
178
+ ok(id, { content: [{ type: "text", text }] });
179
+ } catch (e) {
180
+ toolError(id, e instanceof Error ? e.message : String(e));
181
+ }
182
+ }
183
+ for await (const line of rl) {
184
+ const trimmed = line.trim();
185
+ if (!trimmed) continue;
186
+ let req;
187
+ try {
188
+ req = JSON.parse(trimmed);
189
+ } catch {
190
+ send({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "Parse error" } });
191
+ continue;
192
+ }
193
+ if (req.id === void 0 || req.id === null) continue;
194
+ void handleRequest(req);
195
+ }
196
+ }
197
+ var readline, SEARCH_PROVIDERS, RESEARCH_PROVIDERS, RECENCY_WINDOWS, SUPPORTED_PROTOCOL_VERSIONS, DEFAULT_PROTOCOL_VERSION, DOMAIN_FILTER_SCHEMA, TOOLS, InvalidParams;
198
+ var init_mcp = __esm({
199
+ "src/mcp.ts"() {
200
+ "use strict";
201
+ readline = __toESM(require("readline"), 1);
202
+ init_version();
203
+ SEARCH_PROVIDERS = /* @__PURE__ */ new Set(["perplexity", "exa", "you", "parallel", "tavily", "brave"]);
204
+ RESEARCH_PROVIDERS = /* @__PURE__ */ new Set(["perplexity", "exa", "you", "parallel", "tavily"]);
205
+ RECENCY_WINDOWS = /* @__PURE__ */ new Set(["day", "week", "month", "year"]);
206
+ SUPPORTED_PROTOCOL_VERSIONS = /* @__PURE__ */ new Set(["2025-06-18", "2025-03-26"]);
207
+ DEFAULT_PROTOCOL_VERSION = "2025-06-18";
208
+ DOMAIN_FILTER_SCHEMA = {
209
+ type: "array",
210
+ items: { type: "string" }
211
+ };
212
+ TOOLS = [
213
+ {
214
+ name: "web_search",
215
+ description: "Search the web via the Tangle router and return live results (title, url, snippet). Use it to find primary sources and to VERIFY that a citation (paper, PMID/DOI, patent, fact) actually exists before relying on it.",
216
+ inputSchema: {
217
+ type: "object",
218
+ properties: {
219
+ query: { type: "string", description: "The search query." },
220
+ provider: {
221
+ type: "string",
222
+ description: "Optional provider: exa | parallel | perplexity | tavily | brave | you."
223
+ },
224
+ maxResults: { type: "number", description: "Optional max number of results." },
225
+ recency: { type: "string", description: "Optional recency window: day | week | month | year." },
226
+ includeDomains: { ...DOMAIN_FILTER_SCHEMA, description: "Optional: restrict results to these domains." },
227
+ excludeDomains: { ...DOMAIN_FILTER_SCHEMA, description: "Optional: drop results from these domains." }
228
+ },
229
+ required: ["query"]
230
+ }
231
+ },
232
+ {
233
+ name: "deep_research",
234
+ description: 'Run a multi-step DEEP RESEARCH task via the Tangle router: the provider reads many sources and returns one synthesized answer with citations. Slower and costlier than web_search \u2014 use it for questions that need cross-source synthesis (current API/version landscapes, comparisons, "state of X"), not single-fact lookups.',
235
+ inputSchema: {
236
+ type: "object",
237
+ properties: {
238
+ query: { type: "string", description: "The research question." },
239
+ provider: {
240
+ type: "string",
241
+ description: "Optional provider: you | exa | perplexity | tavily | parallel."
242
+ },
243
+ effort: {
244
+ type: "string",
245
+ description: "Optional depth/cost dial (provider-specific): you lite|standard|deep|exhaustive \xB7 perplexity minimal|low|medium|high \xB7 exa deep-lite|deep|deep-reasoning \xB7 tavily mini|pro|auto \xB7 parallel lite|base|core|pro|ultra."
246
+ },
247
+ maxResults: { type: "number", description: "Optional max number of supporting sources." },
248
+ searchRecency: { type: "string", description: "Optional recency window: day | week | month | year." },
249
+ includeDomains: { ...DOMAIN_FILTER_SCHEMA, description: "Optional: restrict sources to these domains." },
250
+ excludeDomains: { ...DOMAIN_FILTER_SCHEMA, description: "Optional: drop sources from these domains." },
251
+ outputSchema: {
252
+ type: "object",
253
+ description: "Optional JSON schema requesting structured output from the provider."
254
+ }
255
+ },
256
+ required: ["query"]
257
+ }
258
+ }
259
+ ];
260
+ InvalidParams = class extends Error {
261
+ };
262
+ }
263
+ });
264
+
26
265
  // src/cli.ts
27
266
  var import_commander = require("commander");
28
267
 
@@ -1026,6 +1265,26 @@ var TCloudClient = class _TCloudClient {
1026
1265
  })
1027
1266
  });
1028
1267
  }
1268
+ /** Run a multi-step deep-research task through Tangle Router billing and
1269
+ * provider routing (POST /v1/research). Slower and costlier than `search` —
1270
+ * the provider synthesizes an answer over many fetches. Pick depth with
1271
+ * `effort` (provider-specific vocabulary; see {@link ResearchOptions.effort}). */
1272
+ async research(options) {
1273
+ return this._request(`${this.baseURL}/research`, {
1274
+ method: "POST",
1275
+ body: JSON.stringify({
1276
+ query: options.query,
1277
+ provider: options.provider,
1278
+ model: options.model,
1279
+ effort: options.effort,
1280
+ maxResults: options.maxResults,
1281
+ searchRecency: options.searchRecency,
1282
+ includeDomains: options.includeDomains,
1283
+ excludeDomains: options.excludeDomains,
1284
+ outputSchema: options.outputSchema
1285
+ })
1286
+ });
1287
+ }
1029
1288
  /** Text-to-speech */
1030
1289
  async speech(options) {
1031
1290
  const res = await this._requestRaw(`${this.baseURL}/audio/speech`, {
@@ -2007,20 +2266,20 @@ var TCloud = class _TCloud extends TCloudClient {
2007
2266
  };
2008
2267
 
2009
2268
  // src/cli.ts
2010
- var fs = __toESM(require("fs"), 1);
2269
+ init_version();
2270
+ var fs2 = __toESM(require("fs"), 1);
2011
2271
  var path = __toESM(require("path"), 1);
2012
- var readline = __toESM(require("readline"), 1);
2272
+ var readline2 = __toESM(require("readline"), 1);
2013
2273
  var import_child_process = require("child_process");
2014
- var import_meta = {};
2015
2274
  var CONFIG_DIR = path.join(process.env.HOME || "~", ".tcloud");
2016
2275
  var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
2017
2276
  var WALLETS_FILE = path.join(CONFIG_DIR, "wallets.json");
2018
2277
  function ensureDir() {
2019
- if (!fs.existsSync(CONFIG_DIR)) fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
2278
+ if (!fs2.existsSync(CONFIG_DIR)) fs2.mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
2020
2279
  }
2021
2280
  function loadConfig() {
2022
2281
  ensureDir();
2023
- const fileConfig = fs.existsSync(CONFIG_FILE) ? JSON.parse(fs.readFileSync(CONFIG_FILE, "utf-8")) : { apiUrl: "https://router.tangle.tools", defaultModel: "gpt-4o-mini", chainId: 3799 };
2282
+ const fileConfig = fs2.existsSync(CONFIG_FILE) ? JSON.parse(fs2.readFileSync(CONFIG_FILE, "utf-8")) : { apiUrl: "https://router.tangle.tools", defaultModel: "gpt-4o-mini", chainId: 3799 };
2024
2283
  return {
2025
2284
  ...fileConfig,
2026
2285
  ...process.env.TANGLE_ROUTER_URL ? { apiUrl: process.env.TANGLE_ROUTER_URL.replace(/\/v1\/?$/, "") } : {},
@@ -2031,16 +2290,16 @@ function loadConfig() {
2031
2290
  }
2032
2291
  function saveConfig(c) {
2033
2292
  ensureDir();
2034
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(c, null, 2), { mode: 384 });
2293
+ fs2.writeFileSync(CONFIG_FILE, JSON.stringify(c, null, 2), { mode: 384 });
2035
2294
  }
2036
2295
  function loadWallets() {
2037
2296
  ensureDir();
2038
- if (fs.existsSync(WALLETS_FILE)) return JSON.parse(fs.readFileSync(WALLETS_FILE, "utf-8"));
2297
+ if (fs2.existsSync(WALLETS_FILE)) return JSON.parse(fs2.readFileSync(WALLETS_FILE, "utf-8"));
2039
2298
  return [];
2040
2299
  }
2041
2300
  function saveWallets(w) {
2042
2301
  ensureDir();
2043
- fs.writeFileSync(WALLETS_FILE, JSON.stringify(w, null, 2), { mode: 384 });
2302
+ fs2.writeFileSync(WALLETS_FILE, JSON.stringify(w, null, 2), { mode: 384 });
2044
2303
  }
2045
2304
  function getClient(opts) {
2046
2305
  const config = loadConfig();
@@ -2073,16 +2332,6 @@ function teeType(value) {
2073
2332
  if (!value) return void 0;
2074
2333
  return value.toLowerCase();
2075
2334
  }
2076
- function packageVersion() {
2077
- try {
2078
- const packageJson = JSON.parse(
2079
- fs.readFileSync(new URL("../package.json", import_meta.url), "utf-8")
2080
- );
2081
- if (typeof packageJson.version === "string") return packageJson.version;
2082
- } catch {
2083
- }
2084
- return "0.0.0";
2085
- }
2086
2335
  function printJson(value) {
2087
2336
  console.log(JSON.stringify(value, null, 2));
2088
2337
  }
@@ -2292,7 +2541,7 @@ program.command("chat").description("Chat with a model").argument("[message]", "
2292
2541
  const client = getClient({ private: opts.private });
2293
2542
  const model = opts.model || loadConfig().defaultModel;
2294
2543
  if (!message) {
2295
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
2544
+ const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
2296
2545
  console.log(`tcloud chat \u2014 ${model}${opts.private ? " (private)" : ""}
2297
2546
  Ctrl+C to exit.
2298
2547
  `);
@@ -2382,6 +2631,11 @@ program.command("search").description("Search the web").argument("<query>", "Sea
2382
2631
  process.exit(1);
2383
2632
  }
2384
2633
  });
2634
+ program.command("mcp").description('Run a Model Context Protocol (stdio) server exposing Tangle tools (web_search, deep_research). Mount with: { "command": ["tcloud", "mcp"] }').action(async () => {
2635
+ const { runMcpServer: runMcpServer2 } = await Promise.resolve().then(() => (init_mcp(), mcp_exports));
2636
+ const client = getClient();
2637
+ await runMcpServer2(client);
2638
+ });
2385
2639
  program.command("image-generate").description("Generate an image").requiredOption("-p, --prompt <prompt>", "Prompt").option("-m, --model <model>", "Image model").option("--size <size>", "Output size").option("--quality <quality>", "Output quality").option("-n, --count <n>", "Number of images").option("--response-format <format>", "url or b64_json").option("--json", "Print raw JSON response").action(async (opts) => {
2386
2640
  const client = getClient();
2387
2641
  try {
@@ -2441,7 +2695,7 @@ program.command("speech").description("Generate speech audio").requiredOption("-
2441
2695
  model: opts.model,
2442
2696
  voice: opts.voice
2443
2697
  });
2444
- fs.writeFileSync(opts.output, Buffer.from(audio));
2698
+ fs2.writeFileSync(opts.output, Buffer.from(audio));
2445
2699
  const result = { output: opts.output, bytes: audio.byteLength };
2446
2700
  if (opts.json) printJson(result);
2447
2701
  else console.log(opts.output);
@@ -2453,7 +2707,7 @@ program.command("speech").description("Generate speech audio").requiredOption("-
2453
2707
  program.command("transcribe").description("Transcribe an audio file").argument("<file>", "Audio file").option("-m, --model <model>", "Transcription model").option("--language <language>", "Language hint").option("--prompt <prompt>", "Prompt hint").option("--json", "Print raw JSON response").action(async (file, opts) => {
2454
2708
  const client = getClient();
2455
2709
  try {
2456
- const data = fs.readFileSync(file);
2710
+ const data = fs2.readFileSync(file);
2457
2711
  const blob = new Blob([data]);
2458
2712
  const resp = await client.transcribe(blob, {
2459
2713
  model: opts.model,
package/dist/cli.js CHANGED
@@ -1,14 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  TCloud
4
- } from "./chunk-UBUCUSCF.js";
4
+ } from "./chunk-ZJCWJGPM.js";
5
+ import {
6
+ packageVersion
7
+ } from "./chunk-GHMW4RWJ.js";
5
8
  import {
6
9
  TCloudSandbox
7
10
  } from "./chunk-GNACB23W.js";
8
11
  import {
9
12
  generateWallet
10
- } from "./chunk-THOMQXU5.js";
11
- import "./chunk-U4VOGRVW.js";
13
+ } from "./chunk-26M4HGWE.js";
14
+ import "./chunk-LUJXMUH5.js";
12
15
 
13
16
  // src/cli.ts
14
17
  import { Command } from "commander";
@@ -77,16 +80,6 @@ function teeType(value) {
77
80
  if (!value) return void 0;
78
81
  return value.toLowerCase();
79
82
  }
80
- function packageVersion() {
81
- try {
82
- const packageJson = JSON.parse(
83
- fs.readFileSync(new URL("../package.json", import.meta.url), "utf-8")
84
- );
85
- if (typeof packageJson.version === "string") return packageJson.version;
86
- } catch {
87
- }
88
- return "0.0.0";
89
- }
90
83
  function printJson(value) {
91
84
  console.log(JSON.stringify(value, null, 2));
92
85
  }
@@ -386,6 +379,11 @@ program.command("search").description("Search the web").argument("<query>", "Sea
386
379
  process.exit(1);
387
380
  }
388
381
  });
382
+ program.command("mcp").description('Run a Model Context Protocol (stdio) server exposing Tangle tools (web_search, deep_research). Mount with: { "command": ["tcloud", "mcp"] }').action(async () => {
383
+ const { runMcpServer } = await import("./mcp.js");
384
+ const client = getClient();
385
+ await runMcpServer(client);
386
+ });
389
387
  program.command("image-generate").description("Generate an image").requiredOption("-p, --prompt <prompt>", "Prompt").option("-m, --model <model>", "Image model").option("--size <size>", "Output size").option("--quality <quality>", "Output quality").option("-n, --count <n>", "Number of images").option("--response-format <format>", "url or b64_json").option("--json", "Print raw JSON response").action(async (opts) => {
390
388
  const client = getClient();
391
389
  try {
@@ -186,6 +186,54 @@ interface SearchResponse {
186
186
  billing_units?: Record<string, unknown>;
187
187
  };
188
188
  }
189
+ /** Providers served by the router's research API (POST /v1/research). Mirrors
190
+ * SearchProvider minus `brave` (no research API). Each has its own `effort`
191
+ * vocabulary — see ResearchOptions.effort. */
192
+ type ResearchProvider = 'perplexity' | 'exa' | 'you' | 'parallel' | 'tavily';
193
+ interface ResearchOptions {
194
+ query: string;
195
+ provider?: ResearchProvider;
196
+ /** Alias accepted by the Router for provider-compatible clients. */
197
+ model?: ResearchProvider;
198
+ /** Depth/cost dial, provider-specific:
199
+ * perplexity minimal|low|medium|high · you lite|standard|deep|exhaustive ·
200
+ * exa deep-lite|deep|deep-reasoning · tavily mini|pro|auto ·
201
+ * parallel lite|base|core|pro|ultra. Omit for the provider default. */
202
+ effort?: string;
203
+ maxResults?: number;
204
+ searchRecency?: SearchRecency;
205
+ includeDomains?: string[];
206
+ excludeDomains?: string[];
207
+ /** Optional JSON schema requesting structured output from the provider. */
208
+ outputSchema?: unknown;
209
+ }
210
+ interface ResearchHit {
211
+ title: string;
212
+ url: string;
213
+ snippet?: string;
214
+ publishedAt?: string;
215
+ source?: string;
216
+ }
217
+ interface ResearchResponse {
218
+ id: string;
219
+ object: 'research.result' | string;
220
+ provider: ResearchProvider;
221
+ query: string;
222
+ /** The synthesized multi-step research answer. */
223
+ answer: string;
224
+ /** Supporting sources behind the answer. */
225
+ results: ResearchHit[];
226
+ citations: string[];
227
+ /** Present when an outputSchema was requested and the provider honored it. */
228
+ structured?: unknown;
229
+ usage?: {
230
+ upstream_cost?: number;
231
+ billed_cost?: number;
232
+ gross_margin?: number;
233
+ markup?: number;
234
+ billing_units?: Record<string, unknown>;
235
+ };
236
+ }
189
237
  interface WebSearchPlugin {
190
238
  id: 'web';
191
239
  engine?: 'native' | 'exa' | 'parallel' | 'firecrawl';
@@ -1039,6 +1087,11 @@ declare class TCloudClient {
1039
1087
  rerank(options: RerankOptions): Promise<RerankResponse>;
1040
1088
  /** Search the web through Tangle Router billing and provider routing. */
1041
1089
  search(options: SearchOptions): Promise<SearchResponse>;
1090
+ /** Run a multi-step deep-research task through Tangle Router billing and
1091
+ * provider routing (POST /v1/research). Slower and costlier than `search` —
1092
+ * the provider synthesizes an answer over many fetches. Pick depth with
1093
+ * `effort` (provider-specific vocabulary; see {@link ResearchOptions.effort}). */
1094
+ research(options: ResearchOptions): Promise<ResearchResponse>;
1042
1095
  /** Text-to-speech */
1043
1096
  speech(options: {
1044
1097
  model?: string;
@@ -1356,4 +1409,4 @@ declare class TCloudError extends Error {
1356
1409
  constructor(status: number, message: string);
1357
1410
  }
1358
1411
 
1359
- export { type SpendAuth as $, type ApiKeyInfo as A, type BatchJobResponse as B, type ChatCompletion as C, type RerankOptions as D, type EmbeddingOptions as E, type FineTuningJob as F, type GatewayOptions as G, type RerankResponse as H, type ImageEditAttachment as I, type JobEvent as J, type RetryConfig as K, type RotatingRoutingConfig as L, type Model as M, type RotationStats as N, type Operator as O, type PricingTier as P, type RoutingConfig as Q, type RotatingClientConfig as R, type RoutingStrategy as S, TCloudClient as T, type SandboxChatOptions as U, type SearchHit as V, type SearchOptions as W, type SearchProvider as X, type SearchRecency as Y, type SearchResponse as Z, type ShieldedConfig as _, type TCloudConfig as a, type SpendingLimits as a0, TCloudError as a1, type TierConfig as a2, type TranscriptionResponse as a3, type UpdateKeyOptions as a4, type VideoGenerateOptions as a5, type VideoResponse as a6, type WatchJobOptions as a7, type WebSearchPlugin as a8, type AvatarGenerateRequest as b, type AvatarGenerateResponse as c, type AvatarJobStatus as d, type AvatarResult as e, type BatchRequest as f, type BridgeOptions as g, BridgeSession as h, type ChatCompletionChunk as i, type ChatMessage as j, type ChatOptions as k, type ChatPlugin as l, type CompletionOptions as m, type CompletionResponse as n, type CreateKeyOptions as o, type CreatedKey as p, type CreditBalance as q, type EmbeddingResponse as r, type FineTuningJobOptions as s, type ImageEditOptions as t, type ImageGenerateOptions as u, type ImageResponse as v, type OperatorInfo as w, type PrivacyConfig as x, PrivateRouter as y, type PrivateRouterConfig as z };
1412
+ export { type SearchProvider as $, type ApiKeyInfo as A, type BatchJobResponse as B, type ChatCompletion as C, type RerankOptions as D, type EmbeddingOptions as E, type FineTuningJob as F, type GatewayOptions as G, type RerankResponse as H, type ImageEditAttachment as I, type JobEvent as J, type ResearchHit as K, type ResearchOptions as L, type Model as M, type ResearchProvider as N, type Operator as O, type PricingTier as P, type ResearchResponse as Q, type RotatingClientConfig as R, type RetryConfig as S, type TCloudConfig as T, type RotatingRoutingConfig as U, type RotationStats as V, type RoutingConfig as W, type RoutingStrategy as X, type SandboxChatOptions as Y, type SearchHit as Z, type SearchOptions as _, TCloudClient as a, type SearchRecency as a0, type SearchResponse as a1, type ShieldedConfig as a2, type SpendAuth as a3, type SpendingLimits as a4, TCloudError as a5, type TierConfig as a6, type TranscriptionResponse as a7, type UpdateKeyOptions as a8, type VideoGenerateOptions as a9, type VideoResponse as aa, type WatchJobOptions as ab, type WebSearchPlugin as ac, type AvatarGenerateRequest as b, type AvatarGenerateResponse as c, type AvatarJobStatus as d, type AvatarResult as e, type BatchRequest as f, type BridgeOptions as g, BridgeSession as h, type ChatCompletionChunk as i, type ChatMessage as j, type ChatOptions as k, type ChatPlugin as l, type CompletionOptions as m, type CompletionResponse as n, type CreateKeyOptions as o, type CreatedKey as p, type CreditBalance as q, type EmbeddingResponse as r, type FineTuningJobOptions as s, type ImageEditOptions as t, type ImageGenerateOptions as u, type ImageResponse as v, type OperatorInfo as w, type PrivacyConfig as x, PrivateRouter as y, type PrivateRouterConfig as z };
@@ -186,6 +186,54 @@ interface SearchResponse {
186
186
  billing_units?: Record<string, unknown>;
187
187
  };
188
188
  }
189
+ /** Providers served by the router's research API (POST /v1/research). Mirrors
190
+ * SearchProvider minus `brave` (no research API). Each has its own `effort`
191
+ * vocabulary — see ResearchOptions.effort. */
192
+ type ResearchProvider = 'perplexity' | 'exa' | 'you' | 'parallel' | 'tavily';
193
+ interface ResearchOptions {
194
+ query: string;
195
+ provider?: ResearchProvider;
196
+ /** Alias accepted by the Router for provider-compatible clients. */
197
+ model?: ResearchProvider;
198
+ /** Depth/cost dial, provider-specific:
199
+ * perplexity minimal|low|medium|high · you lite|standard|deep|exhaustive ·
200
+ * exa deep-lite|deep|deep-reasoning · tavily mini|pro|auto ·
201
+ * parallel lite|base|core|pro|ultra. Omit for the provider default. */
202
+ effort?: string;
203
+ maxResults?: number;
204
+ searchRecency?: SearchRecency;
205
+ includeDomains?: string[];
206
+ excludeDomains?: string[];
207
+ /** Optional JSON schema requesting structured output from the provider. */
208
+ outputSchema?: unknown;
209
+ }
210
+ interface ResearchHit {
211
+ title: string;
212
+ url: string;
213
+ snippet?: string;
214
+ publishedAt?: string;
215
+ source?: string;
216
+ }
217
+ interface ResearchResponse {
218
+ id: string;
219
+ object: 'research.result' | string;
220
+ provider: ResearchProvider;
221
+ query: string;
222
+ /** The synthesized multi-step research answer. */
223
+ answer: string;
224
+ /** Supporting sources behind the answer. */
225
+ results: ResearchHit[];
226
+ citations: string[];
227
+ /** Present when an outputSchema was requested and the provider honored it. */
228
+ structured?: unknown;
229
+ usage?: {
230
+ upstream_cost?: number;
231
+ billed_cost?: number;
232
+ gross_margin?: number;
233
+ markup?: number;
234
+ billing_units?: Record<string, unknown>;
235
+ };
236
+ }
189
237
  interface WebSearchPlugin {
190
238
  id: 'web';
191
239
  engine?: 'native' | 'exa' | 'parallel' | 'firecrawl';
@@ -1039,6 +1087,11 @@ declare class TCloudClient {
1039
1087
  rerank(options: RerankOptions): Promise<RerankResponse>;
1040
1088
  /** Search the web through Tangle Router billing and provider routing. */
1041
1089
  search(options: SearchOptions): Promise<SearchResponse>;
1090
+ /** Run a multi-step deep-research task through Tangle Router billing and
1091
+ * provider routing (POST /v1/research). Slower and costlier than `search` —
1092
+ * the provider synthesizes an answer over many fetches. Pick depth with
1093
+ * `effort` (provider-specific vocabulary; see {@link ResearchOptions.effort}). */
1094
+ research(options: ResearchOptions): Promise<ResearchResponse>;
1042
1095
  /** Text-to-speech */
1043
1096
  speech(options: {
1044
1097
  model?: string;
@@ -1356,4 +1409,4 @@ declare class TCloudError extends Error {
1356
1409
  constructor(status: number, message: string);
1357
1410
  }
1358
1411
 
1359
- export { type SpendAuth as $, type ApiKeyInfo as A, type BatchJobResponse as B, type ChatCompletion as C, type RerankOptions as D, type EmbeddingOptions as E, type FineTuningJob as F, type GatewayOptions as G, type RerankResponse as H, type ImageEditAttachment as I, type JobEvent as J, type RetryConfig as K, type RotatingRoutingConfig as L, type Model as M, type RotationStats as N, type Operator as O, type PricingTier as P, type RoutingConfig as Q, type RotatingClientConfig as R, type RoutingStrategy as S, TCloudClient as T, type SandboxChatOptions as U, type SearchHit as V, type SearchOptions as W, type SearchProvider as X, type SearchRecency as Y, type SearchResponse as Z, type ShieldedConfig as _, type TCloudConfig as a, type SpendingLimits as a0, TCloudError as a1, type TierConfig as a2, type TranscriptionResponse as a3, type UpdateKeyOptions as a4, type VideoGenerateOptions as a5, type VideoResponse as a6, type WatchJobOptions as a7, type WebSearchPlugin as a8, type AvatarGenerateRequest as b, type AvatarGenerateResponse as c, type AvatarJobStatus as d, type AvatarResult as e, type BatchRequest as f, type BridgeOptions as g, BridgeSession as h, type ChatCompletionChunk as i, type ChatMessage as j, type ChatOptions as k, type ChatPlugin as l, type CompletionOptions as m, type CompletionResponse as n, type CreateKeyOptions as o, type CreatedKey as p, type CreditBalance as q, type EmbeddingResponse as r, type FineTuningJobOptions as s, type ImageEditOptions as t, type ImageGenerateOptions as u, type ImageResponse as v, type OperatorInfo as w, type PrivacyConfig as x, PrivateRouter as y, type PrivateRouterConfig as z };
1412
+ export { type SearchProvider as $, type ApiKeyInfo as A, type BatchJobResponse as B, type ChatCompletion as C, type RerankOptions as D, type EmbeddingOptions as E, type FineTuningJob as F, type GatewayOptions as G, type RerankResponse as H, type ImageEditAttachment as I, type JobEvent as J, type ResearchHit as K, type ResearchOptions as L, type Model as M, type ResearchProvider as N, type Operator as O, type PricingTier as P, type ResearchResponse as Q, type RotatingClientConfig as R, type RetryConfig as S, type TCloudConfig as T, type RotatingRoutingConfig as U, type RotationStats as V, type RoutingConfig as W, type RoutingStrategy as X, type SandboxChatOptions as Y, type SearchHit as Z, type SearchOptions as _, TCloudClient as a, type SearchRecency as a0, type SearchResponse as a1, type ShieldedConfig as a2, type SpendAuth as a3, type SpendingLimits as a4, TCloudError as a5, type TierConfig as a6, type TranscriptionResponse as a7, type UpdateKeyOptions as a8, type VideoGenerateOptions as a9, type VideoResponse as aa, type WatchJobOptions as ab, type WebSearchPlugin as ac, type AvatarGenerateRequest as b, type AvatarGenerateResponse as c, type AvatarJobStatus as d, type AvatarResult as e, type BatchRequest as f, type BridgeOptions as g, BridgeSession as h, type ChatCompletionChunk as i, type ChatMessage as j, type ChatOptions as k, type ChatPlugin as l, type CompletionOptions as m, type CompletionResponse as n, type CreateKeyOptions as o, type CreatedKey as p, type CreditBalance as q, type EmbeddingResponse as r, type FineTuningJobOptions as s, type ImageEditOptions as t, type ImageGenerateOptions as u, type ImageResponse as v, type OperatorInfo as w, type PrivacyConfig as x, PrivateRouter as y, type PrivateRouterConfig as z };