
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
Analytics SDK for tracking and analyzing Model Context Protocol (MCP) server interactions.
npm install agnost
import { trackMCP, createConfig } from 'agnost';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
// Create your MCP server instance
const server = new Server(
{
name: "my-server",
version: "1.0.0"
},
{
capabilities: {
tools: {}
}
}
);
// Configure analytics
const config = createConfig({
endpoint: "https://api.agnost.ai",
disableInput: false,
disableOutput: false
});
// Enable analytics tracking
const trackedServer = trackMCP(server, "your-organization-id", config);
import { trackMCP, createConfig } from 'agnost';
// Create a custom configuration
const config = createConfig({
endpoint: "https://api.agnost.ai",
disableInput: false, // Set to true to disable input tracking
disableOutput: false, // Set to true to disable output tracking
disableLogs: false // Set to true to completely disable all SDK logs
});
// Apply the configuration
trackMCP(
server,
"your-organization-id",
config
);
To completely disable all SDK logs (including error logs), you can use the disableLogs option:
import { trackMCP, createConfig } from 'agnost';
// Configuration with all logs disabled
const config = createConfig({
endpoint: "https://api.agnost.ai",
disableLogs: true // This will disable ALL SDK logs
});
trackMCP(server, "your-organization-id", config);
Alternatively, you can use environment variables:
# Disable all logs via environment variable
export AGNOST_DISABLE_LOGS=true
# Or control log level (debug, info, warning, error)
export AGNOST_LOG_LEVEL=error
The SDK supports user identification to track analytics per user. This is especially useful for understanding usage patterns across different users and roles.
import { trackMCP, createConfig } from 'agnost';
// Enable user identification
trackMCP(server, 'your-org-id', {
// .. other config like disableInput, disableOutput
identify: (request, env) => ({
userId: request?.headers?.['x-user-id'] || env?.USER_ID || 'anonymous',
email: request?.headers?.['x-user-email'] || env?.USER_EMAIL,
role: request?.headers?.['x-user-role'] || env?.USER_ROLE || 'user'
})
});
import { trackMCP, createConfig } from 'agnost';
// Complex identification logic with async operations
trackMCP(server, 'your-org-id', {
identify: async (request, env) => {
try {
// Extract token from headers
const token = request?.headers?.['authorization']?.replace('Bearer ', '');
if (!token) {
return { userId: 'anonymous' };
}
// You could validate token and fetch user info
// const userInfo = await validateTokenAndGetUser(token);
// Return user identity with custom fields
return {
userId: 'user-123',
email: 'user@example.com',
role: 'admin',
organization: 'acme-corp',
subscription: 'premium'
};
} catch (error) {
console.warn('User identification failed:', error);
return { userId: 'anonymous' };
}
}
});
The identify function should return a UserIdentity object or null:
interface UserIdentity {
userId: string; // Required: Unique user identifier
[key: string]: any; // Optional: Any additional user properties
}
type IdentifyFunction = (
request?: any, // MCP request object with headers, params, etc.
env?: Record<string, string | undefined> // Environment variables (process.env)
) => UserIdentity | null | Promise<UserIdentity | null>;
request: The incoming MCP request object containing:
headers: HTTP-style headers (e.g., x-user-id, authorization)params: Request parameters including tool name and argumentsenv: Environment variables from process.env, useful for:
identify: (request, env) => ({
userId: request?.headers?.['x-user-id'] || 'anonymous',
role: request?.headers?.['x-user-role'] || 'user'
})
identify: (request, env) => ({
userId: env?.USER_ID || env?.LOGGED_IN_USER || 'anonymous',
workspace: env?.WORKSPACE_ID
})
identify: async (request, env) => {
const authHeader = request?.headers?.['authorization'];
if (authHeader?.startsWith('Bearer ')) {
const token = authHeader.replace('Bearer ', '');
const decoded = await decodeJWT(token);
return {
userId: decoded.sub,
email: decoded.email,
role: decoded.role
};
}
return { userId: 'anonymous' };
}
userId field is required in the returned UserIdentity objectnull or { userId: 'anonymous' }userId are included in analytics for segmentation| Option | Type | Default | Description |
|---|---|---|---|
endpoint | string | "https://api.agnost.ai" | API endpoint URL |
disableInput | boolean | false | Disable tracking of input arguments |
disableOutput | boolean | false | Disable tracking of output results |
disableLogs | boolean | false | Completely disable all SDK logs |
identify | IdentifyFunction | undefined | Function to identify users from request context |
The TypeScript SDK provides a powerful checkpoint() function for detailed latency breakup of tool calls. Checkpoints allow you to track specific points in your tool's execution, providing granular observability into where time is being spent.
When analyzing tool performance, knowing the total execution time is often not enough. The checkpoint() function lets you mark specific points in your execution flow to understand:
All checkpoint data is automatically captured and visualized in the Agnost AI dashboard with interactive timeline charts.
import { checkpoint } from 'agnost';
checkpoint(name: string, metadata?: any): void
Parameters:
name (string): A descriptive name for the checkpoint (e.g., "database_query_start", "api_call_complete")metadata (optional): Any additional context to attach to this checkpoint (e.g., row counts, response sizes, status codes)import { checkpoint } from 'agnost';
import { z } from 'zod';
// Define your tool
server.tool(
'get_user_data',
'Fetches and processes user data from the database',
{
userId: z.string().describe('The user ID to fetch')
},
async ({ userId }) => {
// Mark the start of input validation
checkpoint('input_validation_start');
if (!userId || userId.length === 0) {
throw new Error('Invalid user ID');
}
checkpoint('input_validation_complete');
// Mark the start of database query
checkpoint('database_query_start');
const userData = await db.query('SELECT * FROM users WHERE id = ?', [userId]);
checkpoint('database_query_complete', {
rowCount: userData.length
});
// Mark the start of data processing
checkpoint('data_processing_start');
const processed = await processUserData(userData);
checkpoint('data_processing_complete', {
recordsProcessed: processed.length
});
return {
content: [
{
type: 'text',
text: JSON.stringify(processed)
}
]
};
}
);
import { checkpoint } from 'agnost';
server.tool(
'fetch_weather',
'Fetches weather data from external API',
{
city: z.string()
},
async ({ city }) => {
// Track input normalization
checkpoint('input_normalization_start');
const normalizedCity = city.trim().toLowerCase();
checkpoint('input_normalization_complete');
// Track cache lookup
checkpoint('cache_lookup_start');
const cached = await cache.get(`weather:${normalizedCity}`);
checkpoint('cache_lookup_complete', { cacheHit: !!cached });
if (cached) {
checkpoint('returning_cached_data');
return cached;
}
// Track external API call
checkpoint('api_call_start');
const response = await fetch(`https://api.weather.com/v1/${normalizedCity}`);
checkpoint('api_call_complete', {
statusCode: response.status,
responseSize: response.headers.get('content-length')
});
// Track response parsing
checkpoint('response_parsing_start');
const data = await response.json();
checkpoint('response_parsing_complete');
// Track cache update
checkpoint('cache_update_start');
await cache.set(`weather:${normalizedCity}`, data, 3600);
checkpoint('cache_update_complete');
return {
content: [
{
type: 'text',
text: JSON.stringify(data)
}
]
};
}
);
Each checkpoint is recorded with the following structure:
interface Checkpoint {
name: string; // The checkpoint name
timestamp: number; // Milliseconds since execution start
metadata?: any; // Optional metadata object
}
Checkpoints are automatically visualized in the Agnost AI dashboard with:
Example timeline visualization:
[0ms--------50ms][50ms---------200ms][200ms----250ms]
Input Valid DB Query Processing
Use Descriptive Names: Make checkpoint names clear and specific
// Good
checkpoint('database_query_complete');
checkpoint('external_api_call_start');
// Avoid
checkpoint('step1');
checkpoint('done');
Track Start and End: For operations you want to measure, add both start and end checkpoints
checkpoint('operation_start');
await expensiveOperation();
checkpoint('operation_complete');
Add Useful Metadata: Include context that helps debug performance issues
checkpoint('query_complete', {
rowCount: results.length,
queryTime: Date.now() - startTime,
cacheHit: false
});
Focus on Expensive Operations: Add checkpoints around:
Don't Over-checkpoint: Too many checkpoints can make analysis harder. Focus on meaningful boundaries
// Good: Major operation boundaries
checkpoint('fetch_data_start');
checkpoint('fetch_data_complete');
checkpoint('process_data_complete');
// Avoid: Too granular
checkpoint('variable_declared');
checkpoint('loop_iteration_1');
checkpoint('loop_iteration_2');
checkpoint('db_connection_start');
const connection = await pool.getConnection();
checkpoint('db_connection_acquired');
checkpoint('db_query_start');
const results = await connection.query(sql);
checkpoint('db_query_complete', { rowCount: results.length });
checkpoint('fetch_raw_data');
const raw = await fetchData();
checkpoint('transform_data');
const transformed = transform(raw);
checkpoint('validate_data');
const validated = validate(transformed);
checkpoint('store_data');
await store(validated);
checkpoint('pipeline_complete');
checkpoint('parallel_operations_start');
const [result1, result2, result3] = await Promise.all([
operation1(),
operation2(),
operation3()
]);
checkpoint('parallel_operations_complete', {
operation1Time: result1.duration,
operation2Time: result2.duration,
operation3Time: result3.duration
});
Checkpoints not appearing in dashboard:
checkpoint() inside a tracked tool handlertrackMCP()Timestamps seem incorrect:
checkpoint() outside of tool execution contextPerformance concerns:
FAQs
Analytics SDK for Model Context Protocol Servers
We found that agnost demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.