Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,32 @@

let commandScopes = new Set<string>();

export const authProxyOrigin = () =>

Check warning on line 8 in src/api.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing return type on function
utils.envOverride("FIREBASE_AUTHPROXY_URL", "https://auth.firebase.tools");
// "In this context, the client secret is obviously not treated as a secret"
// https://developers.google.com/identity/protocols/OAuth2InstalledApp
export const clientId = () =>

Check warning on line 12 in src/api.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing return type on function
utils.envOverride(
"FIREBASE_CLIENT_ID",
"563584335869-fgrhgmd47bqnekij5i8b5pr03ho849e6.apps.googleusercontent.com",
);
export const clientSecret = () =>

Check warning on line 17 in src/api.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing return type on function
utils.envOverride("FIREBASE_CLIENT_SECRET", "j9iVZfS8kkCEFUPaAeJV0sAi");
export const cloudbillingOrigin = () =>

Check warning on line 19 in src/api.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing return type on function
utils.envOverride("FIREBASE_CLOUDBILLING_URL", "https://cloudbilling.googleapis.com");
export const cloudloggingOrigin = () =>

Check warning on line 21 in src/api.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing return type on function
utils.envOverride("FIREBASE_CLOUDLOGGING_URL", "https://logging.googleapis.com");
export const cloudMonitoringOrigin = () =>

Check warning on line 23 in src/api.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing return type on function
utils.envOverride("CLOUD_MONITORING_URL", "https://monitoring.googleapis.com");
export const containerRegistryDomain = () =>

Check warning on line 25 in src/api.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing return type on function
utils.envOverride("CONTAINER_REGISTRY_DOMAIN", "gcr.io");

export const developerConnectOrigin = () =>

Check warning on line 28 in src/api.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing return type on function
utils.envOverride("DEVELOPERCONNECT_URL", "https://developerconnect.googleapis.com");
export const developerConnectP4SADomain = () =>

Check warning on line 30 in src/api.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing return type on function
utils.envOverride("DEVELOPERCONNECT_P4SA_DOMAIN", "gcp-sa-devconnect.iam.gserviceaccount.com");

export const artifactRegistryDomain = () =>

