Pendo MCP代码执行
将您的Pendo MCP服务器转换为 代码第一个API 这将令牌使用率降低了98%,同时实现了更复杂的数据工作流。
问题
当前的MCP实施面临着严重的效率低下问题:
- 令牌块:所有11个以上的Pendo工具定义都预先加载到AI环境中
- 环境污染:即使只是移动数据,中间查询结果也会通过AI的上下文窗口
- 昂贵的工作流程:复杂的多工具Pendo分析可能会消耗15万多个代币
解决方案
该MCP服务器提供了一个 沙盒代码执行环境 AI代理可以:
- 编写TypeScript代码与Pendo API交互
- 在代码中处理和过滤数据(而不是在上下文中)
- 仅将最终结果返回给AI
结果:复杂的Pendo工作流的令牌使用量减少了98%。
特性
- 🔒 安全沙盒执行 -代码在具有超时保护的隔离Worker线程中运行
- 📦 自动生成包装 -适用于所有11个Pendo MCP工具的TypeScript包装器
- 🚀 并行查询执行 -同时执行多个Pendo查询
- 💾 查询缓存 -基于文件的缓存,具有可配置的TTL
- 📚 可重复使用的技能 -常见Pendo分析模式库
- 🎯 类型安全 -具有适当接口的完全TypeScript支持
安装
从源代码安装
git clone https://github.com/adamteece/pendo-mcp-code-exec.git
cd pendo-mcp-code-exec
npm install
npm run build备注:此包尚未发布到npm。从源代码安装,如上所示。
配置
Claude桌面设置
将此添加到您的Claude Desktop配置中(~/Library/Application Support/Claude/claude_desktop_config.json 在macOS上):
{
"mcpServers": {
"pendo-code-execution": {
"command": "node",
"args": ["/absolute/path/to/pendo-mcp-code-exec/dist/server/index.js"],
"env": {
"PENDO_API_KEY": "your-api-key-here",
"PENDO_SUBSCRIPTION_ID": "your-sub-id",
"PENDO_MCP_COMMAND": "pendo-mcp",
"PENDO_MCP_ARGS": "[]"
}
}
}
}重要:替换 /absolute/path/to/pendo-mcp-code-exec 使用克隆存储库的实际绝对路径。环境变量
PENDO_API_KEY-您的Pendo API密钥(必需)PENDO_SUBSCRIPTION_ID-您的Pendo订阅ID(必填)PENDO_MCP_COMMAND-运行实际Pendo MCP服务器的命令(默认值:pendo-mcp)PENDO_MCP_ARGS-Pendo MCP服务器的JSON参数数组(默认值:[])
用法
生成包装文件
在使用服务器之前,为所有Pendo工具生成TypeScript包装器:
npm run generate-wrappers这将在中创建包装文件 servers/pendo/ 适用于所有11个Pendo工具。
可用工具
MCP服务器公开了三个主要工具:
1. execute_pendo_code
执行与Pendo数据交互的TypeScript代码。
例子:
import { activityQuery } from './servers/pendo/activityQuery.js';
import { searchEntities } from './servers/pendo/searchEntities.js';
// Search for pages
const pages = await searchEntities({
subId: 'your-sub-id',
appId: 'your-app-id',
itemType: 'page',
substring: 'dashboard'
});
// Get activity for top pages
const topPageIds = pages.slice(0, 5).map(p => p.id);
const activity = await activityQuery({
subId: 'your-sub-id',
appId: 'your-app-id',
entityType: 'page',
startDate: '2025-01-01',
endDate: '2025-01-31',
itemIds: topPageIds,
period: 'dayRange'
});
// Process results in code
const summary = activity.rows
.filter(row => row.numEvents > 100)
.map(row => ({
pageId: row.pageId,
events: row.numEvents
}));
console.log(JSON.stringify(summary, null, 2));2. list_pendo_tools
列出所有可用的Pendo工具包装器及其TypeScript接口。
3. save_skill
将可重用的TypeScript函数保存到skills目录。
// Example: Save a skill
save_skill({
name: 'low-engagement-finder',
category: 'pendo-helpers',
code: `
export async function findLowEngagementAccounts(config) {
// ... implementation
}
`
});可用的吊坠工具
以下Pendo工具可作为TypeScript模块在 ./servers/pendo/:
- 列出所有应用程序 -列出订阅和应用程序
- accountMetadataSchema -获取可用的帐户元数据字段
- accountQuery -使用元数据查询帐户数据
- visitorMetadataSchema -获取可用的访问者元数据字段
- visitorQuery -使用元数据查询访客数据
- activity查询 -查询活动/使用数据(事件、页面、功能)
- guideMetrics -获取指导绩效指标
- 产品参与度得分 -计算PES分数
- search实体 -搜索页面、功能、指南、曲目类型
- 分段列表 -列出可用细分市场
- 会话回放列表 -获取会话回放记录
可重复使用的技能
这 skills/pendo-helpers/ 目录包含预构建的分析函数:
采用分析
import { analyzeFeatureAdoption } from './skills/pendo-helpers/adoption-analysis.js';
const metrics = await analyzeFeatureAdoption({
subId: 'your-sub-id',
appId: 'your-app-id',
featureName: 'Export Button',
startDate: '2025-01-01',
endDate: '2025-01-31'
});
console.log(metrics);
// {
// featureName: 'Export Button',
// totalUsers: 1000,
// activeUsers: 450,
// adoptionRate: 45,
// avgEventsPerUser: 3.2
// }并行查询
import { getMultiPageActivity } from './skills/pendo-helpers/parallel-queries.js';
const results = await getMultiPageActivity({
subId: 'your-sub-id',
appId: 'your-app-id',
pageIds: ['page1', 'page2', 'page3'],
startDate: '2025-01-01',
endDate: '2025-01-31'
});参与度评分
import { calculateEngagementScores } from './skills/pendo-helpers/engagement-scoring.js';
const scores = await calculateEngagementScores({
subId: 'your-sub-id',
appId: 'your-app-id',
startDate: '2025-01-01',
endDate: '2025-01-31'
});
console.log(scores);
// [{
// accountId: 'acc123',
// score: 75,
// factors: { frequency: 80, breadth: 70, depth: 75, recency: 100 },
// risk: 'low'
// }]示例用例
寻找高价值、低参与度的客户
import { accountQuery } from './servers/pendo/accountQuery.js';
import { activityQuery } from './servers/pendo/activityQuery.js';
const accounts = await accountQuery({
subId: 'your-sub-id',
select: ['metadata.auto.lastvisit', 'metadata.custom.ARR']
});
const activity = await activityQuery({
subId: 'your-sub-id',
appId: 'your-app-id',
entityType: 'account',
startDate: '2025-10-01',
endDate: '2025-10-31'
});
const lowEngagement = accounts
.filter(acc => {
const arr = acc.metadata?.custom?.ARR || 0;
const events = activity.rows.find(a => a.accountId === acc.accountId)?.numEvents || 0;
return arr > 50000 && events b.metadata.custom.ARR - a.metadata.custom.ARR);
console.log(JSON.stringify({ lowEngagementAccounts: lowEngagement }, null, 2));比较指南性能
import { searchEntities } from './servers/pendo/searchEntities.js';
import { guideMetrics } from './servers/pendo/guideMetrics.js';
const guides = await searchEntities({
subId: 'your-sub-id',
appId: 'your-app-id',
itemType: 'guide',
substring: 'onboarding'
});
const metricsPromises = guides.map(guide =>
guideMetrics({
subId: 'your-sub-id',
guideId: guide.id,
startDate: '2025-10-01',
endDate: '2025-10-31'
})
);
const allMetrics = await Promise.all(metricsPromises);
const comparison = guides.map((guide, idx) => ({
guideName: guide.name,
completionRate: allMetrics[idx].completionRate || 0,
dismissalRate: allMetrics[idx].dismissalRate || 0
})).sort((a, b) => b.completionRate - a.completionRate);
console.log(JSON.stringify({ guideComparison: comparison }, null, 2));发展
建筑
npm run build以开发模式运行
npm run dev运行测试
npm test建筑
┌─────────────────────────────────────────────────────────────┐
│ AI Agent (Claude) │
│ - Writes TypeScript code to interact with Pendo │
│ - Sees only filesystem structure and final results │
└────────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Sandboxed Execution Environment │
│ - Runs TypeScript code securely in Worker threads │
│ - Has access to Pendo wrapper modules │
│ - Processes/filters data before returning to AI │
└────────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ TypeScript Wrapper Layer │
│ servers/pendo/*.ts (auto-generated wrappers) │
└────────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Pendo MCP Server │
│ - Original 11 tool implementations │
│ - Connects to actual Pendo API │
└─────────────────────────────────────────────────────────────┘安全
沙盒环境提供了多层安全性:
- 工作线程隔离 -代码在隔离的Worker线程中运行
- 超时保护 -30秒默认超时(可配置)
- 内存限制 -512MB默认内存限制
- 文件系统限制 -只读访问
servers/和skills/,仅对以下对象具有写访问权限cache/ - 无子进程生成 -防止执行任意命令
演出
- 查询缓存 -结果缓存1小时(可配置TTL)
- 并行执行 -多个Pendo查询同时运行
- 速率限制 -内置速率限制器(默认100个请求/分钟)
代币节省
“查找高价值、低参与度客户”的示例比较:
传统MCP方法:
- 加载11个工具定义:约2000个令牌
- 查询帐户:~50000个令牌(完整数据集)
- 查询活动:约50000个令牌(完整数据集)
- 按上下文筛选:约5000个令牌
- 总计:约107000个代币
代码执行方法:
- 使用筛选执行代码:约2000个令牌
- 仅返回筛选结果:约500个令牌
- 总计:约2500个代币
节省:减少98% 🎉
贡献
欢迎投稿!请阅读我们的投稿指南并提交pull请求。
许可证
麻省理工学院
支持
对于问题和疑问:
- GitHub问题:
- 彭多社区: 社区.pendo.io
致谢
专为Pendo MCP黑客马拉松(2025年11月12日-12月12日)而建。
受到Anthropic关于使用MCP和Cloudflare的“代码模式”实现执行代码的博客文章的启发。
