@unbrowse/sdk 12.0.1 → 12.1.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/index.d.ts CHANGED
@@ -2,3 +2,5 @@ export { DEFAULT_BASE_URL, Unbrowse, UnbrowseError, normalizeHost } from "./clie
2
2
  export type { LoginView, UnbrowseOptions } from "./client.ts";
3
3
  export type * from "./types.ts";
4
4
  export { cursorInstallLink, mcpCommands, vscodeInstallLink } from "./mcp-install.ts";
5
+ export { DEFAULT_MCP_URL, MCP_PROTOCOL_VERSION, UnbrowseMcp, parseRpcBody, resultText } from "./mcp.ts";
6
+ export type { McpContent, McpOptions, McpTool, McpToolResult } from "./mcp.ts";
package/dist/index.js CHANGED
@@ -175,12 +175,96 @@ function mcpCommands(url) {
175
175
  json: JSON.stringify({ mcpServers: { unbrowse: { url } } }, null, 2)
176
176
  };
177
177
  }
178
+ // src/mcp.ts
179
+ var DEFAULT_MCP_URL = "https://unbrowse.ai/mcp";
180
+ var MCP_PROTOCOL_VERSION = "2025-11-25";
181
+ var env2 = (name) => (typeof process !== "undefined" ? process.env?.[name] : undefined) || undefined;
182
+ function parseRpcBody(text) {
183
+ const trimmed = text.trim();
184
+ if (trimmed.startsWith("{"))
185
+ return JSON.parse(trimmed);
186
+ const events = trimmed.split(/\n\n+/).flatMap((block) => {
187
+ const data = block.split(`
188
+ `).filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trimStart()).join(`
189
+ `);
190
+ return data ? [data] : [];
191
+ });
192
+ if (!events.length)
193
+ throw new UnbrowseError("Empty MCP response", 502, "mcp_empty");
194
+ return JSON.parse(events[events.length - 1]);
195
+ }
196
+ function resultText(result) {
197
+ const text = result.content?.filter((c) => c.type === "text" && typeof c.text === "string").map((c) => c.text).join(`
198
+ `);
199
+ return text || (result.structuredContent === undefined ? "" : JSON.stringify(result.structuredContent));
200
+ }
201
+
202
+ class UnbrowseMcp {
203
+ url;
204
+ apiKey;
205
+ endUser;
206
+ client;
207
+ fetchImpl;
208
+ nextId = 1;
209
+ constructor(opts = {}) {
210
+ this.url = (opts.url ?? env2("UNBROWSE_MCP_URL") ?? DEFAULT_MCP_URL).replace(/\/+$/, "");
211
+ this.apiKey = opts.apiKey ?? env2("UNBROWSE_API_KEY");
212
+ this.endUser = opts.endUser ?? env2("UNBROWSE_END_USER");
213
+ this.client = opts.client ?? "sdk";
214
+ this.fetchImpl = opts.fetch ?? ((...a) => globalThis.fetch(...a));
215
+ }
216
+ async rpc(method, params) {
217
+ const res = await this.fetchImpl(this.url, {
218
+ method: "POST",
219
+ headers: {
220
+ ...this.apiKey ? { authorization: `Bearer ${this.apiKey}` } : {},
221
+ ...this.endUser ? { "x-unbrowse-end-user": this.endUser } : {},
222
+ "content-type": "application/json",
223
+ accept: "application/json, text/event-stream",
224
+ "mcp-protocol-version": MCP_PROTOCOL_VERSION,
225
+ "user-agent": `unbrowse-${this.client}`
226
+ },
227
+ body: JSON.stringify({ jsonrpc: "2.0", id: this.nextId++, method, ...params ? { params } : {} })
228
+ });
229
+ const text = await res.text();
230
+ if (!res.ok) {
231
+ let body = {};
232
+ try {
233
+ body = JSON.parse(text);
234
+ } catch {}
235
+ const err = typeof body.error === "object" ? body.error : undefined;
236
+ throw new UnbrowseError(err?.message ?? body.error_description ?? (res.statusText || `HTTP ${res.status}`), res.status, err?.code ?? (typeof body.error === "string" ? body.error : "http_error"), body);
237
+ }
238
+ const msg = parseRpcBody(text);
239
+ if (msg.error)
240
+ throw new UnbrowseError(msg.error.message, 200, msg.error.data?.code ?? `rpc_${msg.error.code}`, msg.error);
241
+ return msg.result;
242
+ }
243
+ async listTools() {
244
+ const tools = [];
245
+ let cursor;
246
+ do {
247
+ const page = await this.rpc("tools/list", cursor ? { cursor } : undefined);
248
+ tools.push(...page.tools);
249
+ cursor = page.nextCursor;
250
+ } while (cursor);
251
+ return tools;
252
+ }
253
+ callTool(name, args = {}) {
254
+ return this.rpc("tools/call", { name, arguments: args });
255
+ }
256
+ }
178
257
  export {
179
258
  DEFAULT_BASE_URL,
259
+ DEFAULT_MCP_URL,
260
+ MCP_PROTOCOL_VERSION,
180
261
  Unbrowse,
181
262
  UnbrowseError,
263
+ UnbrowseMcp,
182
264
  cursorInstallLink,
183
265
  mcpCommands,
184
266
  normalizeHost,
267
+ parseRpcBody,
268
+ resultText,
185
269
  vscodeInstallLink
186
270
  };
