Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ See [Vault payments](docs/vault-payments.md) for both provider flows, safety rul
- `webmcp` - List native page tools across every tab and frame in a browser, then synchronously invoke an exact opaque `tool_ref` with structured input.
- `exec_command` - Run shell commands inside a browser VM. Returns decoded stdout/stderr.
- `search_docs` - Search Kernel platform documentation and guides.
- `submit_feedback` - send product, mcp, or documentation feedback directly to the KERNEL team without interrupting the current task.
- `submit_feedback` - send product, bot-detection, mcp, or documentation feedback directly to the KERNEL team without interrupting the current task. Bot-detection reports capture a public registrable domain, observed outcome, reproducibility, browser configuration, and optional session or config-registry references for prioritization.
- `open_auth_login` - Open a secure interactive Managed Auth MCP App after user consent. Registered only for clients that declare MCP Apps support; credentials and MFA never enter MCP/model traffic.

## Resources
Expand Down
114 changes: 104 additions & 10 deletions src/lib/mcp/analytics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,21 @@ describe("captureMcpFeedback", () => {
feedback_type: "product",
feedback_sentiment: "mixed",
feedback_product_area: "browsers",
feedback_destination: undefined,
feedback_bot_detection_registrable_domain: undefined,
feedback_bot_detection_observed_outcome: undefined,
feedback_bot_detection_suspected_vendor: undefined,
feedback_bot_detection_challenge_type: undefined,
feedback_bot_detection_stealth: undefined,
feedback_bot_detection_proxy_type: undefined,
feedback_bot_detection_region: undefined,
feedback_bot_detection_browser_version: undefined,
feedback_bot_detection_browser_image_version: undefined,
feedback_bot_detection_reproducibility: undefined,
feedback_bot_detection_browser_session_id: undefined,
feedback_bot_detection_config_registry_analysis_id: undefined,
feedback_bot_detection_config_registry_recommendation_applied:
undefined,
feedback_category: undefined,
feedback_task_completed: true,
feedback_tools_used: ["manage_browsers"],
Expand All @@ -631,6 +646,74 @@ describe("captureMcpFeedback", () => {
},
]);
});

test("routes structured bot-detection feedback to config registry prioritization", async () => {
const captured: unknown[] = [];
const analytics = {
capture: async (event: unknown) => {
captured.push(event);
},
} as McpAnalytics;

await captureMcpFeedback(
{
summary: "Stealth sessions were consistently blocked",
feedback_type: "bot_detection",
sentiment: "negative",
task_completed: false,
tools_used: ["manage_browsers", "execute_playwright_code"],
bot_detection: {
registrable_domain: "example.com",
observed_outcome: "blocked",
suspected_vendor: "Akamai Bot Manager",
challenge_type: "access_denied",
stealth: "enabled",
proxy_type: "isp",
region: "us-east",
browser_version: "152.0.7977.42",
browser_image_version: "2026.09.14",
reproducibility: "consistent",
browser_session_id: "session_123",
config_registry_analysis_id: "analysis_123",
config_registry_recommendation_applied: true,
},
},
{
authInfo: {
extra: {
connectionContext: {
scope: { organizationId: "org_analytics" },
},
},
},
},
analytics,
);

expect(captured).toEqual([
{
event: MCP_FEEDBACK_SUBMITTED_EVENT,
properties: expect.objectContaining({
$groups: { organization: "org_analytics" },
feedback_type: "bot_detection",
feedback_destination: "config_registry_prioritization",
feedback_bot_detection_registrable_domain: "example.com",
feedback_bot_detection_observed_outcome: "blocked",
feedback_bot_detection_suspected_vendor: "Akamai Bot Manager",
feedback_bot_detection_challenge_type: "access_denied",
feedback_bot_detection_stealth: "enabled",
feedback_bot_detection_proxy_type: "isp",
feedback_bot_detection_region: "us-east",
feedback_bot_detection_browser_version: "152.0.7977.42",
feedback_bot_detection_browser_image_version: "2026.09.14",
feedback_bot_detection_reproducibility: "consistent",
feedback_bot_detection_browser_session_id: "session_123",
feedback_bot_detection_config_registry_analysis_id: "analysis_123",
feedback_bot_detection_config_registry_recommendation_applied: true,
}),
},
]);
});
});

