PageSourceSearch

https://bastio.com/_sw/webmcp.js

js bastio.com collected 2026-10-09 22:55:16 UTC 7,255 bytes, 217 lines download raw bytes

1/* SerpWise WebMCP bridge 1.0.0 | https://serpwise.ai/docs/mcp-server */
2(function () {
3  "use strict";
4
5  if (typeof window === "undefined" || window.__swWebMcp) { return; }
6
7  var VERSION = "1.0.0";
8  var PROTOCOL = "2025-06-18";
9  var ENDPOINT = "/mcp";
10  var BINDINGS = [{"n":"search_site","d":"Search this website. Returns ranked pages with title, URL and snippet.","p":"query","pd":"What to look for on this site, in natural language","t":"search_content","a":"query"},{"n":"get_page","d":"Read a page of this website as markdown, by its URL path.","p":"path","pd":"URL path of the page, for example /about or /blog/my-post","t":"get_page","a":"url_path"}];
11  var TIMEOUT_MS = 20000;
12  var MAX_BYTES = 262144;
13
14  window.__swWebMcp = { version: VERSION, registered: 0 };
15
16  var seq = 0;
17  var sessionId = "";
18  var initialized = false;
19
20  function headers() {
21    var h = {
22      "Content-Type": "application/json",
23      "Accept": "application/json, text/event-stream",
24      "MCP-Protocol-Version": PROTOCOL
25    };
26    if (sessionId) { h["Mcp-Session-Id"] = sessionId; }
27    return h;
28  }
29
30  function parseEnvelope(text) {
31    var body = (text || "").replace(/^\s+|\s+$/g, "");
32    if (body && body.charAt(0) !== "{" && body.charAt(0) !== "[") {
33      var lines = body.split(/\r?\n/);
34      var joined = "";
35      for (var i = 0; i < lines.length; i++) {
36        if (lines[i].indexOf("data:") === 0) {
37          joined += lines[i].slice(5).replace(/^\s+/, "");
38        }
39      }
40      body = joined;
41    }
42    if (!body) { return null; }
43    try { return JSON.parse(body); } catch (e) { return null; }
44  }
45
46  function post(payload) {
47    var controller = typeof AbortController === "function" ? new AbortController() : null;
48    var timer = null;
49    if (controller) {
50      timer = setTimeout(function () { controller.abort(); }, TIMEOUT_MS);
51    }
52    function done() {
53      if (timer) { clearTimeout(timer); timer = null; }
54    }
55    var options = {
56      method: "POST",
57      headers: headers(),
58      credentials: "omit",
59      body: JSON.stringify(payload)
60    };
61    if (controller) { options.signal = controller.signal; }
62
63    return fetch(ENDPOINT, options).then(function (res) {
64      var sid = res.headers.get("Mcp-Session-Id");
65      if (sid) { sessionId = sid; }
66      var declared = parseInt(res.headers.get("Content-Length") || "", 10);
67      if (declared > MAX_BYTES) {
68        done();
69        if (controller) { controller.abort(); }
70        return { status: res.status, envelope: null, tooLarge: true };
71      }
72      return res.text().then(function (text) {
73        done();
74        if (text.length > MAX_BYTES) {
75          return { status: res.status, envelope: null, tooLarge: true };
76        }
77        return { status: res.status, envelope: parseEnvelope(text) };
78      }, function (err) { done(); throw err; });
79    }, function (err) { done(); throw err; });
80  }
81
82  function rpc(method, params) {
83    return post({ jsonrpc: "2.0", id: ++seq, method: method, params: params });
84  }
85
86  function needsInitialize(out) {
87    if (initialized) { return false; }
88    if (out.status === 400) { return true; }
89    var err = out.envelope && out.envelope.error;
90    var message = err && typeof err.message === "string" ? err.message.toLowerCase() : "";
91    return message.indexOf("initial") >= 0;
92  }
93
94  function initialize() {
95    return rpc("initialize", {
96      protocolVersion: PROTOCOL,
97      capabilities: {},
98      clientInfo: { name: "serpwise-webmcp", version: VERSION }
99    }).then(function () {
100      initialized = true;
101      return post({ jsonrpc: "2.0", method: "notifications/initialized" }).then(
102        function () { return true; },
103        function () { return true; }
104      );
105    });
106  }
107
108  function textResult(message) {
109    return { content: [{ type: "text", text: message }], isError: true };
110  }
111
112  function unwrap(out) {
113    if (out.tooLarge) {
114      return textResult("MCP response was too large to read.");
115    }
116    var envelope = out.envelope;
117    if (envelope && envelope.result) { return envelope.result; }
118    if (envelope && envelope.error && envelope.error.message) {
119      return textResult(envelope.error.message);
120    }
121    if (out.status === 429) {
122      return textResult("Rate limited by this site. Try again in a minute.");
123    }
124    if (out.status === 404) {
125      return textResult("This site has no MCP endpoint.");
126    }
127    return textResult("MCP endpoint returned status " + out.status + ".");
128  }
129
130  function callTool(tool, args) {
131    return rpc("tools/call", { name: tool, arguments: args }).then(function (out) {
132      if (!needsInitialize(out)) { return out; }
133      return initialize().then(function () {
134        return rpc("tools/call", { name: tool, arguments: args });
135      });
136    }).then(unwrap, function (err) {
137      var why = err && err.name === "AbortError" ? "timed out" : "failed";
138      return textResult("MCP request " + why + ".");
139    });
140  }
141
142  function descriptor(binding) {
143    var properties = {};
144    properties[binding.p] = { type: "string", description: binding.pd };
145    return {
146      name: binding.n,
147      description: binding.d,
148      inputSchema: {
149        type: "object",
150        properties: properties,
151        required: [binding.p]
152      },
153      execute: function (args) {
154        var value = args && args[binding.p] != null ? String(args[binding.p]) : "";
155        var payload = {};
156        payload[binding.a] = value;
157        return callTool(binding.t, payload);
158      }
159    };
160  }
161
162  function descriptors() {
163    var out = [];
164    for (var i = 0; i < BINDINGS.length; i++) {
165      out.push(descriptor(BINDINGS[i]));
166    }
167    return out;
168  }
169
170  function pageSummary() {
171    if (typeof document === "undefined") { return ""; }
172    var parts = [];
173    if (document.title) { parts.push(document.title); }
174    var meta = document.querySelector ? document.querySelector('meta[name="description"]') : null;
175    var description = meta && meta.getAttribute ? meta.getAttribute("content") : "";
176    if (description) { parts.push(description); }
177    return parts.join(". ");
178  }
179
180  function register(tools) {
181    if (!tools.length) { return 0; }
182
183    var nav = typeof navigator !== "undefined" ? navigator.modelContext : null;
184    var doc = typeof document !== "undefined" ? document.modelContext : null;
185    var i;
186
187    if (nav && typeof nav.registerTool === "function") {
188      for (i = 0; i < tools.length; i++) {
189        navigator.modelContext.registerTool(tools[i]);
190      }
191      return tools.length;
192    }
193    if (doc && typeof doc.registerTool === "function") {
194      for (i = 0; i < tools.length; i++) {
195        document.modelContext.registerTool(tools[i]);
196      }
197      return tools.length;
198    }
199
200    if (nav && typeof nav.provideContext === "function") {
201      navigator.modelContext.provideContext({ tools: tools, description: pageSummary() });
202      return tools.length;
203    }
204    if (doc && typeof doc.provideContext === "function") {
205      document.modelContext.provideContext({ tools: tools, description: pageSummary() });
206      return tools.length;
207    }
208
209    return 0;
210  }
211
212  try {
213    window.__swWebMcp.registered = register(descriptors());
214  } catch (e) {
215    window.__swWebMcp.error = String(e && e.message ? e.message : e);
216  }
217})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.