Check warning on line 33 in src/api.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing return type on function
utils.envOverride("ARTIFACT_REGISTRY_DOMAIN", "https://artifactregistry.googleapis.com");
export const appDistributionOrigin = () =>
utils.envOverride(
Expand Down Expand Up @@ -125,6 +125,8 @@
utils.envOverride("FIREBASE_REMOTE_CONFIG_URL", "https://firebaseremoteconfig.googleapis.com");
export const messagingApiOrigin = () =>
utils.envOverride("FIREBASE_MESSAGING_CONFIG_URL", "https://fcm.googleapis.com");
export const messagingDataApiOrigin = () =>
utils.envOverride("FIREBASE_MESSAGING_DATA_CONFIG_URL", "https://content-fcmdata.googleapis.com");
export const crashlyticsApiOrigin = () =>
utils.envOverride("FIREBASE_CRASHLYTICS_URL", "https://firebasecrashlytics.googleapis.com");
export const resourceManagerOrigin = () =>
Expand Down
39 changes: 39 additions & 0 deletions src/mcp/tools/messaging/get_delivery_data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { z } from "zod";
import { tool } from "../../tool";
import { mcpError, toContent } from "../../util";
import { getAndroidDeliveryData } from "../../../messaging/getDeliveryData";

export const get_fcm_delivery_data = tool(
"messaging",
{
name: "get_fcm_delivery_data",
description: "Gets FCM's delivery data",
inputSchema: z.object({
appId: z.string().describe("appId to fetch data for"),
pageSize: z

Check failure on line 13 in src/mcp/tools/messaging/get_delivery_data.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Replace `⏎········.number()⏎········.optional()⏎········.describe("How·many·results·to·fetch",` with `.number().optional().describe("How·many·results·to·fetch"`

Check failure on line 13 in src/mcp/tools/messaging/get_delivery_data.ts

View workflow job for this annotation

GitHub Actions / unit (22)

Replace `⏎········.number()⏎········.optional()⏎········.describe("How·many·results·to·fetch",` with `.number().optional().describe("How·many·results·to·fetch"`

Check failure on line 13 in src/mcp/tools/messaging/get_delivery_data.ts

View workflow job for this annotation

GitHub Actions / unit (22)

Replace `⏎········.number()⏎········.optional()⏎········.describe("How·many·results·to·fetch",` with `.number().optional().describe("How·many·results·to·fetch"`

Check failure on line 13 in src/mcp/tools/messaging/get_delivery_data.ts

View workflow job for this annotation

GitHub Actions / unit (20)

Replace `⏎········.number()⏎········.optional()⏎········.describe("How·many·results·to·fetch",` with `.number().optional().describe("How·many·results·to·fetch"`
.number()
.optional()
.describe("How many results to fetch",),
pageToken: z

Check failure on line 17 in src/mcp/tools/messaging/get_delivery_data.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Replace `⏎········.string()⏎········.optional()⏎········` with `.string().optional()`

Check failure on line 17 in src/mcp/tools/messaging/get_delivery_data.ts

View workflow job for this annotation

GitHub Actions / unit (22)

Replace `⏎········.string()⏎········.optional()⏎········` with `.string().optional()`

Check failure on line 17 in src/mcp/tools/messaging/get_delivery_data.ts

View workflow job for this annotation

GitHub Actions / unit (22)

Replace `⏎········.string()⏎········.optional()⏎········` with `.string().optional()`

Check failure on line 17 in src/mcp/tools/messaging/get_delivery_data.ts

View workflow job for this annotation

GitHub Actions / unit (20)

Replace `⏎········.string()⏎········.optional()⏎········` with `.string().optional()`
.string()
.optional()
.describe("Next page token"),
}),
annotations: {
title: "Fetch FCM Delivery Data",
},
_meta: {
requiresAuth: true,
requiresProject: true,
},
},
async ({ appId, pageSize, pageToken }, { projectId }) => {
if (!appId.includes(":android:")) {
return mcpError(`Invalid app id provided: ${appId}. Currently fcm delivery data is only available for android apps.`)

Check failure on line 32 in src/mcp/tools/messaging/get_delivery_data.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Replace ``Invalid·app·id·provided:·${appId}.·Currently·fcm·delivery·data·is·only·available·for·android·apps.`)` with `⏎········`Invalid·app·id·provided:·${appId}.·Currently·fcm·delivery·data·is·only·available·for·android·apps.`,⏎······);`

Check failure on line 32 in src/mcp/tools/messaging/get_delivery_data.ts

View workflow job for this annotation

GitHub Actions / unit (22)

Replace ``Invalid·app·id·provided:·${appId}.·Currently·fcm·delivery·data·is·only·available·for·android·apps.`)` with `⏎········`Invalid·app·id·provided:·${appId}.·Currently·fcm·delivery·data·is·only·available·for·android·apps.`,⏎······);`

Check failure on line 32 in src/mcp/tools/messaging/get_delivery_data.ts

View workflow job for this annotation

GitHub Actions / unit (22)

Replace ``Invalid·app·id·provided:·${appId}.·Currently·fcm·delivery·data·is·only·available·for·android·apps.`)` with `⏎········`Invalid·app·id·provided:·${appId}.·Currently·fcm·delivery·data·is·only·available·for·android·apps.`,⏎······);`

Check failure on line 32 in src/mcp/tools/messaging/get_delivery_data.ts

View workflow job for this annotation

GitHub Actions / unit (20)

Replace ``Invalid·app·id·provided:·${appId}.·Currently·fcm·delivery·data·is·only·available·for·android·apps.`)` with `⏎········`Invalid·app·id·provided:·${appId}.·Currently·fcm·delivery·data·is·only·available·for·android·apps.`,⏎······);`
}

return toContent(

Check failure on line 35 in src/mcp/tools/messaging/get_delivery_data.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Replace `⏎······await·getAndroidDeliveryData(projectId,·appId,·{·pageSize,·pageToken·}),⏎····` with `await·getAndroidDeliveryData(projectId,·appId,·{·pageSize,·pageToken·})`

Check failure on line 35 in src/mcp/tools/messaging/get_delivery_data.ts

View workflow job for this annotation

GitHub Actions / unit (22)

Replace `⏎······await·getAndroidDeliveryData(projectId,·appId,·{·pageSize,·pageToken·}),⏎····` with `await·getAndroidDeliveryData(projectId,·appId,·{·pageSize,·pageToken·})`

Check failure on line 35 in src/mcp/tools/messaging/get_delivery_data.ts

View workflow job for this annotation

GitHub Actions / unit (22)

Replace `⏎······await·getAndroidDeliveryData(projectId,·appId,·{·pageSize,·pageToken·}),⏎····` with `await·getAndroidDeliveryData(projectId,·appId,·{·pageSize,·pageToken·})`

Check failure on line 35 in src/mcp/tools/messaging/get_delivery_data.ts

View workflow job for this annotation

GitHub Actions / unit (20)

Replace `⏎······await·getAndroidDeliveryData(projectId,·appId,·{·pageSize,·pageToken·}),⏎····` with `await·getAndroidDeliveryData(projectId,·appId,·{·pageSize,·pageToken·})`
await getAndroidDeliveryData(projectId, appId, { pageSize, pageToken }),
);
},
);
3 changes: 2 additions & 1 deletion src/mcp/tools/messaging/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ServerTool } from "../../tool";
import { send_message } from "./send_message";
import { get_fcm_delivery_data } from "./get_delivery_data";

