oxylabs-ai-studio-mcp 1.0.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 +21 -0
- package/README.md +48 -0
- package/package.json +32 -0
- package/server.js +234 -0
package/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 Oxylabs.io
|
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,48 @@
|
|
1
|
+
|
2
|
+
<p align="center">
|
3
|
+
<img src="assets/logo.svg" alt="Oxylabs + MCP">
|
4
|
+
</p>
|
5
|
+
<h1 align="center" style="border-bottom: none;">
|
6
|
+
Oxylabs AI Studio MCP Server
|
7
|
+
</h1>
|
8
|
+
|
9
|
+
<div align="center">
|
10
|
+
|
11
|
+
[](https://github.com/oxylabs/oxylabs-ai-studio-mcp-py/blob/main/LICENSE)
|
12
|
+
|
13
|
+
</div>
|
14
|
+
|
15
|
+
---
|
16
|
+
|
17
|
+
## 📖 Overview
|
18
|
+
|
19
|
+
The Oxylabs AI Studio MCP server provides various AI tools for your agents.:
|
20
|
+
- Scrape: Allows getting content from any url in json or markdown format.
|
21
|
+
- Crawl: Based on prompt crawls a website and collects data in markdown or json format.
|
22
|
+
- Browser Agent: Given a task, agent controls a browser to achieve given object and returns data in markdown, json, html or screenshot formats.
|
23
|
+
- Search: Allows search the web for urls and their contents.
|
24
|
+
|
25
|
+
---
|
26
|
+
|
27
|
+
## ✅ Prerequisites
|
28
|
+
|
29
|
+
- API KEY: obtain your Api Key from Oxylabs AI Studio dashboard:
|
30
|
+
- node.js
|
31
|
+
|
32
|
+
|
33
|
+
### Basic Usage
|
34
|
+
|
35
|
+
Cursor setup:
|
36
|
+
```json
|
37
|
+
{
|
38
|
+
"mcpServers": {
|
39
|
+
"oxylabs-ai-studio": {
|
40
|
+
"command": "npx",
|
41
|
+
"args": ["oxylabs-ai-studio-mcp"],
|
42
|
+
"env": {
|
43
|
+
"OXYLABS_AI_STUDIO_API_KEY": "OXYLABS_AI_STUDIO_API_KEY"
|
44
|
+
}
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
```
|
package/package.json
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
{
|
2
|
+
"name": "oxylabs-ai-studio-mcp",
|
3
|
+
"version": "1.0.0",
|
4
|
+
"scripts": {
|
5
|
+
"lint": "eslint ."
|
6
|
+
},
|
7
|
+
"keywords": [
|
8
|
+
"mcp",
|
9
|
+
"oxylabs",
|
10
|
+
"ai",
|
11
|
+
"studio"
|
12
|
+
],
|
13
|
+
"author": "Oxylabs",
|
14
|
+
"license": "MIT",
|
15
|
+
"description": "Oxylabs AI Studio MCP Server",
|
16
|
+
"type": "module",
|
17
|
+
"bin": {
|
18
|
+
"oxylabs-ai-studio-mcp": "./server.js"
|
19
|
+
},
|
20
|
+
"dependencies": {
|
21
|
+
"fastmcp": "^3.3.1",
|
22
|
+
"oxylabs-ai-studio": "^1.0.10",
|
23
|
+
"zod": "^3.25.67"
|
24
|
+
},
|
25
|
+
"files": [
|
26
|
+
"server.js"
|
27
|
+
],
|
28
|
+
"devDependencies": {
|
29
|
+
"eslint": "^9.29.0",
|
30
|
+
"eslint-js": "github:eslint/js"
|
31
|
+
}
|
32
|
+
}
|
package/server.js
ADDED
@@ -0,0 +1,234 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
|
3
|
+
import { FastMCP } from 'fastmcp';
|
4
|
+
import { z } from "zod";
|
5
|
+
import {
|
6
|
+
OxylabsAIStudioSDK
|
7
|
+
} from 'oxylabs-ai-studio';
|
8
|
+
|
9
|
+
|
10
|
+
const api_key = process.env.OXYLABS_AI_STUDIO_API_KEY;
|
11
|
+
const api_url = process.env.OXYLABS_AI_STUDIO_API_URL || 'https://api-aistudio.oxylabs.io/v1';
|
12
|
+
|
13
|
+
|
14
|
+
if (!api_key) {
|
15
|
+
throw new Error("OXYLABS_AI_STUDIO_API_KEY is not set");
|
16
|
+
}
|
17
|
+
|
18
|
+
const sdk = new OxylabsAIStudioSDK({
|
19
|
+
apiUrl: api_url,
|
20
|
+
apiKey: api_key,
|
21
|
+
timeout: 240000,
|
22
|
+
retryAttempts: 3,
|
23
|
+
});
|
24
|
+
|
25
|
+
|
26
|
+
const server = new FastMCP({
|
27
|
+
name: "Oxylabs AI Studio MCP",
|
28
|
+
version: "0.1.0",
|
29
|
+
logLevel: "debug",
|
30
|
+
});
|
31
|
+
|
32
|
+
|
33
|
+
server.addTool({
|
34
|
+
name: "generate_schema",
|
35
|
+
description: "Generates json schema in openapi format from provided prompt.",
|
36
|
+
parameters: z.object({
|
37
|
+
user_prompt: z.string(),
|
38
|
+
app_name: z.enum(["ai_crawl", "ai_scrape", "browser_agent"]),
|
39
|
+
}),
|
40
|
+
execute: async (args) => {
|
41
|
+
if (args.app_name === 'ai_scrape') {
|
42
|
+
const response = await sdk.aiScrape.generateSchema({ user_prompt: args.user_prompt });
|
43
|
+
return JSON.stringify(response);
|
44
|
+
} else if (args.app_name === 'ai_crawl') {
|
45
|
+
const response = await sdk.aiCrawl.generateSchema({ user_prompt: args.user_prompt });
|
46
|
+
return JSON.stringify(response);
|
47
|
+
} else if (args.app_name === 'browser_agent') {
|
48
|
+
const response = await sdk.aiBrowse.generateSchema({ user_prompt: args.user_prompt });
|
49
|
+
return JSON.stringify(response);
|
50
|
+
} else {
|
51
|
+
throw new Error(`Invalid app name ${args.app_name}`);
|
52
|
+
}
|
53
|
+
},
|
54
|
+
});
|
55
|
+
|
56
|
+
|
57
|
+
server.addTool({
|
58
|
+
name: "ai_scrape",
|
59
|
+
description: `
|
60
|
+
Scrape the contents of the web page and return the data in the specified format.
|
61
|
+
Schema is required only if output_format is json.
|
62
|
+
'render_javascript' is used to render javascript heavy websites.
|
63
|
+
|
64
|
+
Parameters:
|
65
|
+
- url: The URL to scrape.
|
66
|
+
- output_format: The format of the output. If json, the schema is required. Markdown returns full text of the page.
|
67
|
+
- user_prompt: What user wants to extract from the page. Optional, only used if output_format is json and no schema is provided.
|
68
|
+
- schema: The schema to use for the scrape. Only required if output_format is json. In openapi format.
|
69
|
+
- render_javascript: Whether to render the HTML of the page using javascript. Much slower, therefore use it only for websites that require javascript to render the page. Unless user asks to use it, first try to scrape the page without it. If results are unsatisfactory, try to use it.
|
70
|
+
`,
|
71
|
+
parameters: z.object({
|
72
|
+
url: z.string().url(),
|
73
|
+
output_format: z.enum(["json", "markdown"]),
|
74
|
+
user_prompt: z.string().optional(),
|
75
|
+
schema: z.record(z.any()).optional().nullable().default(null),
|
76
|
+
render_javascript: z.boolean().default(false).optional(),
|
77
|
+
}),
|
78
|
+
execute: async (args) => {
|
79
|
+
try {
|
80
|
+
const payload = {
|
81
|
+
url: args.url,
|
82
|
+
output_format: args.output_format,
|
83
|
+
user_prompt: args.user_prompt,
|
84
|
+
openapi_schema: args.schema,
|
85
|
+
render_html: args.render_javascript };
|
86
|
+
if (args.output_format === 'json' && !args.schema) {
|
87
|
+
const response = await sdk.aiScrape.scrapeWithAutoSchema(payload);
|
88
|
+
return JSON.stringify({ content: response.data });
|
89
|
+
}
|
90
|
+
const response = await sdk.aiScrape.scrape(payload);
|
91
|
+
return JSON.stringify({ content: response.data });
|
92
|
+
} catch (error) {
|
93
|
+
console.error(error);
|
94
|
+
throw new Error(`Error scraping ${args.url}: ${error}`);
|
95
|
+
}
|
96
|
+
},
|
97
|
+
});
|
98
|
+
|
99
|
+
server.addTool({
|
100
|
+
name: "ai_crawl",
|
101
|
+
description: `
|
102
|
+
Tool useful for crawling a website from starting url and returning data in a specified format.
|
103
|
+
Schema is required only if output_format is json.
|
104
|
+
'render_javascript' is used to render javascript heavy websites.
|
105
|
+
'return_sources_limit' is used to limit the number of sources to return, for example if you expect results from single source, you can set it to 1.
|
106
|
+
|
107
|
+
Parameters:
|
108
|
+
- url: The URL from which crawling will be started.
|
109
|
+
- crawl_prompt: What information user wants to extract from the domain.
|
110
|
+
- output_format: The format of the output. Json or Markdown. If json, the schema is required. Markdown returns full text of the page.
|
111
|
+
- schema: The schema to use for the crawl. Only required if 'output_format' is json. In openapi format.
|
112
|
+
- render_javascript: Whether to render the HTML of the page using javascript. Much slower, therefore use it only for websites that require javascript to render the page. Unless user asks to use it, first try to crawl the page without it. If results are unsatisfactory, try to use it.
|
113
|
+
- return_sources_limit: The maximum number of sources to return. For example if you expect results from single source, you can set it to 1.
|
114
|
+
`,
|
115
|
+
parameters: z.object({
|
116
|
+
url: z.string().url(),
|
117
|
+
crawl_prompt: z.string(),
|
118
|
+
output_format: z.enum(["json", "markdown"]),
|
119
|
+
schema: z.record(z.any()).optional().nullable().default(null),
|
120
|
+
render_javascript: z.boolean().default(false).optional(),
|
121
|
+
return_sources_limit: z.number().optional(),
|
122
|
+
}),
|
123
|
+
execute: async (args) => {
|
124
|
+
try {
|
125
|
+
if (args.output_format === 'json' && !args.schema) {
|
126
|
+
const payload = {
|
127
|
+
url: args.url,
|
128
|
+
crawl_prompt: args.crawl_prompt,
|
129
|
+
output_format: args.output_format,
|
130
|
+
render_html: args.render_javascript,
|
131
|
+
max_pages: args.return_sources_limit,
|
132
|
+
}
|
133
|
+
const response = await sdk.aiCrawl.crawlWithAutoSchema(payload);
|
134
|
+
return JSON.stringify({ content: response.data });
|
135
|
+
}
|
136
|
+
const payload = {
|
137
|
+
url: args.url,
|
138
|
+
crawl_prompt: args.crawl_prompt,
|
139
|
+
output_format: args.output_format,
|
140
|
+
openapi_schema: args.schema,
|
141
|
+
render_html: args.render_javascript,
|
142
|
+
max_pages: args.return_sources_limit };
|
143
|
+
const response = await sdk.aiCrawl.crawl(payload);
|
144
|
+
return JSON.stringify({ content: response.data });
|
145
|
+
} catch (error) {
|
146
|
+
console.error(error);
|
147
|
+
throw new Error(`Error crawling ${args.url}: ${error}`);
|
148
|
+
}
|
149
|
+
},
|
150
|
+
});
|
151
|
+
|
152
|
+
server.addTool({
|
153
|
+
name: "ai_browser_agent",
|
154
|
+
description: `
|
155
|
+
Run the browser agent and return the data in the specified format.
|
156
|
+
This tool is useful if you need navigate around the website and do some actions.
|
157
|
+
It allows navigating to any url, clicking on links, filling forms, scrolling, etc.
|
158
|
+
Finally it returns the data in the specified format. Schema is required only if output_format is json.
|
159
|
+
'browse_prompt' describes what browser agent should achieve.
|
160
|
+
|
161
|
+
Parameters:
|
162
|
+
- url: The URL to start the browser agent navigation from.
|
163
|
+
- browse_prompt: What browser agent should do.
|
164
|
+
- output_format: The output format. Screenshot is base64 encoded jpeg image. Markdown returns full text of the page including links. If json, the schema is required.
|
165
|
+
- schema: The schema in openapi format to use for the browser agent. Only required if 'output_format' is json.
|
166
|
+
`,
|
167
|
+
parameters: z.object({
|
168
|
+
url: z.string().url(),
|
169
|
+
browse_prompt: z.string(),
|
170
|
+
output_format: z.enum(["json", "markdown", "html", "screenshot"]),
|
171
|
+
schema: z.record(z.any()).optional().nullable(),
|
172
|
+
}),
|
173
|
+
execute: async (args) => {
|
174
|
+
try {
|
175
|
+
if (args.output_format === 'json' && !args.schema) {
|
176
|
+
const payload = {
|
177
|
+
url: args.url,
|
178
|
+
browse_prompt: args.browse_prompt,
|
179
|
+
output_format: args.output_format,
|
180
|
+
parse_prompt: args.browse_prompt,
|
181
|
+
openapi_schema: args.schema };
|
182
|
+
const response = await sdk.aiBrowse.browseWithAutoSchema(payload, 240000);
|
183
|
+
return JSON.stringify({ content: response.data });
|
184
|
+
}
|
185
|
+
const payload = {
|
186
|
+
url: args.url,
|
187
|
+
browse_prompt: args.browse_prompt,
|
188
|
+
output_format: args.output_format,
|
189
|
+
openapi_schema: args.schema };
|
190
|
+
const response = await sdk.aiBrowse.browse(payload, 240000);
|
191
|
+
return JSON.stringify({ content: response.data });
|
192
|
+
} catch (error) {
|
193
|
+
console.error(error);
|
194
|
+
throw new Error(`Error running browser agent: ${error}`);
|
195
|
+
}
|
196
|
+
},
|
197
|
+
});
|
198
|
+
|
199
|
+
server.addTool({
|
200
|
+
name: "ai_search",
|
201
|
+
description: `
|
202
|
+
Search the web based on a provided query.
|
203
|
+
'return_content' is used to return markdown content for each search result. If 'return_content' is set to True, you don't need to use ai_scrape to get the content of the search results urls, because it is already included in the search results.
|
204
|
+
if 'return_content' is set to True, prefer lower 'limit' to reduce payload size.
|
205
|
+
|
206
|
+
Parameters:
|
207
|
+
- query: The query to search for.
|
208
|
+
- limit: Maximum number of results to return. Default is 10.
|
209
|
+
- render_javascript: Whether to render the HTML of the page using javascript. Much slower, therefore use it only if user asks to use it. First try to search with setting it to False.
|
210
|
+
- return_content: Whether to return markdown content of the search results. Default is True.
|
211
|
+
`,
|
212
|
+
parameters: z.object({
|
213
|
+
query: z.string(),
|
214
|
+
limit: z.number().lte(50).default(10).optional(),
|
215
|
+
render_javascript: z.boolean().default(false).optional(),
|
216
|
+
return_content: z.boolean().default(true).optional(),
|
217
|
+
}),
|
218
|
+
execute: async (args) => {
|
219
|
+
try {
|
220
|
+
console.error(args);
|
221
|
+
const response = await sdk.aiSearch.search(args);
|
222
|
+
return JSON.stringify({ content: response.data });
|
223
|
+
} catch (error) {
|
224
|
+
console.error(error);
|
225
|
+
throw new Error(`Error searching: ${error}`);
|
226
|
+
}
|
227
|
+
},
|
228
|
+
});
|
229
|
+
|
230
|
+
|
231
|
+
server.start({
|
232
|
+
transportType: "stdio",
|
233
|
+
});
|
234
|
+
|