@agishub/mcp 2.1.1 → 2.1.3
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/README.md +8 -2
- package/dist/stdio.js +347 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
**Pay-per-call tools for AI agents — no signup, no API keys, just USDC.**
|
|
6
6
|
|
|
7
|
+
### ▶ [Try any tool live in your browser — no install](https://api.agishub.com/try)
|
|
8
|
+
|
|
7
9
|
Live data & utilities over [x402](https://x402.org) micropayments on **Base**. Your agent
|
|
8
10
|
pays a few tenths of a cent per call from its own wallet; no accounts, no monthly plans.
|
|
9
11
|
|
|
@@ -53,7 +55,7 @@ Discovery: https://api.agishub.com/openapi.json
|
|
|
53
55
|
|
|
54
56
|
## Tools
|
|
55
57
|
|
|
56
|
-
**
|
|
58
|
+
**34 tools across 12 services.** Prices are per call in USDC on Base. Tools marked
|
|
57
59
|
**MCP · HTTP** are **free via MCP** and pay‑per‑call via **HTTP x402**; tools marked **HTTP**
|
|
58
60
|
are paid‑only (they touch metered infrastructure, so they run exclusively on the x402 endpoint).
|
|
59
61
|
|
|
@@ -71,11 +73,15 @@ are paid‑only (they touch metered infrastructure, so they run exclusively on t
|
|
|
71
73
|
| `is_holiday` | Is a date a public holiday in a country (ISO 3166‑1)? Authoritative public‑holiday data. | MCP · HTTP | $0.001 |
|
|
72
74
|
| `list_timezones` | List or search valid IANA timezone names. | MCP | free |
|
|
73
75
|
|
|
74
|
-
### 🕸️ Web — read
|
|
76
|
+
### 🕸️ Web — read, scrape & extract from any page
|
|
75
77
|
|
|
76
78
|
| Tool | What it does | Channels | Cost (x402) |
|
|
77
79
|
|------|--------------|:--------:|:-----------:|
|
|
78
80
|
| `extract` | Fetch any public URL and return its main content as clean, token‑efficient markdown (title, headings, links, lists). Optional `render:true` runs a headless browser for JS‑heavy pages / SPAs. Built for RAG. | MCP · HTTP | **$0.004** |
|
|
81
|
+
| `scrape` | Extract specific elements from a JS‑rendered page by CSS selector — text and attributes of every match. Backed by a headless browser, so it works on SPAs. | HTTP | $0.004 |
|
|
82
|
+
| `structured` | AI‑powered structured extraction: give a URL plus a prompt and/or a JSON Schema and get back clean structured JSON (e.g. product name, price, rating). | HTTP | $0.006 |
|
|
83
|
+
| `snapshot` | Capture several representations in one call — rendered HTML, a PNG screenshot, and optional markdown & accessibility tree. | HTTP | $0.008 |
|
|
84
|
+
| `links` | Return every hyperlink on a JS‑rendered page as absolute URLs, with visible‑only and same‑site filters. | HTTP | $0.002 |
|
|
79
85
|
|
|
80
86
|
### 🤖 AI — NLP & generation (no external API key)
|
|
81
87
|
|
package/dist/stdio.js
CHANGED
|
@@ -36083,6 +36083,27 @@ var extract = external_exports.object({
|
|
|
36083
36083
|
include_images: external_exports.boolean().optional().describe("Keep images as markdown (default false)."),
|
|
36084
36084
|
max_chars: external_exports.number().int().positive().optional().describe("Truncate the markdown to at most this many characters (sets truncated:true).")
|
|
36085
36085
|
});
|
|
36086
|
+
var scrape = external_exports.object({
|
|
36087
|
+
url: external_exports.string().url().describe("Full http/https URL of the page to scrape."),
|
|
36088
|
+
selectors: external_exports.array(external_exports.string().min(1)).min(1).max(20).describe("CSS selectors to extract, e.g. ['h1', 'a.product', '.price']. Returns the text and attributes of every match per selector.")
|
|
36089
|
+
});
|
|
36090
|
+
var links = external_exports.object({
|
|
36091
|
+
url: external_exports.string().url().describe("Full http/https URL of the page to read links from."),
|
|
36092
|
+
visible_only: external_exports.boolean().optional().describe("Return only links visible in the rendered layout (default false)."),
|
|
36093
|
+
exclude_external: external_exports.boolean().optional().describe("Drop links pointing to other domains, keeping only same-site links (default false).")
|
|
36094
|
+
});
|
|
36095
|
+
var structured = external_exports.object({
|
|
36096
|
+
url: external_exports.string().url().describe("Full http/https URL of the page to extract data from."),
|
|
36097
|
+
prompt: external_exports.string().optional().describe("Natural-language instruction of what to extract, e.g. 'the product name, price and rating'. Provide this and/or a schema."),
|
|
36098
|
+
schema: external_exports.record(external_exports.any()).optional().describe("Optional JSON Schema object describing the exact shape of the data to return. When given, the output is constrained to it.")
|
|
36099
|
+
});
|
|
36100
|
+
var snapshot = external_exports.object({
|
|
36101
|
+
url: external_exports.string().url().describe("Full http/https URL to capture."),
|
|
36102
|
+
formats: external_exports.array(external_exports.enum(["html", "screenshot", "markdown", "accessibilityTree"])).optional().describe("Which representations to return (default ['html','screenshot']). Add 'markdown' and/or 'accessibilityTree' as needed."),
|
|
36103
|
+
full_page: external_exports.boolean().optional().describe("Capture the full scrollable page in the screenshot instead of just the viewport (default false)."),
|
|
36104
|
+
width: external_exports.number().int().positive().optional().describe("Viewport width in pixels (default 1280)."),
|
|
36105
|
+
height: external_exports.number().int().positive().optional().describe("Viewport height in pixels (default 800).")
|
|
36106
|
+
});
|
|
36086
36107
|
|
|
36087
36108
|
// src/services/web/core/extract.ts
|
|
36088
36109
|
var import_node_html_parser = __toESM(require_dist2(), 1);
|
|
@@ -36416,26 +36437,154 @@ async function extract2(opts, env2) {
|
|
|
36416
36437
|
};
|
|
36417
36438
|
}
|
|
36418
36439
|
|
|
36440
|
+
// src/services/web/core/quickactions.ts
|
|
36441
|
+
var TIMEOUT_MS = 3e4;
|
|
36442
|
+
var QuickActionError = class extends Error {
|
|
36443
|
+
};
|
|
36444
|
+
function assertPublicHttpUrl2(raw) {
|
|
36445
|
+
let u;
|
|
36446
|
+
try {
|
|
36447
|
+
u = new URL(raw);
|
|
36448
|
+
} catch {
|
|
36449
|
+
throw new QuickActionError("URL inv\xE1lida.");
|
|
36450
|
+
}
|
|
36451
|
+
if (u.protocol !== "http:" && u.protocol !== "https:") {
|
|
36452
|
+
throw new QuickActionError("Solo se admiten URLs http/https.");
|
|
36453
|
+
}
|
|
36454
|
+
const host = u.hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
36455
|
+
const isPrivate = host === "localhost" || host.endsWith(".localhost") || host.endsWith(".internal") || host === "::1" || /^(0\.|127\.|10\.|192\.168\.|169\.254\.)/.test(host) || /^172\.(1[6-9]|2\d|3[01])\./.test(host);
|
|
36456
|
+
if (isPrivate) throw new QuickActionError("Host no permitido (red interna).");
|
|
36457
|
+
return u.toString();
|
|
36458
|
+
}
|
|
36459
|
+
async function callJson(env2, endpoint, body) {
|
|
36460
|
+
const token = env2?.CF_API_TOKEN;
|
|
36461
|
+
const acct = env2?.CF_ACCOUNT_ID;
|
|
36462
|
+
if (!token || !acct) {
|
|
36463
|
+
throw new QuickActionError(
|
|
36464
|
+
"Browser Rendering is not configured (missing CF_API_TOKEN / CF_ACCOUNT_ID)."
|
|
36465
|
+
);
|
|
36466
|
+
}
|
|
36467
|
+
const ctrl = new AbortController();
|
|
36468
|
+
const timer2 = setTimeout(() => ctrl.abort(), TIMEOUT_MS);
|
|
36469
|
+
try {
|
|
36470
|
+
const res = await fetch(
|
|
36471
|
+
`https://api.cloudflare.com/client/v4/accounts/${acct}/browser-rendering/${endpoint}`,
|
|
36472
|
+
{
|
|
36473
|
+
method: "POST",
|
|
36474
|
+
signal: ctrl.signal,
|
|
36475
|
+
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
|
|
36476
|
+
body: JSON.stringify(body)
|
|
36477
|
+
}
|
|
36478
|
+
);
|
|
36479
|
+
const data = await res.json().catch(() => null);
|
|
36480
|
+
if (!res.ok || !data || data.success === false) {
|
|
36481
|
+
const detail = data?.errors ? JSON.stringify(data.errors).slice(0, 240) : `HTTP ${res.status}`;
|
|
36482
|
+
throw new QuickActionError(`Browser Rendering ${endpoint} failed: ${detail}`);
|
|
36483
|
+
}
|
|
36484
|
+
return data.result;
|
|
36485
|
+
} catch (e) {
|
|
36486
|
+
if (e instanceof QuickActionError) throw e;
|
|
36487
|
+
throw new QuickActionError(
|
|
36488
|
+
`Browser Rendering ${endpoint} error: ${e instanceof Error ? e.message : String(e)}`
|
|
36489
|
+
);
|
|
36490
|
+
} finally {
|
|
36491
|
+
clearTimeout(timer2);
|
|
36492
|
+
}
|
|
36493
|
+
}
|
|
36494
|
+
async function scrape2(o, env2) {
|
|
36495
|
+
const url = assertPublicHttpUrl2(o.url);
|
|
36496
|
+
const groups = await callJson(env2, "scrape", {
|
|
36497
|
+
url,
|
|
36498
|
+
elements: o.selectors.map((selector) => ({ selector }))
|
|
36499
|
+
});
|
|
36500
|
+
const elements = (groups || []).map((g) => ({
|
|
36501
|
+
selector: g.selector,
|
|
36502
|
+
count: g.results?.length ?? 0,
|
|
36503
|
+
matches: (g.results || []).map((m) => ({
|
|
36504
|
+
text: (m.text || "").trim(),
|
|
36505
|
+
attributes: Object.fromEntries((m.attributes || []).map((a) => [a.name, a.value]))
|
|
36506
|
+
}))
|
|
36507
|
+
}));
|
|
36508
|
+
return { url, elements, scraped_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
36509
|
+
}
|
|
36510
|
+
async function links2(o, env2) {
|
|
36511
|
+
const url = assertPublicHttpUrl2(o.url);
|
|
36512
|
+
const result = await callJson(env2, "links", {
|
|
36513
|
+
url,
|
|
36514
|
+
visibleLinksOnly: !!o.visible_only,
|
|
36515
|
+
excludeExternalLinks: !!o.exclude_external
|
|
36516
|
+
});
|
|
36517
|
+
const list = Array.isArray(result) ? result : [];
|
|
36518
|
+
return { url, count: list.length, links: list, fetched_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
36519
|
+
}
|
|
36520
|
+
async function structured2(o, env2) {
|
|
36521
|
+
const url = assertPublicHttpUrl2(o.url);
|
|
36522
|
+
if (!o.prompt && !o.schema) {
|
|
36523
|
+
throw new QuickActionError("Provide a 'prompt' and/or a JSON 'schema' describing what to extract.");
|
|
36524
|
+
}
|
|
36525
|
+
const body = { url };
|
|
36526
|
+
if (o.prompt) body.prompt = o.prompt;
|
|
36527
|
+
if (o.schema) body.response_format = { type: "json_schema", json_schema: o.schema };
|
|
36528
|
+
const data = await callJson(env2, "json", body);
|
|
36529
|
+
return { url, data, extracted_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
36530
|
+
}
|
|
36531
|
+
async function snapshot2(o, env2) {
|
|
36532
|
+
const url = assertPublicHttpUrl2(o.url);
|
|
36533
|
+
const formats = o.formats && o.formats.length ? o.formats : ["html", "screenshot"];
|
|
36534
|
+
const body = {
|
|
36535
|
+
url,
|
|
36536
|
+
formats,
|
|
36537
|
+
viewport: { width: o.width || 1280, height: o.height || 800 },
|
|
36538
|
+
screenshotOptions: { fullPage: !!o.full_page, type: "png" }
|
|
36539
|
+
};
|
|
36540
|
+
const r = await callJson(env2, "snapshot", body);
|
|
36541
|
+
const out = { url, formats, captured_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
36542
|
+
if (r.content != null) out.html = r.content;
|
|
36543
|
+
if (r.markdown != null) out.markdown = r.markdown;
|
|
36544
|
+
if (r.accessibilityTree != null) out.accessibility_tree = r.accessibilityTree;
|
|
36545
|
+
if (r.screenshot) {
|
|
36546
|
+
out.screenshot = { mime: "image/png", base64: r.screenshot, data_uri: `data:image/png;base64,${r.screenshot}` };
|
|
36547
|
+
}
|
|
36548
|
+
return out;
|
|
36549
|
+
}
|
|
36550
|
+
|
|
36419
36551
|
// src/services/web/handlers.ts
|
|
36552
|
+
var FREE_MAX_CHARS = 8e3;
|
|
36420
36553
|
async function extract3(ctx) {
|
|
36421
36554
|
const { url, render, include_links, include_images, max_chars } = ctx.input;
|
|
36422
36555
|
const mcp = ctx.transport === "mcp";
|
|
36556
|
+
const effectiveMax = mcp ? Math.min(max_chars ?? FREE_MAX_CHARS, FREE_MAX_CHARS) : max_chars;
|
|
36423
36557
|
const result = await extract2(
|
|
36424
|
-
{ url, render: mcp ? false : render, include_links, include_images, max_chars },
|
|
36558
|
+
{ url, render: mcp ? false : render, include_links, include_images, max_chars: effectiveMax },
|
|
36425
36559
|
ctx.env
|
|
36426
36560
|
);
|
|
36427
|
-
if (mcp
|
|
36428
|
-
|
|
36429
|
-
|
|
36430
|
-
|
|
36431
|
-
};
|
|
36561
|
+
if (mcp) {
|
|
36562
|
+
const capped = result.truncated === true;
|
|
36563
|
+
const nudge = render && capped ? "Free MCP tier: static fetch, capped at 8,000 chars. For JavaScript rendering and the full document, use the paid HTTP endpoint POST /v1/web-scraper (x402, $0.004)." : render ? "JavaScript rendering is only on the paid HTTP endpoint POST /v1/web-scraper (x402, $0.004). Returned the static fetch." : capped ? "Free MCP tier: output capped at 8,000 chars. For the full document use the paid HTTP endpoint POST /v1/web-scraper (x402, $0.004)." : void 0;
|
|
36564
|
+
return nudge ? { ...result, tier: "free", note: nudge } : result;
|
|
36432
36565
|
}
|
|
36433
36566
|
return result;
|
|
36434
36567
|
}
|
|
36568
|
+
function scrape3(ctx) {
|
|
36569
|
+
return scrape2(ctx.input, ctx.env);
|
|
36570
|
+
}
|
|
36571
|
+
function links3(ctx) {
|
|
36572
|
+
return links2(ctx.input, ctx.env);
|
|
36573
|
+
}
|
|
36574
|
+
function structured3(ctx) {
|
|
36575
|
+
return structured2(ctx.input, ctx.env);
|
|
36576
|
+
}
|
|
36577
|
+
function snapshot3(ctx) {
|
|
36578
|
+
return snapshot2(ctx.input, ctx.env);
|
|
36579
|
+
}
|
|
36435
36580
|
|
|
36436
36581
|
// src/services/web/operations.ts
|
|
36437
36582
|
var operations2 = {
|
|
36438
|
-
extract: defineOperation(extract, extract3)
|
|
36583
|
+
extract: defineOperation(extract, extract3),
|
|
36584
|
+
scrape: defineOperation(scrape, scrape3),
|
|
36585
|
+
links: defineOperation(links, links3),
|
|
36586
|
+
structured: defineOperation(structured, structured3),
|
|
36587
|
+
snapshot: defineOperation(snapshot, snapshot3)
|
|
36439
36588
|
};
|
|
36440
36589
|
|
|
36441
36590
|
// src/services/web/index.ts
|
|
@@ -36460,7 +36609,7 @@ var screenshot = external_exports.object({
|
|
|
36460
36609
|
});
|
|
36461
36610
|
|
|
36462
36611
|
// src/services/render/core/browser.ts
|
|
36463
|
-
var
|
|
36612
|
+
var TIMEOUT_MS2 = 3e4;
|
|
36464
36613
|
var RenderError = class extends Error {
|
|
36465
36614
|
};
|
|
36466
36615
|
async function callBinary(env2, endpoint, body) {
|
|
@@ -36470,7 +36619,7 @@ async function callBinary(env2, endpoint, body) {
|
|
|
36470
36619
|
throw new RenderError("Browser Rendering is not configured (missing CF_API_TOKEN / CF_ACCOUNT_ID).");
|
|
36471
36620
|
}
|
|
36472
36621
|
const ctrl = new AbortController();
|
|
36473
|
-
const timer2 = setTimeout(() => ctrl.abort(),
|
|
36622
|
+
const timer2 = setTimeout(() => ctrl.abort(), TIMEOUT_MS2);
|
|
36474
36623
|
try {
|
|
36475
36624
|
const res = await fetch(
|
|
36476
36625
|
`https://api.cloudflare.com/client/v4/accounts/${acct}/browser-rendering/${endpoint}`,
|
|
@@ -39495,11 +39644,11 @@ var Observable = function() {
|
|
|
39495
39644
|
return this;
|
|
39496
39645
|
};
|
|
39497
39646
|
Observable2.prototype.pipe = function() {
|
|
39498
|
-
var
|
|
39647
|
+
var operations14 = [];
|
|
39499
39648
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
39500
|
-
|
|
39649
|
+
operations14[_i] = arguments[_i];
|
|
39501
39650
|
}
|
|
39502
|
-
return pipeFromArray(
|
|
39651
|
+
return pipeFromArray(operations14)(this);
|
|
39503
39652
|
};
|
|
39504
39653
|
Observable2.prototype.toPromise = function(promiseCtor) {
|
|
39505
39654
|
var _this = this;
|
|
@@ -57579,6 +57728,150 @@ var browserService = {
|
|
|
57579
57728
|
operations: operations12
|
|
57580
57729
|
};
|
|
57581
57730
|
|
|
57731
|
+
// src/services/feedback/schemas.ts
|
|
57732
|
+
init_zod();
|
|
57733
|
+
var request_feature = external_exports.object({
|
|
57734
|
+
title: external_exports.string().min(3).max(120).describe(
|
|
57735
|
+
"A short, specific title for the request, e.g. 'Add a PDF-merge tool' or 'Support Solana in crypto.price'."
|
|
57736
|
+
),
|
|
57737
|
+
details: external_exports.string().min(10).max(4e3).describe(
|
|
57738
|
+
"What you want and why: the new service, the improvement to an existing one, or the bug. Be concrete about the use case so it can be prioritized."
|
|
57739
|
+
),
|
|
57740
|
+
type: external_exports.enum(["new_service", "improvement", "bug", "other"]).default("other").describe(
|
|
57741
|
+
"The kind of request: a brand-new service, an improvement to an existing one, a bug report, or other feedback."
|
|
57742
|
+
),
|
|
57743
|
+
service: external_exports.string().max(60).optional().describe("Optional: the existing service/tool this relates to, e.g. 'crypto.price' or 'web.extract'."),
|
|
57744
|
+
contact: external_exports.string().max(200).optional().describe(
|
|
57745
|
+
"Optional: how the team can reach you for follow-up \u2014 an email, an X/GitHub handle, or a wallet address. Leave empty to stay anonymous."
|
|
57746
|
+
)
|
|
57747
|
+
});
|
|
57748
|
+
|
|
57749
|
+
// src/services/feedback/core/github.ts
|
|
57750
|
+
var GQL = "https://api.github.com/graphql";
|
|
57751
|
+
var OWNER = "agishub";
|
|
57752
|
+
var REPO = "agishub-mcp";
|
|
57753
|
+
var CATEGORY = "Ideas";
|
|
57754
|
+
var cachedIds = null;
|
|
57755
|
+
async function gql(token, query, variables) {
|
|
57756
|
+
const r = await fetch(GQL, {
|
|
57757
|
+
method: "POST",
|
|
57758
|
+
headers: {
|
|
57759
|
+
authorization: `Bearer ${token}`,
|
|
57760
|
+
"content-type": "application/json",
|
|
57761
|
+
// GitHub rejects API requests without a User-Agent.
|
|
57762
|
+
"user-agent": "agishub-mcp"
|
|
57763
|
+
},
|
|
57764
|
+
body: JSON.stringify({ query, variables })
|
|
57765
|
+
});
|
|
57766
|
+
const json = await r.json().catch(() => ({}));
|
|
57767
|
+
if (!r.ok || json.errors?.length) {
|
|
57768
|
+
const msg = json.errors?.map((e) => e.message).join("; ") || `HTTP ${r.status}`;
|
|
57769
|
+
throw new Error(`GitHub API error: ${msg}`);
|
|
57770
|
+
}
|
|
57771
|
+
return json.data;
|
|
57772
|
+
}
|
|
57773
|
+
async function resolveIds(token) {
|
|
57774
|
+
if (cachedIds) return cachedIds;
|
|
57775
|
+
const data = await gql(
|
|
57776
|
+
token,
|
|
57777
|
+
`query($owner:String!,$repo:String!){
|
|
57778
|
+
repository(owner:$owner,name:$repo){
|
|
57779
|
+
id
|
|
57780
|
+
discussionCategories(first:25){ nodes{ id name } }
|
|
57781
|
+
}
|
|
57782
|
+
}`,
|
|
57783
|
+
{ owner: OWNER, repo: REPO }
|
|
57784
|
+
);
|
|
57785
|
+
const cats = data.repository.discussionCategories.nodes;
|
|
57786
|
+
const cat = cats.find((c) => c.name === CATEGORY) ?? cats[0];
|
|
57787
|
+
if (!cat) throw new Error("The repository has Discussions enabled but no categories.");
|
|
57788
|
+
cachedIds = { repositoryId: data.repository.id, categoryId: cat.id };
|
|
57789
|
+
return cachedIds;
|
|
57790
|
+
}
|
|
57791
|
+
async function createDiscussion(token, input) {
|
|
57792
|
+
const { repositoryId, categoryId } = await resolveIds(token);
|
|
57793
|
+
const data = await gql(
|
|
57794
|
+
token,
|
|
57795
|
+
`mutation($repositoryId:ID!,$categoryId:ID!,$title:String!,$body:String!){
|
|
57796
|
+
createDiscussion(input:{repositoryId:$repositoryId,categoryId:$categoryId,title:$title,body:$body}){
|
|
57797
|
+
discussion{ url number }
|
|
57798
|
+
}
|
|
57799
|
+
}`,
|
|
57800
|
+
{ repositoryId, categoryId, title: input.title, body: input.body }
|
|
57801
|
+
);
|
|
57802
|
+
return data.createDiscussion.discussion;
|
|
57803
|
+
}
|
|
57804
|
+
|
|
57805
|
+
// src/services/feedback/core/notify.ts
|
|
57806
|
+
async function notifyTeam(env2, args) {
|
|
57807
|
+
const key = env2.RESEND_API_KEY;
|
|
57808
|
+
const from2 = env2.FEEDBACK_EMAIL_FROM;
|
|
57809
|
+
const to = env2.FEEDBACK_NOTIFY_EMAIL || "jmavid@gmail.com";
|
|
57810
|
+
if (!key || !from2) return;
|
|
57811
|
+
try {
|
|
57812
|
+
await fetch("https://api.resend.com/emails", {
|
|
57813
|
+
method: "POST",
|
|
57814
|
+
headers: { authorization: `Bearer ${key}`, "content-type": "application/json" },
|
|
57815
|
+
body: JSON.stringify({ from: from2, to, subject: args.subject, text: args.text })
|
|
57816
|
+
});
|
|
57817
|
+
} catch {
|
|
57818
|
+
}
|
|
57819
|
+
}
|
|
57820
|
+
|
|
57821
|
+
// src/services/feedback/handlers.ts
|
|
57822
|
+
var IDEAS_BOARD = "https://github.com/agishub/agishub-mcp/discussions/categories/ideas";
|
|
57823
|
+
var LABEL = {
|
|
57824
|
+
new_service: "New service",
|
|
57825
|
+
improvement: "Improvement",
|
|
57826
|
+
bug: "Bug report",
|
|
57827
|
+
other: "Feedback"
|
|
57828
|
+
};
|
|
57829
|
+
async function feedback_request_feature(ctx) {
|
|
57830
|
+
const { title, details, type, service, contact } = ctx.input;
|
|
57831
|
+
const label = LABEL[type] ?? "Feedback";
|
|
57832
|
+
const token = ctx.env?.GITHUB_TOKEN;
|
|
57833
|
+
if (!token) {
|
|
57834
|
+
return {
|
|
57835
|
+
ok: false,
|
|
57836
|
+
message: "The request channel is not fully configured yet. Please post your request on the community board.",
|
|
57837
|
+
board: IDEAS_BOARD
|
|
57838
|
+
};
|
|
57839
|
+
}
|
|
57840
|
+
const body = [
|
|
57841
|
+
details.trim(),
|
|
57842
|
+
"",
|
|
57843
|
+
"---",
|
|
57844
|
+
`**Type:** ${label}`,
|
|
57845
|
+
service ? `**Related service:** \`${service}\`` : "",
|
|
57846
|
+
contact ? `**Contact:** ${contact}` : "",
|
|
57847
|
+
"*Submitted by an agent via the AgisHub API (`feedback.request_feature`).*"
|
|
57848
|
+
].filter(Boolean).join("\n");
|
|
57849
|
+
const discussion = await createDiscussion(token, { title: `[${label}] ${title}`, body });
|
|
57850
|
+
await notifyTeam(ctx.env, {
|
|
57851
|
+
subject: `New AgisHub request: [${label}] ${title}`,
|
|
57852
|
+
text: `${body}
|
|
57853
|
+
|
|
57854
|
+
Discussion: ${discussion.url}`
|
|
57855
|
+
});
|
|
57856
|
+
return {
|
|
57857
|
+
ok: true,
|
|
57858
|
+
message: "Thanks \u2014 your request was posted to the AgisHub roadmap and the team was notified.",
|
|
57859
|
+
url: discussion.url,
|
|
57860
|
+
number: discussion.number
|
|
57861
|
+
};
|
|
57862
|
+
}
|
|
57863
|
+
|
|
57864
|
+
// src/services/feedback/operations.ts
|
|
57865
|
+
var operations13 = {
|
|
57866
|
+
request_feature: defineOperation(request_feature, feedback_request_feature)
|
|
57867
|
+
};
|
|
57868
|
+
|
|
57869
|
+
// src/services/feedback/index.ts
|
|
57870
|
+
var feedbackService = {
|
|
57871
|
+
name: "feedback",
|
|
57872
|
+
operations: operations13
|
|
57873
|
+
};
|
|
57874
|
+
|
|
57582
57875
|
// src/services/index.ts
|
|
57583
57876
|
var services = [
|
|
57584
57877
|
timezoneService,
|
|
@@ -57592,7 +57885,8 @@ var services = [
|
|
|
57592
57885
|
cryptoService,
|
|
57593
57886
|
ragService,
|
|
57594
57887
|
webhookService,
|
|
57595
|
-
browserService
|
|
57888
|
+
browserService,
|
|
57889
|
+
feedbackService
|
|
57596
57890
|
];
|
|
57597
57891
|
var byId = /* @__PURE__ */ new Map();
|
|
57598
57892
|
for (const svc of services) {
|
|
@@ -57687,6 +57981,38 @@ var catalog = {
|
|
|
57687
57981
|
httpPath: "web-scraper",
|
|
57688
57982
|
tags: ["web", "scrape", "markdown", "rag", "reader"],
|
|
57689
57983
|
description: "Fetch any public web page and return its main content as clean, token-efficient Markdown (title, description, headings, links, lists). Set render:true to execute JavaScript first for single-page apps or JS-heavy pages that would otherwise come back empty. Built for RAG and for agents that need to read the contents of a URL."
|
|
57984
|
+
},
|
|
57985
|
+
scrape: {
|
|
57986
|
+
channels: ["http"],
|
|
57987
|
+
pricing: { x402: "$0.004" },
|
|
57988
|
+
visibility: "public",
|
|
57989
|
+
httpPath: "scrape",
|
|
57990
|
+
tags: ["web", "scrape", "selectors", "extract", "html"],
|
|
57991
|
+
description: "Extract specific elements from a JavaScript-rendered page by CSS selector. Give a list of selectors (e.g. 'h1', '.price', 'a.product') and get back the text and attributes of every match. Backed by a headless browser, so it works on SPAs and JS-heavy pages."
|
|
57992
|
+
},
|
|
57993
|
+
links: {
|
|
57994
|
+
channels: ["http"],
|
|
57995
|
+
pricing: { x402: "$0.002" },
|
|
57996
|
+
visibility: "public",
|
|
57997
|
+
httpPath: "links",
|
|
57998
|
+
tags: ["web", "links", "crawl", "urls"],
|
|
57999
|
+
description: "Return every hyperlink on a JavaScript-rendered page as a list of absolute URLs, with options to keep only visible links or only same-site links. Backed by a headless browser. Use it to map a site or seed a crawler."
|
|
58000
|
+
},
|
|
58001
|
+
structured: {
|
|
58002
|
+
channels: ["http"],
|
|
58003
|
+
pricing: { x402: "$0.006" },
|
|
58004
|
+
visibility: "public",
|
|
58005
|
+
httpPath: "extract-json",
|
|
58006
|
+
tags: ["web", "ai", "extract", "structured", "json"],
|
|
58007
|
+
description: "AI-powered structured extraction: give a URL plus a natural-language prompt and/or a JSON Schema, and get back clean structured JSON (e.g. product name, price, rating). Renders the page in a headless browser first, so it works on SPAs."
|
|
58008
|
+
},
|
|
58009
|
+
snapshot: {
|
|
58010
|
+
channels: ["http"],
|
|
58011
|
+
pricing: { x402: "$0.008" },
|
|
58012
|
+
visibility: "public",
|
|
58013
|
+
httpPath: "snapshot",
|
|
58014
|
+
tags: ["web", "snapshot", "html", "screenshot", "markdown"],
|
|
58015
|
+
description: "Capture several representations of a page in one call \u2014 rendered HTML plus a PNG screenshot by default, and optionally Markdown and the accessibility tree. Backed by a headless browser. Saves round-trips when an agent needs both the content and a visual of a page."
|
|
57690
58016
|
}
|
|
57691
58017
|
},
|
|
57692
58018
|
render: {
|
|
@@ -57861,6 +58187,14 @@ var catalog = {
|
|
|
57861
58187
|
description: "Generate an image from a text prompt (returned base64-encoded PNG)."
|
|
57862
58188
|
}
|
|
57863
58189
|
},
|
|
58190
|
+
feedback: {
|
|
58191
|
+
request_feature: {
|
|
58192
|
+
channels: ["mcp"],
|
|
58193
|
+
visibility: "public",
|
|
58194
|
+
tags: ["feedback", "feature-request", "roadmap", "support", "community"],
|
|
58195
|
+
description: "Request a new service, an improvement to an existing tool, or report a bug to the AgisHub team. Use this whenever the capability you need doesn't exist yet, an existing tool falls short, or something is broken \u2014 describe what you want and the use case. Free. Your request is posted to the public AgisHub roadmap and the team is notified, so agents (and their humans) can ask for new functionality directly."
|
|
58196
|
+
}
|
|
58197
|
+
},
|
|
57864
58198
|
link: {
|
|
57865
58199
|
shorten: {
|
|
57866
58200
|
channels: ["mcp", "http"],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agishub/mcp",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.3",
|
|
4
4
|
"mcpName": "com.agishub/mcp",
|
|
5
5
|
"description": "AgisHub MCP — pay-per-call tools for AI agents (x402, USDC on Base). Timezone, world clock, date math & scheduling; free via MCP, paid via HTTP. No API key.",
|
|
6
6
|
"keywords": [
|