export const messagingTools: ServerTool[] = [send_message];
export const messagingTools: ServerTool[] = [send_message, get_fcm_delivery_data];
57 changes: 57 additions & 0 deletions src/messaging/getDeliveryData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { messagingDataApiOrigin } from "../api";
import { Client } from "../apiv2";
import { logger } from "../logger";
import { FirebaseError } from "../error";
import { ListAndroidDeliveryDataResponse } from "./interfaces";

const TIMEOUT = 10000;

const apiClient = new Client({
urlPrefix: messagingDataApiOrigin(),
apiVersion: "v1beta1",
});


Check failure on line 14 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Delete `⏎`

Check failure on line 14 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / unit (22)

Delete `⏎`

Check failure on line 14 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / unit (22)

Delete `⏎`

Check failure on line 14 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / unit (20)

Delete `⏎`
export async function getAndroidDeliveryData(
projectId: string,
androidAppId: string,
options: {
pageSize?: number;
pageToken?: string;
},
): Promise<ListAndroidDeliveryDataResponse> {
try {
// API docs for fetching Android delivery data are here:

Check failure on line 24 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Delete `·`

Check failure on line 24 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / unit (22)

Delete `·`

Check failure on line 24 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / unit (22)

Delete `·`

Check failure on line 24 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / unit (20)

Delete `·`
// https://firebase.google.com/docs/reference/fcmdata/rest/v1beta1/projects.androidApps.deliveryData/list#http-request

Check failure on line 25 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Delete `·`

Check failure on line 25 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / unit (22)

Delete `·`

Check failure on line 25 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / unit (22)

Delete `·`

Check failure on line 25 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / unit (20)

Delete `·`

const customHeaders ={

Check failure on line 27 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Insert `·`

Check failure on line 27 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / unit (22)

Insert `·`

Check failure on line 27 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / unit (22)

Insert `·`

Check failure on line 27 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / unit (20)

Insert `·`
"Content-Type": "application/json",

Check failure on line 28 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Replace `········` with `······`

Check failure on line 28 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / unit (22)

Replace `········` with `······`

Check failure on line 28 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / unit (22)

Replace `········` with `······`

Check failure on line 28 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / unit (20)

Replace `········` with `······`
"x-goog-user-project": projectId,

Check failure on line 29 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Delete `··`

Check failure on line 29 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / unit (22)

Delete `··`

Check failure on line 29 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / unit (22)

Delete `··`

Check failure on line 29 in src/messaging/getDeliveryData.ts

View workflow job for this annotation

GitHub Actions / unit (20)

Delete `··`
};

// set up query params
const params = new URLSearchParams();
if (options.pageSize) {
params.set("pageSize", String(options.pageSize));
}
if (options.pageToken) {
params.set("pageToken", options.pageToken);
}

logger.debug(`requesting android delivery data for ${projectId}, ${androidAppId}`)

const res = await apiClient.request<null, ListAndroidDeliveryDataResponse>({
method: "GET",
path: `/projects/${projectId}/androidApps/${androidAppId}/deliveryData`,
queryParams: params,
headers: customHeaders,
timeout: TIMEOUT,
});

logger.debug(`${res.status}, ${res.response}, ${res.body}`);
return res.body;
} catch (err: any) {
logger.debug(err.message);
throw new FirebaseError(`Failed to fetch delivery data for project ${projectId} and ${androidAppId}, ${err}.`, {original: err});
}
}
127 changes: 127 additions & 0 deletions src/messaging/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,130 @@ export interface Notification {
/** URL of an image to include in the notification. */
image?: string;
}


// -----------------------------------------------------------------------------
// FM Delivery Data Interfaces
// -----------------------------------------------------------------------------

