@tangle-network/tcloud 0.4.13 → 0.5.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/dist/{chunk-THOMQXU5.js → chunk-GDGQQRE3.js} +1 -1
- package/dist/{chunk-U4VOGRVW.js → chunk-H3K3A3EI.js} +46 -35
- package/dist/{chunk-UBUCUSCF.js → chunk-O33Y34UQ.js} +2 -2
- package/dist/chunk-YK2SGW76.js +11 -0
- package/dist/cli.cjs +287 -46
- package/dist/cli.js +11 -13
- package/dist/{client-CaD5Oal0.d.ts → client-CAedbgfN.d.cts} +56 -8
- package/dist/{client-CaD5Oal0.d.cts → client-CAedbgfN.d.ts} +56 -8
- package/dist/index.cjs +48 -33
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -3
- package/dist/instance.cjs +50 -45
- package/dist/instance.d.cts +1 -1
- package/dist/instance.d.ts +1 -1
- package/dist/instance.js +2 -1
- package/dist/mcp.cjs +248 -0
- package/dist/mcp.d.cts +27 -0
- package/dist/mcp.d.ts +27 -0
- package/dist/mcp.js +207 -0
- package/dist/shielded.cjs +48 -33
- package/dist/shielded.d.cts +1 -1
- package/dist/shielded.d.ts +1 -1
- package/dist/shielded.js +3 -2
- package/package.json +10 -10
package/dist/mcp.cjs
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/mcp.ts
|
|
31
|
+
var mcp_exports = {};
|
|
32
|
+
__export(mcp_exports, {
|
|
33
|
+
runMcpServer: () => runMcpServer
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(mcp_exports);
|
|
36
|
+
var readline = __toESM(require("readline"), 1);
|
|
37
|
+
|
|
38
|
+
// package.json
|
|
39
|
+
var version = "0.5.0";
|
|
40
|
+
|
|
41
|
+
// src/version.ts
|
|
42
|
+
function packageVersion() {
|
|
43
|
+
return version;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/mcp.ts
|
|
47
|
+
var SEARCH_PROVIDERS = /* @__PURE__ */ new Set(["perplexity", "exa", "you", "parallel", "tavily", "brave"]);
|
|
48
|
+
var RESEARCH_PROVIDERS = /* @__PURE__ */ new Set(["perplexity", "exa", "you", "parallel", "tavily"]);
|
|
49
|
+
var RECENCY_WINDOWS = /* @__PURE__ */ new Set(["day", "week", "month", "year"]);
|
|
50
|
+
var SUPPORTED_PROTOCOL_VERSIONS = /* @__PURE__ */ new Set(["2025-06-18", "2025-03-26"]);
|
|
51
|
+
var DEFAULT_PROTOCOL_VERSION = "2025-06-18";
|
|
52
|
+
var DOMAIN_FILTER_SCHEMA = {
|
|
53
|
+
type: "array",
|
|
54
|
+
items: { type: "string" }
|
|
55
|
+
};
|
|
56
|
+
var TOOLS = [
|
|
57
|
+
{
|
|
58
|
+
name: "web_search",
|
|
59
|
+
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.",
|
|
60
|
+
inputSchema: {
|
|
61
|
+
type: "object",
|
|
62
|
+
properties: {
|
|
63
|
+
query: { type: "string", description: "The search query." },
|
|
64
|
+
provider: {
|
|
65
|
+
type: "string",
|
|
66
|
+
description: "Optional provider: exa | parallel | perplexity | tavily | brave | you."
|
|
67
|
+
},
|
|
68
|
+
maxResults: { type: "number", description: "Optional max number of results." },
|
|
69
|
+
recency: { type: "string", description: "Optional recency window: day | week | month | year." },
|
|
70
|
+
includeDomains: { ...DOMAIN_FILTER_SCHEMA, description: "Optional: restrict results to these domains." },
|
|
71
|
+
excludeDomains: { ...DOMAIN_FILTER_SCHEMA, description: "Optional: drop results from these domains." }
|
|
72
|
+
},
|
|
73
|
+
required: ["query"]
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: "deep_research",
|
|
78
|
+
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.',
|
|
79
|
+
inputSchema: {
|
|
80
|
+
type: "object",
|
|
81
|
+
properties: {
|
|
82
|
+
query: { type: "string", description: "The research question." },
|
|
83
|
+
provider: {
|
|
84
|
+
type: "string",
|
|
85
|
+
description: "Optional provider: you | exa | perplexity | tavily | parallel."
|
|
86
|
+
},
|
|
87
|
+
effort: {
|
|
88
|
+
type: "string",
|
|
89
|
+
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."
|
|
90
|
+
},
|
|
91
|
+
maxResults: { type: "number", description: "Optional max number of supporting sources." },
|
|
92
|
+
searchRecency: { type: "string", description: "Optional recency window: day | week | month | year." },
|
|
93
|
+
includeDomains: { ...DOMAIN_FILTER_SCHEMA, description: "Optional: restrict sources to these domains." },
|
|
94
|
+
excludeDomains: { ...DOMAIN_FILTER_SCHEMA, description: "Optional: drop sources from these domains." },
|
|
95
|
+
outputSchema: {
|
|
96
|
+
type: "object",
|
|
97
|
+
description: "Optional JSON schema requesting structured output from the provider."
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
required: ["query"]
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
];
|
|
104
|
+
var InvalidParams = class extends Error {
|
|
105
|
+
};
|
|
106
|
+
function optionalMaxResults(value) {
|
|
107
|
+
if (value == null) return void 0;
|
|
108
|
+
const n = Number(value);
|
|
109
|
+
return Number.isFinite(n) && n >= 0 ? n : void 0;
|
|
110
|
+
}
|
|
111
|
+
function requireEnum(value, allowed, label) {
|
|
112
|
+
if (value == null) return void 0;
|
|
113
|
+
if (typeof value !== "string" || !allowed.has(value)) {
|
|
114
|
+
throw new InvalidParams(`invalid ${label}: ${String(value)}`);
|
|
115
|
+
}
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
function optionalDomains(value) {
|
|
119
|
+
if (!Array.isArray(value)) return void 0;
|
|
120
|
+
const domains = value.filter((d) => typeof d === "string");
|
|
121
|
+
return domains.length ? domains : void 0;
|
|
122
|
+
}
|
|
123
|
+
async function runMcpServer(client, opts = {}) {
|
|
124
|
+
const input = opts.input ?? process.stdin;
|
|
125
|
+
const output = opts.output ?? process.stdout;
|
|
126
|
+
const rl = readline.createInterface({ input });
|
|
127
|
+
const send = (msg) => {
|
|
128
|
+
output.write(`${JSON.stringify(msg)}
|
|
129
|
+
`);
|
|
130
|
+
};
|
|
131
|
+
const ok = (id, result) => send({ jsonrpc: "2.0", id, result });
|
|
132
|
+
const fail = (id, code, message) => send({ jsonrpc: "2.0", id, error: { code, message } });
|
|
133
|
+
const toolError = (id, message) => ok(id, { content: [{ type: "text", text: message }], isError: true });
|
|
134
|
+
process.stderr.write("tcloud mcp: web_search + deep_research server ready (stdio)\n");
|
|
135
|
+
async function handleRequest(req) {
|
|
136
|
+
const { id, method, params } = req;
|
|
137
|
+
try {
|
|
138
|
+
if (method === "initialize") {
|
|
139
|
+
const requested = params?.protocolVersion ?? DEFAULT_PROTOCOL_VERSION;
|
|
140
|
+
const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.has(requested) ? requested : DEFAULT_PROTOCOL_VERSION;
|
|
141
|
+
ok(id, {
|
|
142
|
+
protocolVersion,
|
|
143
|
+
capabilities: { tools: {} },
|
|
144
|
+
serverInfo: { name: "tangle-tcloud", version: packageVersion() }
|
|
145
|
+
});
|
|
146
|
+
} else if (method === "tools/list") {
|
|
147
|
+
ok(id, { tools: TOOLS });
|
|
148
|
+
} else if (method === "tools/call") {
|
|
149
|
+
const name = params?.name;
|
|
150
|
+
if (name === "web_search") {
|
|
151
|
+
await handleWebSearch(id, params?.arguments ?? {});
|
|
152
|
+
} else if (name === "deep_research") {
|
|
153
|
+
await handleDeepResearch(id, params?.arguments ?? {});
|
|
154
|
+
} else {
|
|
155
|
+
fail(id, -32602, `unknown tool: ${String(name)}`);
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
fail(id, -32601, `method not found: ${String(method)}`);
|
|
159
|
+
}
|
|
160
|
+
} catch (e) {
|
|
161
|
+
if (e instanceof InvalidParams) {
|
|
162
|
+
fail(id, -32602, e.message);
|
|
163
|
+
} else {
|
|
164
|
+
fail(id, -32603, e instanceof Error ? e.message : String(e));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
async function handleWebSearch(id, args) {
|
|
169
|
+
const query = String(args.query ?? "").trim();
|
|
170
|
+
if (!query) {
|
|
171
|
+
fail(id, -32602, 'web_search requires a non-empty "query"');
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const provider = requireEnum(args.provider, SEARCH_PROVIDERS, "provider");
|
|
175
|
+
const searchRecency = requireEnum(args.recency, RECENCY_WINDOWS, "recency");
|
|
176
|
+
const maxResults = optionalMaxResults(args.maxResults);
|
|
177
|
+
const includeDomains = optionalDomains(args.includeDomains);
|
|
178
|
+
const excludeDomains = optionalDomains(args.excludeDomains);
|
|
179
|
+
try {
|
|
180
|
+
const resp = await client.search({
|
|
181
|
+
query,
|
|
182
|
+
...provider ? { provider } : {},
|
|
183
|
+
...maxResults != null ? { maxResults } : {},
|
|
184
|
+
...searchRecency ? { searchRecency } : {},
|
|
185
|
+
...includeDomains ? { includeDomains } : {},
|
|
186
|
+
...excludeDomains ? { excludeDomains } : {}
|
|
187
|
+
});
|
|
188
|
+
const hits = resp.data ?? [];
|
|
189
|
+
const text = hits.length ? hits.map((h, i) => `${i + 1}. ${h.title}
|
|
190
|
+
${h.url}${h.snippet ? `
|
|
191
|
+
${h.snippet}` : ""}`).join("\n\n") : `No results for "${resp.query ?? query}".`;
|
|
192
|
+
ok(id, { content: [{ type: "text", text }] });
|
|
193
|
+
} catch (e) {
|
|
194
|
+
toolError(id, e instanceof Error ? e.message : String(e));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
async function handleDeepResearch(id, args) {
|
|
198
|
+
const query = String(args.query ?? "").trim();
|
|
199
|
+
if (!query) {
|
|
200
|
+
fail(id, -32602, 'deep_research requires a non-empty "query"');
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const provider = requireEnum(args.provider, RESEARCH_PROVIDERS, "provider");
|
|
204
|
+
const searchRecency = requireEnum(args.searchRecency, RECENCY_WINDOWS, "searchRecency");
|
|
205
|
+
const maxResults = optionalMaxResults(args.maxResults);
|
|
206
|
+
const includeDomains = optionalDomains(args.includeDomains);
|
|
207
|
+
const excludeDomains = optionalDomains(args.excludeDomains);
|
|
208
|
+
try {
|
|
209
|
+
const resp = await client.research({
|
|
210
|
+
query,
|
|
211
|
+
...provider ? { provider } : {},
|
|
212
|
+
...typeof args.effort === "string" ? { effort: args.effort } : {},
|
|
213
|
+
...maxResults != null ? { maxResults } : {},
|
|
214
|
+
...searchRecency ? { searchRecency } : {},
|
|
215
|
+
...includeDomains ? { includeDomains } : {},
|
|
216
|
+
...excludeDomains ? { excludeDomains } : {},
|
|
217
|
+
...args.outputSchema !== void 0 ? { outputSchema: args.outputSchema } : {}
|
|
218
|
+
});
|
|
219
|
+
const sources = resp.results ?? [];
|
|
220
|
+
const sourceList = sources.length ? `
|
|
221
|
+
|
|
222
|
+
Sources:
|
|
223
|
+
${sources.map((h, i) => `${i + 1}. ${h.title}
|
|
224
|
+
${h.url}`).join("\n")}` : "";
|
|
225
|
+
const text = `${resp.answer ?? ""}${sourceList}`.trim() || `No research result for "${resp.query ?? query}".`;
|
|
226
|
+
ok(id, { content: [{ type: "text", text }] });
|
|
227
|
+
} catch (e) {
|
|
228
|
+
toolError(id, e instanceof Error ? e.message : String(e));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
for await (const line of rl) {
|
|
232
|
+
const trimmed = line.trim();
|
|
233
|
+
if (!trimmed) continue;
|
|
234
|
+
let req;
|
|
235
|
+
try {
|
|
236
|
+
req = JSON.parse(trimmed);
|
|
237
|
+
} catch {
|
|
238
|
+
send({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "Parse error" } });
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
if (req.id === void 0 || req.id === null) continue;
|
|
242
|
+
void handleRequest(req);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
246
|
+
0 && (module.exports = {
|
|
247
|
+
runMcpServer
|
|
248
|
+
});
|
package/dist/mcp.d.cts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Readable, Writable } from 'node:stream';
|
|
2
|
+
import { TCloud } from './index.cjs';
|
|
3
|
+
import './shielded.cjs';
|
|
4
|
+
import 'viem';
|
|
5
|
+
import './client-CAedbgfN.cjs';
|
|
6
|
+
import '@tangle-network/sandbox';
|
|
7
|
+
import './sandbox.cjs';
|
|
8
|
+
import '@tangle-network/tcloud-attestation';
|
|
9
|
+
|
|
10
|
+
/** Transport streams the server reads/writes. Defaults bind to the process
|
|
11
|
+
* stdio; tests inject in-memory streams. */
|
|
12
|
+
interface McpServerOptions {
|
|
13
|
+
input?: Readable;
|
|
14
|
+
output?: Writable;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Run the MCP stdio server against an authenticated TCloud client. Resolves when input closes.
|
|
18
|
+
* Handles `initialize`, `tools/list`, `tools/call` (+ ignores notifications, which carry no id).
|
|
19
|
+
*
|
|
20
|
+
* Requests are dispatched without blocking the read loop, so a slow `deep_research`
|
|
21
|
+
* call cannot stall concurrent `web_search` requests — each response carries its
|
|
22
|
+
* own id. Pass `{ input, output }` to drive the transport over arbitrary streams
|
|
23
|
+
* (tests inject in-memory streams); both default to the process stdio.
|
|
24
|
+
*/
|
|
25
|
+
declare function runMcpServer(client: TCloud, opts?: McpServerOptions): Promise<void>;
|
|
26
|
+
|
|
27
|
+
export { type McpServerOptions, runMcpServer };
|
package/dist/mcp.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Readable, Writable } from 'node:stream';
|
|
2
|
+
import { TCloud } from './index.js';
|
|
3
|
+
import './shielded.js';
|
|
4
|
+
import 'viem';
|
|
5
|
+
import './client-CAedbgfN.js';
|
|
6
|
+
import '@tangle-network/sandbox';
|
|
7
|
+
import './sandbox.js';
|
|
8
|
+
import '@tangle-network/tcloud-attestation';
|
|
9
|
+
|
|
10
|
+
/** Transport streams the server reads/writes. Defaults bind to the process
|
|
11
|
+
* stdio; tests inject in-memory streams. */
|
|
12
|
+
interface McpServerOptions {
|
|
13
|
+
input?: Readable;
|
|
14
|
+
output?: Writable;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Run the MCP stdio server against an authenticated TCloud client. Resolves when input closes.
|
|
18
|
+
* Handles `initialize`, `tools/list`, `tools/call` (+ ignores notifications, which carry no id).
|
|
19
|
+
*
|
|
20
|
+
* Requests are dispatched without blocking the read loop, so a slow `deep_research`
|
|
21
|
+
* call cannot stall concurrent `web_search` requests — each response carries its
|
|
22
|
+
* own id. Pass `{ input, output }` to drive the transport over arbitrary streams
|
|
23
|
+
* (tests inject in-memory streams); both default to the process stdio.
|
|
24
|
+
*/
|
|
25
|
+
declare function runMcpServer(client: TCloud, opts?: McpServerOptions): Promise<void>;
|
|
26
|
+
|
|
27
|
+
export { type McpServerOptions, runMcpServer };
|
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import {
|
|
2
|
+
packageVersion
|
|
3
|
+
} from "./chunk-YK2SGW76.js";
|
|
4
|
+
|
|
5
|
+
// src/mcp.ts
|
|
6
|
+
import * as readline from "readline";
|
|
7
|
+
var SEARCH_PROVIDERS = /* @__PURE__ */ new Set(["perplexity", "exa", "you", "parallel", "tavily", "brave"]);
|
|
8
|
+
var RESEARCH_PROVIDERS = /* @__PURE__ */ new Set(["perplexity", "exa", "you", "parallel", "tavily"]);
|
|
9
|
+
var RECENCY_WINDOWS = /* @__PURE__ */ new Set(["day", "week", "month", "year"]);
|
|
10
|
+
var SUPPORTED_PROTOCOL_VERSIONS = /* @__PURE__ */ new Set(["2025-06-18", "2025-03-26"]);
|
|
11
|
+
var DEFAULT_PROTOCOL_VERSION = "2025-06-18";
|
|
12
|
+
var DOMAIN_FILTER_SCHEMA = {
|
|
13
|
+
type: "array",
|
|
14
|
+
items: { type: "string" }
|
|
15
|
+
};
|
|
16
|
+
var TOOLS = [
|
|
17
|
+
{
|
|
18
|
+
name: "web_search",
|
|
19
|
+
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.",
|
|
20
|
+
inputSchema: {
|
|
21
|
+
type: "object",
|
|
22
|
+
properties: {
|
|
23
|
+
query: { type: "string", description: "The search query." },
|
|
24
|
+
provider: {
|
|
25
|
+
type: "string",
|
|
26
|
+
description: "Optional provider: exa | parallel | perplexity | tavily | brave | you."
|
|
27
|
+
},
|
|
28
|
+
maxResults: { type: "number", description: "Optional max number of results." },
|
|
29
|
+
recency: { type: "string", description: "Optional recency window: day | week | month | year." },
|
|
30
|
+
includeDomains: { ...DOMAIN_FILTER_SCHEMA, description: "Optional: restrict results to these domains." },
|
|
31
|
+
excludeDomains: { ...DOMAIN_FILTER_SCHEMA, description: "Optional: drop results from these domains." }
|
|
32
|
+
},
|
|
33
|
+
required: ["query"]
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "deep_research",
|
|
38
|
+
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.',
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: "object",
|
|
41
|
+
properties: {
|
|
42
|
+
query: { type: "string", description: "The research question." },
|
|
43
|
+
provider: {
|
|
44
|
+
type: "string",
|
|
45
|
+
description: "Optional provider: you | exa | perplexity | tavily | parallel."
|
|
46
|
+
},
|
|
47
|
+
effort: {
|
|
48
|
+
type: "string",
|
|
49
|
+
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."
|
|
50
|
+
},
|
|
51
|
+
maxResults: { type: "number", description: "Optional max number of supporting sources." },
|
|
52
|
+
searchRecency: { type: "string", description: "Optional recency window: day | week | month | year." },
|
|
53
|
+
includeDomains: { ...DOMAIN_FILTER_SCHEMA, description: "Optional: restrict sources to these domains." },
|
|
54
|
+
excludeDomains: { ...DOMAIN_FILTER_SCHEMA, description: "Optional: drop sources from these domains." },
|
|
55
|
+
outputSchema: {
|
|
56
|
+
type: "object",
|
|
57
|
+
description: "Optional JSON schema requesting structured output from the provider."
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
required: ["query"]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
];
|
|
64
|
+
var InvalidParams = class extends Error {
|
|
65
|
+
};
|
|
66
|
+
function optionalMaxResults(value) {
|
|
67
|
+
if (value == null) return void 0;
|
|
68
|
+
const n = Number(value);
|
|
69
|
+
return Number.isFinite(n) && n >= 0 ? n : void 0;
|
|
70
|
+
}
|
|
71
|
+
function requireEnum(value, allowed, label) {
|
|
72
|
+
if (value == null) return void 0;
|
|
73
|
+
if (typeof value !== "string" || !allowed.has(value)) {
|
|
74
|
+
throw new InvalidParams(`invalid ${label}: ${String(value)}`);
|
|
75
|
+
}
|
|
76
|
+
return value;
|
|
77
|
+
}
|
|
78
|
+
function optionalDomains(value) {
|
|
79
|
+
if (!Array.isArray(value)) return void 0;
|
|
80
|
+
const domains = value.filter((d) => typeof d === "string");
|
|
81
|
+
return domains.length ? domains : void 0;
|
|
82
|
+
}
|
|
83
|
+
async function runMcpServer(client, opts = {}) {
|
|
84
|
+
const input = opts.input ?? process.stdin;
|
|
85
|
+
const output = opts.output ?? process.stdout;
|
|
86
|
+
const rl = readline.createInterface({ input });
|
|
87
|
+
const send = (msg) => {
|
|
88
|
+
output.write(`${JSON.stringify(msg)}
|
|
89
|
+
`);
|
|
90
|
+
};
|
|
91
|
+
const ok = (id, result) => send({ jsonrpc: "2.0", id, result });
|
|
92
|
+
const fail = (id, code, message) => send({ jsonrpc: "2.0", id, error: { code, message } });
|
|
93
|
+
const toolError = (id, message) => ok(id, { content: [{ type: "text", text: message }], isError: true });
|
|
94
|
+
process.stderr.write("tcloud mcp: web_search + deep_research server ready (stdio)\n");
|
|
95
|
+
async function handleRequest(req) {
|
|
96
|
+
const { id, method, params } = req;
|
|
97
|
+
try {
|
|
98
|
+
if (method === "initialize") {
|
|
99
|
+
const requested = params?.protocolVersion ?? DEFAULT_PROTOCOL_VERSION;
|
|
100
|
+
const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.has(requested) ? requested : DEFAULT_PROTOCOL_VERSION;
|
|
101
|
+
ok(id, {
|
|
102
|
+
protocolVersion,
|
|
103
|
+
capabilities: { tools: {} },
|
|
104
|
+
serverInfo: { name: "tangle-tcloud", version: packageVersion() }
|
|
105
|
+
});
|
|
106
|
+
} else if (method === "tools/list") {
|
|
107
|
+
ok(id, { tools: TOOLS });
|
|
108
|
+
} else if (method === "tools/call") {
|
|
109
|
+
const name = params?.name;
|
|
110
|
+
if (name === "web_search") {
|
|
111
|
+
await handleWebSearch(id, params?.arguments ?? {});
|
|
112
|
+
} else if (name === "deep_research") {
|
|
113
|
+
await handleDeepResearch(id, params?.arguments ?? {});
|
|
114
|
+
} else {
|
|
115
|
+
fail(id, -32602, `unknown tool: ${String(name)}`);
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
fail(id, -32601, `method not found: ${String(method)}`);
|
|
119
|
+
}
|
|
120
|
+
} catch (e) {
|
|
121
|
+
if (e instanceof InvalidParams) {
|
|
122
|
+
fail(id, -32602, e.message);
|
|
123
|
+
} else {
|
|
124
|
+
fail(id, -32603, e instanceof Error ? e.message : String(e));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async function handleWebSearch(id, args) {
|
|
129
|
+
const query = String(args.query ?? "").trim();
|
|
130
|
+
if (!query) {
|
|
131
|
+
fail(id, -32602, 'web_search requires a non-empty "query"');
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const provider = requireEnum(args.provider, SEARCH_PROVIDERS, "provider");
|
|
135
|
+
const searchRecency = requireEnum(args.recency, RECENCY_WINDOWS, "recency");
|
|
136
|
+
const maxResults = optionalMaxResults(args.maxResults);
|
|
137
|
+
const includeDomains = optionalDomains(args.includeDomains);
|
|
138
|
+
const excludeDomains = optionalDomains(args.excludeDomains);
|
|
139
|
+
try {
|
|
140
|
+
const resp = await client.search({
|
|
141
|
+
query,
|
|
142
|
+
...provider ? { provider } : {},
|
|
143
|
+
...maxResults != null ? { maxResults } : {},
|
|
144
|
+
...searchRecency ? { searchRecency } : {},
|
|
145
|
+
...includeDomains ? { includeDomains } : {},
|
|
146
|
+
...excludeDomains ? { excludeDomains } : {}
|
|
147
|
+
});
|
|
148
|
+
const hits = resp.data ?? [];
|
|
149
|
+
const text = hits.length ? hits.map((h, i) => `${i + 1}. ${h.title}
|
|
150
|
+
${h.url}${h.snippet ? `
|
|
151
|
+
${h.snippet}` : ""}`).join("\n\n") : `No results for "${resp.query ?? query}".`;
|
|
152
|
+
ok(id, { content: [{ type: "text", text }] });
|
|
153
|
+
} catch (e) {
|
|
154
|
+
toolError(id, e instanceof Error ? e.message : String(e));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async function handleDeepResearch(id, args) {
|
|
158
|
+
const query = String(args.query ?? "").trim();
|
|
159
|
+
if (!query) {
|
|
160
|
+
fail(id, -32602, 'deep_research requires a non-empty "query"');
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const provider = requireEnum(args.provider, RESEARCH_PROVIDERS, "provider");
|
|
164
|
+
const searchRecency = requireEnum(args.searchRecency, RECENCY_WINDOWS, "searchRecency");
|
|
165
|
+
const maxResults = optionalMaxResults(args.maxResults);
|
|
166
|
+
const includeDomains = optionalDomains(args.includeDomains);
|
|
167
|
+
const excludeDomains = optionalDomains(args.excludeDomains);
|
|
168
|
+
try {
|
|
169
|
+
const resp = await client.research({
|
|
170
|
+
query,
|
|
171
|
+
...provider ? { provider } : {},
|
|
172
|
+
...typeof args.effort === "string" ? { effort: args.effort } : {},
|
|
173
|
+
...maxResults != null ? { maxResults } : {},
|
|
174
|
+
...searchRecency ? { searchRecency } : {},
|
|
175
|
+
...includeDomains ? { includeDomains } : {},
|
|
176
|
+
...excludeDomains ? { excludeDomains } : {},
|
|
177
|
+
...args.outputSchema !== void 0 ? { outputSchema: args.outputSchema } : {}
|
|
178
|
+
});
|
|
179
|
+
const sources = resp.results ?? [];
|
|
180
|
+
const sourceList = sources.length ? `
|
|
181
|
+
|
|
182
|
+
Sources:
|
|
183
|
+
${sources.map((h, i) => `${i + 1}. ${h.title}
|
|
184
|
+
${h.url}`).join("\n")}` : "";
|
|
185
|
+
const text = `${resp.answer ?? ""}${sourceList}`.trim() || `No research result for "${resp.query ?? query}".`;
|
|
186
|
+
ok(id, { content: [{ type: "text", text }] });
|
|
187
|
+
} catch (e) {
|
|
188
|
+
toolError(id, e instanceof Error ? e.message : String(e));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
for await (const line of rl) {
|
|
192
|
+
const trimmed = line.trim();
|
|
193
|
+
if (!trimmed) continue;
|
|
194
|
+
let req;
|
|
195
|
+
try {
|
|
196
|
+
req = JSON.parse(trimmed);
|
|
197
|
+
} catch {
|
|
198
|
+
send({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "Parse error" } });
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (req.id === void 0 || req.id === null) continue;
|
|
202
|
+
void handleRequest(req);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
export {
|
|
206
|
+
runMcpServer
|
|
207
|
+
};
|
package/dist/shielded.cjs
CHANGED
|
@@ -259,48 +259,43 @@ var PrivateRouter = class {
|
|
|
259
259
|
}
|
|
260
260
|
};
|
|
261
261
|
|
|
262
|
+
// package.json
|
|
263
|
+
var version = "0.5.0";
|
|
264
|
+
|
|
265
|
+
// src/version.ts
|
|
266
|
+
function packageVersion() {
|
|
267
|
+
return version;
|
|
268
|
+
}
|
|
269
|
+
|
|
262
270
|
// src/client.ts
|
|
263
271
|
var ROTATING_MARKER = "__tcloudRotating";
|
|
264
272
|
var DIRECT_CLI_BRIDGE_MARKER = "__tcloudDirectCliBridge";
|
|
265
273
|
var DEFAULT_BASE_URL = "https://router.tangle.tools/v1";
|
|
266
|
-
var SDK_VERSION = "0.4.0";
|
|
267
274
|
async function proxiedFetch(privacy, url, init, streaming) {
|
|
268
275
|
if (!privacy || privacy.mode === "direct") {
|
|
269
276
|
return fetch(url, init);
|
|
270
277
|
}
|
|
271
|
-
if (privacy.mode
|
|
272
|
-
|
|
273
|
-
throw new Error('relayerUrl is required when privacy mode is "relayer"');
|
|
274
|
-
}
|
|
275
|
-
const proxyPath = streaming ? "/relay/proxy-stream" : "/relay/proxy";
|
|
276
|
-
const hdrs = {};
|
|
277
|
-
if (init.headers) {
|
|
278
|
-
const entries = init.headers instanceof Headers ? Array.from(init.headers.entries()) : Object.entries(init.headers);
|
|
279
|
-
for (const [k, v] of entries) hdrs[k] = v;
|
|
280
|
-
}
|
|
281
|
-
return fetch(`${privacy.relayerUrl}${proxyPath}`, {
|
|
282
|
-
method: "POST",
|
|
283
|
-
headers: { "Content-Type": "application/json" },
|
|
284
|
-
body: JSON.stringify({
|
|
285
|
-
target: url,
|
|
286
|
-
body: typeof init.body === "string" ? JSON.parse(init.body) : init.body,
|
|
287
|
-
headers: hdrs
|
|
288
|
-
})
|
|
289
|
-
});
|
|
278
|
+
if (privacy.mode !== "relayer") {
|
|
279
|
+
throw new Error(`Unsupported privacy mode: ${String(privacy.mode)}`);
|
|
290
280
|
}
|
|
291
|
-
if (privacy.
|
|
292
|
-
|
|
293
|
-
throw new Error('socksProxy is required when privacy mode is "socks5"');
|
|
294
|
-
}
|
|
295
|
-
const { SocksProxyAgent } = await import("socks-proxy-agent");
|
|
296
|
-
const agent = new SocksProxyAgent(privacy.socksProxy);
|
|
297
|
-
return fetch(url, {
|
|
298
|
-
...init,
|
|
299
|
-
// @ts-expect-error agent is supported by Node's undici but not in the standard RequestInit type
|
|
300
|
-
agent
|
|
301
|
-
});
|
|
281
|
+
if (!privacy.relayerUrl) {
|
|
282
|
+
throw new Error('relayerUrl is required when privacy mode is "relayer"');
|
|
302
283
|
}
|
|
303
|
-
|
|
284
|
+
const proxyPath = streaming ? "/relay/proxy-stream" : "/relay/proxy";
|
|
285
|
+
const headers = {};
|
|
286
|
+
if (init.headers) {
|
|
287
|
+
const entries = init.headers instanceof Headers ? Array.from(init.headers.entries()) : Object.entries(init.headers);
|
|
288
|
+
for (const [key, value] of entries) headers[key] = value;
|
|
289
|
+
}
|
|
290
|
+
return fetch(`${privacy.relayerUrl}${proxyPath}`, {
|
|
291
|
+
method: "POST",
|
|
292
|
+
headers: { "Content-Type": "application/json" },
|
|
293
|
+
body: JSON.stringify({
|
|
294
|
+
target: url,
|
|
295
|
+
body: typeof init.body === "string" ? JSON.parse(init.body) : init.body,
|
|
296
|
+
headers
|
|
297
|
+
})
|
|
298
|
+
});
|
|
304
299
|
}
|
|
305
300
|
var DEFAULT_RETRY = {
|
|
306
301
|
maxRetries: 3,
|
|
@@ -465,7 +460,7 @@ var TCloudClient = class _TCloudClient {
|
|
|
465
460
|
this.timeoutMs = config.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
466
461
|
this.headers = {
|
|
467
462
|
"Content-Type": "application/json",
|
|
468
|
-
"X-Tangle-Client": `tcloud-sdk/${
|
|
463
|
+
"X-Tangle-Client": `tcloud-sdk/${packageVersion()}`
|
|
469
464
|
};
|
|
470
465
|
if (this.apiKey) {
|
|
471
466
|
this.headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
@@ -1039,6 +1034,26 @@ var TCloudClient = class _TCloudClient {
|
|
|
1039
1034
|
})
|
|
1040
1035
|
});
|
|
1041
1036
|
}
|
|
1037
|
+
/** Run a multi-step deep-research task through Tangle Router billing and
|
|
1038
|
+
* provider routing (POST /v1/research). Slower and costlier than `search` —
|
|
1039
|
+
* the provider synthesizes an answer over many fetches. Pick depth with
|
|
1040
|
+
* `effort` (provider-specific vocabulary; see {@link ResearchOptions.effort}). */
|
|
1041
|
+
async research(options) {
|
|
1042
|
+
return this._request(`${this.baseURL}/research`, {
|
|
1043
|
+
method: "POST",
|
|
1044
|
+
body: JSON.stringify({
|
|
1045
|
+
query: options.query,
|
|
1046
|
+
provider: options.provider,
|
|
1047
|
+
model: options.model,
|
|
1048
|
+
effort: options.effort,
|
|
1049
|
+
maxResults: options.maxResults,
|
|
1050
|
+
searchRecency: options.searchRecency,
|
|
1051
|
+
includeDomains: options.includeDomains,
|
|
1052
|
+
excludeDomains: options.excludeDomains,
|
|
1053
|
+
outputSchema: options.outputSchema
|
|
1054
|
+
})
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1042
1057
|
/** Text-to-speech */
|
|
1043
1058
|
async speech(options) {
|
|
1044
1059
|
const res = await this._requestRaw(`${this.baseURL}/audio/speech`, {
|
package/dist/shielded.d.cts
CHANGED
package/dist/shielded.d.ts
CHANGED
package/dist/shielded.js
CHANGED
|
@@ -3,8 +3,9 @@ import {
|
|
|
3
3
|
estimateCost,
|
|
4
4
|
generateWallet,
|
|
5
5
|
signSpendAuth
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-GDGQQRE3.js";
|
|
7
|
+
import "./chunk-H3K3A3EI.js";
|
|
8
|
+
import "./chunk-YK2SGW76.js";
|
|
8
9
|
export {
|
|
9
10
|
createShieldedClient,
|
|
10
11
|
estimateCost,
|