Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
56 changes: 0 additions & 56 deletions packages/mcp-core/src/api-client/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1286,62 +1286,6 @@ describe("API query builders", () => {
urls.map((url) => new URL(url).searchParams.get("attributeType")),
).toEqual(["string", "boolean"]);
});

it("should validate exact trace item attributes", async () => {
const apiService = new SentryApiService({
host: "sentry.io",
accessToken: "test-token",
});
let requestUrl: string | undefined;
let requestOptions: RequestInit | undefined;

globalThis.fetch = vi
.fn()
.mockImplementation((url: string, options: RequestInit) => {
requestUrl = url;
requestOptions = options;
return Promise.resolve({
ok: true,
headers: {
get: (key: string) =>
key === "content-type" ? "application/json" : null,
},
json: () =>
Promise.resolve({
attributes: {
"tags[type]": { valid: true, type: "string" },
"tags[missing]": {
valid: false,
error: "Unknown attribute: tags[missing]",
},
},
}),
});
});

const result = await apiService.validateTraceItemAttributes({
organizationSlug: "test-org",
itemType: "spans",
attributes: ["tags[type]", "tags[missing]"],
project: "123",
statsPeriod: "7d",
});

expect(result).toEqual({
"tags[type]": { valid: true, type: "string" },
"tags[missing]": {
valid: false,
error: "Unknown attribute: tags[missing]",
},
});
expect(requestUrl).toContain(
"/api/0/organizations/test-org/trace-items/attributes/validate/?itemType=spans&project=123&statsPeriod=7d",
);
expect(requestOptions?.method).toBe("POST");
expect(JSON.parse(String(requestOptions?.body))).toEqual({
attributes: ["tags[type]", "tags[missing]"],
});
});
});

describe("Web URL builders", () => {
Expand Down
59 changes: 0 additions & 59 deletions packages/mcp-core/src/api-client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1960,65 +1960,6 @@ export class SentryApiService {
return attributeResponses.flat();
}

async validateTraceItemAttributes(
{
organizationSlug,
itemType = "spans",
attributes,
project,
statsPeriod,
start,
end,
}: {
organizationSlug: string;
itemType?: TraceItemType;
attributes: string[];
project?: string;
statsPeriod?: string;
start?: string;
end?: string;
},
opts?: RequestOptions,
): Promise<Record<string, TraceItemAttributeValidationResult>> {
const queryParams = new URLSearchParams();
queryParams.set("itemType", itemType);
if (project) {
queryParams.set("project", project);
}
this.applyTimeParams(queryParams, statsPeriod, start, end);

const body = await this.requestJSON(
`/organizations/${organizationSlug}/trace-items/attributes/validate/?${queryParams.toString()}`,
{
method: "POST",
body: JSON.stringify({ attributes }),
},
opts,
);

if (!isRecord(body) || !isRecord(body.attributes)) {
return {};
}

const results: Record<string, TraceItemAttributeValidationResult> = {};
for (const [attribute, value] of Object.entries(body.attributes)) {
if (!isRecord(value) || typeof value.valid !== "boolean") {
continue;
}
const validationResult: TraceItemAttributeValidationResult = {
valid: value.valid,
};
if (isTraceItemAttributeType(value.type)) {
validationResult.type = value.type;
}
if (typeof value.error === "string") {
validationResult.error = value.error;
}
results[attribute] = validationResult;
}
return results;
}

private async fetchTraceItemAttributesByType(
organizationSlug: string,
itemType: TraceItemType,
Expand Down
31 changes: 0 additions & 31 deletions packages/mcp-core/src/tools/support/search-events/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,32 +420,12 @@
if (normalizedDataset === "spans") {
return "spans";
}
return null;
}

function formatValidationResults(
validationResults: Record<string, TraceItemAttributeValidationResult>,
): string {
const entries = Object.entries(validationResults);
if (entries.length === 0) {
return "";
}

return `Validated Attributes:
${entries
.map(([attribute, result]) => {
if (result.valid) {
return `- ${attribute}: valid${result.type ? ` (${result.type})` : ""}`;
}
return `- ${attribute}: invalid${result.error ? ` (${result.error})` : ""}`;
})
.join("\n")}
`;
}

/**
* Create a tool for the agent to query available attributes by dataset
* The tool is pre-bound with the API service and organization configured for the appropriate region

Check warning on line 428 in packages/mcp-core/src/tools/support/search-events/utils.ts

View check run for this annotation

@sentry/warden / warden: mcp-audit

Tool `inputSchema` advertises `attributes` validation parameter that is never consumed after removing validation logic

The `attributes` parameter remains in the exported `inputSchema` with description "Optional exact attribute keys to validate" but its value is destructured and silently ignored in the execute function, so clients/models will supply it expecting validation that no longer occurs. The tool-level description also still claims it can "validate" attributes. Remove the parameter and update the description, or restore validation logic.
Comment on lines 423 to 428

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tool inputSchema advertises attributes validation parameter that is never consumed after removing validation logic

The attributes parameter remains in the exported inputSchema with description "Optional exact attribute keys to validate" but its value is destructured and silently ignored in the execute function, so clients/models will supply it expecting validation that no longer occurs. The tool-level description also still claims it can "validate" attributes. Remove the parameter and update the description, or restore validation logic.

Evidence
  • attributes is declared in the tool's Zod parameters schema (utils.ts:466-472) with description "Optional exact attribute keys to validate, such as ['tags[type]', 'span.duration']".
  • In the execute handler (utils.ts:474-480) attributes is destructured from params but is never passed to fetchCustomAttributes (which only receives attributeTypes, substringMatch, query) nor used anywhere in the function body.
  • traceItemType is computed via getTraceItemType(normalizedDataset) but never used, indicating leftover code from the now-removed validation path.
  • The tool-level description still says "Query, filter, and validate available attributes", so the exported wire metadata misrepresents the tool's actual behavior to the model.

Identified by Warden mcp-audit · WDB-XU9

*/
export function createDatasetAttributesTool(options: {
apiService: SentryApiService;
Expand Down Expand Up @@ -513,16 +493,6 @@
const normalizedDataset = normalizeEventsDataset(dataset);
const traceItemType = getTraceItemType(normalizedDataset);
const attributeTimeParams = { statsPeriod: "14d" };
const validationResults =
traceItemType && attributes?.length
? await apiService.validateTraceItemAttributes({
organizationSlug,
itemType: traceItemType,
attributes,
project: projectId,
...attributeTimeParams,
})
: {};
const { attributes: customAttributes, fieldTypes } =
await fetchCustomAttributes(
apiService,
Expand Down Expand Up @@ -560,7 +530,6 @@
recordAgentToolResultCount(fieldCount);

return `Dataset: ${dataset}
${formatValidationResults(validationResults)}

Available Fields (${fieldCount} total):
${Object.entries(allFields)
Expand Down
Loading