autotel-cli 0.9.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4506,6 +4506,7 @@ const INVESTIGATE_COMMANDS = [
4506
4506
  investigateCmd("security", "Security telemetry (parent)"),
4507
4507
  investigateCmd("security summary", "Security posture: events by severity/category, probe signals, denied responses"),
4508
4508
  investigateCmd("security events", "List spans carrying security.* events"),
4509
+ investigateCmd("security mcp", "MCP protocol-boundary security: injection verdicts, output-budget breaches, untrusted-content tool calls"),
4509
4510
  investigateCmd("semconv", "Semantic conventions lookup (parent)", { static: true }),
4510
4511
  investigateCmd("semconv list", "List semconv namespaces", {
4511
4512
  static: true,
@@ -6073,6 +6074,83 @@ async function runSecurityEvents(flags) {
6073
6074
  };
6074
6075
  });
6075
6076
  }
6077
+ /**
6078
+ * `autotel security mcp` — MCP protocol-boundary security signals emitted by
6079
+ * `autotel-mcp-instrumentation`:
6080
+ * - `mcp.security.injection.*` — pluggable classifier verdicts on tool args/results
6081
+ * - `mcp.security.budget.exceeded` — tool output over the configured char budget
6082
+ * - `mcp.tool.untrusted_content` — tools flagged via the WebMCP untrustedContentHint
6083
+ *
6084
+ * Keep these field literals in sync with `MCP_SEMCONV` in
6085
+ * `autotel-mcp-instrumentation/semantic-conventions`.
6086
+ */
6087
+ const MCP_TOOL_NAME = "gen_ai.tool.name";
6088
+ const MCP_INJECTION_VERDICT = "mcp.security.injection.verdict";
6089
+ const MCP_INJECTION_SOURCE = "mcp.security.injection.source";
6090
+ const MCP_BUDGET_EXCEEDED = "mcp.security.budget.exceeded";
6091
+ const MCP_UNTRUSTED_CONTENT = "mcp.tool.untrusted_content";
6092
+ async function runSecurityMcp(flags) {
6093
+ await runInvestigate("security mcp", flags, async (backend) => {
6094
+ const base = toSpanSearchQuery({
6095
+ serviceName: flags.serviceName,
6096
+ lookbackMinutes: flags.lookbackMinutes,
6097
+ from: flags.from,
6098
+ to: flags.to,
6099
+ limit: flags.limit ?? DEFAULT_LIMIT
6100
+ });
6101
+ const [injection, budget, untrusted] = await Promise.all([
6102
+ backend.searchSpans({
6103
+ ...base,
6104
+ filters: [{
6105
+ field: MCP_INJECTION_VERDICT,
6106
+ operator: "exists"
6107
+ }]
6108
+ }),
6109
+ backend.searchSpans({
6110
+ ...base,
6111
+ filters: [{
6112
+ field: MCP_BUDGET_EXCEEDED,
6113
+ operator: "exists"
6114
+ }]
6115
+ }),
6116
+ backend.searchSpans({
6117
+ ...base,
6118
+ filters: [{
6119
+ field: MCP_UNTRUSTED_CONTENT,
6120
+ operator: "exists"
6121
+ }]
6122
+ })
6123
+ ]);
6124
+ const suspected = injection.items.filter((span) => span.tags[MCP_INJECTION_VERDICT] !== void 0 && String(span.tags[MCP_INJECTION_VERDICT]) !== "clean");
6125
+ const untrustedTrue = untrusted.items.filter((span) => String(span.tags[MCP_UNTRUSTED_CONTENT]) === "true");
6126
+ return {
6127
+ window: {
6128
+ lookbackMinutes: flags.lookbackMinutes ?? 60,
6129
+ from: flags.from,
6130
+ to: flags.to,
6131
+ limitPerQuery: flags.limit ?? DEFAULT_LIMIT
6132
+ },
6133
+ injection: {
6134
+ scanned: injection.items.length,
6135
+ suspected: suspected.length,
6136
+ byVerdict: countBy(injection.items, MCP_INJECTION_VERDICT),
6137
+ bySource: countBy(suspected, MCP_INJECTION_SOURCE),
6138
+ byTool: topEntries(countBy(suspected, MCP_TOOL_NAME), 10),
6139
+ sampleTraceIds: sampleTraceIds(suspected)
6140
+ },
6141
+ budgetBreaches: {
6142
+ total: budget.items.length,
6143
+ byTool: topEntries(countBy(budget.items, MCP_TOOL_NAME), 10),
6144
+ byService: countByService(budget.items),
6145
+ sampleTraceIds: sampleTraceIds(budget.items)
6146
+ },
6147
+ untrustedContent: {
6148
+ toolCalls: untrustedTrue.length,
6149
+ byTool: topEntries(countBy(untrustedTrue, MCP_TOOL_NAME), 10)
6150
+ }
6151
+ };
6152
+ });
6153
+ }
6076
6154
  function csvIntArg(value) {
6077
6155
  return value.split(",").map((part) => Number.parseInt(part.trim(), 10)).filter((n) => !Number.isNaN(n));
6078
6156
  }
@@ -6095,9 +6173,17 @@ function registerSecurityCommands(program) {
6095
6173
  severity: o.severity
6096
6174
  });
6097
6175
  });
6176
+ const mcpCmd = addTimeWindowFlags(new Command("mcp")).description("MCP protocol-boundary security: prompt-injection verdicts, output-budget breaches, untrusted-content tool calls").action(async function() {
6177
+ const o = this.optsWithGlobals();
6178
+ await runSecurityMcp({
6179
+ ...backendFlagsFromOpts(o),
6180
+ ...windowFlagsFromOpts(o)
6181
+ });
6182
+ });
6098
6183
  addBackendFlags(securityCmd);
6099
6184
  securityCmd.addCommand(summaryCmd);
6100
6185
  securityCmd.addCommand(eventsCmd);
6186
+ securityCmd.addCommand(mcpCmd);
6101
6187
  program.addCommand(securityCmd);
6102
6188
  }
6103
6189
  //#endregion