Skip to content

Commit fa8f421

Browse files
MQ37jirispilkakatzinojirimoravcik
authored
feat: segment telemetry (apify#329)
* feat: implement segment telemetry --------- Co-authored-by: Jiří Spilka <[email protected]> Co-authored-by: Tomas Katz <[email protected]> Co-authored-by: Jiří Moravčík <[email protected]>
1 parent 02343ed commit fa8f421

20 files changed

+841
-170
lines changed

README.md

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ The Apify Model Context Protocol (MCP) server at [**mcp.apify.com**](https://mcp
3434
- [🤖 MCP clients and examples](#-mcp-clients-and-examples)
3535
- [🪄 Try Apify MCP instantly](#-try-apify-mcp-instantly)
3636
- [🛠️ Tools, resources, and prompts](#-tools-resources-and-prompts)
37+
- [📊 Telemetry](#telemetry)
3738
- [🐛 Troubleshooting (local MCP server)](#-troubleshooting-local-mcp-server)
3839
- [⚙️ Development](#-development)
3940
- [🤝 Contributing](#-contributing)
@@ -266,13 +267,32 @@ The server provides a set of predefined example prompts to help you get started
266267

267268
The server does not yet provide any resources.
268269

269-
### Debugging the NPM package
270+
## 📡 Telemetry
270271

271-
To debug the server, use the [MCP Inspector](https://github.com/modelcontextprotocol/inspector) tool:
272+
The Apify MCP Server collects telemetry data about tool calls to help Apify understand usage patterns and improve the service.
273+
By default, telemetry is **enabled** for all tool calls.
272274

273-
```shell
274-
export APIFY_TOKEN="your-apify-token"
275-
npx @modelcontextprotocol/inspector npx -y @apify/actors-mcp-server
275+
### Opting out of telemetry
276+
277+
You can opt out of telemetry by setting the `--telemetry-enabled` CLI flag to `false` or the `TELEMETRY_ENABLED` environment variable to `false`.
278+
CLI flags take precedence over environment variables.
279+
280+
#### Examples
281+
282+
**For the remote server (mcp.apify.com)**:
283+
```text
284+
# Disable via URL parameter
285+
https://mcp.apify.com?telemetry-enabled=false
286+
```
287+
288+
**For the local stdio server**:
289+
```bash
290+
# Disable via CLI flag
291+
npx @apify/actors-mcp-server --telemetry-enabled=false
292+
293+
# Or set environment variable
294+
export TELEMETRY_ENABLED=false
295+
npx @apify/actors-mcp-server
276296
```
277297

278298
# ⚙️ Development
@@ -333,6 +353,15 @@ The Apify MCP Server is also available on [Docker Hub](https://hub.docker.com/mc
333353
- Make sure the `APIFY_TOKEN` environment variable is set.
334354
- Always use the latest version of the MCP server by using `@apify/actors-mcp-server@latest`.
335355

356+
### Debugging the NPM package
357+
358+
To debug the server, use the [MCP Inspector](https://github.com/modelcontextprotocol/inspector) tool:
359+
360+
```shell
361+
export APIFY_TOKEN="your-apify-token"
362+
npx @modelcontextprotocol/inspector npx -y @apify/actors-mcp-server
363+
```
364+
336365
## 💡 Limitations
337366

338367
The Actor input schema is processed to be compatible with most MCP clients while adhering to [JSON Schema](https://json-schema.org/) standards. The processing includes:

package-lock.json

Lines changed: 123 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"files": [
2121
"dist",
2222
"LICENSE",
23+
"package.json",
2324
"server.json",
2425
"manifest.json"
2526
],
@@ -42,6 +43,7 @@
4243
"@apify/datastructures": "^2.0.3",
4344
"@apify/log": "^2.5.16",
4445
"@modelcontextprotocol/sdk": "^1.18.1",
46+
"@segment/analytics-node": "^2.3.0",
4547
"@types/cheerio": "^0.22.35",
4648
"@types/turndown": "^5.0.5",
4749
"ajv": "^8.17.1",

src/actor/server.ts

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import log from '@apify/log';
1414

1515
import { ApifyClient } from '../apify-client.js';
1616
import { ActorsMcpServer } from '../mcp/server.js';
17+
import { parseBooleanFromString } from '../utils/generic.js';
1718
import { getHelpMessage, HEADER_READINESS_PROBE, Routes, TransportType } from './const.js';
1819
import { getActorRunData } from './utils.js';
1920

@@ -78,7 +79,21 @@ export function createExpressApp(
7879
rt: Routes.SSE,
7980
tr: TransportType.SSE,
8081
});
81-
const mcpServer = new ActorsMcpServer({ setupSigintHandler: false });
82+
// Extract telemetry query parameters
83+
const urlParams = new URL(req.url, `http://${req.headers.host}`).searchParams;
84+
const telemetryEnabledParam = urlParams.get('telemetry-enabled');
85+
// URL param > env var > default (true)
86+
const telemetryEnabled = parseBooleanFromString(telemetryEnabledParam)
87+
?? parseBooleanFromString(process.env.TELEMETRY_ENABLED)
88+
?? true;
89+
90+
const mcpServer = new ActorsMcpServer({
91+
setupSigintHandler: false,
92+
transportType: 'sse',
93+
telemetry: {
94+
enabled: telemetryEnabled,
95+
},
96+
});
8297
const transport = new SSEServerTransport(Routes.MESSAGE, res);
8398

8499
// Load MCP server tools
@@ -157,12 +172,27 @@ export function createExpressApp(
157172
// Reuse existing transport
158173
transport = transports[sessionId];
159174
} else if (!sessionId && isInitializeRequest(req.body)) {
160-
// New initialization request - use JSON response mode
175+
// New initialization request
161176
transport = new StreamableHTTPServerTransport({
162177
sessionIdGenerator: () => randomUUID(),
163178
enableJsonResponse: false, // Use SSE response mode
164179
});
165-
const mcpServer = new ActorsMcpServer({ setupSigintHandler: false, initializeRequestData: req.body as InitializeRequest });
180+
// Extract telemetry query parameters
181+
const urlParams = new URL(req.url, `http://${req.headers.host}`).searchParams;
182+
const telemetryEnabledParam = urlParams.get('telemetry-enabled');
183+
// URL param > env var > default (true)
184+
const telemetryEnabled = parseBooleanFromString(telemetryEnabledParam)
185+
?? parseBooleanFromString(process.env.TELEMETRY_ENABLED)
186+
?? true;
187+
188+
const mcpServer = new ActorsMcpServer({
189+
setupSigintHandler: false,
190+
initializeRequestData: req.body as InitializeRequest,
191+
transportType: 'http',
192+
telemetry: {
193+
enabled: telemetryEnabled,
194+
},
195+
});
166196

167197
// Load MCP server tools
168198
const apifyToken = process.env.APIFY_TOKEN as string;

src/const.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ export const GET_HTML_SKELETON_CACHE_TTL_SECS = 5 * 60; // 5 minutes
7676
export const GET_HTML_SKELETON_CACHE_MAX_SIZE = 200;
7777
export const MCP_SERVER_CACHE_MAX_SIZE = 500;
7878
export const MCP_SERVER_CACHE_TTL_SECS = 30 * 60; // 30 minutes
79+
export const USER_CACHE_MAX_SIZE = 200;
80+
export const USER_CACHE_TTL_SECS = 60 * 60; // 1 hour
7981

8082
export const ACTOR_PRICING_MODEL = {
8183
/** Rental Actors */
@@ -104,3 +106,21 @@ export const ALGOLIA = {
104106
export const PROGRESS_NOTIFICATION_INTERVAL_MS = 5_000; // 5 seconds
105107

106108
export const APIFY_STORE_URL = 'https://apify.com';
109+
110+
// Telemetry
111+
export const TELEMETRY_ENV = {
112+
DEV: 'DEV',
113+
PROD: 'PROD',
114+
} as const;
115+
export type TelemetryEnv = (typeof TELEMETRY_ENV)[keyof typeof TELEMETRY_ENV];
116+
117+
export const DEFAULT_TELEMETRY_ENABLED = true;
118+
export const DEFAULT_TELEMETRY_ENV: TelemetryEnv = TELEMETRY_ENV.PROD;
119+
120+
// We are using the same values as apify-core for consistency (despite that we ship events of different types).
121+
// https://github.com/apify/apify-core/blob/2284766c122c6ac5bc4f27ec28051f4057d6f9c0/src/packages/analytics/src/server/segment.ts#L28
122+
// Reasoning from the apify-core:
123+
// Flush at 50 events to avoid sending too many small requests (default is 15)
124+
export const SEGMENT_FLUSH_AT_EVENTS = 50;
125+
// Flush interval in milliseconds (default is 10000)
126+
export const SEGMENT_FLUSH_INTERVAL_MS = 5_000;

0 commit comments

Comments
 (0)