@scavio/mcp-server 0.11.0 → 0.13.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/README.md +390 -4
- package/dist/index.js +26 -7
- package/dist/index.js.map +1 -1
- package/dist/lib/env.d.ts +1 -0
- package/dist/lib/env.js +4 -0
- package/dist/lib/env.js.map +1 -1
- package/dist/lib/tool-error.d.ts +22 -0
- package/dist/lib/tool-error.js +28 -0
- package/dist/lib/tool-error.js.map +1 -0
- package/dist/tools/airbnb.d.ts +3 -0
- package/dist/tools/airbnb.js +121 -0
- package/dist/tools/airbnb.js.map +1 -0
- package/dist/tools/amazon.js +13 -13
- package/dist/tools/amazon.js.map +1 -1
- package/dist/tools/appstore.d.ts +3 -0
- package/dist/tools/appstore.js +59 -0
- package/dist/tools/appstore.js.map +1 -0
- package/dist/tools/booking.d.ts +3 -0
- package/dist/tools/booking.js +116 -0
- package/dist/tools/booking.js.map +1 -0
- package/dist/tools/capterra.d.ts +3 -0
- package/dist/tools/capterra.js +53 -0
- package/dist/tools/capterra.js.map +1 -0
- package/dist/tools/companieshouse.d.ts +3 -0
- package/dist/tools/companieshouse.js +56 -0
- package/dist/tools/companieshouse.js.map +1 -0
- package/dist/tools/ebay.d.ts +3 -0
- package/dist/tools/ebay.js +79 -0
- package/dist/tools/ebay.js.map +1 -0
- package/dist/tools/extract.d.ts +3 -0
- package/dist/tools/extract.js +21 -0
- package/dist/tools/extract.js.map +1 -0
- package/dist/tools/g2.d.ts +3 -0
- package/dist/tools/g2.js +69 -0
- package/dist/tools/g2.js.map +1 -0
- package/dist/tools/glassdoor.d.ts +3 -0
- package/dist/tools/glassdoor.js +72 -0
- package/dist/tools/glassdoor.js.map +1 -0
- package/dist/tools/google.js +1 -12
- package/dist/tools/google.js.map +1 -1
- package/dist/tools/googleads.d.ts +3 -0
- package/dist/tools/googleads.js +61 -0
- package/dist/tools/googleads.js.map +1 -0
- package/dist/tools/googleplay.d.ts +3 -0
- package/dist/tools/googleplay.js +57 -0
- package/dist/tools/googleplay.js.map +1 -0
- package/dist/tools/homedepot.d.ts +3 -0
- package/dist/tools/homedepot.js +71 -0
- package/dist/tools/homedepot.js.map +1 -0
- package/dist/tools/indeed.d.ts +3 -0
- package/dist/tools/indeed.js +98 -0
- package/dist/tools/indeed.js.map +1 -0
- package/dist/tools/index.d.ts +42 -1
- package/dist/tools/index.js +204 -15
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/instagram.js +19 -30
- package/dist/tools/instagram.js.map +1 -1
- package/dist/tools/kuaishou.d.ts +3 -0
- package/dist/tools/kuaishou.js +207 -0
- package/dist/tools/kuaishou.js.map +1 -0
- package/dist/tools/linkedin.js +1 -12
- package/dist/tools/linkedin.js.map +1 -1
- package/dist/tools/metaads.d.ts +3 -0
- package/dist/tools/metaads.js +63 -0
- package/dist/tools/metaads.js.map +1 -0
- package/dist/tools/reddit.js +9 -22
- package/dist/tools/reddit.js.map +1 -1
- package/dist/tools/redfin.d.ts +3 -0
- package/dist/tools/redfin.js +100 -0
- package/dist/tools/redfin.js.map +1 -0
- package/dist/tools/sec.d.ts +3 -0
- package/dist/tools/sec.js +104 -0
- package/dist/tools/sec.js.map +1 -0
- package/dist/tools/target.d.ts +3 -0
- package/dist/tools/target.js +105 -0
- package/dist/tools/target.js.map +1 -0
- package/dist/tools/threads.d.ts +3 -0
- package/dist/tools/threads.js +93 -0
- package/dist/tools/threads.js.map +1 -0
- package/dist/tools/tiktok-shop.js +3 -11
- package/dist/tools/tiktok-shop.js.map +1 -1
- package/dist/tools/tiktok.js +12 -23
- package/dist/tools/tiktok.js.map +1 -1
- package/dist/tools/tripadvisor.d.ts +3 -0
- package/dist/tools/tripadvisor.js +103 -0
- package/dist/tools/tripadvisor.js.map +1 -0
- package/dist/tools/usage.js +2 -10
- package/dist/tools/usage.js.map +1 -1
- package/dist/tools/walmart.js +108 -41
- package/dist/tools/walmart.js.map +1 -1
- package/dist/tools/x.js +5 -16
- package/dist/tools/x.js.map +1 -1
- package/dist/tools/yelp.d.ts +3 -0
- package/dist/tools/yelp.js +65 -0
- package/dist/tools/yelp.js.map +1 -0
- package/dist/tools/youtube.js +19 -30
- package/dist/tools/youtube.js.map +1 -1
- package/dist/tools/zillow.d.ts +3 -0
- package/dist/tools/zillow.js +123 -0
- package/dist/tools/zillow.js.map +1 -0
- package/package.json +12 -2
package/dist/tools/reddit.js
CHANGED
|
@@ -1,26 +1,11 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
3
|
-
import { ApiError } from "../lib/errors.js";
|
|
4
|
-
function handleApiError(err) {
|
|
5
|
-
if (err instanceof ApiError) {
|
|
6
|
-
if (err.status === 429)
|
|
7
|
-
return { isError: true, content: [{ type: "text", text: "Rate limited. Wait and retry." }] };
|
|
8
|
-
if (err.status === 401)
|
|
9
|
-
throw new McpError(ErrorCode.InternalError, "Invalid SCAVIO_API_KEY. Check your configuration.");
|
|
10
|
-
return { isError: true, content: [{ type: "text", text: `Scavio API error (${err.status}): ${err.message}` }] };
|
|
11
|
-
}
|
|
12
|
-
throw new McpError(ErrorCode.InternalError, String(err));
|
|
13
|
-
}
|
|
2
|
+
import { handleApiError } from "../lib/tool-error.js";
|
|
14
3
|
export function registerRedditTools(server, getClient) {
|
|
15
|
-
server.tool("search_reddit", `Search Reddit posts across all of Reddit as JSON.
|
|
4
|
+
server.tool("search_reddit", `Search Reddit posts across all of Reddit as JSON. Returns data.results, each with post_id, title, text, url, subreddit, author, score, upvote_ratio, num_comments, created_at, is_nsfw, is_video, thumbnail, and media. Use data.next_cursor as the cursor parameter for the next page while data.has_more is true. Results cannot be sorted or filtered by type - the API returns relevance order only. Slower than other platforms (5-15 seconds). Costs 1 credit.`, {
|
|
16
5
|
query: z.string().min(1).max(500)
|
|
17
6
|
.describe("Search query (1-500 chars)."),
|
|
18
|
-
type: z.enum(["posts", "comments"]).optional()
|
|
19
|
-
.describe("Result type (server default 'posts')."),
|
|
20
|
-
sort: z.enum(["relevance", "hot", "top", "new", "comments"]).default("relevance")
|
|
21
|
-
.describe("Sort order. 'relevance' (default), 'hot', 'top', 'new', or 'comments'."),
|
|
22
7
|
cursor: z.string().optional()
|
|
23
|
-
.describe("Pagination
|
|
8
|
+
.describe("Pagination cursor. Use data.next_cursor from previous response for next page."),
|
|
24
9
|
}, async (params) => {
|
|
25
10
|
try {
|
|
26
11
|
const data = await getClient().post("/api/v1/reddit/search", params);
|
|
@@ -30,9 +15,11 @@ export function registerRedditTools(server, getClient) {
|
|
|
30
15
|
return handleApiError(err);
|
|
31
16
|
}
|
|
32
17
|
});
|
|
33
|
-
server.tool("get_reddit_post", `Get a
|
|
34
|
-
|
|
35
|
-
.describe("
|
|
18
|
+
server.tool("get_reddit_post", `Get a single Reddit post by URL or post id as JSON. Returns a flat post object: post_id, title, text, url, subreddit, author, score, upvote_ratio, num_comments, created_at, is_nsfw, is_video, thumbnail, and media. Does NOT return comments - call get_reddit_post_comments with the post_id for those. Pass post_id or url (at least one is required). Slower than other platforms (5-15 seconds). Costs 1 credit.`, {
|
|
19
|
+
post_id: z.string().min(1).optional()
|
|
20
|
+
.describe("Post fullname (t3_...) or bare id, e.g. 't3_1v6ngaf'. Either this or url is required."),
|
|
21
|
+
url: z.string().url().optional()
|
|
22
|
+
.describe("Full Reddit post URL, e.g. 'https://www.reddit.com/r/Python/comments/1smb9du/fastapi_vs_django/'. Either this or post_id is required."),
|
|
36
23
|
}, async (params) => {
|
|
37
24
|
try {
|
|
38
25
|
const data = await getClient().post("/api/v1/reddit/post", params);
|
|
@@ -43,7 +30,7 @@ export function registerRedditTools(server, getClient) {
|
|
|
43
30
|
}
|
|
44
31
|
});
|
|
45
32
|
server.tool("get_reddit_search_suggestions", `Get Reddit search autocomplete suggestions for a query as JSON. Returns a list of suggested search strings. Use to expand a seed keyword or surface what people search for. Costs 1 credit.`, {
|
|
46
|
-
|
|
33
|
+
query: z.string().min(1).max(500)
|
|
47
34
|
.describe("Partial or seed search query to autocomplete."),
|
|
48
35
|
}, async (params) => {
|
|
49
36
|
try {
|
package/dist/tools/reddit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reddit.js","sourceRoot":"","sources":["../../src/tools/reddit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"reddit.js","sourceRoot":"","sources":["../../src/tools/reddit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { handleApiError } from "../lib/tool-error.js";
|
|
3
|
+
// Redfin - US residential real estate. Three tools, 1 credit each, flat:
|
|
4
|
+
// property and market read the listing/region PAGE, whose inlined request cache
|
|
5
|
+
// replaces the ~40 internal calls the page made, which is why a full fact sheet
|
|
6
|
+
// costs the same as a search page.
|
|
7
|
+
//
|
|
8
|
+
// Two things a model must be told or it will burn a billed call:
|
|
9
|
+
// 1. CITY NAMES ARE NOT ACCEPTED on `location`. Redfin's own name lookup is
|
|
10
|
+
// the single path its edge blocks our proxy pool from. A redfin.com region
|
|
11
|
+
// URL, a bare 5-digit ZIP, or region_id + region_type together.
|
|
12
|
+
// 2. days_on_market IS ALWAYS NULL on search - Redfin's mainHouseInfo has no
|
|
13
|
+
// `dom` key. It is described as null here rather than omitted, because a
|
|
14
|
+
// model that sees "days on market" in a field list will otherwise plan
|
|
15
|
+
// around a value that never arrives.
|
|
16
|
+
//
|
|
17
|
+
// No zod `.default()` anywhere in this file, deliberately: the MCP SDK applies
|
|
18
|
+
// a zod default BEFORE the handler runs, so a declared default is posted on
|
|
19
|
+
// every call. `sold_within_days` is the sharp edge - the backend REJECTS it
|
|
20
|
+
// unless listing_status=sold, so a default of 90 here would 400 every for-sale
|
|
21
|
+
// search. Defaults belong to the backend and are documented in .describe() text.
|
|
22
|
+
/** location OR (region_id AND region_type) - the region halves must travel together. */
|
|
23
|
+
const regionRef = {
|
|
24
|
+
location: z.string().min(1).max(500).optional()
|
|
25
|
+
.describe("Region to look up: a redfin.com region URL ('https://www.redfin.com/city/30818/TX/Austin', /neighborhood/, /county/, /zipcode/) or a bare 5-digit ZIP ('78701'). CITY NAMES ARE NOT ACCEPTED - 'Austin, TX' will fail. Required unless you pass region_id AND region_type."),
|
|
26
|
+
region_id: z.number().int().min(1).optional()
|
|
27
|
+
.describe("Redfin's internal region id, as returned in region.id by a previous response. NOT a ZIP code - the two are different number spaces, and a ZIP here resolves to another city rather than failing. Must be sent together with region_type."),
|
|
28
|
+
region_type: z.union([z.literal(1), z.literal(2), z.literal(5), z.literal(6)]).optional()
|
|
29
|
+
.describe("Redfin region type: 1 neighborhood, 2 ZIP, 5 county, 6 city. Must be sent together with region_id."),
|
|
30
|
+
};
|
|
31
|
+
export function registerRedfinTools(server, getClient) {
|
|
32
|
+
const call = (path) => async (params) => {
|
|
33
|
+
try {
|
|
34
|
+
const data = await getClient().post(path, params);
|
|
35
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
return handleApiError(err);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
server.tool("search_redfin", `Search Redfin, the US residential real-estate marketplace, and return listings as JSON: price, price per sqft, beds, baths, living area, lot size, year built, coordinates, listing remarks and full photo galleries. Covers for-sale, sold and for-rent inventory. IMPORTANT: days_on_market is ALWAYS NULL in the response - Redfin does not expose it on this feed, so filter with max_days_on_market / min_days_on_market instead of reading it back. CITY NAMES ARE NOT ACCEPTED: pass \`location\` as a redfin.com region URL (/city/, /neighborhood/, /county/, /zipcode/) or a bare 5-digit ZIP, or pass region_id AND region_type together. Paginated with page + limit, up to 350 listings per page (backend default 100). Every numeric filter is truncated into Redfin's query, so fractional bounds are rejected - 1.5 baths is not expressible. max_days_on_market and min_days_on_market cannot be combined, and sold_within_days is rejected unless listing_status=sold. Costs 1 credit.`, {
|
|
42
|
+
...regionRef,
|
|
43
|
+
listing_status: z.enum(["for_sale", "sold", "for_rent"]).optional()
|
|
44
|
+
.describe("Market to search. Backend default is 'for_sale'."),
|
|
45
|
+
sold_within_days: z.number().int().min(1).optional()
|
|
46
|
+
.describe("Sold within the last N days. ONLY valid with listing_status='sold', where the backend defaults it to 90 - sending it on any other listing_status is a 400."),
|
|
47
|
+
page: z.number().int().min(1).optional()
|
|
48
|
+
.describe("Results page, 1-based."),
|
|
49
|
+
limit: z.number().int().min(1).max(350).optional()
|
|
50
|
+
.describe("Listings per page, 1-350. Backend default is 100."),
|
|
51
|
+
sort: z.enum([
|
|
52
|
+
"recommended",
|
|
53
|
+
"price_low",
|
|
54
|
+
"price_high",
|
|
55
|
+
"newest",
|
|
56
|
+
"oldest",
|
|
57
|
+
"sqft_low",
|
|
58
|
+
"sqft_high",
|
|
59
|
+
"price_per_sqft_low",
|
|
60
|
+
"price_per_sqft_high",
|
|
61
|
+
]).optional()
|
|
62
|
+
.describe("Result sort order. Backend default is Redfin's own 'recommended' ranking."),
|
|
63
|
+
min_price: z.number().min(0).optional()
|
|
64
|
+
.describe("Minimum price, inclusive. This is monthly rent when listing_status='for_rent'."),
|
|
65
|
+
max_price: z.number().min(0).optional()
|
|
66
|
+
.describe("Maximum price, inclusive. This is monthly rent when listing_status='for_rent'."),
|
|
67
|
+
beds_min: z.number().int().min(0).optional()
|
|
68
|
+
.describe("Minimum bedrooms. Whole numbers only."),
|
|
69
|
+
beds_max: z.number().int().min(0).optional()
|
|
70
|
+
.describe("Maximum bedrooms. Whole numbers only."),
|
|
71
|
+
baths_min: z.number().int().min(0).optional()
|
|
72
|
+
.describe("Minimum bathrooms, WHOLE baths only - 1.5 is rejected, not rounded."),
|
|
73
|
+
sqft_min: z.number().int().min(0).optional()
|
|
74
|
+
.describe("Minimum living area in square feet."),
|
|
75
|
+
sqft_max: z.number().int().min(0).optional()
|
|
76
|
+
.describe("Maximum living area in square feet."),
|
|
77
|
+
lot_size_min: z.number().int().min(0).optional()
|
|
78
|
+
.describe("Minimum lot size in square feet."),
|
|
79
|
+
year_built_min: z.number().int().min(0).optional()
|
|
80
|
+
.describe("Earliest year built."),
|
|
81
|
+
year_built_max: z.number().int().min(0).optional()
|
|
82
|
+
.describe("Latest year built."),
|
|
83
|
+
max_hoa: z.number().min(0).optional()
|
|
84
|
+
.describe("Maximum monthly HOA fee in dollars."),
|
|
85
|
+
property_type: z.enum(["house", "condo", "townhouse", "multi_family", "land", "other", "co_op"]).optional()
|
|
86
|
+
.describe("Restrict to one property type."),
|
|
87
|
+
has_pool: z.boolean().optional()
|
|
88
|
+
.describe("Only listings with a pool."),
|
|
89
|
+
max_days_on_market: z.number().int().min(0).optional()
|
|
90
|
+
.describe("Listed at most N days ago. Cannot be combined with min_days_on_market - Redfin expresses both through one param, so sending both is a 400."),
|
|
91
|
+
min_days_on_market: z.number().int().min(0).optional()
|
|
92
|
+
.describe("Listed at least N days ago. Cannot be combined with max_days_on_market."),
|
|
93
|
+
}, call("/api/v1/redfin/search"));
|
|
94
|
+
server.tool("get_redfin_property", `Get one Redfin listing (US residential real estate) in full as JSON: price, the Redfin Estimate and rental estimate, the complete MLS fact sheet, price and tax history, listing agents, open houses, schools, climate risk, walkability and location scores, sun exposure, monthly weather, permits, zoning, comparable sales and photos. Takes a Redfin property id or any redfin.com listing URL carrying one - get either from search_redfin. Not paginated. Costs 1 credit, the same as a search page, because it reads the property page's inlined request cache in one fetch.`, {
|
|
95
|
+
property_id: z.string().min(1).max(500)
|
|
96
|
+
.describe("Redfin property id ('31295086'), or any redfin.com listing URL carrying one ('https://www.redfin.com/TX/Austin/7644-Parkview-Cir-78731/home/31295086'). Only the id in such a URL is used - Redfin resolves the address itself."),
|
|
97
|
+
}, call("/api/v1/redfin/property"));
|
|
98
|
+
server.tool("get_redfin_market", `Get Redfin housing-market statistics for a US region as JSON: median list and sale price, median price per sqft, sale-to-list ratio, average number of offers, average days on market, year-over-year movement, Redfin's 0-100 compete score, live inventory broken down by property type, median price and active listings per bedroom count, and Redfin agent presence with aggregate rating. Region-level aggregates only - use search_redfin for individual listings. Addressed exactly like search_redfin: a redfin.com region URL or a bare 5-digit ZIP in \`location\`, or region_id AND region_type together. CITY NAMES ARE NOT ACCEPTED. Not paginated. Costs 1 credit.`, { ...regionRef }, call("/api/v1/redfin/market"));
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=redfin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redfin.js","sourceRoot":"","sources":["../../src/tools/redfin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { handleApiError } from "../lib/tool-error.js";
|
|
3
|
+
// SEC EDGAR - US Securities and Exchange Commission regulatory filings. Six
|
|
4
|
+
// tools, 1 credit each, flat: the SEC publishes this data itself and even
|
|
5
|
+
// include_history, the one call that can buy up to 10 upstream fetches, is
|
|
6
|
+
// deliberately still one credit.
|
|
7
|
+
//
|
|
8
|
+
// LOOKUP FIRST. Callers hold a ticker (AAPL); the API is keyed by CIK
|
|
9
|
+
// (0000320193). resolve_sec_company exists to bridge that, and both `cik` and
|
|
10
|
+
// `ticker` accept either spelling, which softens the problem without removing
|
|
11
|
+
// it - a company NAME still has to be resolved before anything else runs.
|
|
12
|
+
//
|
|
13
|
+
// No zod `.default()` anywhere in this file, deliberately: the MCP SDK applies
|
|
14
|
+
// a zod default BEFORE the handler runs, so a declared default is posted on
|
|
15
|
+
// every single call whether the model asked for it or not. Defaults belong to
|
|
16
|
+
// the backend, which applies them when a field is absent; they are documented
|
|
17
|
+
// in the .describe() text instead.
|
|
18
|
+
/** Both spellings are accepted by both fields; one of the two is required. */
|
|
19
|
+
const filerRef = {
|
|
20
|
+
cik: z.string().min(1).max(20).optional()
|
|
21
|
+
.describe("Filer CIK in any spelling EDGAR uses: '320193', '0000320193' or 'CIK0000320193'. A ticker is accepted here too. Either this or ticker is required."),
|
|
22
|
+
ticker: z.string().min(1).max(20).optional()
|
|
23
|
+
.describe("Ticker symbol, dotted or dashed ('BRK.B' and 'BRK-B' both resolve). Wins over cik when both are given. Either this or cik is required."),
|
|
24
|
+
};
|
|
25
|
+
const formItem = z.string().min(1).max(50);
|
|
26
|
+
const locationCode = z.string().regex(/^[A-Za-z0-9]{2}$/, "must be a 2-character EDGAR location code");
|
|
27
|
+
export function registerSecTools(server, getClient) {
|
|
28
|
+
const call = (path) => async (params) => {
|
|
29
|
+
try {
|
|
30
|
+
const data = await getClient().post(path, params);
|
|
31
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
return handleApiError(err);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
server.tool("resolve_sec_company", `START HERE for SEC EDGAR, the US Securities and Exchange Commission's public filing system. Resolves a ticker ('AAPL') or a company name to the CIK ('0000320193') that every other SEC tool is keyed by. Returns matching filers as JSON: company name, CIK, ticker symbol, listing exchange, and ready-made submissions, company-facts and EDGAR URLs, tiered by how unambiguous the match is (exact symbol, then exact name, then prefix, then substring) with each row carrying its tier as \`match\`. Not paginated - \`limit\` only sizes the response (1-100, default 10). Costs 1 credit.`, {
|
|
38
|
+
query: z.string().min(1).max(200)
|
|
39
|
+
.describe("A ticker ('AAPL', 'BRK.B'), a company name, or a fragment of one."),
|
|
40
|
+
limit: z.number().int().min(1).max(100).optional()
|
|
41
|
+
.describe("Rows to return, 1-100. Backend default is 10. Sizes the response; this is NOT a page param."),
|
|
42
|
+
exchange: z.enum(["NASDAQ", "NYSE", "OTC", "CBOE"]).optional()
|
|
43
|
+
.describe("Restrict to one listing venue. Closed set, matched case-insensitively. Filers the SEC lists with no exchange at all are excluded by ANY value, so omit this to see them."),
|
|
44
|
+
}, call("/api/v1/sec/lookup"));
|
|
45
|
+
server.tool("get_sec_company", `Get a US SEC EDGAR filer's profile as JSON: legal and former names, SIC industry, filer category, EIN, LEI, state of incorporation, fiscal year end, business and mailing addresses, every ticker with its exchange, which forms it files and how often, plus a preview of its 10 most recent filings. Requires cik or ticker - if you only have a company name, call resolve_sec_company first. Not paginated. Costs 1 credit.`, { ...filerRef }, call("/api/v1/sec/company"));
|
|
46
|
+
server.tool("get_sec_filings", `List one US SEC EDGAR filer's filings as JSON: accession number, form and root form, filing and period dates, 8-K item codes, and direct links to the primary document, the filing index and the attachment directory. Paginated with page + limit (1-500 per page, backend default 50). \`form\` matches the form AND its root form here, so '10-K' also returns the 10-K/A amendments - ask for '10-K/A' to get only amendments. EDGAR's "recent" block is not a fixed window (about a decade for a quiet filer, about a year for a prolific one); include_history reaches further back through up to 10 archived shards and is still 1 credit, with \`history_truncated\` set when the filer had more. Requires cik or ticker. Costs 1 credit.`, {
|
|
47
|
+
...filerRef,
|
|
48
|
+
form: z.union([formItem, z.array(formItem).min(1).max(25)]).optional()
|
|
49
|
+
.describe("Form types to keep: '10-K', ['10-K','10-Q'] or '10-K,8-K'. Matched against the form AND its root form, so '10-K' also returns 10-K/A."),
|
|
50
|
+
date_from: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "must be YYYY-MM-DD").optional()
|
|
51
|
+
.describe("Earliest filing date, inclusive, as YYYY-MM-DD."),
|
|
52
|
+
date_to: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "must be YYYY-MM-DD").optional()
|
|
53
|
+
.describe("Latest filing date, inclusive, as YYYY-MM-DD."),
|
|
54
|
+
page: z.number().int().min(1).optional()
|
|
55
|
+
.describe("Results page, 1-based."),
|
|
56
|
+
limit: z.number().int().min(1).max(500).optional()
|
|
57
|
+
.describe("Filings per page, 1-500. Backend default is 50."),
|
|
58
|
+
include_history: z.boolean().optional()
|
|
59
|
+
.describe("Also fetch the archived filing history beyond EDGAR's recent block - up to 10 shards, still 1 credit. Off by default. Check history_truncated in the response to see whether the filer had more."),
|
|
60
|
+
}, call("/api/v1/sec/filings"));
|
|
61
|
+
server.tool("get_sec_concept", `Get every value a US SEC EDGAR filer reported for one XBRL financial concept as JSON, newest period first, with the form and filing each number came from. XBRL tags are CASE-SENSITIVE: 'netincomeloss' is a 404 upstream, not a match - call get_sec_facts first to list the tags a filer actually reports. Restatements are KEPT, not collapsed, and each row carries the SEC's \`latest\` comparability flag that tells a quarter apart from its year-to-date twin. Here \`form\` is an EXACT match, so '10-K' EXCLUDES 10-K/A - the opposite of get_sec_filings. Not paginated - \`limit\` only sizes the response (1-2000, default 250). Requires cik or ticker. Costs 1 credit.`, {
|
|
62
|
+
...filerRef,
|
|
63
|
+
concept: z.string().min(1).max(120).regex(/^[A-Za-z][A-Za-z0-9]*$/, "must be an XBRL tag name, e.g. NetIncomeLoss")
|
|
64
|
+
.describe("The XBRL tag to pull, CASE-SENSITIVE: 'NetIncomeLoss', 'Revenues', 'Assets'. Use get_sec_facts to discover valid tags for this filer."),
|
|
65
|
+
taxonomy: z.string().min(1).max(40).regex(/^[A-Za-z][A-Za-z0-9-]*$/, "must be an XBRL taxonomy, e.g. us-gaap").optional()
|
|
66
|
+
.describe("XBRL taxonomy: 'us-gaap' (backend default), 'dei', 'ifrs-full' or 'srt'."),
|
|
67
|
+
unit: z.string().min(1).max(40).optional()
|
|
68
|
+
.describe("Keep only facts reported in this unit. A concept can be reported in more than one - per-share figures are 'USD/shares' while the same statement's totals are 'USD'."),
|
|
69
|
+
form: formItem.optional()
|
|
70
|
+
.describe("Keep only facts sourced from this form type. EXACT match here, so '10-K' excludes 10-K/A."),
|
|
71
|
+
limit: z.number().int().min(1).max(2000).optional()
|
|
72
|
+
.describe("Facts to return, 1-2000, newest period first. Backend default is 250. Sizes the response; there is no paging."),
|
|
73
|
+
}, call("/api/v1/sec/concept"));
|
|
74
|
+
server.tool("get_sec_facts", `Get the index of every XBRL concept a US SEC EDGAR filer reports as JSON - tag, label, description, units and most recent value - across us-gaap, dei and any other taxonomy it uses. This is the discovery step: find the tag here, then pull its full history with get_sec_concept. Not paginated - \`limit\` only sizes the response (1-2000, default 250). Requires cik or ticker. Costs 1 credit.`, {
|
|
75
|
+
...filerRef,
|
|
76
|
+
taxonomy: z.string().min(1).max(40).optional()
|
|
77
|
+
.describe("Restrict to one XBRL taxonomy, e.g. 'us-gaap' or 'dei'. Omit to see every taxonomy the filer uses."),
|
|
78
|
+
query: z.string().min(1).max(200).optional()
|
|
79
|
+
.describe("Case-insensitive substring match against the tag name and its label, e.g. 'revenue'."),
|
|
80
|
+
limit: z.number().int().min(1).max(2000).optional()
|
|
81
|
+
.describe("Concepts to return, 1-2000. Backend default is 250. Sizes the response; there is no paging."),
|
|
82
|
+
}, call("/api/v1/sec/facts"));
|
|
83
|
+
server.tool("search_sec_filings", `Full-text search across US SEC EDGAR filing DOCUMENTS. Coverage STARTS IN 2001 - nothing filed earlier is in this index. Each hit comes back as JSON with the document URL, form, filing date and filer identity, plus facets breaking the whole result set down by company, form, industry and state. Accepts NO query at all: a cik, ticker, form or date filter on its own is a valid search. Paginated with page at 100 documents per page and CAPPED AT PAGE 100, because the index refuses a result window past 10,000. Use get_sec_filings instead when you want one known filer's complete filing list rather than a text match. Costs 1 credit.`, {
|
|
84
|
+
query: z.string().min(1).max(500).optional()
|
|
85
|
+
.describe("Full-text query over filing documents. A quoted phrase ('\"climate risk\"') is matched exactly; bare words are matched as a bag of terms. Optional - filters alone are a valid search."),
|
|
86
|
+
cik: z.union([z.string().min(1).max(20), z.array(z.string().min(1).max(20)).min(1).max(25)]).optional()
|
|
87
|
+
.describe("Restrict to one or more filers by CIK, as a string or an array of up to 25. Tickers are accepted here too."),
|
|
88
|
+
ticker: z.union([z.string().min(1).max(20), z.array(z.string().min(1).max(20)).min(1).max(25)]).optional()
|
|
89
|
+
.describe("Restrict to one or more filers by ticker symbol, as a string or an array of up to 25."),
|
|
90
|
+
form: z.union([formItem, z.array(formItem).min(1).max(25)]).optional()
|
|
91
|
+
.describe("Form types to keep: '8-K', ['10-K','10-Q'] or '10-K,10-Q'."),
|
|
92
|
+
date_from: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "must be YYYY-MM-DD").optional()
|
|
93
|
+
.describe("Earliest filing date, inclusive, as YYYY-MM-DD. Full-text coverage starts in 2001."),
|
|
94
|
+
date_to: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "must be YYYY-MM-DD").optional()
|
|
95
|
+
.describe("Latest filing date, inclusive, as YYYY-MM-DD."),
|
|
96
|
+
location: z.union([locationCode, z.array(locationCode).min(1).max(25)]).optional()
|
|
97
|
+
.describe("Filer business-address locations as EDGAR's own 2-character codes ('CA', 'NY', and its alphanumeric codes for foreign jurisdictions), as a string or an array of up to 25."),
|
|
98
|
+
sort: z.enum(["relevance", "newest", "oldest"]).optional()
|
|
99
|
+
.describe("Result ordering. Backend default is the index's own relevance ranking."),
|
|
100
|
+
page: z.number().int().min(1).max(100).optional()
|
|
101
|
+
.describe("Results page, 1-based, 100 documents per page. Page 100 is the last page for ANY query - the index refuses a result window past 10,000."),
|
|
102
|
+
}, call("/api/v1/sec/search"));
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=sec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sec.js","sourceRoot":"","sources":["../../src/tools/sec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { handleApiError } from "../lib/tool-error.js";
|
|
3
|
+
// Every description opens "Target.com, the US retailer" on purpose: read out of
|
|
4
|
+
// context, a tool called get_target_* is easily taken for "the target of the
|
|
5
|
+
// operation". The brand has to be established in the first clause, before the
|
|
6
|
+
// model has decided what the tool is for.
|
|
7
|
+
//
|
|
8
|
+
// No zod `.default()` anywhere in this file. The MCP SDK applies a zod default
|
|
9
|
+
// BEFORE the handler runs, so a defaulted field is posted on every call whether
|
|
10
|
+
// the model set it or not. Upstream defaults are documented in the describe
|
|
11
|
+
// text and left for the backend to apply.
|
|
12
|
+
const SORT_DESCRIPTION = "Sort order. Upstream default is 'relevance'.";
|
|
13
|
+
const storeIdField = z.string().optional()
|
|
14
|
+
.describe("Numeric Target store id. Unlike Walmart, this IS a real request param — it selects the store whose prices, promotions and availability you get back. Upstream default is '3991'.");
|
|
15
|
+
export function registerTargetTools(server, getClient) {
|
|
16
|
+
server.tool("search_target_products", `Target.com, the US retailer — search its catalogue by keyword and return the product grid as JSON. Each row carries the tcin (Target's product id), title, brand, URL, current price and list price, rating and review count, badges and promotions, image, and seller_id / seller_name.
|
|
17
|
+
|
|
18
|
+
seller_id and seller_name are NULL for first-party stock, which is most of Target. NULL means "sold by Target", not missing data — only Target Plus marketplace rows name a third-party vendor.
|
|
19
|
+
|
|
20
|
+
Paginate with page. count sets the page size and is capped at 28: Target rejects anything above that outright (upstream default 24). store_id picks the store whose prices and availability you see.
|
|
21
|
+
|
|
22
|
+
LATENCY, not price, is the thing to plan around: Target refuses proxy pools and is reached through a headless browser, so a search takes roughly 9 seconds and can hold a concurrency slot much longer under retry. Do not put it in a tight loop. Costs 1 credit per page.`, {
|
|
23
|
+
keyword: z.string().min(1).max(500)
|
|
24
|
+
.describe("Product search keyword, e.g. 'office chair'."),
|
|
25
|
+
page: z.number().int().min(1).optional()
|
|
26
|
+
.describe("Results page, 1-based. One page per call, 1 credit each."),
|
|
27
|
+
count: z.number().int().min(1).max(28).optional()
|
|
28
|
+
.describe("Products per page, 1-28. Target rejects anything above 28 outright. Upstream default is 24."),
|
|
29
|
+
sort: z.enum(["relevance", "featured", "price_low", "price_high", "rating_high", "best_seller", "newest"]).optional()
|
|
30
|
+
.describe(SORT_DESCRIPTION),
|
|
31
|
+
store_id: storeIdField,
|
|
32
|
+
}, async (params) => {
|
|
33
|
+
try {
|
|
34
|
+
const data = await getClient().post("/api/v1/target/search", params);
|
|
35
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
return handleApiError(err);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
server.tool("get_target_category", `Target.com, the US retailer — list the products inside one Target category as JSON. Same shape as search_target_products plus the category breadcrumb: tcin, title, brand, URL, price and list price, rating and review count, badges and promotions, image, and seller_id / seller_name.
|
|
42
|
+
|
|
43
|
+
seller_id and seller_name are NULL for first-party stock, which is most of Target — NULL means "sold by Target", not missing data.
|
|
44
|
+
|
|
45
|
+
category_id is the segment after \`N-\` in a target.com /c/ URL. Paginate with page; count is capped at 28 (upstream default 24). store_id picks the store whose prices and availability you see.
|
|
46
|
+
|
|
47
|
+
This is the SLOWEST Target endpoint at roughly 37 seconds per call, with 502-then-retry cases observed near 100 seconds. Use it for catalogue sweeps you can wait on, not for interactive lookups. Costs 1 credit per page.`, {
|
|
48
|
+
category_id: z.string().min(1)
|
|
49
|
+
.describe("Target category id — the segment after `N-` in a target.com /c/ URL, e.g. '5xtg6' from /c/office-chairs/-/N-5xtg6."),
|
|
50
|
+
page: z.number().int().min(1).optional()
|
|
51
|
+
.describe("Results page, 1-based. One page per call, 1 credit each."),
|
|
52
|
+
count: z.number().int().min(1).max(28).optional()
|
|
53
|
+
.describe("Products per page, 1-28. Target rejects anything above 28 outright. Upstream default is 24."),
|
|
54
|
+
sort: z.enum(["relevance", "featured", "price_low", "price_high", "rating_high", "best_seller", "newest"]).optional()
|
|
55
|
+
.describe(SORT_DESCRIPTION),
|
|
56
|
+
store_id: storeIdField,
|
|
57
|
+
}, async (params) => {
|
|
58
|
+
try {
|
|
59
|
+
const data = await getClient().post("/api/v1/target/category", params);
|
|
60
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
return handleApiError(err);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
server.tool("get_target_product", `Target.com, the US retailer — get one product's full detail by TCIN as JSON: title, brand, current price and list price, rating and review count, images, the specification table and feature bullets, variants, the return policy, and fulfillment for the chosen store (ship, order pickup, drive up, same-day delivery).
|
|
67
|
+
|
|
68
|
+
Pass a child TCIN and Target answers with its variation PARENT, with the child present in variants — that is the normal response, not a wrong match.
|
|
69
|
+
|
|
70
|
+
seller_id / seller_name are NULL for first-party stock, which is most of Target; NULL means "sold by Target". Unlike Walmart, store_id IS a real request param here — prices and availability are your choice.
|
|
71
|
+
|
|
72
|
+
Fastest Target endpoint at roughly 4 seconds. Single-product lookup, no pagination. For review bodies call get_target_reviews. Costs 1 credit.`, {
|
|
73
|
+
tcin: z.string().min(1)
|
|
74
|
+
.describe("Target TCIN, the numeric product id from a target.com /p/ URL, e.g. '54551690'. A child TCIN is answered by its variation parent, with the child listed under variants."),
|
|
75
|
+
store_id: storeIdField,
|
|
76
|
+
}, async (params) => {
|
|
77
|
+
try {
|
|
78
|
+
const data = await getClient().post("/api/v1/target/product", params);
|
|
79
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
return handleApiError(err);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
server.tool("get_target_reviews", `Target.com, the US retailer — get a product's reviews by TCIN as JSON: the overall rating with its star distribution, per-attribute averages (quality, value, and so on), the recommendation percentage, guest photos, and the review bodies with author, date, rating, title and text.
|
|
86
|
+
|
|
87
|
+
HARD CAP: 8 REVIEW BODIES MAXIMUM, no matter how large review_count is. Target publishes no more than that anonymously. There is NO page and NO offset param — \`limit\` only TRIMS the 8 you would already receive, it cannot fetch more. If you need a deep review corpus, this endpoint cannot supply it and no amount of extra calls will change that.
|
|
88
|
+
|
|
89
|
+
Roughly 40 seconds per call. Costs 1 credit.`, {
|
|
90
|
+
tcin: z.string().min(1)
|
|
91
|
+
.describe("Target TCIN, the numeric product id from a target.com /p/ URL, e.g. '54551690'."),
|
|
92
|
+
limit: z.number().int().min(1).optional()
|
|
93
|
+
.describe("TRIMS the returned review bodies only. The ceiling is 8 regardless — Target publishes 8 anonymously and offers no paging, so a limit above 8 changes nothing."),
|
|
94
|
+
store_id: storeIdField,
|
|
95
|
+
}, async (params) => {
|
|
96
|
+
try {
|
|
97
|
+
const data = await getClient().post("/api/v1/target/reviews", params);
|
|
98
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
return handleApiError(err);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=target.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"target.js","sourceRoot":"","sources":["../../src/tools/target.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { handleApiError } from "../lib/tool-error.js";
|
|
3
|
+
/**
|
|
4
|
+
* The handle surcharge. Threads' upstream handle lookup is dead, so a username
|
|
5
|
+
* has to be resolved through people search first — a second upstream call, and
|
|
6
|
+
* 2 extra credits. user_id is always the cheap path.
|
|
7
|
+
*/
|
|
8
|
+
const usernameField = z.string().min(1).max(60).optional()
|
|
9
|
+
.describe("Threads handle without the @, e.g. 'natgeo'. Costs 2 EXTRA credits (4 instead of 2) because the handle must be resolved to a numeric id first. Prefer user_id when you have it. Either username or user_id is required.");
|
|
10
|
+
const userIdField = z.string().min(1).optional()
|
|
11
|
+
.describe("Numeric Threads user id, e.g. '63625256886' — the user_id field returned by get_threads_profile or search_threads_users. This is the cheap path: 2 credits. Either username or user_id is required.");
|
|
12
|
+
const cursorField = z.string().optional()
|
|
13
|
+
.describe("Pagination cursor. Pass data.next_cursor from the previous response to get the next page.");
|
|
14
|
+
export function registerThreadsTools(server, getClient) {
|
|
15
|
+
server.tool("get_threads_profile", `Get a Threads (Meta's text app, threads.net) user profile as JSON. Returns user_id, username, full_name, biography, profile_pic_url, follower_count, is_verified and is_private. Address the user by user_id for 2 credits, or by username for 4 credits — the handle costs 2 extra because Threads' upstream handle lookup is dead and the handle has to be resolved through people search first. Resolve a handle to a user_id once with search_threads_users, then reuse that user_id across calls to stay on the 2-credit path. Pass exactly one of username or user_id: sending neither, or both in conflict, returns 422; an unknown user returns 404. Not paginated.`, {
|
|
16
|
+
username: usernameField,
|
|
17
|
+
user_id: userIdField,
|
|
18
|
+
}, async (params) => {
|
|
19
|
+
try {
|
|
20
|
+
const data = await getClient().post("/api/v1/threads/profile", params);
|
|
21
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
return handleApiError(err);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
server.tool("get_threads_user_posts", `List a Threads user's own posts as JSON. Returns data.posts, each with post_id, code, text, taken_at, like_count, reply_count, repost_count, media_type, cover_url, video_url and the author object (user_id, username, full_name, follower_count, is_verified). Cursor-paginated: pass data.next_cursor back as cursor while data.has_more is true. Costs 2 credits per page when addressed by user_id, or 4 credits per page when addressed by username — the handle costs 2 extra because it has to be resolved to a numeric id first, so resolve once and page by user_id. Pass exactly one of username or user_id (422 otherwise, 404 for an unknown user).`, {
|
|
28
|
+
username: usernameField,
|
|
29
|
+
user_id: userIdField,
|
|
30
|
+
cursor: cursorField,
|
|
31
|
+
}, async (params) => {
|
|
32
|
+
try {
|
|
33
|
+
const data = await getClient().post("/api/v1/threads/user/posts", params);
|
|
34
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
return handleApiError(err);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
server.tool("get_threads_user_replies", `List the replies a Threads user has written to other people's posts, as JSON — the "Replies" tab of their profile, not their own posts (use get_threads_user_posts for those). Returns data.posts, each with post_id, code, text, taken_at, like_count, reply_count, repost_count, media_type, cover_url, video_url and the author object. Cursor-paginated: pass data.next_cursor back as cursor while data.has_more is true. Costs 2 credits per page when addressed by user_id, or 4 credits per page when addressed by username — the handle costs 2 extra because it has to be resolved to a numeric id first. Pass exactly one of username or user_id (422 otherwise, 404 for an unknown user).`, {
|
|
41
|
+
username: usernameField,
|
|
42
|
+
user_id: userIdField,
|
|
43
|
+
cursor: cursorField,
|
|
44
|
+
}, async (params) => {
|
|
45
|
+
try {
|
|
46
|
+
const data = await getClient().post("/api/v1/threads/user/replies", params);
|
|
47
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
return handleApiError(err);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
server.tool("get_threads_post", `Get a single Threads post by its id or its threads.net URL, as JSON. Returns a flat post object: post_id, code, text, taken_at, like_count, reply_count, repost_count, media_type, cover_url, video_url and the author (user_id, username, full_name, biography, follower_count, is_verified, is_private). Does NOT return the replies — call get_threads_post_comments with the post_id for those. Pass exactly one of post_id or url; sending neither returns 422. Always costs 2 credits (there is no handle surcharge on this tool). Not paginated.`, {
|
|
54
|
+
post_id: z.string().min(1).optional()
|
|
55
|
+
.describe("Threads post id, e.g. '3349029093483693129'. Either this or url is required."),
|
|
56
|
+
url: z.string().url().optional()
|
|
57
|
+
.describe("Full threads.net post URL, e.g. 'https://www.threads.net/@natgeo/post/C8xY'. Either this or post_id is required."),
|
|
58
|
+
}, async (params) => {
|
|
59
|
+
try {
|
|
60
|
+
const data = await getClient().post("/api/v1/threads/post", params);
|
|
61
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
return handleApiError(err);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
server.tool("get_threads_post_comments", `List the replies to a Threads post as JSON. Returns data.comments, each with post_id, code, text, taken_at, like_count, reply_count, repost_count, media_type, cover_url, video_url and the author object (user_id, username, full_name, follower_count, is_verified). Cursor-paginated: pass data.next_cursor back as cursor while data.has_more is true. Takes a post_id only — there is no username or URL form here; get the post_id from get_threads_post, get_threads_user_posts or a threads.net URL. Always costs 2 credits per page.`, {
|
|
68
|
+
post_id: z.string().min(1)
|
|
69
|
+
.describe("Threads post id, e.g. '3349029093483693129'. Required — this tool does not accept a URL."),
|
|
70
|
+
cursor: cursorField,
|
|
71
|
+
}, async (params) => {
|
|
72
|
+
try {
|
|
73
|
+
const data = await getClient().post("/api/v1/threads/post/comments", params);
|
|
74
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
return handleApiError(err);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
server.tool("search_threads_users", `Search Threads PROFILES by name or handle, as JSON. Returns data.users, each with user_id, username, full_name, biography, profile_pic_url, follower_count, is_verified and is_private. This is people search and it is the ONLY search Threads exposes — there is no post or content search on Threads, so this tool cannot find posts by keyword, hashtag or topic. Its main use is resolving a handle to a numeric user_id once, so that get_threads_profile, get_threads_user_posts and get_threads_user_replies can be called on the cheap 2-credit user_id path instead of the 4-credit username path. Costs 2 credits. Not paginated — one result set per query.`, {
|
|
81
|
+
query: z.string().min(1).max(200)
|
|
82
|
+
.describe("Name or handle to search for, e.g. 'national geographic' or 'natgeo'."),
|
|
83
|
+
}, async (params) => {
|
|
84
|
+
try {
|
|
85
|
+
const data = await getClient().post("/api/v1/threads/search/users", params);
|
|
86
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
return handleApiError(err);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=threads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threads.js","sourceRoot":"","sources":["../../src/tools/threads.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,16 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { handleApiError } from "../lib/tool-error.js";
|
|
3
|
+
// ApiError is imported here (and nowhere else in tools/) because get_tiktok_shop_product
|
|
4
|
+
// has to special-case a 404 before falling through to the shared handler.
|
|
3
5
|
import { ApiError } from "../lib/errors.js";
|
|
4
|
-
function handleApiError(err) {
|
|
5
|
-
if (err instanceof ApiError) {
|
|
6
|
-
if (err.status === 429)
|
|
7
|
-
return { isError: true, content: [{ type: "text", text: "Rate limited. Wait and retry." }] };
|
|
8
|
-
if (err.status === 401)
|
|
9
|
-
throw new McpError(ErrorCode.InternalError, "Invalid SCAVIO_API_KEY. Check your configuration.");
|
|
10
|
-
return { isError: true, content: [{ type: "text", text: `Scavio API error (${err.status}): ${err.message}` }] };
|
|
11
|
-
}
|
|
12
|
-
throw new McpError(ErrorCode.InternalError, String(err));
|
|
13
|
-
}
|
|
14
6
|
const REGIONS_FULL = ["US", "GB", "SG", "MY", "PH", "TH", "VN", "ID"];
|
|
15
7
|
const REGIONS_LISTING = ["US", "GB"];
|
|
16
8
|
export function registerTiktokShopTools(server, getClient) {
|