@spicrawl/mcp 0.0.0-stage → 0.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/LICENSE +61 -0
  3. package/README.md +251 -2
  4. package/dist/client.d.ts +89 -0
  5. package/dist/client.js +187 -0
  6. package/dist/client.js.map +1 -0
  7. package/dist/http.d.ts +27 -0
  8. package/dist/http.js +220 -0
  9. package/dist/http.js.map +1 -0
  10. package/dist/index.d.ts +30 -0
  11. package/dist/index.js +79 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/server.d.ts +16 -0
  14. package/dist/server.js +141 -0
  15. package/dist/server.js.map +1 -0
  16. package/dist/tools/actions.d.ts +534 -0
  17. package/dist/tools/actions.js +103 -0
  18. package/dist/tools/actions.js.map +1 -0
  19. package/dist/tools/batch.d.ts +2 -0
  20. package/dist/tools/batch.js +307 -0
  21. package/dist/tools/batch.js.map +1 -0
  22. package/dist/tools/browser.d.ts +2 -0
  23. package/dist/tools/browser.js +101 -0
  24. package/dist/tools/browser.js.map +1 -0
  25. package/dist/tools/common.d.ts +64 -0
  26. package/dist/tools/common.js +64 -0
  27. package/dist/tools/common.js.map +1 -0
  28. package/dist/tools/docs.d.ts +12 -0
  29. package/dist/tools/docs.js +277 -0
  30. package/dist/tools/docs.js.map +1 -0
  31. package/dist/tools/pdf.d.ts +11 -0
  32. package/dist/tools/pdf.js +36 -0
  33. package/dist/tools/pdf.js.map +1 -0
  34. package/dist/tools/requests.d.ts +2 -0
  35. package/dist/tools/requests.js +71 -0
  36. package/dist/tools/requests.js.map +1 -0
  37. package/dist/tools/scrape.d.ts +2 -0
  38. package/dist/tools/scrape.js +213 -0
  39. package/dist/tools/scrape.js.map +1 -0
  40. package/dist/tools/screenshots.d.ts +15 -0
  41. package/dist/tools/screenshots.js +69 -0
  42. package/dist/tools/screenshots.js.map +1 -0
  43. package/dist/tools/sessions.d.ts +2 -0
  44. package/dist/tools/sessions.js +145 -0
  45. package/dist/tools/sessions.js.map +1 -0
  46. package/dist/tools/usage.d.ts +2 -0
  47. package/dist/tools/usage.js +93 -0
  48. package/dist/tools/usage.js.map +1 -0
  49. package/package.json +67 -4
