@amplify-studio/open-mcp 0.8.1 → 0.9.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 CHANGED
@@ -11,10 +11,9 @@ Open MCP is a comprehensive server solution that enables AI assistants (like Cla
11
11
  ## Current Features
12
12
 
13
13
  ### 🌐 Web Search
14
- - General web queries with pagination
15
- - Time-based filtering (day, month, year)
16
- - Language selection
17
- - Safe search levels
14
+ - General web queries via Firecrawl API
15
+ - Configurable result limit (default: 10, max: 100)
16
+ - JSON-formatted structured output
18
17
 
19
18
  ### 📄 URL Content Reading
20
19
  - Extract web page content as text/markdown
@@ -80,56 +79,67 @@ Open MCP is a comprehensive server solution that enables AI assistants (like Cla
80
79
 
81
80
  ## Installation
82
81
 
83
- ### Option 1: From npm (Recommended for Users)
84
-
85
- Install from npm registry:
82
+ ### Option 1: From npm (Recommended)
86
83
 
87
84
  **Using Claude CLI:**
88
85
  ```bash
89
- claude mcp add @amplify-studio/open-mcp
86
+ claude mcp add-json -s user open-mcp '{
87
+ "command": "npx",
88
+ "args": ["-y", "@amplify-studio/open-mcp@latest"]
89
+ }'
90
+ ```
91
+
92
+ **With custom gateway:**
93
+ ```bash
94
+ claude mcp add-json -s user open-mcp '{
95
+ "command": "npx",
96
+ "args": ["-y", "@amplify-studio/open-mcp@latest"],
97
+ "env": {
98
+ "GATEWAY_URL": "http://115.190.91.253:80"
99
+ }
100
+ }'
90
101
  ```
91
102
 
92
- **Or with npx:**
103
+ ### Option 2: Claude Desktop Config
104
+
105
+ Edit `claude_desktop_config.json`:
106
+
93
107
  ```json
94
108
  {
95
109
  "mcpServers": {
96
110
  "open-mcp": {
97
111
  "command": "npx",
98
- "args": ["-y", "@amplify-studio/open-mcp"]
112
+ "args": ["-y", "@amplify-studio/open-mcp@latest"]
99
113
  }
100
114
  }
101
115
  }
102
116
  ```
103
117
 
104
- ### Option 2: From GitHub
105
-
106
- No installation needed - runs directly from GitHub:
107
-
108
- **Claude Desktop Config**:
118
+ **With custom gateway:**
109
119
  ```json
110
120
  {
111
121
  "mcpServers": {
112
122
  "open-mcp": {
113
123
  "command": "npx",
114
- "args": ["-y", "github:amplify-studio/open-mcp"]
124
+ "args": ["-y", "@amplify-studio/open-mcp@latest"],
125
+ "env": {
126
+ "GATEWAY_URL": "http://115.190.91.253:80"
127
+ }
115
128
  }
116
129
  }
117
130
  }
118
131
  ```
119
132
 
120
- ### Option 3: Custom Gateway
133
+ ### Option 3: From GitHub
121
134
 
122
- If you have your own gateway instance:
135
+ Alternative: install directly from GitHub (no npm):
123
136
 
124
137
  ```json
125
138
  {
126
139
  "mcpServers": {
127
140
  "open-mcp": {
128
141
  "command": "npx",
129
- "args": ["-y", "github:amplify-studio/open-mcp"],
130
- "env": {
131
- "GATEWAY_URL": "http://your-gateway.com:80"
132
- }
142
+ "args": ["-y", "github:amplify-studio/open-mcp"]
133
143
  }
134
144
  }
135
145
  }
@@ -180,6 +190,79 @@ npm link
180
190
  # Then use: open-mcp
181
191
  ```
182
192
 
193
+ ## Updating
194
+
195
+ To update to the latest version:
196
+
197
+ ### Using Claude CLI
198
+
199
+ ```bash
200
+ # Remove old version
201
+ claude mcp remove open-mcp
202
+
203
+ # Install latest version
204
+ claude mcp add-json -s user open-mcp '{
205
+ "command": "npx",
206
+ "args": ["-y", "@amplify-studio/open-mcp@latest"]
207
+ }'
208
+ ```
209
+
210
+ ### Clear npx Cache (Optional)
211
+
212
+ If you encounter issues after updating:
213
+
214
+ ```bash
215
+ # Clear npx cache
216
+ npm cache clean --force
217
+
218
+ # Then reinstall
219
+ claude mcp remove open-mcp
220
+ claude mcp add-json -s user open-mcp '{
221
+ "command": "npx",
222
+ "args": ["-y", "@amplify-studio/open-mcp@latest"]
223
+ }'
224
+ ```
225
+
226
+ ### Check Current Version
227
+
228
+ ```bash
229
+ # View your configuration
230
+ cat ~/Library/Application\ Support/Claude/claude_desktop_config.json
231
+ ```
232
+
233
+ ## Output Format
234
+
235
+ Both tools return JSON strings for structured data parsing.
236
+
237
+ ### Web Search Output
238
+
239
+ ```json
240
+ {
241
+ "query": "search term",
242
+ "results": [
243
+ {
244
+ "title": "Result Title",
245
+ "content": "Description or snippet",
246
+ "url": "https://example.com"
247
+ }
248
+ ],
249
+ "totalCount": 1,
250
+ "duration": "234ms"
251
+ }
252
+ ```
253
+
254
+ ### URL Read Output
255
+
256
+ ```json
257
+ {
258
+ "url": "https://example.com",
259
+ "content": "Page content in markdown/text format...",
260
+ "charCount": 1500,
261
+ "duration": "456ms",
262
+ "cached": false
263
+ }
264
+ ```
265
+
183
266
  ## Configuration
184
267
 
185
268
  | Variable | Required | Default | Description |
@@ -198,11 +281,119 @@ The server connects to a Gateway API that provides:
198
281
 
199
282
  | API | Method | Endpoint | Description |
200
283
  |-----|--------|----------|-------------|
201
- | Search | GET | `/api/search/` | Web search |
284
+ | Search | POST | `/api/firecrawl-search` | Web search via Firecrawl |
202
285
  | Read | GET | `/api/read/{url}` | Extract web content |
203
286
  | Health | GET | `/health` | Health check |
204
287
  | Status | GET | `/api/status` | Service status |
205
288
 
289
+ ## HTTP Transport Mode
290
+
291
+ The server supports two transport modes: **STDIO** (default) and **HTTP**.
292
+
293
+ ### STDIO Mode (Default)
294
+
295
+ Standard MCP transport for local development and Claude Desktop integration. The server communicates via stdin/stdout.
296
+
297
+ ### HTTP Mode
298
+
299
+ HTTP transport enables remote server deployment and multi-client connections. Uses the **Streamable HTTP** protocol from MCP specification.
300
+
301
+ #### Starting HTTP Server
302
+
303
+ ```bash
304
+ # Basic
305
+ MCP_HTTP_PORT=3333 npm start
306
+
307
+ # With custom gateway
308
+ GATEWAY_URL=http://115.190.91.253:80 MCP_HTTP_PORT=3333 npm start
309
+
310
+ # Background mode
311
+ MCP_HTTP_PORT=3333 npm start &
312
+ ```
313
+
314
+ #### API Endpoints
315
+
316
+ | Endpoint | Method | Description |
317
+ |----------|--------|-------------|
318
+ | `/health` | GET | Health check |
319
+ | `/mcp` | POST | Send JSON-RPC requests |
320
+ | `/mcp` | GET | Receive SSE notifications |
321
+ | `/mcp` | DELETE | Close session |
322
+
323
+ #### Verify Connection
324
+
325
+ ```bash
326
+ # Health check
327
+ curl http://localhost:3333/health
328
+
329
+ # Expected response
330
+ # {"status":"healthy","server":"ihor-sokoliuk/mcp-searxng","version":"0.8.2","transport":"http"}
331
+ ```
332
+
333
+ #### Claude Code Integration
334
+
335
+ ```bash
336
+ # Add HTTP server to Claude Code
337
+ claude mcp add --transport http open-mcp http://localhost:3333/mcp
338
+
339
+ # Verify
340
+ claude mcp list
341
+ ```
342
+
343
+ #### Example curl Commands
344
+
345
+ ```bash
346
+ # 1. Initialize session
347
+ curl -X POST http://localhost:3333/mcp \
348
+ -H "Content-Type: application/json" \
349
+ -H "Accept: application/json, text/event-stream" \
350
+ -d '{
351
+ "jsonrpc": "2.0",
352
+ "id": 1,
353
+ "method": "initialize",
354
+ "params": {
355
+ "protocolVersion": "2025-06-18",
356
+ "capabilities": {},
357
+ "clientInfo": {"name": "test-client", "version": "1.0"}
358
+ }
359
+ }'
360
+
361
+ # 2. List tools (use returned session-id)
362
+ curl -X POST http://localhost:3333/mcp \
363
+ -H "Content-Type: application/json" \
364
+ -H "mcp-session-id: <session-id>" \
365
+ -d '{"jsonrpc": "2.0", "id": 2, "method": "tools/list"}'
366
+
367
+ # 3. Call search tool
368
+ curl -X POST http://localhost:3333/mcp \
369
+ -H "Content-Type: application/json" \
370
+ -H "mcp-session-id: <session-id>" \
371
+ -d '{
372
+ "jsonrpc": "2.0",
373
+ "id": 3,
374
+ "method": "tools/call",
375
+ "params": {
376
+ "name": "searxng_web_search",
377
+ "arguments": {"query": "test"}
378
+ }
379
+ }'
380
+ ```
381
+
382
+ #### Security Considerations
383
+
384
+ For production deployments:
385
+
386
+ 1. **DNS Rebinding Protection** - Enable `enableDnsRebindingProtection` in `http-server.ts`
387
+ 2. **Bind to localhost** - Use `127.0.0.1` instead of `0.0.0.0` for local development
388
+ 3. **Authentication** - Use API Gateway, reverse proxy, or implement auth headers
389
+ 4. **CORS** - Configure `ALLOWED_ORIGINS` environment variable
390
+
391
+ ```bash
392
+ # Example: Production configuration
393
+ MCP_HTTP_PORT=3333
394
+ ALLOWED_ORIGINS=https://yourdomain.com
395
+ ```
396
+
206
397
  ## Development
207
398
 
208
399
  ```bash
@@ -222,6 +413,60 @@ npm run inspector
222
413
  npm run build
223
414
  ```
224
415
 
416
+ ## Docker Deployment
417
+
418
+ The server can be deployed in HTTP mode using Docker for remote access.
419
+
420
+ ### Quick Start (HTTP Mode)
421
+
422
+ ```bash
423
+ # Build the Docker image
424
+ docker build -t open-mcp:latest .
425
+
426
+ # Run HTTP server on port 3333
427
+ docker run -d -p 3333:3333 \
428
+ -e MCP_HTTP_PORT=3333 \
429
+ -e GATEWAY_URL=http://115.190.91.253:80 \
430
+ --name open-mcp \
431
+ open-mcp:latest
432
+
433
+ # Verify deployment
434
+ curl http://localhost:3333/health
435
+ ```
436
+
437
+ ### Configuration
438
+
439
+ | Environment Variable | Description | Default |
440
+ |---------------------|-------------|---------|
441
+ | `MCP_HTTP_PORT` | HTTP server port | 3333 |
442
+ | `GATEWAY_URL` | Gateway API base URL | `http://115.190.91.253:80` |
443
+ | `ALLOWED_ORIGINS` | CORS allowed origins | `*` |
444
+
445
+ ### Claude Code Integration
446
+
447
+ ```bash
448
+ # Add HTTP server to Claude Code
449
+ claude mcp add --transport http open-mcp http://your-server:3333/mcp
450
+ ```
451
+
452
+ ### Docker Compose (Optional)
453
+
454
+ For production deployments, you can use Docker Compose to manage the service:
455
+
456
+ ```yaml
457
+ services:
458
+ open-mcp:
459
+ image: open-mcp:latest
460
+ container_name: open-mcp
461
+ ports:
462
+ - "3333:3333"
463
+ environment:
464
+ - MCP_HTTP_PORT=3333
465
+ - GATEWAY_URL=http://115.190.91.253:80
466
+ - ALLOWED_ORIGINS=https://yourdomain.com
467
+ restart: unless-stopped
468
+ ```
469
+
225
470
  ## Contributing
226
471
 
227
472
  We're building an open-source community for AI agent infrastructure. Contributions welcome!
@@ -5,19 +5,54 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
5
5
  import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
6
6
  import { logMessage } from "./logging.js";
7
7
  import { packageVersion } from "./index.js";
8
+ // Get allowed origins from environment variable
9
+ const getAllowedOrigins = () => {
10
+ const allowedOrigins = process.env.ALLOWED_ORIGINS;
11
+ if (!allowedOrigins) {
12
+ return '*'; // Default to allowing all origins
13
+ }
14
+ return allowedOrigins.split(',').map(o => o.trim());
15
+ };
8
16
  export async function createHttpServer(server) {
9
17
  const app = express();
10
18
  app.use(express.json());
11
19
  // Add CORS support for web clients
12
20
  app.use(cors({
13
- origin: '*', // Configure appropriately for production
21
+ origin: getAllowedOrigins(),
14
22
  exposedHeaders: ['Mcp-Session-Id'],
15
- allowedHeaders: ['Content-Type', 'mcp-session-id'],
23
+ allowedHeaders: ['Content-Type', 'mcp-session-id', 'Accept'],
24
+ credentials: true,
16
25
  }));
17
26
  // Map to store transports by session ID
18
27
  const transports = {};
28
+ // Validate Accept header for MCP specification compliance
29
+ const validateAcceptHeader = (acceptHeader) => {
30
+ if (!acceptHeader) {
31
+ return false;
32
+ }
33
+ const header = acceptHeader.toLowerCase();
34
+ return header.includes('application/json') || header.includes('text/event-stream');
35
+ };
19
36
  // Handle POST requests for client-to-server communication
20
37
  app.post('/mcp', async (req, res) => {
38
+ // Validate Accept header for MCP spec compliance
39
+ const acceptHeader = req.headers['accept'];
40
+ if (!validateAcceptHeader(acceptHeader)) {
41
+ console.warn(`⚠️ POST request rejected - missing/invalid Accept header:`, {
42
+ clientIP: req.ip || req.connection.remoteAddress,
43
+ accept: acceptHeader || 'undefined',
44
+ userAgent: req.headers['user-agent'],
45
+ });
46
+ res.status(406).json({
47
+ jsonrpc: '2.0',
48
+ error: {
49
+ code: -32000,
50
+ message: 'Not Acceptable: Accept header must include application/json or text/event-stream',
51
+ },
52
+ id: null,
53
+ });
54
+ return;
55
+ }
21
56
  const sessionId = req.headers['mcp-session-id'];
22
57
  let transport;
23
58
  if (sessionId && transports[sessionId]) {
package/dist/index.js CHANGED
@@ -84,7 +84,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
84
84
  if (!isSearXNGWebSearchArgs(args)) {
85
85
  throw new Error("Invalid arguments for web search");
86
86
  }
87
- const result = await performWebSearch(server, args.query, args.pageno, args.time_range, args.language, args.safesearch);
87
+ const result = await performWebSearch(server, args.query, args.limit);
88
88
  return {
89
89
  content: [
90
90
  {
package/dist/search.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
- export declare function performWebSearch(server: Server, query: string, pageno?: number, time_range?: string, language?: string, safesearch?: string): Promise<string>;
2
+ export declare function performWebSearch(server: Server, query: string, limit?: number): Promise<string>;
package/dist/search.js CHANGED
@@ -1,18 +1,11 @@
1
1
  import { createProxyAgent } from "./proxy.js";
2
2
  import { logMessage } from "./logging.js";
3
- import { createConfigurationError, createNetworkError, createServerError, createJSONError, createDataError, createNoResultsMessage } from "./error-handler.js";
4
- export async function performWebSearch(server, query, pageno = 1, time_range, language = "all", safesearch) {
3
+ import { createConfigurationError, createNetworkError, createServerError } from "./error-handler.js";
4
+ export async function performWebSearch(server, query, limit = 10) {
5
5
  const startTime = Date.now();
6
- // Build detailed log message with all parameters
7
- const searchParams = [
8
- `page ${pageno}`,
9
- `lang: ${language}`,
10
- time_range ? `time: ${time_range}` : null,
11
- safesearch ? `safesearch: ${safesearch}` : null
12
- ].filter(Boolean).join(", ");
13
- logMessage(server, "info", `Starting web search: "${query}" (${searchParams})`);
6
+ logMessage(server, "info", `Starting web search: "${query}" (limit: ${limit})`);
14
7
  const gatewayUrl = process.env.GATEWAY_URL || "http://115.190.91.253:80";
15
- // Validate that gatewayUrl is a valid URL
8
+ // Validate gateway URL
16
9
  let parsedUrl;
17
10
  try {
18
11
  parsedUrl = new URL(gatewayUrl);
@@ -20,52 +13,41 @@ export async function performWebSearch(server, query, pageno = 1, time_range, la
20
13
  catch (error) {
21
14
  throw createConfigurationError(`Invalid GATEWAY_URL format: ${gatewayUrl}. Use format: http://115.190.91.253:80`);
22
15
  }
23
- const url = new URL('/api/search/', parsedUrl);
24
- url.searchParams.set("q", query);
25
- url.searchParams.set("format", "json");
26
- url.searchParams.set("pageno", pageno.toString());
27
- if (time_range !== undefined &&
28
- ["day", "month", "year"].includes(time_range)) {
29
- url.searchParams.set("time_range", time_range);
30
- }
31
- if (language && language !== "all") {
32
- url.searchParams.set("language", language);
33
- }
34
- if (safesearch !== undefined && ["0", "1", "2"].includes(safesearch)) {
35
- url.searchParams.set("safesearch", safesearch);
36
- }
37
- // Prepare request options with headers
16
+ const url = new URL('/api/firecrawl-search', parsedUrl);
17
+ // Prepare request body
18
+ const requestBody = {
19
+ query,
20
+ limit
21
+ };
22
+ // Prepare request options
38
23
  const requestOptions = {
39
- method: "GET"
24
+ method: "POST",
25
+ headers: {
26
+ "Content-Type": "application/json"
27
+ },
28
+ body: JSON.stringify(requestBody)
40
29
  };
41
- // Add proxy dispatcher if proxy is configured
42
- // Node.js fetch uses 'dispatcher' option for proxy, not 'agent'
30
+ // Add proxy dispatcher if configured
43
31
  const proxyAgent = createProxyAgent(url.toString());
44
32
  if (proxyAgent) {
45
33
  requestOptions.dispatcher = proxyAgent;
46
34
  }
47
- // Add basic authentication if credentials are provided
35
+ // Add basic authentication if configured
48
36
  const username = process.env.AUTH_USERNAME;
49
37
  const password = process.env.AUTH_PASSWORD;
50
38
  if (username && password) {
51
39
  const base64Auth = Buffer.from(`${username}:${password}`).toString('base64');
52
- requestOptions.headers = {
53
- ...requestOptions.headers,
54
- 'Authorization': `Basic ${base64Auth}`
55
- };
40
+ requestOptions.headers['Authorization'] = `Basic ${base64Auth}`;
56
41
  }
57
- // Add User-Agent header if configured
42
+ // Add User-Agent if configured
58
43
  const userAgent = process.env.USER_AGENT;
59
44
  if (userAgent) {
60
- requestOptions.headers = {
61
- ...requestOptions.headers,
62
- 'User-Agent': userAgent
63
- };
45
+ requestOptions.headers['User-Agent'] = userAgent;
64
46
  }
65
- // Fetch with enhanced error handling
47
+ // Fetch with error handling
66
48
  let response;
67
49
  try {
68
- logMessage(server, "info", `Making request to: ${url.toString()}`);
50
+ logMessage(server, "info", `Making POST request to: ${url.toString()}`);
69
51
  response = await fetch(url.toString(), requestOptions);
70
52
  }
71
53
  catch (error) {
@@ -95,7 +77,7 @@ export async function performWebSearch(server, query, pageno = 1, time_range, la
95
77
  // Parse JSON response
96
78
  let data;
97
79
  try {
98
- data = (await response.json());
80
+ data = await response.json();
99
81
  }
100
82
  catch (error) {
101
83
  let responseText;
@@ -106,25 +88,39 @@ export async function performWebSearch(server, query, pageno = 1, time_range, la
106
88
  responseText = '[Could not read response text]';
107
89
  }
108
90
  const context = { url: url.toString() };
109
- throw createJSONError(responseText, context);
91
+ throw new Error(`Failed to parse JSON response: ${responseText}`);
110
92
  }
111
- if (!data.results) {
112
- const context = { url: url.toString(), query };
113
- throw createDataError(data, context);
93
+ // Handle Firecrawl API response format: {success: true, data: [...]}
94
+ let results = data.results || data.data || [];
95
+ // If wrapped in success object
96
+ if (data.success && data.data) {
97
+ results = data.data;
98
+ }
99
+ if (!Array.isArray(results)) {
100
+ throw new Error(`Invalid response format: results is not an array`);
114
101
  }
115
- const results = data.results.map((result) => ({
116
- title: result.title || "",
117
- content: result.content || "",
118
- url: result.url || "",
119
- score: result.score || 0,
120
- }));
121
102
  if (results.length === 0) {
122
103
  logMessage(server, "info", `No results found for query: "${query}"`);
123
- return createNoResultsMessage(query);
104
+ return JSON.stringify({
105
+ query,
106
+ results: [],
107
+ totalCount: 0,
108
+ duration: `${Date.now() - startTime}ms`
109
+ }, null, 2);
124
110
  }
111
+ // Format results (API returns: url, title, description)
112
+ const formattedResults = results.map((result) => ({
113
+ title: result.title || "",
114
+ content: result.description || result.content || "",
115
+ url: result.url || result.link || ""
116
+ }));
125
117
  const duration = Date.now() - startTime;
126
- logMessage(server, "info", `Search completed: "${query}" (${searchParams}) - ${results.length} results in ${duration}ms`);
127
- return results
128
- .map((r) => `Title: ${r.title}\nDescription: ${r.content}\nURL: ${r.url}\nRelevance Score: ${r.score.toFixed(3)}`)
129
- .join("\n\n");
118
+ logMessage(server, "info", `Search completed: "${query}" - ${formattedResults.length} results in ${duration}ms`);
119
+ // Return as JSON string
120
+ return JSON.stringify({
121
+ query,
122
+ results: formattedResults,
123
+ totalCount: formattedResults.length,
124
+ duration: `${duration}ms`
125
+ }, null, 2);
130
126
  }
package/dist/types.d.ts CHANGED
@@ -4,15 +4,11 @@ export interface SearXNGWeb {
4
4
  title: string;
5
5
  content: string;
6
6
  url: string;
7
- score: number;
8
7
  }>;
9
8
  }
10
9
  export declare function isSearXNGWebSearchArgs(args: unknown): args is {
11
10
  query: string;
12
- pageno?: number;
13
- time_range?: string;
14
- language?: string;
15
- safesearch?: string;
11
+ limit?: number;
16
12
  };
17
13
  export declare const WEB_SEARCH_TOOL: Tool;
18
14
  export declare const READ_URL_TOOL: Tool;
package/dist/types.js CHANGED
@@ -6,35 +6,22 @@ export function isSearXNGWebSearchArgs(args) {
6
6
  }
7
7
  export const WEB_SEARCH_TOOL = {
8
8
  name: "searxng_web_search",
9
- description: "Performs a web search using the SearXNG API, ideal for general queries, news, articles, and online content. " +
10
- "Use this for broad information gathering, recent events, or when you need diverse web sources.",
9
+ description: "Performs web search using the Gateway API Firecrawl search. " +
10
+ "Returns search results with title, content, URL, and relevance score. " +
11
+ "Use this for general queries, news, articles, and online content.",
11
12
  inputSchema: {
12
13
  type: "object",
13
14
  properties: {
14
15
  query: {
15
16
  type: "string",
16
- description: "The search query. This is the main input for the web search",
17
+ description: "The search query string",
17
18
  },
18
- pageno: {
19
+ limit: {
19
20
  type: "number",
20
- description: "Search page number (starts at 1)",
21
- default: 1,
22
- },
23
- time_range: {
24
- type: "string",
25
- description: "Time range of search (day, month, year)",
26
- enum: ["day", "month", "year"],
27
- },
28
- language: {
29
- type: "string",
30
- description: "Language code for search results (e.g., 'en', 'fr', 'de'). Default is instance-dependent.",
31
- default: "all",
32
- },
33
- safesearch: {
34
- type: "string",
35
- description: "Safe search filter level (0: None, 1: Moderate, 2: Strict)",
36
- enum: ["0", "1", "2"],
37
- default: "0",
21
+ description: "Maximum number of results to return (default: 10, max: 100)",
22
+ default: 10,
23
+ minimum: 1,
24
+ maximum: 100,
38
25
  },
39
26
  },
40
27
  required: ["query"],
@@ -185,10 +185,17 @@ export async function fetchAndConvertToMarkdown(server, url, timeoutMs = 10000,
185
185
  // Cache the markdown content from gateway
186
186
  urlCache.set(url, "", markdownContent);
187
187
  // Apply pagination options
188
- const result = applyPaginationOptions(markdownContent, paginationOptions);
188
+ const content = applyPaginationOptions(markdownContent, paginationOptions);
189
189
  const duration = Date.now() - startTime;
190
- logMessage(server, "info", `Successfully fetched and converted URL: ${url} (${result.length} chars in ${duration}ms)`);
191
- return result;
190
+ logMessage(server, "info", `Successfully fetched and converted URL: ${url} (${content.length} chars in ${duration}ms)`);
191
+ // Return as JSON string
192
+ return JSON.stringify({
193
+ url,
194
+ content,
195
+ charCount: content.length,
196
+ duration: `${duration}ms`,
197
+ cached: !!cachedEntry
198
+ }, null, 2);
192
199
  }
193
200
  catch (error) {
194
201
  if (error.name === "AbortError") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amplify-studio/open-mcp",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "Open MCP server for web search and URL reading via Gateway API",
5
5
  "license": "MIT",
6
6
  "author": "Amplify Studio (https://github.com/amplify-studio)",