/**
* Additional information about [proxy notification] delivery.
* All percentages are calculated with 'countNotificationsAccepted' as the denominator.
*/
export interface ProxyNotificationInsightPercents {
/** The percentage of accepted notifications that were successfully proxied. */
proxied?: number;
/** The percentage of accepted notifications that failed to be proxied. */
failed?: number;
/** The percentage of accepted notifications that were skipped because proxy notification is unsupported for the recipient. */
skippedUnsupported: number;
/** The percentage of accepted notifications that were skipped because the messages were not throttled. */
skippedNotThrottled: number;
/** The percentage of accepted notifications that were skipped because configurations required for notifications to be proxied were missing. */
skippedUnconfigured: number;
/** The percentage of accepted notifications that were skipped because the app disallowed these messages to be proxied. */
skippedOptedOut: number;
}

/**
* Additional information about message delivery. All percentages are calculated
* with 'countMessagesAccepted' as the denominator.
*/
export interface MessageInsightPercents {
/** The percentage of accepted messages that had their priority lowered from high to normal. */
priorityLowered: number;
}

/**
* Overview of delivery performance for messages that were successfully delivered.
* All percentages are calculated with 'countMessagesAccepted' as the denominator.
*/
export interface DeliveryPerformancePercents {
/** The percentage of accepted messages that were delivered to the device without delay from the FCM system. */
deliveredNoDelay: number;
/** The percentage of accepted messages that were delayed because the target device was not connected at the time of sending. */
delayedDeviceOffline: number;
/** The percentage of accepted messages that were delayed because the device was in doze mode. */
delayedDeviceDoze: number;
/** The percentage of accepted messages that were delayed due to message throttling. */
delayedMessageThrottled: number;
/** The percentage of accepted messages that were delayed because the intended device user-profile was stopped. */
delayedUserStopped: number;
}

/**
* Percentage breakdown of message delivery outcomes. These categories are mutually exclusive.
* All percentages are calculated with 'countMessagesAccepted' as the denominator.
*/
export interface MessageOutcomePercents {
/** The percentage of all accepted messages that were successfully delivered to the device. */
delivered: number;
/** The percentage of messages accepted that were not dropped and not delivered, due to the device being disconnected. */
pending: number;
/** The percentage of accepted messages that were collapsed by another message. */
collapsed: number;
/** The percentage of accepted messages that were dropped due to too many undelivered non-collapsible messages. */
droppedTooManyPendingMessages: number;
/** The percentage of accepted messages that were dropped because the application was force stopped. */
droppedAppForceStopped: number;
/** The percentage of accepted messages that were dropped because the target device is inactive. */
droppedDeviceInactive: number;
/** The percentage of accepted messages that expired because Time To Live (TTL) elapsed. */
droppedTtlExpired: number;
}

/**
* Data detailing messaging delivery
*/
export interface Data {
/** Count of messages accepted by FCM intended for Android devices. */
countMessagesAccepted: string; // Use string for int64 to prevent potential precision issues
/** Count of notifications accepted by FCM intended for Android devices. */
countNotificationsAccepted: string; // Use string for int64
/** Mutually exclusive breakdown of message delivery outcomes. */
messageOutcomePercents: MessageOutcomePercents;
/** Additional information about delivery performance for messages that were successfully delivered. */
deliveryPerformancePercents: DeliveryPerformancePercents;
/** Additional general insights about message delivery. */
messageInsightPercents: MessageInsightPercents;
/** Additional insights about proxy notification delivery. */
proxyNotificationInsightPercents: ProxyNotificationInsightPercents;
}

// -----------------------------------------------------------------------------
// Core API Interfaces
// -----------------------------------------------------------------------------

/**
* Message delivery data for a given date, app, and analytics label combination.
*/
export interface AndroidDeliveryData {
/** The app ID to which the messages were sent. */
appId: string;
/** The date represented by this entry. */
date: {
year: number;
month: number;
day: number;
};
/** The analytics label associated with the messages sent. */
analyticsLabel: string;
/** The data for the specified combination. */
data: Data;
}

/**
* Response message for ListAndroidDeliveryData.
*/
export interface ListAndroidDeliveryDataResponse {
/**
* The delivery data for the provided app.
* There will be one entry per combination of app, date, and analytics label.
*/
androidDeliveryData: AndroidDeliveryData[];
/**
* A token, which can be sent as `page_token` to retrieve the next page.
* If this field is omitted, there are no subsequent pages.
*/
nextPageToken?: string;
}
Loading