describe("instrumentMcpAnalytics (SDK integration)", () => {
Expand Down Expand Up @@ -815,11 +898,17 @@ describe("instrumentMcpAnalytics (SDK integration)", () => {
name: KERNEL_FEEDBACK_TOOL_NAME,
arguments: {
context:
"Reporting that browser timeout guidance did not explain when the caller should retry.",
summary: "Browser timeout guidance was unclear",
feedback_type: "product",
sentiment: "mixed",
product_area: "browsers",
"Reporting a repeatable site block so the affected domain can be prioritized for a working browser configuration.",
summary: "Stealth sessions were consistently blocked",
feedback_type: "bot_detection",
sentiment: "negative",
task_completed: false,
bot_detection: {
registrable_domain: "example.com",
observed_outcome: "blocked",
suspected_vendor: "Akamai Bot Manager",
reproducibility: "consistent",
},
},
});

Expand All @@ -843,13 +932,18 @@ describe("instrumentMcpAnalytics (SDK integration)", () => {
[PostHogMCPAnalyticsProperty.ProtocolVersion]: "2025-03-26",
[PostHogMCPAnalyticsProperty.ServerName]: "test",
[PostHogMCPAnalyticsProperty.ServerVersion]: "0.0.0",
feedback_summary: "Browser timeout guidance was unclear",
feedback_type: "product",
feedback_sentiment: "mixed",
feedback_product_area: "browsers",
feedback_summary: "Stealth sessions were consistently blocked",
feedback_type: "bot_detection",
feedback_sentiment: "negative",
feedback_task_completed: false,
feedback_destination: "config_registry_prioritization",
feedback_bot_detection_registrable_domain: "example.com",
feedback_bot_detection_observed_outcome: "blocked",
feedback_bot_detection_suspected_vendor: "Akamai Bot Manager",
feedback_bot_detection_reproducibility: "consistent",
});
expect(toolCall.properties[PostHogMCPAnalyticsProperty.Intent]).toBe(
"Reporting that browser timeout guidance did not explain when the caller should retry.",
"Reporting a repeatable site block so the affected domain can be prioritized for a working browser configuration.",
);
});

