@apify/actors-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/README.md ADDED
@@ -0,0 +1,316 @@
1
+ # Apify Model Context Protocol (MCP) Server
2
+
3
+ Implementation of an MCP server for all [Apify Actors](https://apify.com/store).
4
+ This server enables interaction with one or more Apify Actors that can be defined in the MCP Server configuration.
5
+
6
+ The server can be used in two ways:
7
+ - 🇦 **Apify MCP Server Actor**: runs an HTTP server with MCP protocol via Server-Sent Events.
8
+ - ⾕ **Apify MCP Server Stdio**: provides support for the MCP protocol via standard input/output stdio.
9
+
10
+ # 🎯 What does Apify MCP server do?
11
+
12
+ The MCP Server Actor allows an AI assistant to use any [Apify Actor](https://apify.com/store) as a tool to perform a specific task.
13
+ For example it can:
14
+ - use [Facebook Posts Scraper](https://apify.com/apify/facebook-posts-scraper) to extract data from Facebook posts from multiple pages/profiles
15
+ - use [Google Maps Email Extractor](https://apify.com/lukaskrivka/google-maps-with-contact-details) to extract Google Maps contact details
16
+ - use [Google Search Results Scraper](https://apify.com/apify/google-search-scraper) to scrape Google Search Engine Results Pages (SERPs)
17
+ - use [Instagram Scraper](https://apify.com/apify/instagram-scraper) to scrape Instagram posts, profiles, places, photos, and comments
18
+ - use [RAG Web Browser](https://apify.com/apify/web-scraper) to search the web, scrape the top N URLs, and return their content
19
+
20
+ To interact with the Apify MCP server, you can use MCP clients such as [Claude Desktop](https://claude.ai/download), [Superinference.ai](https://superinterface.ai/), or [LibreChat](https://www.librechat.ai/).
21
+ Additionally, you can use simple example clients found in the [examples](https://github.com/apify/actor-mcp-server/tree/main/src/examples) directory.
22
+
23
+ When you have Actors integrated with the MCP server, you can ask:
24
+ - "Search web and summarize recent trends about AI Agents"
25
+ - "Find top 10 best Italian restaurants in San Francisco"
26
+ - "Find and analyze Instagram profile of The Rock"
27
+ - "Provide a step-by-step guide on using the Model Context Protocol with source URLs."
28
+ - "What Apify Actors I can use?"
29
+
30
+ In the future, we plan to load Actors dynamically and provide Apify's dataset and key-value store as resources.
31
+ See the [Roadmap](#-roadmap-january-2025) for more details.
32
+
33
+ # 🔄 What is the Model Context Protocol?
34
+
35
+ The Model Context Protocol (MCP) allows AI applications (and AI agents), such as Claude Desktop, to connect to external tools and data sources.
36
+ MCP is an open protocol that enables secure, controlled interactions between AI applications, AI Agents, and local or remote resources.
37
+
38
+ # 🧱 Components
39
+
40
+ ## Tools
41
+
42
+ Any [Apify Actor](https://apify.com/store) can be used as a tool.
43
+ By default, the server is pre-configured with the Actors specified below, but it can be overridden by providing Actor input.
44
+
45
+ ```text
46
+ 'apify/instagram-scraper'
47
+ 'apify/rag-web-browser'
48
+ 'lukaskrivka/google-maps-with-contact-details'
49
+ ```
50
+ The MCP server loads the Actor input schema and creates MCP tools corresponding to the Actors.
51
+ See this example of input schema for the [RAG Web Browser](https://apify.com/apify/rag-web-browser/input-schema).
52
+
53
+ The tool name must always be the full Actor name, such as `apify/rag-web-browser`.
54
+ The arguments for an MCP tool represent the input parameters of the Actor.
55
+ For example, for the `apify/rag-web-browser` Actor, the arguments are:
56
+
57
+ ```json
58
+ {
59
+ "query": "restaurants in San Francisco",
60
+ "maxResults": 3
61
+ }
62
+ ```
63
+ You don't need to specify the input parameters or which Actor to call, everything is managed by an LLM.
64
+ When a tool is called, the arguments are automatically passed to the Actor by the LLM.
65
+ You can refer to the specific Actor's documentation for a list of available arguments.
66
+
67
+ ## Prompt & Resources
68
+
69
+ The server does not provide any resources and prompts.
70
+ We plan to add Apify's dataset and key-value store as resources in the future.
71
+
72
+ # ⚙️ Usage
73
+
74
+ The Apify MCP Server can be used in two ways: **as an Apify Actor** running at Apify platform
75
+ or as a **local server** running on your machine.
76
+
77
+ ## 🇦 MCP Server Actor
78
+
79
+ ### Standby web server
80
+
81
+ The Actor runs in [**Standby mode**](https://docs.apify.com/platform/actors/running/standby) with an HTTP web server that receives and processes requests.
82
+
83
+ Start server with default Actors. To use the Apify MCP Server with set of default Actors,
84
+ send an HTTP GET request with your [Apify API token](https://console.apify.com/settings/integrations) to the following URL.
85
+ ```
86
+ https://actors-mcp-server.apify.actor?token=<APIFY_TOKEN>
87
+ ```
88
+ It is also possible to start the MCP server with a different set of Actors.
89
+ To do this, create a [task](https://docs.apify.com/platform/actors/running/tasks) and specify the list of Actors you want to use.
90
+
91
+ Then, run task in Standby mode with the selected Actors using your Apify API token.
92
+ ```shell
93
+ https://actors-mcp-server-task.apify.actor?token=<APIFY_TOKEN>
94
+ ```
95
+
96
+ You can find a list of all available Actors in the [Apify Store](https://apify.com/store).
97
+
98
+ #### 💬 Interact with the MCP Server
99
+
100
+ Once the server is running, you can interact with Server-Sent Events (SSE) to send messages to the server and receive responses.
101
+ You can use MCP clients such as [Superinference.ai](https://superinterface.ai/) or [LibreChat](https://www.librechat.ai/).
102
+ ([Claude Desktop](https://claude.ai/download) does not support SSE transport yet)
103
+
104
+ In the client settings you need to provide server configuration:
105
+ ```json
106
+ {
107
+ "mcpServers": {
108
+ "apify": {
109
+ "type": "sse",
110
+ "url": "https://actors-mcp-server.apify.actor/sse",
111
+ "env": {
112
+ "APIFY_TOKEN": "your-apify-token"
113
+ }
114
+ }
115
+ }
116
+ }
117
+ ```
118
+ Alternatively, you can use simple python [client_see.py](https://github.com/apify/actor-mcp-server/tree/main/src/examples/client_sse.py) or test the server using `curl` </> commands.
119
+
120
+ 1. Initiate Server-Sent-Events (SSE) by sending a GET request to the following URL:
121
+ ```
122
+ curl https://actors-mcp-server.apify.actor/sse?token=<APIFY_TOKEN>
123
+ ```
124
+ The server will respond with a `sessionId`, which you can use to send messages to the server:
125
+ ```shell
126
+ event: endpoint
127
+ data: /message?sessionId=a1b
128
+ ```
129
+
130
+ 2. Send a message to the server by making a POST request with the `sessionId`:
131
+ ```shell
132
+ curl -X POST "https://actors-mcp-server.apify.actor?token=<APIFY_TOKEN>&session_id=a1b" -H "Content-Type: application/json" -d '{
133
+ "jsonrpc": "2.0",
134
+ "id": 1,
135
+ "method": "tools/call",
136
+ "params": {
137
+ "arguments": { "searchStringsArray": ["restaurants in San Francisco"], "maxCrawledPlacesPerSearch": 3 },
138
+ "name": "lukaskrivka/google-maps-with-contact-details"
139
+ }
140
+ }'
141
+ ```
142
+ The MCP server will start the Actor `lukaskrivka/google-maps-with-contact-details` with the provided arguments as input parameters.
143
+ For this POST request, the server will respond with:
144
+
145
+ ```text
146
+ Accepted
147
+ ```
148
+
149
+ 3. Receive the response. The server will invoke the specified Actor as a tool using the provided query parameters and stream the response back to the client via SSE.
150
+ The response will be returned as JSON text.
151
+
152
+ ```text
153
+ event: message
154
+ data: {"result":{"content":[{"type":"text","text":"{\"searchString\":\"restaurants in San Francisco\",\"rank\":1,\"title\":\"Gary Danko\",\"description\":\"Renowned chef Gary Danko's fixed-price menus of American cuisine ... \",\"price\":\"$100+\"...}}]}}
155
+ ```
156
+
157
+ ## ⾕ MCP Server at a local host
158
+
159
+ ### Prerequisites
160
+
161
+ - MacOS or Windows
162
+ - The latest version of Claude Desktop must be installed (or another MCP client)
163
+ - [Node.js](https://nodejs.org/en) (v18 or higher)
164
+ - [Apify API Token](https://docs.apify.com/platform/integrations/api#api-token) (`APIFY_TOKEN`)
165
+
166
+ ### Install
167
+
168
+ Follow the steps below to set up and run the server on your local machine:
169
+ First, clone the repository using the following command:
170
+
171
+ ```bash
172
+ git clone git@github.com:apify/actor-mcp-server.git
173
+ ```
174
+ Navigate to the project directory and install the required dependencies:
175
+
176
+ ```bash
177
+ cd actor-mcp-server
178
+ npm install
179
+ ```
180
+
181
+ Before running the server, you need to build the project:
182
+
183
+ ```bash
184
+ npm run build
185
+ ```
186
+
187
+ #### Claude Desktop
188
+
189
+ Configure Claude Desktop to recognize the MCP server.
190
+
191
+ 1. Open your Claude Desktop configuration and edit the following file:
192
+
193
+ - On macOS: `~/Library/Application\ Support/Claude/claude_desktop_config.json`
194
+ - On Windows: `%APPDATA%/Claude/claude_desktop_config.json`
195
+
196
+ ```text
197
+ "mcpServers": {
198
+ "apify": {
199
+ "command": "npx",
200
+ "args": [
201
+ "/path/to/actor-mcp-server/dist/index.js"
202
+ ]
203
+ "env": {
204
+ "APIFY_TOKEN": "your-apify-token"
205
+ }
206
+ }
207
+ }
208
+ ```
209
+ Alternatively, you can use the following command to select one or more Apify Actors:
210
+ ```text
211
+ "mcpServers": {
212
+ "apify-mcp-server": {
213
+ "command": "npx",
214
+ "args": [
215
+ "/path/to/actor-mcp-server/dist/index.js",
216
+ "--actors",
217
+ "lukaskrivka/google-maps-with-contact-details,apify/instagram-scraper"
218
+ ]
219
+ "env": {
220
+ "APIFY_TOKEN": "your-apify-token"
221
+ }
222
+ }
223
+ }
224
+ ```
225
+
226
+ 2. Restart Claude Desktop
227
+
228
+ - Fully quit Claude Desktop (ensure it’s not just minimized or closed).
229
+ - Restart Claude Desktop.
230
+ - Look for the 🔌 icon to confirm that the Exa server is connected.
231
+
232
+ 3. Examples
233
+
234
+ You can ask Claude to perform web searches, such as:
235
+ ```text
236
+ Find and analyze recent research papers about LLMs.
237
+ Find top 10 best Italian restaurants in San Francisco.
238
+ Find and analyze instagram profile of the Rock.
239
+ ```
240
+
241
+ #### Stdio clients
242
+
243
+ Create environment file `.env` with the following content:
244
+ ```text
245
+ APIFY_TOKEN=your-apify-token
246
+ # ANTHROPIC_API_KEY is only required when you want to run examples/clientStdioChat.js
247
+ ANTHROPIC_API_KEY=your-anthropic-api-token
248
+ ```
249
+ In the `examples` directory, you can find two clients that interact with the server via
250
+ standard input/output (stdio):
251
+ 1. [`clientStdio.ts`](https://github.com/apify/actor-mcp-server/tree/main/src/examples/clientStdio.ts)
252
+ This client script starts the MCP server with two specified Actors.
253
+ It then calls the `apify/rag-web-browser` tool with a query and prints the result.
254
+ It demonstrates how to connect to the MCP server, list available tools, and call a specific tool using stdio transport.
255
+ ```bash
256
+ node dist/examples/clientStdio.js
257
+ ```
258
+
259
+ 2. [`clientStdioChat.ts`](https://github.com/apify/actor-mcp-server/tree/main/src/examples/clientStdioChat.ts)
260
+ This client script also starts the MCP server but provides an interactive command-line chat interface.
261
+ It prompts the user to interact with the server, allowing for dynamic tool calls and responses.
262
+ This example is useful for testing and debugging interactions with the MCP server in conversational manner.
263
+
264
+ ```bash
265
+ node dist/examples/clientStdioChat.js
266
+ ```
267
+
268
+ # 👷🏼 Development
269
+
270
+ ## Prerequisites
271
+
272
+ - [Node.js](https://nodejs.org/en) (v18 or higher)
273
+ - Python 3.6 or higher
274
+
275
+ Create environment file `.env` with the following content:
276
+ ```text
277
+ APIFY_TOKEN=your-apify-token
278
+ # ANTHROPIC_API_KEY is only required when you want to run examples/clientStdioChat.js
279
+ ANTHROPIC_API_KEY=your-anthropic-api-token
280
+ ```
281
+ ## Local client (SSE)
282
+
283
+ To test the server with the SSE transport, you can use python script `examples/client_sse.py`:
284
+ Currently, the node.js client does not support to establish a connection to remote server witch custom headers.
285
+ You need to change URL to your local server URL in the script.
286
+
287
+ ```bash
288
+ python src/examples/client_sse.py
289
+ ```
290
+
291
+ ## Debugging
292
+
293
+ Since MCP servers operate over standard input/output (stdio), debugging can be challenging.
294
+ For the best debugging experience, use the [MCP Inspector](https://github.com/modelcontextprotocol/inspector).
295
+
296
+ Build the actor-mcp-server package:
297
+
298
+ ```bash
299
+ npm run build
300
+ ```
301
+
302
+ You can launch the MCP Inspector via [`npm`](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) with this command:
303
+
304
+ ```bash
305
+ npx @modelcontextprotocol/inspector node /path/to/actor-mcp-server/dist/index.js --env APIFY_TOKEN=your-apify-token
306
+ ```
307
+
308
+ Upon launching, the Inspector will display a URL that you can access in your browser to begin debugging.
309
+
310
+ # 🚀 Roadmap (January 2025)
311
+
312
+ - Document examples for [Superinference.ai](https://superinterface.ai/) and [LibreChat](https://www.librechat.ai/).
313
+ - Provide tools to search for Actors and load them as needed.
314
+ - Add Apify's dataset and key-value store as resources.
315
+ - Add tools such as Actor logs and Actor runs for debugging.
316
+ - Prune Actors input schema to reduce context size.
@@ -0,0 +1,16 @@
1
+ import apify from '@apify/eslint-config';
2
+
3
+ // eslint-disable-next-line import/no-default-export
4
+ export default [
5
+ { ignores: ['**/dist'] }, // Ignores need to happen first
6
+ ...apify,
7
+ {
8
+ languageOptions: {
9
+ sourceType: 'module',
10
+
11
+ parserOptions: {
12
+ project: 'tsconfig.eslint.json',
13
+ },
14
+ },
15
+ },
16
+ ];
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@apify/actors-mcp-server",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Model Context Protocol Server for Apify Actors",
6
+ "engines": {
7
+ "node": ">=18.0.0"
8
+ },
9
+ "main": "dist/index.js",
10
+ "bin": {
11
+ "actors-mcp-server": "dist/index.js"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/apify/actors-mcp-server.git"
16
+ },
17
+ "bugs": {
18
+ "url": "https://github.com/apify/actors-mcp-server/issues"
19
+ },
20
+ "homepage": "https://apify.com/apify/actors-mcp-server",
21
+ "keywords": [
22
+ "apify",
23
+ "mcp",
24
+ "server",
25
+ "actors",
26
+ "model context protocol"
27
+ ],
28
+ "dependencies": {
29
+ "@modelcontextprotocol/sdk": "^1.1.1",
30
+ "ajv": "^8.17.1",
31
+ "apify": "^3.2.6",
32
+ "apify-client": "^2.11.1",
33
+ "express": "^4.21.2",
34
+ "minimist": "^1.2.8"
35
+ },
36
+ "devDependencies": {
37
+ "@anthropic-ai/sdk": "^0.33.1",
38
+ "@anthropic-ai/tokenizer": "^0.0.4",
39
+ "@apify/eslint-config": "^0.5.0-beta.2",
40
+ "@apify/tsconfig": "^0.1.0",
41
+ "@types/express": "^4.0.0",
42
+ "@types/minimist": "^1.2.5",
43
+ "dotenv": "^16.4.7",
44
+ "eslint": "^9.17.0",
45
+ "eventsource": "^3.0.2",
46
+ "tsx": "^4.6.2",
47
+ "typescript": "^5.3.3",
48
+ "typescript-eslint": "^8.18.2"
49
+ },
50
+ "scripts": {
51
+ "start": "npm run start:dev",
52
+ "start:prod": "node dist/main.js",
53
+ "start:dev": "tsx src/main.ts",
54
+ "lint": "eslint .",
55
+ "lint:fix": "eslint . --fix",
56
+ "build": "tsc",
57
+ "test": "echo \"Error: oops, the actor has no tests yet, sad!\" && exit 1",
58
+ "watch": "tsc --watch"
59
+ },
60
+ "author": "Apify",
61
+ "license": "MIT"
62
+ }
@@ -0,0 +1,87 @@
1
+ import { Ajv } from 'ajv';
2
+ import { ApifyClient } from 'apify-client';
3
+
4
+ import { log } from './logger.js';
5
+ import type { ActorDefinitionWithDesc, Tool } from './types';
6
+
7
+ /**
8
+ * Get actor input schema by actor name.
9
+ * First, fetch the actor details to get the default build tag and buildId.
10
+ * Then, fetch the build details and return actorName, description, and input schema.
11
+ * @param {string} actorFullName - The full name of the actor.
12
+ * @returns {Promise<ActorDefinitionWithDesc | null>} - The actor definition with description or null if not found.
13
+ */
14
+ async function fetchActorDefinition(actorFullName: string): Promise<ActorDefinitionWithDesc | null> {
15
+ if (!process.env.APIFY_TOKEN) {
16
+ log.error('APIFY_TOKEN is required but not set. Please set it as an environment variable');
17
+ return null;
18
+ }
19
+ const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
20
+ const actorClient = client.actor(actorFullName);
21
+
22
+ try {
23
+ // Fetch actor details
24
+ const actor = await actorClient.get();
25
+ if (!actor) {
26
+ log.error(`Failed to fetch input schema for actor: ${actorFullName}. Actor not found.`);
27
+ return null;
28
+ }
29
+
30
+ // fnesveda: The default build is not necessarily tagged, you can specify any build number as default build.
31
+ // There will be a new API endpoint to fetch a default build.
32
+ // For now, we'll use the tagged build, it will work for 90% of Actors. Later, we can update this.
33
+ const tag = actor.defaultRunOptions?.build || '';
34
+ const buildId = actor.taggedBuilds?.[tag]?.buildId || '';
35
+
36
+ if (!buildId) {
37
+ log.error(`Failed to fetch input schema for actor: ${actorFullName}. Build ID not found.`);
38
+ return null;
39
+ }
40
+ // Fetch build details and return the input schema
41
+ const buildDetails = await client.build(buildId).get();
42
+ if (buildDetails?.actorDefinition) {
43
+ const actorDefinitions = buildDetails?.actorDefinition as ActorDefinitionWithDesc;
44
+ actorDefinitions.description = actor.description || '';
45
+ actorDefinitions.name = actorFullName;
46
+ return actorDefinitions;
47
+ }
48
+ return null;
49
+ } catch (error) {
50
+ log.error(`Failed to fetch input schema for actor: ${actorFullName} with error ${error}.`);
51
+ return null;
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Fetches actor input schemas by actor full names and creates MCP tools.
57
+ *
58
+ * This function retrieves the input schemas for the specified actors and compiles them into MCP tools.
59
+ * It uses the AJV library to validate the input schemas.
60
+ *
61
+ * Tool name can't contain /, so it is replaced with _
62
+ *
63
+ * @param {string[]} actors - An array of actor full names.
64
+ * @returns {Promise<Tool[]>} - A promise that resolves to an array of MCP tools.
65
+ */
66
+ export async function getActorsAsTools(actors: string[]): Promise<Tool[]> {
67
+ // Fetch input schemas in parallel
68
+ const ajv = new Ajv({ coerceTypes: 'array', strict: false });
69
+ const results = await Promise.all(actors.map(fetchActorDefinition));
70
+ const tools = [];
71
+ for (const result of results) {
72
+ if (result) {
73
+ try {
74
+ tools.push({
75
+ name: result.name.replace('/', '_'),
76
+ actorName: result.name,
77
+ description: result.description,
78
+ inputSchema: result.input || {},
79
+ ajvValidate: ajv.compile(result.input || {}),
80
+ });
81
+ } catch (validationError) {
82
+ log.error(`Failed to compile AJV schema for actor: ${result.name}. Error: ${validationError}`);
83
+ }
84
+ }
85
+ }
86
+ return tools;
87
+ }
package/src/const.ts ADDED
@@ -0,0 +1,20 @@
1
+ export const SERVER_NAME = 'apify-mcp-server';
2
+ export const SERVER_VERSION = '0.1.0';
3
+
4
+ export const defaults = {
5
+ actors: [
6
+ 'apify/instagram-scraper',
7
+ 'apify/rag-web-browser',
8
+ 'lukaskrivka/google-maps-with-contact-details',
9
+ ],
10
+ };
11
+
12
+ export const ACTOR_OUTPUT_MAX_CHARS_PER_ITEM = 2_000;
13
+ export const ACTOR_OUTPUT_TRUNCATED_MESSAGE = `Output was truncated because it will not fit into context.`
14
+ + ` There is no reason to call this tool again!`;
15
+
16
+ export enum Routes {
17
+ ROOT = '/',
18
+ SSE = '/sse',
19
+ MESSAGE = '/message',
20
+ }
@@ -0,0 +1,100 @@
1
+ /* eslint-disable no-console */
2
+ /**
3
+ * Connect to the MCP server using SSE transport and call a tool.
4
+ * The Actors MCP Server will load default Actors.
5
+ *
6
+ * !!! This example needs to be fixed as it does not work !!!
7
+ */
8
+
9
+ import path from 'path';
10
+ import { fileURLToPath } from 'url';
11
+
12
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
13
+ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
14
+ import { CallToolResultSchema } from '@modelcontextprotocol/sdk/types.js';
15
+ import dotenv from 'dotenv';
16
+ import { EventSource } from 'eventsource';
17
+
18
+ // Resolve dirname equivalent in ES module
19
+ const filename = fileURLToPath(import.meta.url);
20
+ const dirname = path.dirname(filename);
21
+
22
+ dotenv.config({ path: path.resolve(dirname, '../../.env') });
23
+
24
+ const SERVER_URL = 'https://actors-mcp-server/sse';
25
+ // We need to change forward slash / to underscore _ in the tool name as Anthropic does not allow forward slashes in the tool name
26
+ const SELECTED_TOOL = 'apify_rag-web-browser';
27
+
28
+ if (!process.env.APIFY_TOKEN) {
29
+ console.error('APIFY_TOKEN is required but not set in the environment variables.');
30
+ process.exit(1);
31
+ }
32
+
33
+ if (typeof globalThis.EventSource === 'undefined') {
34
+ globalThis.EventSource = EventSource as unknown as typeof globalThis.EventSource;
35
+ }
36
+
37
+ async function main(): Promise<void> {
38
+ const transport = new SSEClientTransport(
39
+ new URL(SERVER_URL),
40
+ {
41
+ requestInit: {
42
+ headers: {
43
+ authorization: `Bearer ${process.env.APIFY_TOKEN}`,
44
+ },
45
+ },
46
+ eventSourceInit: {
47
+ // The EventSource package augments EventSourceInit with a "fetch" parameter.
48
+ // You can use this to set additional headers on the outgoing request.
49
+ // Based on this example: https://github.com/modelcontextprotocol/typescript-sdk/issues/118
50
+ async fetch(input: Request | URL | string, init?: RequestInit) {
51
+ const headers = new Headers(init?.headers || {});
52
+ headers.set('authorization', `Bearer ${process.env.APIFY_TOKEN}`);
53
+ return fetch(input, { ...init, headers });
54
+ },
55
+ // We have to cast to "any" to use it, since it's non-standard
56
+ } as any, // eslint-disable-line @typescript-eslint/no-explicit-any
57
+ },
58
+ );
59
+ const client = new Client(
60
+ { name: 'example-client', version: '1.0.0' },
61
+ { capabilities: {} },
62
+ );
63
+
64
+ try {
65
+ // Connect to the MCP server
66
+ await client.connect(transport);
67
+
68
+ // List available tools
69
+ const tools = await client.listTools();
70
+ console.log('Available tools:', tools);
71
+
72
+ if (tools.tools.length === 0) {
73
+ console.log('No tools available');
74
+ return;
75
+ }
76
+
77
+ const selectedTool = tools.tools.find((tool) => tool.name === SELECTED_TOOL);
78
+ if (!selectedTool) {
79
+ console.error(`The specified tool: ${selectedTool} is not available. Exiting.`);
80
+ return;
81
+ }
82
+
83
+ // Call a tool
84
+ console.log('Calling actor ...');
85
+ const result = await client.callTool(
86
+ { name: SELECTED_TOOL, arguments: { query: 'web browser for Anthropic' } },
87
+ CallToolResultSchema,
88
+ );
89
+ console.log('Tool result:', JSON.stringify(result, null, 2));
90
+ } catch (error: unknown) {
91
+ if (error instanceof Error) {
92
+ console.error('Error:', error.message);
93
+ console.error(error.stack);
94
+ } else {
95
+ console.error('An unknown error occurred:', error);
96
+ }
97
+ }
98
+ }
99
+
100
+ await main();
@@ -0,0 +1,85 @@
1
+ /* eslint-disable no-console */
2
+ /**
3
+ * Connect to the MCP server using stdio transport and call a tool.
4
+ * This script uses a selected tool without LLM involvement.
5
+ * You need to provide the path to the MCP server and `APIFY_TOKEN` in the `.env` file.
6
+ * You can choose actors to run in the server, for example: `apify/rag-web-browser`.
7
+ */
8
+
9
+ import { execSync } from 'child_process';
10
+ import path from 'path';
11
+ import { fileURLToPath } from 'url';
12
+
13
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
14
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
15
+ import { CallToolResultSchema } from '@modelcontextprotocol/sdk/types.js';
16
+ import dotenv from 'dotenv';
17
+
18
+ // Resolve dirname equivalent in ES module
19
+ const filename = fileURLToPath(import.meta.url);
20
+ const dirname = path.dirname(filename);
21
+
22
+ dotenv.config({ path: path.resolve(dirname, '../../.env') });
23
+ const SERVER_PATH = path.resolve(dirname, '../../dist/index.js');
24
+ const NODE_PATH = execSync(process.platform === 'win32' ? 'where node' : 'which node').toString().trim();
25
+
26
+ const TOOLS = 'apify/rag-web-browser,lukaskrivka/google-maps-with-contact-details';
27
+ const SELECTED_TOOL = 'apify_rag-web-browser'; // We need to change / to _ in the tool name
28
+
29
+ if (!process.env.APIFY_TOKEN) {
30
+ console.error('APIFY_TOKEN is required but not set in the environment variables.');
31
+ process.exit(1);
32
+ }
33
+
34
+ // Create server parameters for stdio connection
35
+ const transport = new StdioClientTransport({
36
+ command: NODE_PATH,
37
+ args: [SERVER_PATH, '--actors', TOOLS],
38
+ env: { APIFY_TOKEN: process.env.APIFY_TOKEN || '' },
39
+ });
40
+
41
+ // Create a new client instance
42
+ const client = new Client(
43
+ { name: 'example-client', version: '0.1.0' },
44
+ { capabilities: {} },
45
+ );
46
+
47
+ // Main function to run the example client
48
+ async function run() {
49
+ try {
50
+ // Connect to the MCP server
51
+ await client.connect(transport);
52
+
53
+ // List available tools
54
+ const tools = await client.listTools();
55
+ console.log('Available tools:', tools);
56
+
57
+ if (tools.tools.length === 0) {
58
+ console.log('No tools available');
59
+ return;
60
+ }
61
+
62
+ // Example: Call the first available tool
63
+ const selectedTool = tools.tools.find((tool) => tool.name === SELECTED_TOOL);
64
+
65
+ if (!selectedTool) {
66
+ console.error(`The specified tool: ${selectedTool} is not available. Exiting.`);
67
+ return;
68
+ }
69
+
70
+ // Call a tool
71
+ console.log('Calling actor ...');
72
+ const result = await client.callTool(
73
+ { name: SELECTED_TOOL, arguments: { query: 'web browser for Anthropic' } },
74
+ CallToolResultSchema,
75
+ );
76
+ console.log('Tool result:', JSON.stringify(result));
77
+ } catch (error) {
78
+ console.error('Error:', error);
79
+ }
80
+ }
81
+
82
+ run().catch((error) => {
83
+ console.error('Unhandled error:', error);
84
+ process.exit(1);
85
+ });