Skip to content
Draft
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
1 change: 1 addition & 0 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"allow": ["URL", "URLSearchParams"]
}
],
"typescript/no-unnecessary-type-parameters": "error",
"jsx-a11y/interactive-supports-focus": "off",
"unicorn/prefer-spread": "error"
},
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/lib/alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,9 +353,9 @@ export type AliasMapResult = {
* `project` slugs of a {@link ResolvedTarget}
* @returns Alias map and DB entries
*/
export function buildProjectAliasMap<
T extends { target: Pick<ResolvedTarget, "org" | "project"> },
>(results: T[]): AliasMapResult {
export function buildProjectAliasMap(
results: readonly { target: Pick<ResolvedTarget, "org" | "project"> }[],
): AliasMapResult {
const entries: Record<string, ProjectAliasEntry> = {};
const pairs = results.map((r) => ({
org: r.target.org,
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/lib/api/infrastructure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ export function throwApiError(
* @param context - Human-readable context for error messages
* @returns The data from the successful response
*/
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- callers name the app type for SDK data, which often differs from @sentry/api's generated types
export function unwrapResult<T>(
result: Pick<SdkResult, "data" | "error" | "response">,
context: string,
Expand Down
12 changes: 8 additions & 4 deletions packages/cli/src/lib/formatters/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ export function parseFieldsList(input: string): string[] {
/**
* Format data as pretty-printed JSON
*/
export function formatJson<T>(data: T): string {
export function formatJson(data: unknown): string {
return JSON.stringify(data, null, 2);
}

Expand All @@ -257,7 +257,11 @@ export function formatJson<T>(data: T): string {
* @param data - Data to serialize
* @param fields - Optional field paths to include (dot-notation supported)
*/
export function writeJson<T>(stream: Writer, data: T, fields?: string[]): void {
export function writeJson(
stream: Writer,
data: unknown,
fields?: string[],
): void {
const output =
fields && fields.length > 0 ? filterFields(data, fields) : data;
stream.write(`${formatJson(output)}\n`);
Expand Down Expand Up @@ -287,9 +291,9 @@ export function writeJson<T>(stream: Writer, data: T, fields?: string[]): void {
* // → { "data": [{ "id": "1", "title": "Bug" }, ...], "hasMore": true }
* ```
*/
export function writeJsonList<T>(
export function writeJsonList(
stream: Writer,
items: T[],
items: readonly unknown[],
options: {
hasMore: boolean;
nextCursor?: string | null;
Expand Down
7 changes: 4 additions & 3 deletions packages/cli/src/lib/positional-parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
*/

import { ArgumentParseError } from "@stricli/core";
import type { SentryContext } from "../context.js";
import {
type CompletionTarget,
withCompletionTarget,
Expand Down Expand Up @@ -143,14 +144,14 @@ export type BoundArguments<T, R> = BoundPositional<T, R, readonly string[]>;
* @param options.resolve - Resolves the parsed value; gets the command
* context and the raw arguments
*/
export function boundArguments<T, R, C>(options: {
export function boundArguments<T, R>(options: {
readonly placeholder: string;
readonly brief: string;
readonly kind?: CompletionTarget;
readonly parse: (raw: readonly string[]) => T;
readonly resolve: (
value: T,
context: C,
context: SentryContext,
raw: readonly string[],
) => Promise<R>;
}): {
Expand Down Expand Up @@ -187,7 +188,7 @@ export function boundArguments<T, R, C>(options: {
);
}
return new BoundPositional(raw, parsed, (value: T) =>
options.resolve(value, this as C, raw),
options.resolve(value, this as SentryContext, raw),
);
});
return positional;
Expand Down
20 changes: 10 additions & 10 deletions packages/cli/src/lib/sdk-invoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,28 +268,28 @@ function concatChunksToBytes(chunks: Array<string | Uint8Array>): Uint8Array {
* Parse captured output: prefer zero-copy object, then JSON, then raw string.
* @internal Exported for testing
*/
export function parseOutput<T>(
export function parseOutput(
capturedResult: unknown,
stdoutChunks: Array<string | Uint8Array>,
): T {
): unknown {
if (capturedResult !== undefined) {
return capturedResult as T;
return capturedResult;
}
// Binary output (e.g. `sentry api` attachment downloads) must be returned
// byte-faithfully. If any chunk is a Uint8Array, joining as strings would
// stringify it to comma-separated decimals — return raw bytes instead.
if (stdoutChunks.some((c) => c instanceof Uint8Array)) {
return concatChunksToBytes(stdoutChunks) as T;
return concatChunksToBytes(stdoutChunks);
}
const stdoutStr = stdoutChunks.join("");
if (!stdoutStr.trim()) {
return undefined as T;
return undefined;
}
// oxlint-disable-next-line sentry-cli/no-silent-catch -- grandfathered silent catch — see #1531; drain by adding log.debug()/log.warn() or re-throwing.
try {
return JSON.parse(stdoutStr) as T;
return JSON.parse(stdoutStr);
} catch {
return stdoutStr.trim() as T;
return stdoutStr.trim();
}
}

Expand Down Expand Up @@ -450,10 +450,10 @@ async function executeWithCapture<T>(
);
}

return parseOutput<T>(
return parseOutput(
captureCtx.getCapturedResult(),
captureCtx.stdoutChunks,
);
) as T;
});
});
}
Expand Down Expand Up @@ -526,7 +526,7 @@ function executeWithStream<T>(
// dropped when the channel closes. No streaming-capable command emits
// binary today, but this keeps the streaming path faithful to the
// capture path (see parseOutput) if one ever does.
const trailing = parseOutput<T>(undefined, captureCtx.stdoutChunks);
const trailing = parseOutput(undefined, captureCtx.stdoutChunks) as T;
if (trailing !== undefined) {
channel.push(trailing);
}
Expand Down
7 changes: 4 additions & 3 deletions packages/mcp-cloudflare/src/server/oauth/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -489,9 +489,10 @@ function recordTokenExchangeOutcome(
});
}

function buildSuccessfulTokenExchangeResult<
TProps extends Record<string, unknown>,
>(props: TProps, accessTokenTTL: number): TokenExchangeCallbackResult {
function buildSuccessfulTokenExchangeResult(
props: Record<string, unknown>,
accessTokenTTL: number,
): TokenExchangeCallbackResult {
return {
newProps: props,
accessTokenTTL,
Expand Down
7 changes: 2 additions & 5 deletions packages/mcp-core/src/internal/agents/callEmbeddedAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,7 @@ interface EmbeddedAgentResult<T> {
* - Errors are re-thrown for the calling agent to handle
* - Each agent can implement its own error handling strategy
*/
export async function callEmbeddedAgent<
TOutput,
TSchema extends z.ZodType<TOutput, unknown>,
>({
export async function callEmbeddedAgent<TOutput>({
system,
prompt,
tools,
Expand All @@ -121,7 +118,7 @@ export async function callEmbeddedAgent<
system: string;
prompt: string;
tools: Record<string, Tool>;
schema: TSchema;
schema: z.ZodType<TOutput>;
/** Whether this step produced a result that permits a final response. */
isReadyToFinalize?: (step: StepResult<Record<string, Tool>>) => boolean;
}): Promise<EmbeddedAgentResult<TOutput>> {
Expand Down
1 change: 1 addition & 0 deletions packages/mcp-core/src/test-utils/dataset-search-tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const params = {
* `SEARCH_REPLAYS_STUB` for search_replays) from `./search-events-stub`
* instead of "ok"; search-errors.test.ts shows how.
*/
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- accepts any tool's handler params type
export async function inspectDatasetSearchTool<P>(tool: {
inputSchema: object;
handler(params: P, context: ServerContext): Promise<unknown>;
Expand Down
1 change: 1 addition & 0 deletions packages/mcp-core/src/test-utils/structured-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export function getTextContent(result: unknown): string {
}

/** Returns structuredContent after checking the result has an object payload. */
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- lets tests name the expected payload type
export function getStructuredContent<T extends Record<string, unknown>>(
result: unknown,
): T {
Expand Down
5 changes: 1 addition & 4 deletions packages/mcp-core/src/tools/support/search-events/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,7 @@ export async function searchEventsAgent(
options.environmentNames ?? (await fetchEnvironmentNames(options));

// Use callEmbeddedAgent to translate the query with tool call capture
return await callEmbeddedAgent<
z.output<typeof searchEventsAgentOutputSchema>,
typeof searchEventsAgentOutputSchema
>({
return await callEmbeddedAgent({
system: buildSystemPromptWithEnvironments(systemPrompt, environmentNames),
prompt: options.query,
tools: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ function searchEventsOutputSchema<

/** Output schema for the dataset search tool that searches `dataset`. */
export function buildSearchEventsOutputSchema<
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- the inferred return type depends on TDataset
TDataset extends StructuredSearchDataset,
>(dataset: TDataset) {
return searchEventsOutputSchema(z.object(STRUCTURED_DATASETS[dataset].links));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,7 @@ export async function searchIssueEventsAgent(
});

// Use callEmbeddedAgent to translate the query with tool call capture
return await callEmbeddedAgent<
z.output<typeof searchIssueEventsAgentOutputSchema>,
typeof searchIssueEventsAgentOutputSchema
>({
return await callEmbeddedAgent({
system: systemPrompt,
prompt: options.query,
tools: {
Expand Down
5 changes: 1 addition & 4 deletions packages/mcp-core/src/tools/support/search-issues/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,7 @@ export async function searchIssuesAgent(
}> {
// Provider check happens in callEmbeddedAgent via getAgentProvider()
// Create tools pre-bound with the provided API service and organization
return await callEmbeddedAgent<
z.output<typeof searchIssuesAgentOutputSchema>,
typeof searchIssuesAgentOutputSchema
>({
return await callEmbeddedAgent({
system: systemPrompt,
prompt: options.query,
tools: {
Expand Down
4 changes: 2 additions & 2 deletions packages/mcp-core/src/transports/stdio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ export type StdioConnection = {
* Context is already captured in tool handler closures by `buildServer()`;
* it is only read here for telemetry attributes.
*/
export async function startStdio<Context extends StdioServerContext>(
export async function startStdio(
server: McpServer,
context: Context,
context: StdioServerContext,
): Promise<StdioConnection> {
const transport = new StdioServerTransport();
await startNewTrace(async () => {
Expand Down
Loading