Expand Down
49 changes: 49 additions & 0 deletions src/lib/mcp/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,20 @@ const SENT_PROPERTIES = new Set<string>([
"feedback_type",
"feedback_sentiment",
"feedback_product_area",
"feedback_destination",
"feedback_bot_detection_registrable_domain",
"feedback_bot_detection_observed_outcome",
"feedback_bot_detection_suspected_vendor",
"feedback_bot_detection_challenge_type",
"feedback_bot_detection_stealth",
"feedback_bot_detection_proxy_type",
"feedback_bot_detection_region",
"feedback_bot_detection_browser_version",
"feedback_bot_detection_browser_image_version",
"feedback_bot_detection_reproducibility",
"feedback_bot_detection_browser_session_id",
"feedback_bot_detection_config_registry_analysis_id",
"feedback_bot_detection_config_registry_recommendation_applied",
"feedback_category",
"feedback_task_completed",
"feedback_tools_used",
Expand Down Expand Up @@ -517,13 +531,48 @@ export function captureMcpFeedback(
extra: unknown,
analytics: McpAnalytics,
) {
const botDetection =
feedback.feedback_type === "bot_detection"
? feedback.bot_detection
: undefined;
return captureMcpCustomEvent(analytics, extra, MCP_FEEDBACK_SUBMITTED_EVENT, {
feedback_summary: redactAnalyticsText(feedback.summary),
feedback_type: feedback.feedback_type,
feedback_sentiment: feedback.sentiment,
feedback_product_area: feedback.product_area
? redactAnalyticsText(feedback.product_area)
: undefined,
feedback_destination: botDetection
? "config_registry_prioritization"
: undefined,
feedback_bot_detection_registrable_domain: botDetection?.registrable_domain,
feedback_bot_detection_observed_outcome: botDetection?.observed_outcome,
feedback_bot_detection_suspected_vendor: botDetection?.suspected_vendor
? redactAnalyticsText(botDetection.suspected_vendor)
: undefined,
feedback_bot_detection_challenge_type: botDetection?.challenge_type,
feedback_bot_detection_stealth: botDetection?.stealth,
feedback_bot_detection_proxy_type: botDetection?.proxy_type,
feedback_bot_detection_region: botDetection?.region
? redactAnalyticsText(botDetection.region)
: undefined,
feedback_bot_detection_browser_version: botDetection?.browser_version
? redactAnalyticsText(botDetection.browser_version)
: undefined,
feedback_bot_detection_browser_image_version:
botDetection?.browser_image_version
? redactAnalyticsText(botDetection.browser_image_version)
: undefined,
feedback_bot_detection_reproducibility: botDetection?.reproducibility,
feedback_bot_detection_browser_session_id: botDetection?.browser_session_id
? redactAnalyticsText(botDetection.browser_session_id)
: undefined,
feedback_bot_detection_config_registry_analysis_id:
botDetection?.config_registry_analysis_id
? redactAnalyticsText(botDetection.config_registry_analysis_id)
: undefined,
feedback_bot_detection_config_registry_recommendation_applied:
botDetection?.config_registry_recommendation_applied,
feedback_category: feedback.category,
feedback_task_completed: feedback.task_completed,
feedback_tools_used: feedback.tools_used?.map(redactAnalyticsText),
Comment thread
masnwilliams marked this conversation as resolved.
Expand Down
162 changes: 162 additions & 0 deletions src/lib/mcp/tools/feedback.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,168 @@ describe("submit_feedback", () => {
}
});

test("records structured bot-detection outcomes for config registry prioritization", async () => {
const captured: KernelFeedback[] = [];
const { client, close } = await connectTestMcp(
(server) =>
registerFeedbackTool(server, (feedback) => {
captured.push(feedback);
}),
{},
);

try {
const tools = await client.listTools();
const tool = tools.tools.find(
({ name }) => name === KERNEL_FEEDBACK_TOOL_NAME,
);
expect(JSON.stringify(tool?.inputSchema)).toContain('"bot_detection"');
expect(JSON.stringify(tool?.inputSchema)).toContain(
'"registrable_domain"',
);

const result = await client.callTool({
name: KERNEL_FEEDBACK_TOOL_NAME,
arguments: {
context:
"Reporting a repeatable site block so the affected domain can be prioritized for a working browser configuration.",
summary: "Stealth sessions were consistently blocked",
feedback_type: "bot_detection",
sentiment: "negative",
task_completed: false,
tools_used: ["manage_browsers", "execute_playwright_code"],
bot_detection: {
registrable_domain: "Example.COM",
observed_outcome: "blocked",
suspected_vendor: "Akamai Bot Manager",
challenge_type: "access_denied",
stealth: "enabled",
proxy_type: "isp",
region: "us-east",
browser_version: "152.0.7977.42",
browser_image_version: "2026.09.14",
reproducibility: "consistent",
browser_session_id: "session_123",
config_registry_analysis_id: "analysis_123",
config_registry_recommendation_applied: true,
},
},
});

expect(toolResultJSON(result)).toMatchObject({
recorded: true,
feedback_type: "bot_detection",
sentiment: "negative",
});
expect(captured).toEqual([
{
summary: "Stealth sessions were consistently blocked",
feedback_type: "bot_detection",
sentiment: "negative",
task_completed: false,
tools_used: ["manage_browsers", "execute_playwright_code"],
bot_detection: {
registrable_domain: "example.com",
observed_outcome: "blocked",
suspected_vendor: "Akamai Bot Manager",
challenge_type: "access_denied",
stealth: "enabled",
proxy_type: "isp",
region: "us-east",
browser_version: "152.0.7977.42",
browser_image_version: "2026.09.14",
reproducibility: "consistent",
browser_session_id: "session_123",
config_registry_analysis_id: "analysis_123",
config_registry_recommendation_applied: true,
},
},
]);
} finally {
await close();
}
});

test("requires structured fields only for bot-detection feedback", async () => {
const captured: KernelFeedback[] = [];
const { client, close } = await connectTestMcp(
(server) =>
registerFeedbackTool(server, (feedback) => {
captured.push(feedback);
}),
{},
);

try {
const missingReport = await client.callTool({
name: KERNEL_FEEDBACK_TOOL_NAME,
arguments: {
context:
"Reporting a site-specific browser block without the structured observation required for config registry prioritization.",
summary: "A site blocked the browser",
feedback_type: "bot_detection",
sentiment: "negative",
},
});
expect(missingReport.isError).toBe(true);
expect(captured).toEqual([]);

const reportOnProductFeedback = await client.callTool({
name: KERNEL_FEEDBACK_TOOL_NAME,
arguments: {
context:
"Reporting general browser feedback without routing it into the site-specific bot-detection prioritization queue.",
summary: "Browser startup was clear",
feedback_type: "product",
sentiment: "positive",
bot_detection: {
registrable_domain: "example.com",
observed_outcome: "passed",
reproducibility: "single_observation",
},
},
});
expect(reportOnProductFeedback.isError).toBe(true);
expect(captured).toEqual([]);
} finally {
await close();
}
});

test("rejects URLs in bot-detection domain reports", async () => {
const captured: KernelFeedback[] = [];
const { client, close } = await connectTestMcp(
(server) =>
registerFeedbackTool(server, (feedback) => {
captured.push(feedback);
}),
{},
);

try {
const result = await client.callTool({
name: KERNEL_FEEDBACK_TOOL_NAME,
arguments: {
context:
"Reporting a site outcome while ensuring paths and query strings cannot enter the prioritization event.",
summary: "A site blocked the browser",
feedback_type: "bot_detection",
sentiment: "negative",
bot_detection: {
registrable_domain: "https://example.com/account?user=1",
observed_outcome: "blocked",
reproducibility: "single_observation",
},
},
});

expect(result.isError).toBe(true);
expect(captured).toEqual([]);
} finally {
await close();
}
});

test("keeps analytics failures from failing the tool call", async () => {
const { client, close } = await connectTestMcp(
(server) =>
Expand Down
Loading
Loading