package/dist/mcp.d.ts ADDED
@@ -0,0 +1,60 @@
1
+ import type { Json } from "./types.ts";
2
+ export declare const DEFAULT_MCP_URL = "https://unbrowse.ai/mcp";
3
+ /** Sent as `Mcp-Protocol-Version`. The hosted server refuses 2025-06-18 on its streaming path. */
4
+ export declare const MCP_PROTOCOL_VERSION = "2025-11-25";
5
+ export type McpOptions = {
6
+ /** API key (`ub_live_…`) or OAuth access token. Defaults to `UNBROWSE_API_KEY`. */
7
+ apiKey?: string;
8
+ /** Defaults to `UNBROWSE_MCP_URL`, then https://unbrowse.ai/mcp. */
9
+ url?: string;
10
+ /** Org keys: the end user the call is for (`X-Unbrowse-End-User`). */
11
+ endUser?: string;
12
+ fetch?: typeof globalThis.fetch;
13
+ /** Client name sent on the wire (User-Agent suffix). */
14
+ client?: string;
15
+ };
16
+ export type McpTool = {
17
+ name: string;
18
+ description?: string;
19
+ inputSchema: {
20
+ type: "object";
21
+ properties?: Record<string, Json>;
22
+ required?: string[];
23
+ } & Record<string, unknown>;
24
+ };
25
+ export type McpContent = {
26
+ type: string;
27
+ text?: string;
28
+ } & Record<string, unknown>;
29
+ export type McpToolResult = {
30
+ content: McpContent[];
31
+ structuredContent?: Json;
32
+ isError?: boolean;
33
+ };
34
+ /** The JSON-RPC message in a response body: plain JSON, or the last `data:` event of an SSE stream. */
35
+ export declare function parseRpcBody(text: string): {
36
+ result?: unknown;
37
+ error?: {
38
+ code: number;
39
+ message: string;
40
+ data?: {
41
+ code?: string;
42
+ } & Record<string, unknown>;
43
+ };
44
+ };
45
+ /** Plain text of a tool result: its text parts, else its structured content as JSON. */
46
+ export declare function resultText(result: McpToolResult): string;
47
+ export declare class UnbrowseMcp {
48
+ readonly url: string;
49
+ private apiKey?;
50
+ private endUser?;
51
+ private client;
52
+ private fetchImpl;
53
+ private nextId;
54
+ constructor(opts?: McpOptions);
55
+ /** One JSON-RPC call. MCP errors become UnbrowseError with the server's `data.code` (e.g. `browser_capacity`). */
56
+ rpc<T>(method: string, params?: Record<string, unknown>): Promise<T>;
57
+ /** Every tool this key can call: the core tools plus the workspace's learned and indexed ones. */
58
+ listTools(): Promise<McpTool[]>;
59
+ callTool(name: string, args?: Record<string, unknown>): Promise<McpToolResult>;
60
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbrowse/sdk",
3
- "version": "12.0.1",
3
+ "version": "12.1.0",
4
4
  "description": "TypeScript client for the Unbrowse API (unbrowse.ai/api/v1): runs, capabilities, learning, logins and the public site registry.",
5
5
  "license": "MIT",
6
6
  "type": "module",