1515let uniqueUserId = 'unknown' ;
1616
1717// --- Telemetry Proxy (direct BigQuery ingestion) ---
18- const TELEMETRY_PROXY_URL = 'https://dc-telemetry-proxy-83847352264.europe-west1.run.app/mp/collect' ;
19- const TELEMETRY_PROXY_TOKEN = 'Od44UB_fTrVfGPGRPLr5QdVgFhuKdiGaBmvazTdxVdQ' ;
18+ // TODO: Move proxy endpoints, auth header setup, request retry/fallback, and
19+ // transport code into a dedicated telemetry utility once this migration lands.
20+ // TODO(security): bearer token was removed, so this endpoint is now unauthenticated.
21+ // Confirm the proxy enforces rate limiting / payload validation server-side,
22+ // otherwise anyone can POST arbitrary events straight into BigQuery ingestion.
23+ const TELEMETRY_PROXY_URL = 'https://telemetry.desktopcommander.app/mp/collect' ;
24+ const TELEMETRY_PROXY_FALLBACK_URL = 'https://dc-telemetry-proxy-83847352264.europe-west1.run.app/mp/collect' ;
2025
2126
2227/**
@@ -54,10 +59,13 @@ export function sanitizeError(error: any): { message: string, code?: string } {
5459}
5560
5661/**
57- * Send an event to Google Analytics
62+ * Send an event to telemetry
5863 * @param event Event name
5964 * @param properties Optional event properties
6065 */
66+ // TODO(cleanup): captureBase is now dead code — no caller remains after the GA
67+ // removal (only referenced in a comment). It still carries the full GA4-flavored
68+ // send path. Remove it, or repurpose it as the shared proxy transport.
6169export const captureBase = async ( captureURL : string , event : string , properties ?: any ) => {
6270 try {
6371 // Check if telemetry is enabled in config (defaults to true if not set)
@@ -199,7 +207,7 @@ export const captureBase = async (captureURL: string, event: string, properties?
199207 ...sanitizedProperties
200208 } ;
201209
202- // Prepare GA4 payload
210+ // Prepare telemetry payload
203211 const payload = {
204212 client_id : uniqueUserId ,
205213 non_personalized_ads : false ,
@@ -210,7 +218,7 @@ export const captureBase = async (captureURL: string, event: string, properties?
210218 } ]
211219 } ;
212220
213- // Send data to Google Analytics
221+ // Send data to telemetry endpoint
214222 const postData = JSON . stringify ( payload ) ;
215223
216224 const options = {
@@ -232,7 +240,7 @@ export const captureBase = async (captureURL: string, event: string, properties?
232240 const success = res . statusCode === 200 || res . statusCode === 204 ;
233241 if ( ! success ) {
234242 // Optional debug logging
235- // console.debug(`GA tracking error: ${res.statusCode} ${data}`);
243+ // console.debug(`Telemetry tracking error: ${res.statusCode} ${data}`);
236244 }
237245 } ) ;
238246 } ) ;
@@ -256,7 +264,7 @@ export const captureBase = async (captureURL: string, event: string, properties?
256264} ;
257265
258266/**
259- * Build the standard event properties used by both GA4 and the telemetry proxy.
267+ * Build the standard event properties used by the telemetry proxy.
260268 * Extracted from captureBase so both paths get identical data.
261269 */
262270const buildEventProperties = async ( properties ?: any ) => {
@@ -361,8 +369,7 @@ const buildEventProperties = async (properties?: any) => {
361369
362370/**
363371 * Send event to the telemetry proxy (direct BigQuery ingestion).
364- * Runs in parallel with GA4 — used for high-volume events to avoid
365- * the 1M/day GA4 BigQuery export limit.
372+ * Uses the custom domain first and retries the generated Cloud Run URL on failure.
366373 */
367374const sendToTelemetryProxy = async ( event : string , eventProperties : any ) => {
368375 try {
@@ -378,74 +385,61 @@ const sendToTelemetryProxy = async (event: string, eventProperties: any) => {
378385 } ]
379386 } ) ;
380387
381- const url = new URL ( TELEMETRY_PROXY_URL ) ;
388+ const sent = await postTelemetryPayload ( TELEMETRY_PROXY_URL , payload ) ;
389+ if ( ! sent ) {
390+ await postTelemetryPayload ( TELEMETRY_PROXY_FALLBACK_URL , payload ) ;
391+ }
392+ } catch {
393+ // Silent fail — telemetry should never break functionality
394+ }
395+ } ;
396+
397+ const postTelemetryPayload = async ( endpoint : string , payload : string ) : Promise < boolean > => {
398+ return await new Promise ( ( resolve ) => {
399+ const url = new URL ( endpoint ) ;
382400 const options = {
383401 hostname : url . hostname ,
384402 port : 443 ,
385403 path : url . pathname ,
386404 method : 'POST' ,
387405 headers : {
388406 'Content-Type' : 'application/json' ,
389- 'Authorization' : `Bearer ${ TELEMETRY_PROXY_TOKEN } ` ,
390407 'Content-Length' : Buffer . byteLength ( payload )
391408 }
392409 } ;
393410
394411 const req = https . request ( options , ( res ) => {
395- res . resume ( ) ; // drain response
412+ res . resume ( ) ;
413+ res . on ( 'end' , ( ) => resolve ( res . statusCode !== undefined && res . statusCode >= 200 && res . statusCode < 300 ) ) ;
414+ } ) ;
415+ req . on ( 'error' , ( ) => resolve ( false ) ) ;
416+ req . setTimeout ( 3000 , ( ) => {
417+ req . destroy ( ) ;
418+ resolve ( false ) ;
396419 } ) ;
397- req . on ( 'error' , ( ) => { } ) ; // silent fail
398- req . setTimeout ( 3000 , ( ) => req . destroy ( ) ) ;
399420 req . write ( payload ) ;
400421 req . end ( ) ;
401- } catch {
402- // Silent fail — telemetry should never break functionality
403- }
422+ } ) ;
404423} ;
405424
406- export const capture_call_tool = async ( event : string , properties ?: any ) => {
407- // Old property (G-8L163XZ1CE) — keeps lower-volume tool events
408- const GA_OLD_ID = 'G-8L163XZ1CE' ;
409- const GA_OLD_SECRET = 'hNxh4TK2TnSy4oLZn4RwTA' ;
410- const GA_OLD_URL = `https://www.google-analytics.com/mp/collect?measurement_id=${ GA_OLD_ID } &api_secret=${ GA_OLD_SECRET } ` ;
411-
412- // New property (dc_high_volume) — receives highest-volume tool events to avoid 1M/day BQ export limit
413- const GA_NEW_ID = 'G-ZDF1M5403Z' ;
414- const GA_NEW_SECRET = 'cUEilpa0SpWfc2UjblDtKQ' ;
415- const GA_NEW_URL = `https://www.google-analytics.com/mp/collect?measurement_id=${ GA_NEW_ID } &api_secret=${ GA_NEW_SECRET } ` ;
416-
417- // Route highest-volume tools to new property, rest to old
418- const HIGH_VOLUME_TOOLS = [ 'start_process' , 'track_ui_event' ] ;
419- const toolName = properties ?. tool_name ?? properties ?. name ;
420- const gaUrl = HIGH_VOLUME_TOOLS . includes ( toolName ) ? GA_NEW_URL : GA_OLD_URL ;
421-
422- // Build properties once, send to GA4 + telemetry proxy in parallel
423- const eventProperties = await buildEventProperties ( properties ) ;
424- await Promise . all ( [
425- captureBase ( gaUrl , event , properties ) , // GA4 (routed by tool name)
426- sendToTelemetryProxy ( event , eventProperties ) , // direct BigQuery (all events)
427- ] ) ;
428- }
429-
425+ // TODO(behavior): capture() is now fire-and-forget — every `await capture(...)`
426+ // call site resolves before the network send completes. Fine for the long-running
427+ // MCP server, but events fired right before process exit (e.g. opt-out, feedback)
428+ // can be silently dropped. If we need delivery guarantees on short-lived paths,
429+ // expose an awaitable variant or flush-before-exit hook.
430430export const capture = async ( event : string , properties ?: any ) => {
431- const GA_MEASUREMENT_ID = 'G-F3GK01G39Y' ;
432- const GA_API_SECRET = 'SqdcIAweSQS1RQErURMdEA' ;
433- const GA_BASE_URL = `https://www.google-analytics.com/mp/collect?measurement_id=${ GA_MEASUREMENT_ID } &api_secret=${ GA_API_SECRET } ` ;
434-
435- // Build properties once, send to both GA4 and telemetry proxy in parallel
436- const eventProperties = await buildEventProperties ( properties ) ;
437- await Promise . all ( [
438- captureBase ( GA_BASE_URL , event , properties ) , // existing GA4
439- sendToTelemetryProxy ( event , eventProperties ) , // new: direct BigQuery
440- ] ) ;
431+ void ( async ( ) => {
432+ try {
433+ const eventProperties = await buildEventProperties ( properties ) ;
434+ await sendToTelemetryProxy ( event , eventProperties ) ;
435+ } catch {
436+ // Silent fail — telemetry should never break functionality
437+ }
438+ } ) ( ) ;
441439}
442440
443- export const capture_ui_event = async ( event : string , properties ?: any ) => {
444- const GA_MEASUREMENT_ID = 'G-MPFSWEGQ0T' ;
445- const GA_API_SECRET = 'BeK3uyAOQ6-TK6wnaDG2Ww' ;
446- const GA_BASE_URL = `https://www.google-analytics.com/mp/collect?measurement_id=${ GA_MEASUREMENT_ID } &api_secret=${ GA_API_SECRET } ` ;
447- return await captureBase ( GA_BASE_URL , event , properties ) ;
448- }
441+ export const capture_call_tool = capture ;
442+ export const capture_ui_event = capture ;
449443
450444/**
451445 * Wrapper for capture() that automatically adds remote flag for remote-device telemetry
0 commit comments