Guide · Product Analytics
MCP Tools for Segment — identify/track/group calls, Destination delivery, Protocols, and health monitoring
Segment (Twilio Segment) is a customer data platform that routes event streams to dozens of downstream Destinations (Amplitude, Mixpanel, Salesforce, Braze, warehouses, etc.). MCP tools that integrate with Segment send events once and rely on Segment to fan them out. The critical architectural subtlety: Segment's Node.js SDK (@segment/analytics-node) confirms that your event reached Segment's intake API — it cannot confirm that any downstream Destination received or processed the event. A Destination can fail silently for hours while the SDK reports successful delivery. The second major pattern: Segment Protocols can operate in three enforcement modes — blocking (reject non-conforming events), omit-properties (accept but strip invalid properties), or allow (log violations only). An MCP tool sending events with inconsistent schemas may have its data silently stripped in omit-properties mode without any error in the HTTP response.
TL;DR
Init: const analytics = new Analytics({writeKey: 'xxx'}). Calls: analytics.track({userId, event, properties}), analytics.identify({userId, traits}), analytics.group({userId, groupId, traits}). Batch endpoint: https://api.segment.io/v1/batch (SDK uses this). EU data residency: set host: 'https://events.eu1.segmentapis.com'. Shutdown: await analytics.closeAndFlush() — REQUIRED before process exit. Delivery confirmation: the SDK callback fires after intake API accepts the event — not after Destinations process it. Schema enforcement: check your Source's Protocols settings — blocking mode returns 400 for non-conforming events; omit-properties silently strips invalid fields.
Segment architecture for MCP tool authors
Segment's data flow has two stages. First, your MCP tool calls the SDK, which sends events to Segment's Source (identified by your write key). Segment receives the event, validates it against any Protocols schema, and accepts or rejects it. Second, Segment's internal pipeline routes accepted events to your configured Destinations — external tools and warehouses. This second stage is entirely asynchronous and opaque to your MCP tool. If Amplitude is a Destination and Amplitude's ingestion endpoint is down, Segment queues the events internally and retries — but your SDK call already completed with "success."
Write keys are Source-specific. Each Source (e.g., "MCP Server Production", "MCP Server Staging") has its own write key. The write key is write-only — it can send data into Segment but cannot read, query, or manage Segment resources. The Segment Public API uses a different authentication model (workspace token from Settings → API Keys) for management operations.
import { Analytics } from '@segment/analytics-node';
// Module-level singleton
let analytics = null;
function getAnalytics() {
if (analytics !== null) return analytics;
analytics = new Analytics({
writeKey: process.env.SEGMENT_WRITE_KEY,
// EU data residency — must match your Source's data residency setting
// host: 'https://events.eu1.segmentapis.com',
// Batch settings
flushAt: 15, // send when 15 events are queued
flushInterval: 10_000, // or every 10 seconds, whichever comes first
maxEventsInBatch: 15, // same as flushAt for @segment/analytics-node v1.x
// Retry on failure
maxRetries: 3,
});
return analytics;
}
// CRITICAL: must be called before process exit
// closeAndFlush() drains the queue, sends remaining events, and closes HTTP connections
// Without it, events buffered since the last auto-flush are lost
async function shutdown() {
if (!analytics) return;
await analytics.closeAndFlush({ timeout: 10_000 });
analytics = null;
}
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);
The five Segment method types
Segment defines five canonical event types that map to different analytics patterns. MCP tools should use the appropriate type rather than routing everything through track().
function getClient() { return getAnalytics(); }
// TRACK: behavioral event — what the user did
function trackToolCall(userId, toolName, inputTokens, outputTokens, success, context = {}) {
getClient().track({
userId, // identified user; use anonymousId if pre-login
// anonymousId: 'anon-session-id', // required if no userId
event: 'MCP Tool Called',
properties: {
tool_name: toolName,
input_tokens: inputTokens,
output_tokens: outputTokens,
success,
// Segment passes all properties to Destinations unchanged
// Protocols schema enforcement applies here — match your tracking plan
},
context: {
...context,
// app: { name: 'AliveMCP', version: '2.0.0' },
// ip: '0.0.0.0', set to '0.0.0.0' to disable IP collection
},
timestamp: new Date(), // override event time (ISO 8601 string or Date)
// messageId: 'custom-uuid', // set for deduplication (Segment uses this as idempotency key)
});
}
// IDENTIFY: who the user is — sets traits on the user profile
function identifyUser(userId, email, plan, role, createdAt) {
getClient().identify({
userId,
traits: {
email,
plan,
role,
createdAt,
// Segment forwards traits to CRM Destinations (Salesforce, HubSpot)
// as contact/lead properties — match their field names in trait keys
},
});
}
// GROUP: link user to an account/org/team
function associateGroup(userId, groupId, groupName, plan, memberCount) {
getClient().group({
userId,
groupId,
traits: {
name: groupName,
plan,
member_count: memberCount,
},
});
}
// PAGE: page or screen view (useful for MCP web dashboard events)
function trackPageView(userId, pageName, url, referrer) {
getClient().page({
userId,
name: pageName,
properties: { url, referrer },
});
}
// ALIAS: link anonymous ID to identified user ID (for identity merge)
// Call once at login to merge pre-login anonymous events to the user profile
function aliasUser(anonymousId, userId) {
getClient().alias({
previousId: anonymousId,
userId,
});
}
Protocols and schema enforcement
Segment Protocols lets you define a Tracking Plan — a schema that specifies which events are valid, what properties each event may carry, and what types those properties must have. The enforcement mode determines what happens when a non-conforming event arrives.
Enforcement modes and their health monitoring implications:
blocking: Non-conforming events are rejected with HTTP 400. Your MCP tool receives an error. This is the most MCP-monitoring-friendly mode — failures are explicit.omit-properties: Non-conforming events are accepted (HTTP 200), but invalid properties are silently stripped before routing to Destinations. Your SDK call "succeeds" but Amplitude, Mixpanel, or your warehouse receives the event with missing fields. This is the most dangerous mode for MCP tools that iterate on their event schemas.allow(permissive): All events accepted, schema violations logged in Segment's Protocols Violations report only. No runtime impact on event delivery.
// Wrapper that makes Protocols enforcement mode explicit in your MCP tool
async function trackWithProtocolsAwareness(userId, event, properties) {
const client = getClient();
return new Promise((resolve, reject) => {
client.track({
userId,
event,
properties,
}, (err) => {
if (err) {
// In blocking mode: err includes the 400 response body with violation details
// {"type":"track","message":"Event 'MCP Tool Called' not found in Tracking Plan"}
// In omit-properties mode: err is null even if properties were stripped
reject(new Error(`Segment track failed: ${err.message}`));
} else {
resolve();
}
});
// Note: callback fires after INTAKE API confirms — not after Destination delivery
});
}
// Query Segment's Violations API to detect schema drift (requires workspace token)
async function checkProtocolsViolations(workspaceToken, sourceId) {
const res = await fetch(
`https://api.segmentapis.com/catalog/sources/${sourceId}/violations`,
{
headers: { 'Authorization': `Bearer ${workspaceToken}` },
signal: AbortSignal.timeout(10_000),
}
);
if (!res.ok) throw new Error(`Violations API → ${res.status}`);
return res.json();
// Returns recent violations: event name, property, violation type, count
// Use this to detect when a code change started sending non-conforming events
}
Health monitoring: intake vs Destination delivery
Segment's health model has two tiers. The intake API is what your MCP tool directly measures — it accepts events and is highly available. The Destination delivery pipeline is what matters for business outcomes — if Amplitude is receiving events, if Salesforce leads are being created, if the data warehouse is being loaded. Only the intake is directly probeable; Destination health requires monitoring the Destinations themselves or checking Segment's Delivery Overview in the dashboard.
async function probeSegment(writeKey, host = 'https://api.segment.io') {
const results = await Promise.allSettled([
// 1. Intake API health — does Segment accept events?
(async () => {
const body = {
batch: [{
type: 'track',
userId: '__alivemcp_probe__',
event: '__health_probe__',
properties: { synthetic: true },
timestamp: new Date().toISOString(),
messageId: `probe-${Date.now()}`,
}],
writeKey,
sentAt: new Date().toISOString(),
};
const encoded = Buffer.from(`${writeKey}:`).toString('base64');
const res = await fetch(`${host}/v1/batch`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${encoded}`,
},
body: JSON.stringify(body),
signal: AbortSignal.timeout(5_000),
});
if (!res.ok) throw new Error(`Segment batch → ${res.status}`);
// Segment returns 200 with empty body {} on success
return { kind: 'intake', accepted: true };
})(),
// 2. Source connectivity check via single-event endpoint
(async () => {
const encoded = Buffer.from(`${writeKey}:`).toString('base64');
const res = await fetch(`${host}/v1/track`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${encoded}`,
},
body: JSON.stringify({
userId: '__alivemcp_probe__',
event: '__health_probe_single__',
properties: { synthetic: true },
timestamp: new Date().toISOString(),
messageId: `probe-single-${Date.now()}`,
}),
signal: AbortSignal.timeout(5_000),
});
if (!res.ok) throw new Error(`Segment /v1/track → ${res.status}`);
return { kind: 'track_endpoint', accepted: true };
})(),
]);
return {
healthy: results.every(r => r.status === 'fulfilled'),
// Note: healthy=true here means INTAKE accepted events.
// Destination delivery health must be monitored separately at each Destination.
components: results.map(r =>
r.status === 'fulfilled'
? { ...r.value, ok: true }
: { ok: false, error: r.reason?.message }
),
};
}
Set AliveMCP alerts: critical when the intake API returns 4xx (write key revoked, source deleted, or Protocols blocking enforcement triggered); warning when intake latency exceeds 500ms (Segment's intake API is normally sub-100ms — high latency indicates internal queue pressure); info for monitoring key Destinations independently (e.g., probe your Amplitude or Mixpanel Destination directly to verify end-to-end delivery).