@scavio/mcp-server 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Scavio
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,253 @@
1
+ # Scavio MCP Server
2
+
3
+ ![GitHub Repo stars](https://img.shields.io/github/stars/scavio-ai/scavio-mcp?style=social)
4
+ ![License](https://img.shields.io/github/license/scavio-ai/scavio-mcp)
5
+
6
+ An MCP server that connects AI agents to Google, YouTube, Amazon, Walmart, TikTok, and Reddit. 21 tools for web search, product lookup, video discovery, and social media analysis through a single HTTP endpoint.
7
+
8
+ ## Remote MCP Server
9
+
10
+ Connect directly to Scavio's remote MCP server without any local installation:
11
+
12
+ ```
13
+ https://mcp.scavio.dev/mcp
14
+ ```
15
+
16
+ Pass your API key via the `x-api-key` header. Get your key at [scavio.dev](https://scavio.dev).
17
+
18
+ ---
19
+
20
+ ## Run Locally (npx)
21
+
22
+ Prefer to run the server on your own machine? Use `npx` with no clone or build. The server runs over stdio and only needs your `SCAVIO_API_KEY`.
23
+
24
+ ### Claude Code
25
+
26
+ ```bash
27
+ claude mcp add scavio -e SCAVIO_API_KEY=YOUR_SCAVIO_API_KEY -- npx -y @scavio/mcp-server
28
+ ```
29
+
30
+ ### Any MCP-Compatible Client (Claude Desktop, Cursor, Windsurf, VS Code, etc.)
31
+
32
+ ```json
33
+ {
34
+ "mcpServers": {
35
+ "scavio": {
36
+ "command": "npx",
37
+ "args": ["-y", "@scavio/mcp-server"],
38
+ "env": {
39
+ "SCAVIO_API_KEY": "YOUR_SCAVIO_API_KEY"
40
+ }
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ Requires Node.js 20+. Get your API key at [scavio.dev](https://scavio.dev).
47
+
48
+ ---
49
+
50
+ ## Install
51
+
52
+ ### Claude Code
53
+
54
+ ```bash
55
+ claude mcp add scavio --transport http --url https://mcp.scavio.dev/mcp --header "x-api-key: YOUR_SCAVIO_API_KEY"
56
+ ```
57
+
58
+ ### Claude Desktop
59
+
60
+ Add to `claude_desktop_config.json` (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, `%APPDATA%\Claude\claude_desktop_config.json` on Windows):
61
+
62
+ ```json
63
+ {
64
+ "mcpServers": {
65
+ "scavio": {
66
+ "type": "http",
67
+ "url": "https://mcp.scavio.dev/mcp",
68
+ "headers": {
69
+ "x-api-key": "YOUR_SCAVIO_API_KEY"
70
+ }
71
+ }
72
+ }
73
+ }
74
+ ```
75
+
76
+ ### Cursor
77
+
78
+ [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=scavio&config=eyJ1cmwiOiJodHRwczovL21jcC5zY2F2aW8uZGV2L21jcCIsImhlYWRlcnMiOnsieC1hcGkta2V5IjoiWU9VUl9TQ0FWSU9fQVBJX0tFWSJ9fQ%3D%3D)
79
+
80
+ Or add to `.cursor/mcp.json` in your project root or `~/.cursor/mcp.json` for global:
81
+
82
+ ```json
83
+ {
84
+ "mcpServers": {
85
+ "scavio": {
86
+ "type": "http",
87
+ "url": "https://mcp.scavio.dev/mcp",
88
+ "headers": {
89
+ "x-api-key": "YOUR_SCAVIO_API_KEY"
90
+ }
91
+ }
92
+ }
93
+ }
94
+ ```
95
+
96
+ ### Windsurf
97
+
98
+ Add to `~/.codeium/windsurf/mcp_config.json`:
99
+
100
+ ```json
101
+ {
102
+ "mcpServers": {
103
+ "scavio": {
104
+ "type": "http",
105
+ "url": "https://mcp.scavio.dev/mcp",
106
+ "headers": {
107
+ "x-api-key": "YOUR_SCAVIO_API_KEY"
108
+ }
109
+ }
110
+ }
111
+ }
112
+ ```
113
+
114
+ ### VS Code
115
+
116
+ Create `.vscode/mcp.json` in your project root:
117
+
118
+ ```json
119
+ {
120
+ "servers": {
121
+ "scavio": {
122
+ "type": "http",
123
+ "url": "https://mcp.scavio.dev/mcp",
124
+ "headers": {
125
+ "x-api-key": "YOUR_SCAVIO_API_KEY"
126
+ }
127
+ }
128
+ }
129
+ }
130
+ ```
131
+
132
+ ### ChatGPT
133
+
134
+ 1. Go to [ChatGPT](https://chatgpt.com) > **Settings** > **Apps & Connectors**
135
+ 2. Click **Add custom connector**
136
+ 3. Enter the MCP server URL: `https://mcp.scavio.dev/mcp`
137
+ 4. Configure authentication with your API key
138
+
139
+ ### Cline
140
+
141
+ Add to `cline_mcp_settings.json` (open via Cline sidebar > MCP Servers > Configure):
142
+
143
+ ```json
144
+ {
145
+ "mcpServers": {
146
+ "scavio": {
147
+ "type": "http",
148
+ "url": "https://mcp.scavio.dev/mcp",
149
+ "headers": {
150
+ "x-api-key": "YOUR_SCAVIO_API_KEY"
151
+ }
152
+ }
153
+ }
154
+ }
155
+ ```
156
+
157
+ ### Zed
158
+
159
+ Add to settings (`Cmd+,`):
160
+
161
+ ```json
162
+ {
163
+ "assistant": {
164
+ "mcp_servers": {
165
+ "scavio": {
166
+ "type": "http",
167
+ "url": "https://mcp.scavio.dev/mcp",
168
+ "headers": {
169
+ "x-api-key": "YOUR_SCAVIO_API_KEY"
170
+ }
171
+ }
172
+ }
173
+ }
174
+ }
175
+ ```
176
+
177
+ ### Any MCP-Compatible Client
178
+
179
+ ```json
180
+ {
181
+ "type": "http",
182
+ "url": "https://mcp.scavio.dev/mcp",
183
+ "headers": {
184
+ "x-api-key": "YOUR_SCAVIO_API_KEY"
185
+ }
186
+ }
187
+ ```
188
+
189
+ ---
190
+
191
+ ## Available Tools
192
+
193
+ ### Google
194
+
195
+ | Tool | Description |
196
+ |------|-------------|
197
+ | `search_google` | Web search with structured results, news, images, maps, and knowledge graph |
198
+
199
+ ### YouTube
200
+
201
+ | Tool | Description |
202
+ |------|-------------|
203
+ | `search_youtube` | Search videos, channels, and playlists |
204
+ | `get_youtube_metadata` | Get video metadata including title, views, likes, and duration |
205
+
206
+ ### Amazon
207
+
208
+ | Tool | Description |
209
+ |------|-------------|
210
+ | `search_amazon` | Search product listings with price and sort filters |
211
+ | `get_amazon_product` | Get full product details by ASIN |
212
+
213
+ ### Walmart
214
+
215
+ | Tool | Description |
216
+ |------|-------------|
217
+ | `search_walmart` | Search product listings with price and delivery filters |
218
+ | `get_walmart_product` | Get full product details by product ID |
219
+
220
+ ### TikTok
221
+
222
+ | Tool | Description |
223
+ |------|-------------|
224
+ | `get_tiktok_profile` | Get user profile (bio, follower/following counts, likes) |
225
+ | `get_tiktok_user_posts` | List a user's videos with stats |
226
+ | `get_tiktok_video` | Get detailed info for a single video |
227
+ | `get_tiktok_video_comments` | Get comments on a video |
228
+ | `get_tiktok_comment_replies` | Get replies to a specific comment |
229
+ | `search_tiktok_videos` | Search videos by keyword |
230
+ | `search_tiktok_users` | Search users by keyword |
231
+ | `get_tiktok_hashtag` | Get hashtag details and stats |
232
+ | `get_tiktok_hashtag_videos` | List videos for a hashtag |
233
+ | `get_tiktok_user_followers` | Get a user's follower list |
234
+ | `get_tiktok_user_followings` | Get a user's following list |
235
+
236
+ ### Reddit
237
+
238
+ | Tool | Description |
239
+ |------|-------------|
240
+ | `search_reddit` | Search Reddit posts by query with sort and pagination |
241
+ | `get_reddit_post` | Get a full post with threaded comments by URL |
242
+
243
+ ### Account
244
+
245
+ | Tool | Description |
246
+ |------|-------------|
247
+ | `get_usage` | Check credit balance, plan, and usage stats |
248
+
249
+ ---
250
+
251
+ ## License
252
+
253
+ [MIT](LICENSE)
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { env } from "./lib/env.js";
4
+ import { ScavioClient } from "./lib/client.js";
5
+ import { registerAllTools } from "./tools/index.js";
6
+ const SERVER_NAME = "scavio-mcp";
7
+ const SERVER_VERSION = "0.1.0";
8
+ if (env.TRANSPORT === "stdio") {
9
+ await startStdio();
10
+ }
11
+ else {
12
+ await startHttp();
13
+ }
14
+ async function startStdio() {
15
+ const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
16
+ const apiKey = env.SCAVIO_API_KEY;
17
+ if (!apiKey) {
18
+ console.error("SCAVIO_API_KEY is required for stdio transport");
19
+ process.exit(1);
20
+ }
21
+ const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });
22
+ const client = new ScavioClient(apiKey);
23
+ registerAllTools(server, () => client);
24
+ const transport = new StdioServerTransport();
25
+ await server.connect(transport);
26
+ console.error(`${SERVER_NAME} started (stdio)`);
27
+ }
28
+ async function startHttp() {
29
+ const { Hono } = await import("hono");
30
+ const { serve } = await import("@hono/node-server");
31
+ const { WebStandardStreamableHTTPServerTransport } = await import("@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js");
32
+ const app = new Hono();
33
+ app.get("/", (c) => c.json({ status: "ok", server: SERVER_NAME }));
34
+ app.get("/health", (c) => c.json({ status: "ok" }));
35
+ app.post("/mcp", async (c) => {
36
+ const apiKey = c.req.header("x-api-key") ??
37
+ c.req.header("authorization")?.replace("Bearer ", "") ??
38
+ c.req.query("api_key");
39
+ if (!apiKey) {
40
+ return c.json({ error: "Missing SCAVIO_API_KEY. Provide x-api-key or Authorization: Bearer header." }, 401);
41
+ }
42
+ const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });
43
+ const client = new ScavioClient(apiKey);
44
+ registerAllTools(server, () => client);
45
+ const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: undefined });
46
+ await server.connect(transport);
47
+ return transport.handleRequest(c.req.raw);
48
+ });
49
+ serve({ fetch: app.fetch, port: env.PORT }, (info) => {
50
+ console.error(`${SERVER_NAME} started (http) on port ${info.port}`);
51
+ });
52
+ }
53
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export declare class ScavioClient {
2
+ private baseUrl;
3
+ private apiKey;
4
+ constructor(apiKey: string);
5
+ private request;
6
+ get<T>(path: string): Promise<T>;
7
+ post<T>(path: string, body?: unknown): Promise<T>;
8
+ }
@@ -0,0 +1,40 @@
1
+ import { env } from "./env.js";
2
+ import { ApiError } from "./errors.js";
3
+ export class ScavioClient {
4
+ baseUrl;
5
+ apiKey;
6
+ constructor(apiKey) {
7
+ this.baseUrl = env.SCAVIO_API_BASE_URL;
8
+ this.apiKey = apiKey;
9
+ }
10
+ async request(method, path, body) {
11
+ const response = await fetch(`${this.baseUrl}${path}`, {
12
+ method,
13
+ headers: {
14
+ "Content-Type": "application/json",
15
+ "Authorization": `Bearer ${this.apiKey}`,
16
+ },
17
+ ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
18
+ });
19
+ if (!response.ok) {
20
+ const text = await response.text();
21
+ let message;
22
+ try {
23
+ const json = JSON.parse(text);
24
+ message = json.error ?? json.message ?? text;
25
+ }
26
+ catch {
27
+ message = text;
28
+ }
29
+ throw new ApiError(response.status, message);
30
+ }
31
+ return response.json();
32
+ }
33
+ async get(path) {
34
+ return this.request("GET", path);
35
+ }
36
+ async post(path, body) {
37
+ return this.request("POST", path, body);
38
+ }
39
+ }
40
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/lib/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,MAAM,OAAO,YAAY;IACf,OAAO,CAAS;IAChB,MAAM,CAAS;IAEvB,YAAY,MAAc;QACxB,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC,mBAAmB,CAAC;QACvC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAEO,KAAK,CAAC,OAAO,CAAI,MAAc,EAAE,IAAY,EAAE,IAAc;QACnE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,EAAE;YACrD,MAAM;YACN,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,eAAe,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE;aACzC;YACD,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC9D,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACnC,IAAI,OAAe,CAAC;YACpB,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC9B,OAAO,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC;YAC/C,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;YACD,MAAM,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC/C,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,EAAgB,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,GAAG,CAAI,IAAY;QACvB,OAAO,IAAI,CAAC,OAAO,CAAI,KAAK,EAAE,IAAI,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,IAAI,CAAI,IAAY,EAAE,IAAc;QACxC,OAAO,IAAI,CAAC,OAAO,CAAI,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;CACF"}
@@ -0,0 +1,6 @@
1
+ export declare const env: {
2
+ SCAVIO_API_BASE_URL: string;
3
+ PORT: number;
4
+ TRANSPORT: "http" | "stdio";
5
+ SCAVIO_API_KEY?: string | undefined;
6
+ };
@@ -0,0 +1,12 @@
1
+ import { z } from "zod";
2
+ const envSchema = z.object({
3
+ SCAVIO_API_BASE_URL: z.string().url()
4
+ .default("https://api.scavio.dev"),
5
+ SCAVIO_API_KEY: z.string().optional(),
6
+ PORT: z.coerce.number().int().min(1).max(65535)
7
+ .default(3000),
8
+ TRANSPORT: z.enum(["http", "stdio"])
9
+ .default("stdio"),
10
+ });
11
+ export const env = envSchema.parse(process.env);
12
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/lib/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC;IACzB,mBAAmB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;SAClC,OAAO,CAAC,wBAAwB,CAAC;IACpC,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACrC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;SAC5C,OAAO,CAAC,IAAI,CAAC;IAChB,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;SACjC,OAAO,CAAC,OAAO,CAAC;CACpB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare class ApiError extends Error {
2
+ readonly status: number;
3
+ readonly code?: string | undefined;
4
+ constructor(status: number, message: string, code?: string | undefined);
5
+ }
@@ -0,0 +1,11 @@
1
+ export class ApiError extends Error {
2
+ status;
3
+ code;
4
+ constructor(status, message, code) {
5
+ super(message);
6
+ this.status = status;
7
+ this.code = code;
8
+ this.name = "ApiError";
9
+ }
10
+ }
11
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,QAAS,SAAQ,KAAK;IAEf;IAEA;IAHlB,YACkB,MAAc,EAC9B,OAAe,EACC,IAAa;QAE7B,KAAK,CAAC,OAAO,CAAC,CAAC;QAJC,WAAM,GAAN,MAAM,CAAQ;QAEd,SAAI,GAAJ,IAAI,CAAS;QAG7B,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { ScavioClient } from "../lib/client.js";
3
+ export declare function registerAmazonTools(server: McpServer, getClient: () => ScavioClient): void;
@@ -0,0 +1,52 @@
1
+ import { z } from "zod";
2
+ import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
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
+ }
14
+ export function registerAmazonTools(server, getClient) {
15
+ server.tool("search_amazon", `Search Amazon and return product listings as JSON. Each result includes title, ASIN, price, rating, review count, and image URL. Use when the user wants to find products on Amazon or compare prices.`, {
16
+ query: z.string().min(1).max(500)
17
+ .describe("Product search query, e.g. 'wireless noise cancelling headphones'."),
18
+ domain: z.string().default("com")
19
+ .describe("Amazon domain suffix, e.g. 'com' (US), 'co.uk' (UK), 'de' (Germany), 'co.jp' (Japan)."),
20
+ sort_by: z.enum(["most_recent", "price_low_to_high", "price_high_to_low", "featured", "average_review", "bestsellers"]).default("featured")
21
+ .describe("Sort order for results."),
22
+ start_page: z.number().int().min(1).default(1)
23
+ .describe("Starting page number."),
24
+ min_price: z.number().optional()
25
+ .describe("Minimum price filter in the domain's local currency."),
26
+ max_price: z.number().optional()
27
+ .describe("Maximum price filter in the domain's local currency."),
28
+ }, async (params) => {
29
+ try {
30
+ const data = await getClient().post("/api/v1/amazon/search", 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("get_amazon_product", `Get detailed information for an Amazon product by its ASIN. Returns title, full description, price, rating, review count, images, variants, and seller info. Use when the user has a specific Amazon product URL or ASIN and wants full details.`, {
38
+ query: z.string().length(10)
39
+ .describe("Amazon ASIN — the 10-character product ID, e.g. 'B09V3KXJPB'. Extract from the Amazon URL (/dp/ASIN)."),
40
+ domain: z.string().default("com")
41
+ .describe("Amazon domain suffix, e.g. 'com', 'co.uk', 'de'."),
42
+ }, async (params) => {
43
+ try {
44
+ const data = await getClient().post("/api/v1/amazon/product", params);
45
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
46
+ }
47
+ catch (err) {
48
+ return handleApiError(err);
49
+ }
50
+ });
51
+ }
52
+ //# sourceMappingURL=amazon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"amazon.js","sourceRoot":"","sources":["../../src/tools/amazon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { ScavioClient } from "../lib/client.js";
3
+ export declare function registerGoogleTools(server: McpServer, getClient: () => ScavioClient): void;
@@ -0,0 +1,37 @@
1
+ import { z } from "zod";
2
+ import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
3
+ import { ApiError } from "../lib/errors.js";
4
+ export function registerGoogleTools(server, getClient) {
5
+ server.tool("search_google", `Search Google and return structured results as JSON. Each result includes title, URL, and snippet. Also returns top stories, knowledge graph, local results, news, images, and related searches depending on search type. Use when the user asks to search the web, find current information, or look something up online. Returns 1 credit (light) or 2 credits (full).`, {
6
+ query: z.string().min(1).max(500)
7
+ .describe("The search query."),
8
+ search_type: z.enum(["classic", "news", "maps", "images", "lens"]).default("classic")
9
+ .describe("Type of search. Use 'news' for recent articles, 'maps' for local results, 'images' for image results."),
10
+ country_code: z.string().length(2).default("us")
11
+ .describe("ISO 3166-1 alpha-2 country code, e.g. 'us', 'gb', 'fr'."),
12
+ language: z.string().default("en")
13
+ .describe("Language code, e.g. 'en', 'fr', 'de'."),
14
+ page: z.number().int().min(1).max(100).default(1)
15
+ .describe("Page number, 1-indexed."),
16
+ device: z.enum(["desktop", "mobile"]).default("desktop")
17
+ .describe("Device type for results."),
18
+ light_request: z.boolean().default(true)
19
+ .describe("true = 1 credit (fewer results). false = 2 credits (full results). Use false only when the user needs comprehensive results."),
20
+ }, async (params) => {
21
+ try {
22
+ const data = await getClient().post("/api/v1/google", params);
23
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
24
+ }
25
+ catch (err) {
26
+ if (err instanceof ApiError) {
27
+ if (err.status === 429)
28
+ return { isError: true, content: [{ type: "text", text: "Rate limited. Wait and retry." }] };
29
+ if (err.status === 401)
30
+ throw new McpError(ErrorCode.InternalError, "Invalid SCAVIO_API_KEY. Check your configuration.");
31
+ return { isError: true, content: [{ type: "text", text: `Scavio API error (${err.status}): ${err.message}` }] };
32
+ }
33
+ throw new McpError(ErrorCode.InternalError, String(err));
34
+ }
35
+ });
36
+ }
37
+ //# sourceMappingURL=google.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google.js","sourceRoot":"","sources":["../../src/tools/google.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAEzE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,MAAM,UAAU,mBAAmB,CAAC,MAAiB,EAAE,SAA6B;IAClF,MAAM,CAAC,IAAI,CACT,eAAe,EACf,0WAA0W,EAC1W;QACE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;aAC9B,QAAQ,CAAC,mBAAmB,CAAC;QAChC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;aAClF,QAAQ,CAAC,uGAAuG,CAAC;QACpH,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;aAC7C,QAAQ,CAAC,yDAAyD,CAAC;QACtE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;aAC/B,QAAQ,CAAC,uCAAuC,CAAC;QACpD,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;aAC9C,QAAQ,CAAC,yBAAyB,CAAC;QACtC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;aACrD,QAAQ,CAAC,0BAA0B,CAAC;QACvC,aAAa,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;aACrC,QAAQ,CAAC,8HAA8H,CAAC;KAC5I,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;QACf,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,SAAS,EAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;YAC9D,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAC9E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;gBAC5B,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;oBAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,+BAA+B,EAAE,CAAC,EAAE,CAAC;gBACrH,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;oBAAE,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,aAAa,EAAE,mDAAmD,CAAC,CAAC;gBACzH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,qBAAqB,GAAG,CAAC,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC;YAClH,CAAC;YACD,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { ScavioClient } from "../lib/client.js";
3
+ export declare function registerAllTools(server: McpServer, getClient: () => ScavioClient): void;
@@ -0,0 +1,17 @@
1
+ import { registerGoogleTools } from "./google.js";
2
+ import { registerYoutubeTools } from "./youtube.js";
3
+ import { registerAmazonTools } from "./amazon.js";
4
+ import { registerWalmartTools } from "./walmart.js";
5
+ import { registerUsageTool } from "./usage.js";
6
+ import { registerTiktokTools } from "./tiktok.js";
7
+ import { registerRedditTools } from "./reddit.js";
8
+ export function registerAllTools(server, getClient) {
9
+ registerGoogleTools(server, getClient);
10
+ registerYoutubeTools(server, getClient);
11
+ registerAmazonTools(server, getClient);
12
+ registerWalmartTools(server, getClient);
13
+ registerTiktokTools(server, getClient);
14
+ registerRedditTools(server, getClient);
15
+ registerUsageTool(server, getClient);
16
+ }
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,UAAU,gBAAgB,CAAC,MAAiB,EAAE,SAA6B;IAC/E,mBAAmB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACvC,oBAAoB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACxC,mBAAmB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACvC,oBAAoB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACxC,mBAAmB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACvC,mBAAmB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACvC,iBAAiB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { ScavioClient } from "../lib/client.js";
3
+ export declare function registerRedditTools(server: McpServer, getClient: () => ScavioClient): void;
@@ -0,0 +1,44 @@
1
+ import { z } from "zod";
2
+ import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
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
+ }
14
+ export function registerRedditTools(server, getClient) {
15
+ server.tool("search_reddit", `Search Reddit posts across all of Reddit as JSON. Each post includes title, URL, subreddit, author, timestamp, and NSFW flag. Use data.nextCursor as the cursor parameter for the next page; stop when nextCursor is null. Slower than other platforms (5-15 seconds).`, {
16
+ query: z.string().min(1).max(500)
17
+ .describe("Search query (1-500 chars)."),
18
+ sort: z.enum(["relevance", "hot", "top", "new", "comments"]).default("relevance")
19
+ .describe("Sort order. 'relevance' (default), 'hot', 'top', 'new', or 'comments'."),
20
+ cursor: z.string().optional()
21
+ .describe("Pagination token. Use data.nextCursor from previous response for next page."),
22
+ }, async (params) => {
23
+ try {
24
+ const data = await getClient().post("/api/v1/reddit/search", params);
25
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
26
+ }
27
+ catch (err) {
28
+ return handleApiError(err);
29
+ }
30
+ });
31
+ server.tool("get_reddit_post", `Get a full Reddit post by URL as JSON, including body, score, upvote ratio, flair, awards, and the full comment tree. Comments are a flat array in traversal order; use depth (0-indexed) or parentId to reconstruct the thread hierarchy. contentUrl is the external article for link posts. Slower than other platforms (5-15 seconds).`, {
32
+ url: z.string()
33
+ .describe("Full Reddit post URL, e.g. 'https://www.reddit.com/r/Python/comments/1smb9du/fastapi_vs_django/'."),
34
+ }, async (params) => {
35
+ try {
36
+ const data = await getClient().post("/api/v1/reddit/post", params);
37
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
38
+ }
39
+ catch (err) {
40
+ return handleApiError(err);
41
+ }
42
+ });
43
+ }
44
+ //# sourceMappingURL=reddit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reddit.js","sourceRoot":"","sources":["../../src/tools/reddit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAEzE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,SAAS,cAAc,CAAC,GAAY;IAClC,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;QAC5B,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,+BAA+B,EAAE,CAAC,EAAE,CAAC;QACrH,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;YAAE,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,aAAa,EAAE,mDAAmD,CAAC,CAAC;QACzH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,qBAAqB,GAAG,CAAC,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC;IAClH,CAAC;IACD,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAiB,EAAE,SAA6B;IAClF,MAAM,CAAC,IAAI,CACT,eAAe,EACf,wQAAwQ,EACxQ;QACE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;aAC9B,QAAQ,CAAC,6BAA6B,CAAC;QAC1C,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC;aAC9E,QAAQ,CAAC,wEAAwE,CAAC;QACrF,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;aAC1B,QAAQ,CAAC,6EAA6E,CAAC;KAC3F,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;QACf,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,SAAS,EAAE,CAAC,IAAI,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC;YACrE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAC9E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,cAAc,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,IAAI,CACT,iBAAiB,EACjB,2UAA2U,EAC3U;QACE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;aACZ,QAAQ,CAAC,mGAAmG,CAAC;KACjH,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;QACf,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,SAAS,EAAE,CAAC,IAAI,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;YACnE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAC9E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,cAAc,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { ScavioClient } from "../lib/client.js";
3
+ export declare function registerTiktokTools(server: McpServer, getClient: () => ScavioClient): void;
@@ -0,0 +1,196 @@
1
+ import { z } from "zod";
2
+ import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
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
+ }
14
+ export function registerTiktokTools(server, getClient) {
15
+ server.tool("get_tiktok_profile", `Get a TikTok user's profile data as JSON. Returns username, display name, bio, follower/following counts, video count, total likes, and avatar URL. Use when the user wants info about a TikTok account. Provide either username or sec_user_id.`, {
16
+ username: z.string().optional()
17
+ .describe("TikTok handle without the @ symbol, e.g. 'charlidamelio'."),
18
+ sec_user_id: z.string().optional()
19
+ .describe("Secure user ID. Use this if you already have it from a previous request."),
20
+ }, async (params) => {
21
+ try {
22
+ const data = await getClient().post("/api/v1/tiktok/profile", params);
23
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
24
+ }
25
+ catch (err) {
26
+ return handleApiError(err);
27
+ }
28
+ });
29
+ server.tool("get_tiktok_user_posts", `List a TikTok user's videos with pagination as JSON. Each video includes ID, caption, timestamp, and stats (likes, comments, views, shares, bookmarks). Requires sec_user_id from the profile endpoint. Use data.max_cursor for next page; stop when data.has_more is 0.`, {
30
+ sec_user_id: z.string()
31
+ .describe("Secure user ID from the get_tiktok_profile response."),
32
+ cursor: z.string().default("0")
33
+ .describe("Pagination cursor. Use data.max_cursor from previous response for next page."),
34
+ count: z.number().int().min(1).max(30).default(20)
35
+ .describe("Number of results per page (1-30)."),
36
+ sort_type: z.enum(["0", "1"]).default("0")
37
+ .describe("Sort order. '0' = latest first, '1' = most popular first."),
38
+ }, async (params) => {
39
+ try {
40
+ const data = await getClient().post("/api/v1/tiktok/user/posts", params);
41
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
42
+ }
43
+ catch (err) {
44
+ return handleApiError(err);
45
+ }
46
+ });
47
+ server.tool("get_tiktok_video", `Get detailed info for a single TikTok video by ID as JSON. Returns caption, author, music, stats (likes, comments, views, shares, bookmarks), play/download URLs, cover image, duration, hashtags, and mentions. Use when the user has a specific TikTok video URL or ID.`, {
48
+ video_id: z.string()
49
+ .describe("TikTok video ID. Extract from a TikTok URL if the user provides one."),
50
+ }, async (params) => {
51
+ try {
52
+ const data = await getClient().post("/api/v1/tiktok/video", params);
53
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
54
+ }
55
+ catch (err) {
56
+ return handleApiError(err);
57
+ }
58
+ });
59
+ server.tool("get_tiktok_video_comments", `Get comments on a TikTok video as JSON. Each comment includes ID, text, timestamp, like count, reply count, commenter info, and whether the video creator liked it. Use data.cursor for next page; stop when data.has_more is 0.`, {
60
+ video_id: z.string()
61
+ .describe("TikTok video ID."),
62
+ cursor: z.string().default("0")
63
+ .describe("Pagination cursor. Use data.cursor from previous response for next page."),
64
+ count: z.number().int().min(1).max(50).default(20)
65
+ .describe("Number of comments per page (1-50)."),
66
+ }, async (params) => {
67
+ try {
68
+ const data = await getClient().post("/api/v1/tiktok/video/comments", params);
69
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
70
+ }
71
+ catch (err) {
72
+ return handleApiError(err);
73
+ }
74
+ });
75
+ server.tool("get_tiktok_comment_replies", `Get replies to a specific comment on a TikTok video as JSON. Each reply has the same structure as a comment. Requires both video_id and comment_id. Use data.cursor for next page; stop when data.has_more is 0.`, {
76
+ video_id: z.string()
77
+ .describe("TikTok video ID."),
78
+ comment_id: z.string()
79
+ .describe("Comment ID (cid) from the get_tiktok_video_comments response."),
80
+ cursor: z.string().default("0")
81
+ .describe("Pagination cursor. Use data.cursor from previous response for next page."),
82
+ count: z.number().int().min(1).max(50).default(20)
83
+ .describe("Number of replies per page (1-50)."),
84
+ }, async (params) => {
85
+ try {
86
+ const data = await getClient().post("/api/v1/tiktok/video/comments/replies", params);
87
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
88
+ }
89
+ catch (err) {
90
+ return handleApiError(err);
91
+ }
92
+ });
93
+ server.tool("search_tiktok_videos", `Search TikTok videos by keyword as JSON. Each result includes video ID, caption, author, music, stats, and video URLs. Supports sorting by relevance or likes, and filtering by publish time. Use data.cursor for next page; stop when data.has_more is 0.`, {
94
+ keyword: z.string().min(1).max(500)
95
+ .describe("Search query."),
96
+ cursor: z.string().default("0")
97
+ .describe("Pagination cursor. Use data.cursor from previous response for next page."),
98
+ count: z.number().int().min(1).max(30).default(20)
99
+ .describe("Number of results per page (1-30)."),
100
+ sort_type: z.enum(["0", "1"]).default("0")
101
+ .describe("Sort order. '0' = relevance, '1' = most likes."),
102
+ publish_time: z.enum(["0", "1", "7", "30", "90", "180"]).default("0")
103
+ .describe("Time filter. '0'=all time, '1'=last day, '7'=week, '30'=month, '90'=3 months, '180'=6 months."),
104
+ }, async (params) => {
105
+ try {
106
+ const data = await getClient().post("/api/v1/tiktok/search/videos", params);
107
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
108
+ }
109
+ catch (err) {
110
+ return handleApiError(err);
111
+ }
112
+ });
113
+ server.tool("search_tiktok_users", `Search TikTok users by keyword as JSON. Each result includes user ID, username, display name, sec_uid, follower count, and bio. Use data.cursor for next page; stop when data.has_more is 0.`, {
114
+ keyword: z.string().min(1).max(500)
115
+ .describe("Search query."),
116
+ cursor: z.string().default("0")
117
+ .describe("Pagination cursor. Use data.cursor from previous response for next page."),
118
+ count: z.number().int().min(1).max(30).default(20)
119
+ .describe("Number of results per page (1-30)."),
120
+ }, async (params) => {
121
+ try {
122
+ const data = await getClient().post("/api/v1/tiktok/search/users", params);
123
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
124
+ }
125
+ catch (err) {
126
+ return handleApiError(err);
127
+ }
128
+ });
129
+ server.tool("get_tiktok_hashtag", `Get TikTok hashtag details and stats as JSON. Returns hashtag ID, title, description, video count, and view count. Provide either hashtag_name or hashtag_id. Use the returned ID with get_tiktok_hashtag_videos.`, {
130
+ hashtag_name: z.string().optional()
131
+ .describe("Hashtag text without the # symbol, e.g. 'fyp'."),
132
+ hashtag_id: z.string().optional()
133
+ .describe("Numeric hashtag ID. Use if you already have it from a previous request."),
134
+ }, async (params) => {
135
+ try {
136
+ const data = await getClient().post("/api/v1/tiktok/hashtag", params);
137
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
138
+ }
139
+ catch (err) {
140
+ return handleApiError(err);
141
+ }
142
+ });
143
+ server.tool("get_tiktok_hashtag_videos", `List TikTok videos for a given hashtag as JSON. Each video includes ID, caption, author, stats, and video URLs. Requires hashtag_id from the get_tiktok_hashtag response. Use data.cursor for next page; stop when data.has_more is 0.`, {
144
+ hashtag_id: z.string()
145
+ .describe("Hashtag ID from the get_tiktok_hashtag response."),
146
+ cursor: z.string().default("0")
147
+ .describe("Pagination cursor. Use data.cursor from previous response for next page."),
148
+ count: z.number().int().min(1).max(30).default(20)
149
+ .describe("Number of results per page (1-30)."),
150
+ }, async (params) => {
151
+ try {
152
+ const data = await getClient().post("/api/v1/tiktok/hashtag/videos", params);
153
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
154
+ }
155
+ catch (err) {
156
+ return handleApiError(err);
157
+ }
158
+ });
159
+ server.tool("get_tiktok_user_followers", `Get a TikTok user's follower list as JSON. Each follower includes username, display name, sec_uid, follower count, video count, bio, and avatar. Pass both page_token and min_time from previous response for pagination; stop when data.has_more is false.`, {
160
+ sec_user_id: z.string()
161
+ .describe("Secure user ID from the get_tiktok_profile response."),
162
+ count: z.number().int().min(1).max(20).default(20)
163
+ .describe("Number of results per page (1-20)."),
164
+ page_token: z.string().optional()
165
+ .describe("Pagination token from previous response."),
166
+ min_time: z.number().optional()
167
+ .describe("Pagination timestamp from previous response. Must be passed together with page_token."),
168
+ }, async (params) => {
169
+ try {
170
+ const data = await getClient().post("/api/v1/tiktok/user/followers", params);
171
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
172
+ }
173
+ catch (err) {
174
+ return handleApiError(err);
175
+ }
176
+ });
177
+ server.tool("get_tiktok_user_followings", `Get the list of accounts a TikTok user follows as JSON. Each entry includes username, display name, sec_uid, follower count, video count, bio, and avatar. Pass both page_token and min_time from previous response for pagination; stop when data.has_more is false.`, {
178
+ sec_user_id: z.string()
179
+ .describe("Secure user ID from the get_tiktok_profile response."),
180
+ count: z.number().int().min(1).max(20).default(20)
181
+ .describe("Number of results per page (1-20)."),
182
+ page_token: z.string().optional()
183
+ .describe("Pagination token from previous response."),
184
+ min_time: z.number().optional()
185
+ .describe("Pagination timestamp from previous response. Must be passed together with page_token."),
186
+ }, async (params) => {
187
+ try {
188
+ const data = await getClient().post("/api/v1/tiktok/user/followings", params);
189
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
190
+ }
191
+ catch (err) {
192
+ return handleApiError(err);
193
+ }
194
+ });
195
+ }
196
+ //# sourceMappingURL=tiktok.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tiktok.js","sourceRoot":"","sources":["../../src/tools/tiktok.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { ScavioClient } from "../lib/client.js";
3
+ export declare function registerUsageTool(server: McpServer, getClient: () => ScavioClient): void;
@@ -0,0 +1,21 @@
1
+ import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
2
+ import { ApiError } from "../lib/errors.js";
3
+ export function registerUsageTool(server, getClient) {
4
+ server.tool("get_usage", `Get the current user's credit balance, plan, searches used this month, and auto-recharge settings. Use when the user asks how many credits they have left, what plan they're on, or about their usage.`, {}, async () => {
5
+ try {
6
+ const data = await getClient().get("/api/v1/usage");
7
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
8
+ }
9
+ catch (err) {
10
+ if (err instanceof ApiError) {
11
+ if (err.status === 429)
12
+ return { isError: true, content: [{ type: "text", text: "Rate limited. Wait and retry." }] };
13
+ if (err.status === 401)
14
+ throw new McpError(ErrorCode.InternalError, "Invalid SCAVIO_API_KEY. Check your configuration.");
15
+ return { isError: true, content: [{ type: "text", text: `Scavio API error (${err.status}): ${err.message}` }] };
16
+ }
17
+ throw new McpError(ErrorCode.InternalError, String(err));
18
+ }
19
+ });
20
+ }
21
+ //# sourceMappingURL=usage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/tools/usage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAEzE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,MAAM,UAAU,iBAAiB,CAAC,MAAiB,EAAE,SAA6B;IAChF,MAAM,CAAC,IAAI,CACT,WAAW,EACX,wMAAwM,EACxM,EAAE,EACF,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,SAAS,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;YACpD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QAC9E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;gBAC5B,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;oBAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,+BAA+B,EAAE,CAAC,EAAE,CAAC;gBACrH,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;oBAAE,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,aAAa,EAAE,mDAAmD,CAAC,CAAC;gBACzH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,qBAAqB,GAAG,CAAC,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC;YAClH,CAAC;YACD,MAAM,IAAI,QAAQ,CAAC,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { ScavioClient } from "../lib/client.js";
3
+ export declare function registerWalmartTools(server: McpServer, getClient: () => ScavioClient): void;
@@ -0,0 +1,48 @@
1
+ import { z } from "zod";
2
+ import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
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
+ }
14
+ export function registerWalmartTools(server, getClient) {
15
+ server.tool("search_walmart", `Search Walmart and return product listings as JSON. Each result includes title, product ID, price, rating, and image URL. Use when the user wants to find products on Walmart or compare prices with Amazon.`, {
16
+ query: z.string().min(1).max(500)
17
+ .describe("Product search query, e.g. 'air fryer 6 quart'."),
18
+ sort_by: z.enum(["best_match", "price_low", "price_high", "best_seller"]).default("best_match")
19
+ .describe("Sort order for results."),
20
+ min_price: z.number().optional()
21
+ .describe("Minimum price filter in USD."),
22
+ max_price: z.number().optional()
23
+ .describe("Maximum price filter in USD."),
24
+ fulfillment_speed: z.enum(["today", "tomorrow", "2_days", "anytime"]).default("anytime")
25
+ .describe("Delivery speed filter."),
26
+ }, async (params) => {
27
+ try {
28
+ const data = await getClient().post("/api/v1/walmart/search", params);
29
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
30
+ }
31
+ catch (err) {
32
+ return handleApiError(err);
33
+ }
34
+ });
35
+ server.tool("get_walmart_product", `Get detailed information for a Walmart product by its product ID. Returns title, full description, price, rating, images, and seller info. Use when the user has a specific Walmart product URL or product ID and wants full details.`, {
36
+ product_id: z.string()
37
+ .describe("Walmart product ID — numeric string from the product URL (/ip/name/PRODUCT_ID)."),
38
+ }, async (params) => {
39
+ try {
40
+ const data = await getClient().post("/api/v1/walmart/product", params);
41
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
42
+ }
43
+ catch (err) {
44
+ return handleApiError(err);
45
+ }
46
+ });
47
+ }
48
+ //# sourceMappingURL=walmart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walmart.js","sourceRoot":"","sources":["../../src/tools/walmart.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { ScavioClient } from "../lib/client.js";
3
+ export declare function registerYoutubeTools(server: McpServer, getClient: () => ScavioClient): void;
@@ -0,0 +1,48 @@
1
+ import { z } from "zod";
2
+ import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
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
+ }
14
+ export function registerYoutubeTools(server, getClient) {
15
+ server.tool("search_youtube", `Search YouTube and return video results as JSON. Each result includes video ID, title, channel, duration, view count, and upload date. Use when the user asks to find YouTube videos on a topic.`, {
16
+ search: z.string().min(1).max(500)
17
+ .describe("YouTube search query."),
18
+ sort_by: z.enum(["relevance", "date", "view_count", "rating"]).default("relevance")
19
+ .describe("Sort order. Use 'date' for most recent, 'view_count' for most watched."),
20
+ type: z.enum(["video", "channel", "playlist"]).default("video")
21
+ .describe("Result type filter."),
22
+ upload_date: z.enum(["last_hour", "today", "this_week", "this_month", "this_year"]).optional()
23
+ .describe("Filter by upload date. Omit for all time."),
24
+ duration: z.enum(["short", "medium", "long"]).optional()
25
+ .describe("Filter by duration. short=<4min, medium=4-20min, long=>20min."),
26
+ }, async (params) => {
27
+ try {
28
+ const data = await getClient().post("/api/v1/youtube/search", params);
29
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
30
+ }
31
+ catch (err) {
32
+ return handleApiError(err);
33
+ }
34
+ });
35
+ server.tool("get_youtube_metadata", `Get metadata for a YouTube video by its video ID. Returns title, description, channel, duration, view count, like count, publish date, and thumbnail URLs. Use when the user has a specific video ID or URL and wants details about that video.`, {
36
+ video_id: z.string()
37
+ .describe("YouTube video ID, e.g. 'dQw4w9WgXcQ'. Extract from URL if the user provides a youtube.com link."),
38
+ }, async (params) => {
39
+ try {
40
+ const data = await getClient().post("/api/v1/youtube/metadata", params);
41
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
42
+ }
43
+ catch (err) {
44
+ return handleApiError(err);
45
+ }
46
+ });
47
+ }
48
+ //# sourceMappingURL=youtube.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"youtube.js","sourceRoot":"","sources":["../../src/tools/youtube.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@scavio/mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "MCP server connecting AI agents to Google, YouTube, Amazon, Walmart, TikTok, and Reddit. 21 tools for web search, product lookup, video discovery, and social media analysis.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Scavio",
8
+ "homepage": "https://github.com/scavio-ai/scavio-mcp#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/scavio-ai/scavio-mcp.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/scavio-ai/scavio-mcp/issues"
15
+ },
16
+ "keywords": [
17
+ "mcp",
18
+ "model-context-protocol",
19
+ "scavio",
20
+ "google",
21
+ "youtube",
22
+ "amazon",
23
+ "walmart",
24
+ "tiktok",
25
+ "reddit",
26
+ "web-search"
27
+ ],
28
+ "bin": {
29
+ "scavio-mcp": "./dist/index.js"
30
+ },
31
+ "files": [
32
+ "dist"
33
+ ],
34
+ "scripts": {
35
+ "dev": "tsx watch src/index.ts",
36
+ "dev:inspect": "npx @modelcontextprotocol/inspector tsx src/index.ts",
37
+ "build": "tsc",
38
+ "start": "node dist/index.js",
39
+ "typecheck": "tsc --noEmit",
40
+ "prepublishOnly": "npm run build"
41
+ },
42
+ "publishConfig": {
43
+ "access": "public"
44
+ },
45
+ "engines": {
46
+ "node": ">=20.0.0"
47
+ },
48
+ "dependencies": {
49
+ "@hono/node-server": "^1.19.12",
50
+ "@modelcontextprotocol/sdk": "^1.29.0",
51
+ "hono": "^4.12.10",
52
+ "zod": "^4.3.6"
53
+ },
54
+ "devDependencies": {
55
+ "@types/node": "^25.5.2",
56
+ "tsx": "^4.21.0",
57
+ "typescript": "^6.0.2"
58
+ }
59
+ }