@@ -0,0 +1,277 @@
1
+ import { z } from "zod";
2
+ import { run } from "./common.js";
3
+ // Documentation: the public Fumadocs site at <docs base> (client.docsBaseURL:
4
+ // https://docs.spicrawl.com, or http://host:8080/docs on a self-hosted
5
+ // deployment). Three routes, none authenticated:
6
+ // GET <base>/api/search?query=q Fumadocs search: SortedResult[] in page groups,
7
+ // each { id, type: page|heading|text, content, url, breadcrumbs? },
8
+ // `content` Markdown with <mark> highlights
9
+ // GET <base>/<slug>.md one page as Markdown (<base>/index.md for the root page)
10
+ // GET <base>/llms.txt index of every page
11
+ // The docs are public: these calls never carry the caller's API key.
12
+ const DOCS_TIMEOUT_MS = 10_000;
13
+ const MAX_PAGE_CHARS = 60_000;
14
+ const MAX_INDEX_CHARS = 60_000;
15
+ const SNIPPET_CHARS = 240;
16
+ const MATCHES_PER_PAGE = 3;
17
+ const ERROR_CODE_RE = /^ERR::([A-Z0-9_]+)::([A-Z0-9_]+)$/i;
18
+ const SLUG_RE = /^[A-Za-z0-9._~-]+(\/[A-Za-z0-9._~-]+)*$/;
19
+ /** GETs a public docs URL with a timeout; no Authorization header, ever. */
20
+ async function docsGet(url, accept) {
21
+ try {
22
+ return await fetch(url, { headers: { Accept: accept }, signal: AbortSignal.timeout(DOCS_TIMEOUT_MS) });
23
+ }
24
+ catch (err) {
25
+ const why = err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError")
26
+ ? `timed out after ${DOCS_TIMEOUT_MS / 1000} s`
27
+ : err instanceof Error
28
+ ? err.message
29
+ : String(err);
30
+ throw new Error(`Could not reach the Spicrawl docs at ${url}: ${why}. The docs host may be down; retry shortly, or check SPICRAWL_DOCS_URL.`);
31
+ }
32
+ }
33
+ /** Reads a 2xx text body, or throws an error naming the status. */
34
+ async function docsText(url, accept, notFound) {
35
+ const res = await docsGet(url, accept);
36
+ const body = await res.text().catch(() => "");
37
+ if (res.status === 404)
38
+ throw new Error(`${notFound} (HTTP 404 from ${url}).`);
39
+ if (!res.ok)
40
+ throw new Error(`The Spicrawl docs answered HTTP ${res.status} for ${url}; retry shortly.`);
41
+ return body;
42
+ }
43
+ /** Search-result Markdown -> plain text: drops <mark> highlights and Markdown escapes. */
44
+ function plain(s) {
45
+ return (s ?? "")
46
+ .replace(/<\/?mark>/g, "")
47
+ .replace(/\\([\\`*_{}[\]()#+\-.!<>|~])/g, "$1")
48
+ .replace(/\s+/g, " ")
49
+ .trim();
50
+ }
51
+ function clip(s, max) {
52
+ return s.length <= max ? s : `${s.slice(0, max - 1).trimEnd()}…`;
53
+ }
54
+ /** Where the docs lived on docs.spicrawl.com before moving to its root; old links still carry it. */
55
+ const LEGACY_PREFIX = "/docs";
56
+ /**
57
+ * A docs page slug (no leading slash, no base path, no extension; "" for the
58
+ * root page) from a URL path, which may carry the base's own path
59
+ * (`/docs/guides/x` on a self-hosted base) or not (`/guides/x`). On a base at
60
+ * the root of its host, a legacy `/docs/...` path means the same page.
61
+ */
62
+ function slugFromPath(path, basePath) {
63
+ let p = `/${path.replace(/^\/+/, "")}`.replace(/\/+$/, "");
64
+ const prefix = basePath || LEGACY_PREFIX;
65
+ if (p === prefix || p.startsWith(`${prefix}/`))
66
+ p = p.slice(prefix.length);
67
+ const slug = p.replace(/^\/+/, "").replace(/\.mdx?$/, "");
68
+ return slug === "index" ? "" : slug;
69
+ }
70
+ /** The base's own path, no trailing slash: "" for https://docs.spicrawl.com, "/docs" self-hosted. */
71
+ const basePathOf = (base) => base.pathname.replace(/\/+$/, "");
72
+ /**
73
+ * A docs page slug and anchor from a search-result URL, which may be relative
74
+ * (`/guides/x#a`) or absolute; either way it is resolved against the base, so
75
+ * the base path is never added twice.
76
+ */
77
+ function slugOf(url, base) {
78
+ const u = new URL(url, base.origin);
79
+ return { slug: slugFromPath(u.pathname, basePathOf(base)), hash: u.hash };
80
+ }
81
+ const pageURL = (base, slug, hash = "") => `${slug ? `${base}/${slug}` : base}${hash}`;
82
+ const mdURL = (base, slug) => `${base}/${slug || "index"}.md`;
83
+ /** Groups Fumadocs' flat result list by page, keeping search order. */
84
+ function groupResults(results, docsBase, limit) {
85
+ const base = new URL(`${docsBase}/`);
86
+ const pages = new Map();
87
+ const headings = new Map(); // full url -> heading text
88
+ for (const r of results) {
89
+ if (typeof r?.url !== "string")
90
+ continue;
91
+ let loc;
92
+ try {
93
+ loc = slugOf(r.url, base);
94
+ }
95
+ catch {
96
+ continue;
97
+ }
98
+ let page = pages.get(loc.slug);
99
+ if (!page) {
100
+ if (pages.size >= limit)
101
+ continue;
102
+ page = {
103
+ title: r.type === "page" ? plain(r.content) : plain(r.breadcrumbs?.at(-1)) || loc.slug || "Overview",
104
+ url: pageURL(docsBase, loc.slug),
105
+ md_url: mdURL(docsBase, loc.slug),
106
+ matches: [],
107
+ };
108
+ if (r.breadcrumbs?.length)
109
+ page.breadcrumbs = r.breadcrumbs.map(plain);
110
+ pages.set(loc.slug, page);
111
+ }
112
+ if (r.type === "page") {
113
+ page.title = plain(r.content) || page.title;
114
+ continue;
115
+ }
116
+ const url = pageURL(docsBase, loc.slug, loc.hash);
117
+ if (r.type === "heading") {
118
+ headings.set(url, plain(r.content));
119
+ // A text hit under this heading may already be listed; name its section.
120
+ const existing = page.matches.find((m) => m.url === url);
121
+ if (existing) {
122
+ existing.heading ??= plain(r.content);
123
+ continue;
124
+ }
125
+ if (page.matches.length < MATCHES_PER_PAGE)
126
+ page.matches.push({ heading: plain(r.content), url });
127
+ continue;
128
+ }
129
+ const snippet = clip(plain(r.content), SNIPPET_CHARS);
130
+ const existing = page.matches.find((m) => m.url === url && !m.snippet);
131
+ if (existing) {
132
+ existing.snippet = snippet;
133
+ continue;
134
+ }
135
+ if (page.matches.length < MATCHES_PER_PAGE) {
136
+ const heading = headings.get(url) ?? (loc.hash ? decodeURIComponent(loc.hash.slice(1)) : undefined);
137
+ page.matches.push({ ...(heading ? { heading } : {}), snippet, url });
138
+ }
139
+ }
140
+ return [...pages.values()];
141
+ }
142
+ async function search(docsBase, query) {
143
+ const url = `${docsBase}/api/search?query=${encodeURIComponent(query)}`;
144
+ const text = await docsText(url, "application/json", "The docs search endpoint was not found");
145
+ let parsed;
146
+ try {
147
+ parsed = JSON.parse(text);
148
+ }
149
+ catch {
150
+ throw new Error(`The docs search at ${url} did not return JSON; the docs host may be misconfigured.`);
151
+ }
152
+ if (!Array.isArray(parsed))
153
+ throw new Error(`The docs search at ${url} returned an unexpected shape (not an array).`);
154
+ return parsed;
155
+ }
156
+ /**
157
+ * The page slug and anchor from what an agent passes: a slug (`guides/anti-bot`),
158
+ * a docs path (`/guides/anti-bot.md`, or with the base path or the legacy
159
+ * `/docs` prefix) or a full docs URL (from search or llms.txt), with or without
160
+ * `#anchor`. Anything outside the docs (another host, `..`) is refused.
161
+ */
162
+ export function resolveDocsPath(input, docsBase) {
163
+ const base = new URL(`${docsBase}/`);
164
+ let raw = input.trim();
165
+ if (/^[a-z][a-z0-9+.-]*:/i.test(raw) || raw.startsWith("//")) {
166
+ let u;
167
+ try {
168
+ u = new URL(raw, base);
169
+ }
170
+ catch {
171
+ throw new Error(`\`${input}\` is not a valid URL.`);
172
+ }
173
+ if (u.origin !== base.origin) {
174
+ throw new Error(`\`${input}\` is not on the Spicrawl docs (${docsBase}); only docs pages can be read.`);
175
+ }
176
+ raw = u.pathname + u.hash;
177
+ }
178
+ const hashAt = raw.indexOf("#");
179
+ const hash = hashAt >= 0 ? raw.slice(hashAt) : "";
180
+ let path = (hashAt >= 0 ? raw.slice(0, hashAt) : raw).split("?")[0];
181
+ try {
182
+ path = decodeURIComponent(path);
183
+ }
184
+ catch {
185
+ throw new Error(`\`${input}\` is not a valid docs path.`);
186
+ }
187
+ if (path.split(/[\\/]/).some((seg) => seg === ".." || seg === ".")) {
188
+ throw new Error(`\`${input}\` contains \`..\` or \`.\` segments; pass a docs page path like \`guides/anti-bot\`.`);
189
+ }
190
+ // A bare `docs/...` means the same as `/docs/...`: slugFromPath roots it first.
191
+ const slug = slugFromPath(path, basePathOf(base));
192
+ if (slug && !SLUG_RE.test(slug)) {
193
+ throw new Error(`\`${input}\` is not a valid docs page path; use letters, digits, '-', '_' and '/' (e.g. \`guides/anti-bot\`).`);
194
+ }
195
+ return { slug, hash };
196
+ }
197
+ function trimmed(text, max, what) {
198
+ if (text.length <= max)
199
+ return text;
200
+ return `${text.slice(0, max)}\n\n[Truncated: showing the first ${max} of ${text.length} characters of ${what}.]`;
201
+ }
202
+ export const registerDocsTools = (server, client) => {
203
+ const docsBase = client.docsBaseURL;
204
+ server.registerTool("spicrawl_docs_search", {
205
+ title: "Search the Spicrawl docs",
206
+ description: "Full-text search over the Spicrawl documentation (guides, API reference, errors, limits). Use it BEFORE guessing a parameter name, value or " +
207
+ "combination for a spicrawl_* tool, and whenever an error needs explaining: search the error code itself (e.g. `ERR::UPSTREAM::CHALLENGE`) and " +
208
+ "the result also carries a direct link to that code's entry on the errors page. Returns the best-matching pages, each with its title, the " +
209
+ "matching sections (heading, a short snippet, URL) and md_url; then call spicrawl_docs_read with a page's md_url or url to read it in full.",
210
+ inputSchema: {
211
+ query: z.string().trim().min(1).max(200).describe("What to look for: a feature, parameter, error code or question, e.g. 'wait_for selector' or 'ERR::AUTH::INSUFFICIENT_SCOPE'."),
212
+ limit: z.number().int().min(1).max(20).optional().describe("Maximum pages to return, 1-20. Default 8."),
213
+ },
214
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
215
+ }, async ({ query, limit }) => run(async () => {
216
+ const code = ERROR_CODE_RE.exec(query);
217
+ let results = await search(docsBase, query);
218
+ // A search index may tokenise `ERR::X::Y` poorly; fall back to the name.
219
+ if (!results.length && code)
220
+ results = await search(docsBase, code[2].replace(/_/g, " "));
221
+ const pages = groupResults(results, docsBase, limit ?? 8);
222
+ return {
223
+ query,
224
+ ...(code
225
+ ? {
226
+ error_code: {
227
+ code: query.toUpperCase(),
228
+ url: `${docsBase}/errors#${`${code[1]}_${code[2]}`.toUpperCase()}`,
229
+ md_url: mdURL(docsBase, "errors"),
230
+ note: "Every error code is described on the errors page; read it with spicrawl_docs_read and find the code's heading.",
231
+ },
232
+ }
233
+ : {}),
234
+ results: pages,
235
+ ...(pages.length
236
+ ? {}
237
+ : { hint: "No matches. Try fewer or different words, or call spicrawl_docs_index to list every page." }),
238
+ };
239
+ }));
240
+ server.registerTool("spicrawl_docs_read", {
241
+ title: "Read a Spicrawl docs page",
242
+ description: "Fetches one Spicrawl documentation page as Markdown. `path` may be a page path (`guides/anti-bot`), a docs URL from spicrawl_docs_search " +
243
+ "(url or md_url), or either with a `#anchor` (the whole page is returned; the anchor names the section to look at). Only pages on the " +
244
+ `Spicrawl docs (${docsBase}) can be read. Pages over ${MAX_PAGE_CHARS} characters are truncated, and the text says so. ` +
245
+ "Don't know the path? Use spicrawl_docs_search, or spicrawl_docs_index (llms.txt) for the list of every page.",
246
+ inputSchema: {
247
+ path: z
248
+ .string()
249
+ .trim()
250
+ .min(1)
251
+ .max(500)
252
+ .describe("Page path (`guides/anti-bot`, `errors#AUTH_INVALID_KEY`) or a full docs URL, as returned by spicrawl_docs_search."),
253
+ },
254
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
255
+ }, async ({ path }) => run(async () => {
256
+ const { slug, hash } = resolveDocsPath(path, docsBase);
257
+ const url = mdURL(docsBase, slug);
258
+ const text = await docsText(url, "text/markdown, text/plain;q=0.9", `No docs page at \`${slug || "/"}\`. Use spicrawl_docs_search or spicrawl_docs_index to find the right path`);
259
+ if (/^\s*<(!doctype html|html)[\s>]/i.test(text)) {
260
+ throw new Error(`${url} returned an HTML page, not Markdown; check the path with spicrawl_docs_search or spicrawl_docs_index.`);
261
+ }
262
+ const header = `Source: ${pageURL(docsBase, slug, hash)}${hash ? ` (look for the section ${hash})` : ""}\n\n`;
263
+ return header + trimmed(text, MAX_PAGE_CHARS, "this page");
264
+ }));
265
+ server.registerTool("spicrawl_docs_index", {
266
+ title: "List the Spicrawl docs",
267
+ description: "The docs' llms.txt: every Spicrawl documentation page with its title, a one-line description and its Markdown URL. Use it to find the right " +
268
+ "page when a search does not, or to get an overview of what is documented; read a page with spicrawl_docs_read. No parameters.",
269
+ inputSchema: {},
270
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
271
+ }, async () => run(async () => {
272
+ const url = `${docsBase}/llms.txt`;
273
+ const text = await docsText(url, "text/plain, text/markdown;q=0.9", "The docs index (llms.txt) was not found");
274
+ return trimmed(text, MAX_INDEX_CHARS, "the index");
275
+ }));
276
+ };
277
+ //# sourceMappingURL=docs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs.js","sourceRoot":"","sources":["../../src/tools/docs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Attached } from "./common.js";
2
+ export declare const MAX_PDF_BASE64: number;
3
+ /**
4
+ * Moves the PDF of a `response_format=pdf` scrape (`pdf.data`, base64, as the
5
+ * client read it) into an MCP embedded resource block, leaving its size and a
6
+ * pointer to the block in the JSON. `uri` names the resource (the scraped URL);
7
+ * `offset` is the number of blocks already placed after the text.
8
+ *
9
+ * Any payload without an inline base64 `pdf` is returned untouched.
10
+ */
11
+ export declare function attachPdf(data: unknown, uri: string, offset?: number): Attached;
@@ -0,0 +1,36 @@
1
+ // Largest PDF, as base64, returned in the result. A model host caps what one tool
2
+ // result may carry, and a result it rejects loses the text beside the file too;
3
+ // past this the agent is told to fetch the file from the API directly.
4
+ export const MAX_PDF_BASE64 = 10 * 1024 * 1024;
5
+ /**
6
+ * Moves the PDF of a `response_format=pdf` scrape (`pdf.data`, base64, as the
7
+ * client read it) into an MCP embedded resource block, leaving its size and a
8
+ * pointer to the block in the JSON. `uri` names the resource (the scraped URL);
9
+ * `offset` is the number of blocks already placed after the text.
10
+ *
11
+ * Any payload without an inline base64 `pdf` is returned untouched.
12
+ */
13
+ export function attachPdf(data, uri, offset = 0) {
14
+ if (typeof data !== "object" || data === null || Array.isArray(data))
15
+ return { data, blocks: [] };
16
+ const pdf = data.pdf;
17
+ if (typeof pdf !== "object" || pdf === null)
18
+ return { data, blocks: [] };
19
+ const { data: b64, encoding: _encoding, ...rest } = pdf;
20
+ if (typeof b64 !== "string")
21
+ return { data, blocks: [] };
22
+ const placeholder = { ...rest };
23
+ if (b64.length > MAX_PDF_BASE64) {
24
+ placeholder.not_attached =
25
+ `The PDF is ${(b64.length / 1e6).toFixed(1)} MB as base64, over the ${MAX_PDF_BASE64 / (1024 * 1024)} MB ` +
26
+ "this tool returns. Call POST /v1/scrape with response_format=pdf directly for the file.";
27
+ return { data: { ...data, pdf: placeholder }, blocks: [] };
28
+ }
29
+ // content[0] is the JSON text, so this block is content[offset + 1].
30
+ placeholder.attached_as = `resource content block ${offset + 1} (content[${offset + 1}], application/pdf)`;
31
+ return {
32
+ data: { ...data, pdf: placeholder },
33
+ blocks: [{ type: "resource", resource: { uri, mimeType: "application/pdf", blob: b64 } }],
34
+ };
35
+ }
36
+ //# sourceMappingURL=pdf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf.js","sourceRoot":"","sources":["../../src/tools/pdf.ts"],"names":[],"mappings":"AAEA,kFAAkF;AAClF,gFAAgF;AAChF,uEAAuE;AACvE,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAE/C;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CAAC,IAAa,EAAE,GAAW,EAAE,MAAM,GAAG,CAAC;IAC9D,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAClG,MAAM,GAAG,GAAI,IAAgC,CAAC,GAAG,CAAC;IAClD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACzE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,GAAG,GAA8B,CAAC;IACnF,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAEzD,MAAM,WAAW,GAA4B,EAAE,GAAG,IAAI,EAAE,CAAC;IACzD,IAAI,GAAG,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC;QAChC,WAAW,CAAC,YAAY;YACtB,cAAc,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,2BAA2B,cAAc,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM;gBAC1G,yFAAyF,CAAC;QAC5F,OAAO,EAAE,IAAI,EAAE,EAAE,GAAI,IAAgC,EAAE,GAAG,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC1F,CAAC;IACD,qEAAqE;IACrE,WAAW,CAAC,WAAW,GAAG,0BAA0B,MAAM,GAAG,CAAC,aAAa,MAAM,GAAG,CAAC,qBAAqB,CAAC;IAC3G,OAAO;QACL,IAAI,EAAE,EAAE,GAAI,IAAgC,EAAE,GAAG,EAAE,WAAW,EAAE;QAChE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,iBAAiB,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;KAC1F,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { type RegisterTools } from "./common.js";
2
+ export declare const registerRequestsTools: RegisterTools;
@@ -0,0 +1,71 @@
1
+ import { z } from "zod";
2
+ import { run } from "./common.js";
3
+ // Request history: /v1/requests. Per-call records from the request log (Redis),
4
+ // retained only for the org's retention window. The key's own project needs any
5
+ // valid key; all_projects=true (every project in the org) and another project's
6
+ // request by id need the `read` scope.
7
+ // Not billing-grade: totals here need not match an invoice (use spicrawl_usage).
8
+ export const registerRequestsTools = (server, client) => {
9
+ server.registerTool("spicrawl_requests_list", {
10
+ title: "List recent requests",
11
+ description: "Recent API calls in your key's project, newest first, each with project_id, status, engine, http_status, error_code/error_detail, attempts, URL, " +
12
+ "proxy used, latency spans (queue/engine/upstream/total ms), block detection and credits. Use it to find failing calls (only_errors=true) or to spot " +
13
+ "patterns across a target. all_projects=true lists every project in your organization instead (needs a key with the `read` scope; 403 " +
14
+ "ERR::AUTH::INSUFFICIENT_SCOPE otherwise). Records only exist within the retention window reported in page.retention_hours / page.retained_since; " +
15
+ "an empty page means nothing in that window. Paginate by passing page.next_before and page.next_before_id back as before and before_id while " +
16
+ "page.has_more is true, keeping all_projects the same. Not billing-grade: use spicrawl_usage for totals.",
17
+ inputSchema: {
18
+ all_projects: z
19
+ .boolean()
20
+ .optional()
21
+ .describe("Every project in your organization, not just the key's own. Needs the `read` scope. Keep it the same across pages."),
22
+ limit: z.number().int().min(1).max(200).optional().describe("Rows per page, 1-200. Default 50."),
23
+ status: z
24
+ .enum(["success", "failed", "timeout", "blocked", "rejected", "cancelled"])
25
+ .optional()
26
+ .describe("Only requests with this outcome."),
27
+ only_errors: z
28
+ .boolean()
29
+ .optional()
30
+ .describe("Only requests whose status is anything other than 'success'."),
31
+ before: z
32
+ .string()
33
+ .optional()
34
+ .describe("Cursor: RFC 3339 timestamp, exactly as returned in page.next_before. Pass together with before_id."),
35
+ before_id: z
36
+ .string()
37
+ .optional()
38
+ .describe("Cursor: request id, exactly as returned in page.next_before_id."),
39
+ },
40
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
41
+ }, async (args) => run(() => {
42
+ const qs = new URLSearchParams();
43
+ if (args.all_projects)
44
+ qs.set("all_projects", "true");
45
+ if (args.limit !== undefined)
46
+ qs.set("limit", String(args.limit));
47
+ if (args.status)
48
+ qs.set("status", args.status);
49
+ if (args.only_errors)
50
+ qs.set("only_errors", "true");
51
+ if (args.before)
52
+ qs.set("before", args.before);
53
+ if (args.before_id)
54
+ qs.set("before_id", args.before_id);
55
+ const s = qs.toString();
56
+ return client.request("GET", `/v1/requests${s ? `?${s}` : ""}`);
57
+ }));
58
+ server.registerTool("spicrawl_request_get", {
59
+ title: "Get one request",
60
+ description: "The full log record of one past API call by its request id. Every response carries its id in the X-Request-Id header (and error bodies " +
61
+ "carry it too), so this is the tool for debugging a failed or slow scrape: it shows status, error_code/error_detail, attempts, the engine and " +
62
+ "proxy (tier, country, provider, sticky) actually used, whether and how the target blocked it (vendor/signal/rule), per-stage latency spans " +
63
+ "and credits charged. Returns not_found both for unknown ids and for ones older than your retention window, and for another project's request " +
64
+ "unless your key has the `read` scope.",
65
+ inputSchema: {
66
+ id: z.string().min(1).describe("The request id (a ULID, from X-Request-Id or a spicrawl_requests_list row's `id`)."),
67
+ },
68
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
69
+ }, async ({ id }) => run(() => client.request("GET", `/v1/requests/${encodeURIComponent(id)}`)));
70
+ };
71
+ //# sourceMappingURL=requests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requests.js","sourceRoot":"","sources":["../../src/tools/requests.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { type RegisterTools } from "./common.js";
2
+ export declare const registerScrapeTools: RegisterTools;