diff --git a/examples/context7-headlines.ts b/examples/context7-headlines.ts index 2f76e87..09b48a1 100644 --- a/examples/context7-headlines.ts +++ b/examples/context7-headlines.ts @@ -5,68 +5,62 @@ * and print only the markdown headlines. */ -import { createRuntime, createServerProxy, type CallResult } from "../src/index.js"; +import { createRuntime, createServerProxy, type CallResult } from '../src/index.js'; async function main(): Promise { - const apiKey = process.env.CONTEXT7_API_KEY; - const context7Definition = { - name: "context7", - description: "Context7 documentation MCP", - command: { - kind: "http" as const, - url: new URL("https://mcp.context7.com/mcp"), - headers: apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined, - }, - }; - // Inline definitions can also live in config/mcporter.json if you prefer shared config. + const apiKey = process.env.CONTEXT7_API_KEY; + const context7Definition = { + name: 'context7', + description: 'Context7 documentation MCP', + command: { + kind: 'http' as const, + url: new URL('https://mcp.context7.com/mcp'), + headers: apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined, + }, + }; + // Inline definitions can also live in config/mcporter.json if you prefer shared config. - const mcpRuntime = await createRuntime({ servers: [context7Definition] }); - try { - const context7 = createServerProxy(mcpRuntime, "context7") as Record; - const resolveLibraryId = context7.resolveLibraryId as ( - args: unknown, - ) => Promise; - const getLibraryDocs = context7.getLibraryDocs as ( - args: unknown, - ) => Promise; + const mcpRuntime = await createRuntime({ servers: [context7Definition] }); + try { + const context7 = createServerProxy(mcpRuntime, 'context7') as Record; + const resolveLibraryId = context7.resolveLibraryId as (args: unknown) => Promise; + const getLibraryDocs = context7.getLibraryDocs as (args: unknown) => Promise; - const resolved = await resolveLibraryId("react"); - const contextId = extractContext7LibraryId(resolved); - if (!contextId) { - throw new Error("Unable to resolve React documentation ID from Context7."); - } + const resolved = await resolveLibraryId('react'); + const contextId = extractContext7LibraryId(resolved); + if (!contextId) { + throw new Error('Unable to resolve React documentation ID from Context7.'); + } - const docs = await getLibraryDocs(contextId); + const docs = await getLibraryDocs(contextId); - const markdown = docs.markdown() ?? docs.text() ?? ""; - const headlines = markdown - .split("\n") - .filter((line) => /^#+\s/.test(line)) - .join("\n"); + const markdown = docs.markdown() ?? docs.text() ?? ''; + const headlines = markdown + .split('\n') + .filter((line) => /^#+\s/.test(line)) + .join('\n'); - console.log("# Headlines for React"); - console.log(headlines || "(no headlines found)"); - } finally { - await mcpRuntime.close(); - } + console.log('# Headlines for React'); + console.log(headlines || '(no headlines found)'); + } finally { + await mcpRuntime.close(); + } } main().catch((error) => { - console.error(error); - process.exit(1); + console.error(error); + process.exit(1); }); function extractContext7LibraryId(result: CallResult): string | null { - const json = result.json< - { candidates?: Array<{ context7CompatibleLibraryID?: string }> } | undefined - >(); - if (json && json.candidates) { - for (const candidate of json.candidates) { - if (candidate?.context7CompatibleLibraryID) { - return candidate.context7CompatibleLibraryID; - } - } - } - const textMatch = result.text()?.match(/Context7-compatible library ID:\s*([^\s]+)/); - return textMatch?.[1] ?? null; + const json = result.json<{ candidates?: Array<{ context7CompatibleLibraryID?: string }> } | undefined>(); + if (json && json.candidates) { + for (const candidate of json.candidates) { + if (candidate?.context7CompatibleLibraryID) { + return candidate.context7CompatibleLibraryID; + } + } + } + const textMatch = result.text()?.match(/Context7-compatible library ID:\s*([^\s]+)/); + return textMatch?.[1] ?? null; } diff --git a/scripts/docs-list.ts b/scripts/docs-list.ts index 1cdc198..a3a7083 100755 --- a/scripts/docs-list.ts +++ b/scripts/docs-list.ts @@ -28,7 +28,7 @@ function walkMarkdownFiles(dir: string, base: string = dir): string[] { files.push(relative(base, fullPath)); } } - return files.sort((a, b) => a.localeCompare(b)); + return files.toSorted((a, b) => a.localeCompare(b)); } function extractMetadata(fullPath: string): { diff --git a/src/cli/auth-command.ts b/src/cli/auth-command.ts index 357a75f..5f0a9fa 100644 --- a/src/cli/auth-command.ts +++ b/src/cli/auth-command.ts @@ -80,7 +80,7 @@ export async function handleAuth(runtime: Runtime, args: string[]): Promise>; +type Runtime = Awaited>; interface ResolvedCallTarget { server: string; @@ -199,7 +199,7 @@ export function printCallHelp(): void { } async function maybeDescribeServer( - runtime: Awaited>, + runtime: Awaited>, server: string, tool: string, outputFormat: OutputFormat @@ -266,7 +266,7 @@ function resolveCallTarget( } async function enforceSchemaStringTypes( - runtime: Awaited>, + runtime: Awaited>, server: string, tool: string, args: Record, @@ -325,7 +325,7 @@ function schemaAllowsString(descriptor: unknown): boolean { } async function hydratePositionalArguments( - runtime: Awaited>, + runtime: Awaited>, server: string, tool: string, namedArgs: Record, @@ -375,7 +375,7 @@ async function hydratePositionalArguments( type ToolResolution = IdentifierResolution; async function inferSingleToolName( - runtime: Awaited>, + runtime: Awaited>, server: string ): Promise { const tools = await loadToolMetadata(runtime, server, { includeSchema: false }); @@ -391,7 +391,7 @@ async function inferSingleToolName( } async function invokeWithAutoCorrection( - runtime: Awaited>, + runtime: Awaited>, server: string, tool: string, args: Record, @@ -402,7 +402,7 @@ async function invokeWithAutoCorrection( } async function attemptCall( - runtime: Awaited>, + runtime: Awaited>, server: string, tool: string, args: Record, @@ -417,7 +417,8 @@ async function attemptCall( const timeoutDisplay = `${timeoutMs}ms`; await runtime.close(server).catch(() => {}); throw new Error( - `Call to ${server}.${tool} timed out after ${timeoutDisplay}. Override MCPORTER_CALL_TIMEOUT or pass --timeout to adjust.` + `Call to ${server}.${tool} timed out after ${timeoutDisplay}. Override MCPORTER_CALL_TIMEOUT or pass --timeout to adjust.`, + { cause: error } ); } @@ -451,7 +452,7 @@ async function attemptCall( } async function maybeResolveToolName( - runtime: Awaited>, + runtime: Awaited>, server: string, attemptedTool: string, error: unknown diff --git a/src/cli/config/add.ts b/src/cli/config/add.ts index 64c7c39..c9e858c 100644 --- a/src/cli/config/add.ts +++ b/src/cli/config/add.ts @@ -202,7 +202,7 @@ function parseTransport(value: string | undefined): 'http' | 'sse' | 'stdio' { } function parseKeyValue(input: string | undefined, target: Record, flagName: string): void { - if (!input || !input.includes('=')) { + if (!input?.includes('=')) { throw new CliUsageError(`${flagName} requires KEY=value.`); } const [key, ...rest] = input.split('='); diff --git a/src/cli/config/shared.ts b/src/cli/config/shared.ts index cb438bf..6c6c75d 100644 --- a/src/cli/config/shared.ts +++ b/src/cli/config/shared.ts @@ -26,8 +26,8 @@ export function cloneConfig(config: RawConfig): RawConfig { export async function loadOrCreateConfig(loadOptions: LoadConfigOptions): Promise<{ config: RawConfig; path: string }> { try { - const { config, path } = await loadRawConfig(loadOptions); - return { config, path }; + const { config, path: configPath } = await loadRawConfig(loadOptions); + return { config, path: configPath }; } catch (error) { if (isErrno(error, 'ENOENT')) { const rootDir = loadOptions.rootDir ?? process.cwd(); diff --git a/src/cli/emit-ts-command.ts b/src/cli/emit-ts-command.ts index fe40034..7dafb7b 100644 --- a/src/cli/emit-ts-command.ts +++ b/src/cli/emit-ts-command.ts @@ -182,7 +182,7 @@ function getServerDefinition(runtime: Runtime, selector: string): ServerDefiniti return runtime.getDefinition(resolved); } if (error instanceof Error) { - throw new Error(error.message); + throw new Error(error.message, { cause: error }); } throw error; } diff --git a/src/cli/ephemeral-flags.ts b/src/cli/ephemeral-flags.ts index 6b97707..e32e131 100644 --- a/src/cli/ephemeral-flags.ts +++ b/src/cli/ephemeral-flags.ts @@ -66,7 +66,7 @@ export function extractEphemeralServerFlags( if (token === '--env') { const value = args[index + 1]; - if (!value || !value.includes('=')) { + if (!value?.includes('=')) { throw new Error("Flag '--env' requires KEY=value."); } const [key, ...rest] = value.split('='); diff --git a/src/cli/generate/artifacts.ts b/src/cli/generate/artifacts.ts index 1eeaa47..b79a00c 100644 --- a/src/cli/generate/artifacts.ts +++ b/src/cli/generate/artifacts.ts @@ -46,7 +46,7 @@ async function bundleWithRolldown({ runtimeKind: 'node' | 'bun'; minify: boolean; }): Promise { - let rolldownImpl: typeof import('rolldown')['rolldown']; + let rolldownImpl: (typeof import('rolldown'))['rolldown']; try { ({ rolldown: rolldownImpl } = await import('rolldown')); } catch (error) { @@ -56,7 +56,7 @@ async function bundleWithRolldown({ error.message = `${message}\n\n${error.message}`; throw error; } - throw new Error(message); + throw new Error(message, { cause: error }); } const absTarget = path.resolve(targetPath); await fs.mkdir(path.dirname(absTarget), { recursive: true }); diff --git a/src/cli/generate/name-utils.ts b/src/cli/generate/name-utils.ts index 4a9e017..b85a73b 100644 --- a/src/cli/generate/name-utils.ts +++ b/src/cli/generate/name-utils.ts @@ -167,8 +167,7 @@ function deriveNameFromUrl(url: URL): string | undefined { return last; } } - const segments = url.pathname.split('/').filter(Boolean); - const firstSegment = segments[0]; + const firstSegment = url.pathname.split('/').find(Boolean); if (firstSegment) { return firstSegment.replace(/[^a-zA-Z0-9-_]/g, '-'); } diff --git a/src/cli/generate/template.ts b/src/cli/generate/template.ts index 0b0f950..5998b54 100644 --- a/src/cli/generate/template.ts +++ b/src/cli/generate/template.ts @@ -79,11 +79,12 @@ export function renderTemplate({ flagExtras: [{ text: '--raw ' }], }), })); - const renderedTools = toolDocs.map((entry) => ({ - ...renderToolCommand(entry.tool, timeoutMs, serverName, entry.doc), - doc: entry.doc, - tool: entry.tool, - })); + const renderedTools = toolDocs.map((entry) => + Object.assign(renderToolCommand(entry.tool, timeoutMs, serverName, entry.doc), { + doc: entry.doc, + tool: entry.tool, + }) + ); const toolHelp = renderedTools.map((entry) => ({ name: entry.commandName, description: entry.tool.tool.description ?? '', diff --git a/src/cli/generate/tools.ts b/src/cli/generate/tools.ts index 0dea777..57dd590 100644 --- a/src/cli/generate/tools.ts +++ b/src/cli/generate/tools.ts @@ -20,6 +20,26 @@ export interface GeneratedOption { formatHint?: string; } +function resolveSchemaType(value: unknown): GeneratedOption['type'] | undefined { + if (value === 'integer') { + return 'number'; + } + if (value === 'string' || value === 'number' || value === 'boolean' || value === 'array' || value === 'object') { + return value; + } + return undefined; +} + +function resolveArrayItemType(value: unknown): GeneratedOption['arrayItemType'] | undefined { + if (value === 'integer') { + return 'number'; + } + if (value === 'string' || value === 'number' || value === 'boolean') { + return value; + } + return undefined; +} + export function buildToolMetadata(tool: ServerToolInfo): ToolMetadata { const methodName = toProxyMethodName(tool.name); const properties = extractOptions(tool); @@ -230,25 +250,16 @@ export function inferType(descriptor: unknown): GeneratedOption['type'] { return 'unknown'; } const type = (descriptor as Record).type; - const resolveType = (value: unknown): GeneratedOption['type'] | undefined => { - if (value === 'integer') { - return 'number'; - } - if (value === 'string' || value === 'number' || value === 'boolean' || value === 'array' || value === 'object') { - return value; - } - return undefined; - }; if (Array.isArray(type)) { for (const entry of type) { - const resolved = resolveType(entry); + const resolved = resolveSchemaType(entry); if (resolved) { return resolved; } } return 'unknown'; } - const resolved = resolveType(type); + const resolved = resolveSchemaType(type); if (resolved) { return resolved; } @@ -265,25 +276,16 @@ export function inferArrayItemType(descriptor: unknown): GeneratedOption['arrayI } const items = record.items as Record; const itemType = items.type; - const resolveItemType = (value: unknown): GeneratedOption['arrayItemType'] | undefined => { - if (value === 'integer') { - return 'number'; - } - if (value === 'string' || value === 'number' || value === 'boolean') { - return value; - } - return undefined; - }; if (Array.isArray(itemType)) { for (const entry of itemType) { - const resolved = resolveItemType(entry); + const resolved = resolveArrayItemType(entry); if (resolved) { return resolved; } } return 'unknown'; } - const resolved = resolveItemType(itemType); + const resolved = resolveArrayItemType(itemType); if (resolved) { return resolved; } diff --git a/src/cli/list-command.ts b/src/cli/list-command.ts index 952d482..3cf3831 100644 --- a/src/cli/list-command.ts +++ b/src/cli/list-command.ts @@ -86,7 +86,7 @@ export function extractListFlags(args: string[]): { type ListOutputFormat = 'text' | 'json'; export async function handleList( - runtime: Awaited>, + runtime: Awaited>, args: string[] ): Promise { const flags = extractListFlags(args); @@ -341,7 +341,6 @@ export async function handleList( const durationMs = Date.now() - startedAt; printSingleServerHeader(definition, undefined, durationMs, transportSummary, sourcePath); const message = error instanceof Error ? error.message : 'Failed to load tool list.'; - const timeoutMs = flags.timeoutMs ?? LIST_TIMEOUT_MS; const authCommand = buildAuthCommandHint(definition); const advice = classifyListError(error, definition.name, timeoutMs, { authCommand }); console.warn(` Tools: `); @@ -390,7 +389,7 @@ export function printListHelp(): void { } function resolveServerDefinition( - runtime: Awaited>, + runtime: Awaited>, name: string ): { definition: ServerDefinition; name: string } | undefined { try { @@ -423,7 +422,7 @@ function resolveServerDefinition( } function suggestServerName( - runtime: Awaited>, + runtime: Awaited>, attempted: string ) { const definitions = runtime.getDefinitions(); diff --git a/src/cli/list-output.ts b/src/cli/list-output.ts index 0153a3e..21b38d7 100644 --- a/src/cli/list-output.ts +++ b/src/cli/list-output.ts @@ -33,7 +33,7 @@ export interface ListJsonServerEntry { } export function printSingleServerHeader( - definition: ReturnType>['getDefinition']>, + definition: ReturnType>['getDefinition']>, toolCount: number | undefined, durationMs: number | undefined, transportSummary: string, @@ -70,7 +70,7 @@ export function printSingleServerHeader( } export function printToolDetail( - definition: ReturnType>['getDefinition']>, + definition: ReturnType>['getDefinition']>, metadata: ToolMetadata, includeSchema: boolean, requiredOnly: boolean @@ -107,7 +107,7 @@ export function printToolDetail( } function buildExampleOptions( - definition: ReturnType>['getDefinition']> + definition: ReturnType>['getDefinition']> ): { selector?: string; wrapExpression?: boolean } | undefined { if (definition.source?.kind !== 'local' || definition.source.path !== '') { return undefined; @@ -150,7 +150,7 @@ export function buildJsonListEntry( description: result.server.description, transport: formatTransportSummary( result.server as ReturnType< - Awaited>['getDefinition'] + Awaited>['getDefinition'] > ), source: result.server.source, @@ -164,7 +164,7 @@ export function buildJsonListEntry( }; } const authCommand = buildAuthCommandHint( - result.server as ReturnType>['getDefinition']> + result.server as ReturnType>['getDefinition']> ); const advice = classifyListError(result.error, result.server.name, timeoutSeconds, { authCommand }); return { @@ -173,7 +173,9 @@ export function buildJsonListEntry( durationMs: result.durationMs, description: result.server.description, transport: formatTransportSummary( - result.server as ReturnType>['getDefinition']> + result.server as ReturnType< + Awaited>['getDefinition'] + > ), source: result.server.source, sources: options.includeSources ? result.server.sources : undefined, @@ -193,7 +195,7 @@ export function createUnknownResult(server: ServerDefinition): ListSummaryResult } export function buildAuthCommandHint( - definition: ReturnType>['getDefinition']> + definition: ReturnType>['getDefinition']> ): string { if (definition.source?.kind === 'local' && definition.source.path === '') { if (definition.command.kind === 'http') { diff --git a/src/daemon/client.ts b/src/daemon/client.ts index 3cf471e..e99e760 100644 --- a/src/daemon/client.ts +++ b/src/daemon/client.ts @@ -346,5 +346,5 @@ function normalizeLayers( ): Array<{ path: string; mtimeMs: number | null }> { return layers .map((entry) => ({ path: path.resolve(entry.path), mtimeMs: entry.mtimeMs ?? null })) - .sort((a, b) => a.path.localeCompare(b.path)); + .toSorted((a, b) => a.path.localeCompare(b.path)); } diff --git a/src/env.ts b/src/env.ts index faecffb..3021f9e 100644 --- a/src/env.ts +++ b/src/env.ts @@ -68,7 +68,7 @@ export function resolveEnvPlaceholders(value: string): string { }); if (missing.size > 0) { - const names = [...missing].sort().join(', '); + const names = [...missing].toSorted().join(', '); throw new Error(`Environment variable(s) ${names} must be set for MCP header substitution.`); } diff --git a/src/index.ts b/src/index.ts index 622361b..455c02c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,13 +2,7 @@ export type { CommandSpec, ServerDefinition } from './config.js'; export { loadServerDefinitions } from './config.js'; export type { CallResult, ConnectionIssue, ImageContent } from './result-utils.js'; export { createCallResult, describeConnectionIssue, wrapCallResult } from './result-utils.js'; -export type { - CallOptions, - ListToolsOptions, - Runtime, - RuntimeLogger, - ServerToolInfo, -} from './runtime.js'; +export type { CallOptions, ListToolsOptions, Runtime, RuntimeLogger, ServerToolInfo } from './runtime.js'; export { callOnce, createRuntime } from './runtime.js'; export type { ServerProxyOptions } from './server-proxy.js'; export { createServerProxy } from './server-proxy.js'; diff --git a/src/runtime-header-utils.ts b/src/runtime-header-utils.ts index 3ad0f41..969daa2 100644 --- a/src/runtime-header-utils.ts +++ b/src/runtime-header-utils.ts @@ -15,7 +15,7 @@ export function materializeHeaders( resolved[key] = resolveEnvPlaceholders(value); } catch (error) { const message = error instanceof Error ? error.message : String(error); - throw new Error(`Failed to resolve header '${key}' for server '${serverName}': ${message}`); + throw new Error(`Failed to resolve header '${key}' for server '${serverName}': ${message}`, { cause: error }); } } diff --git a/src/runtime.ts b/src/runtime.ts index d7a49df..8d0e415 100644 --- a/src/runtime.ts +++ b/src/runtime.ts @@ -122,7 +122,7 @@ class McpRuntime implements Runtime { // listServers returns configured names sorted alphabetically for stable CLI output. listServers(): string[] { - return [...this.definitions.keys()].sort((a, b) => a.localeCompare(b)); + return [...this.definitions.keys()].toSorted((a, b) => a.localeCompare(b)); } // getDefinitions exposes raw server metadata to consumers such as the CLI. diff --git a/src/sdk-patches.ts b/src/sdk-patches.ts index 45204ef..2a3eaad 100644 --- a/src/sdk-patches.ts +++ b/src/sdk-patches.ts @@ -73,6 +73,8 @@ if (STDIO_TRACE_ENABLED) { console.log('[mcporter] STDIO trace logging enabled (set MCPORTER_STDIO_TRACE=0 to disable).'); } +function ignoreEmitterError(): void {} + function destroyStream(stream: unknown): void { if (!stream || typeof stream !== 'object') { return; @@ -85,9 +87,8 @@ function destroyStream(stream: unknown): void { end?: () => void; unref?: () => void; }; - const swallowError = () => {}; try { - emitter.on?.('error', swallowError); + emitter.on?.('error', ignoreEmitterError); } catch { // ignore } @@ -107,12 +108,12 @@ function destroyStream(stream: unknown): void { // ignore } try { - emitter.off?.('error', swallowError); + emitter.off?.('error', ignoreEmitterError); } catch { // ignore } try { - emitter.removeListener?.('error', swallowError); + emitter.removeListener?.('error', ignoreEmitterError); } catch { // ignore } @@ -130,9 +131,8 @@ function waitForChildClose(child: MaybeChildProcess | undefined, timeoutMs: numb } return new Promise((resolve) => { let settled = false; - const swallowProcessError = () => {}; try { - child.on?.('error', swallowProcessError); + child.on?.('error', ignoreEmitterError); } catch { // ignore } @@ -149,7 +149,7 @@ function waitForChildClose(child: MaybeChildProcess | undefined, timeoutMs: numb child.removeListener('close', finish); child.removeListener('error', finish); try { - child.removeListener?.('error', swallowProcessError); + child.removeListener?.('error', ignoreEmitterError); } catch { // ignore } @@ -393,9 +393,8 @@ function patchStdioStart(): void { meta.stderrChunks.push(chunk.toString('utf8')); } }; - const swallowError = () => {}; (targetStream as NodeJS.EventEmitter).on('data', handleChunk); - (targetStream as NodeJS.EventEmitter).on('error', swallowError); + (targetStream as NodeJS.EventEmitter).on('error', ignoreEmitterError); meta.listeners.push({ stream: targetStream as NodeJS.EventEmitter & { removeListener?: (event: string, listener: (...args: unknown[]) => void) => void; @@ -408,7 +407,7 @@ function patchStdioStart(): void { removeListener?: (event: string, listener: (...args: unknown[]) => void) => void; }, event: 'error', - handler: swallowError, + handler: ignoreEmitterError, }); } @@ -426,9 +425,8 @@ function patchStdioStart(): void { meta.stdoutChunks.push(chunk.toString('utf8')); } }; - const swallowStdoutError = () => {}; stdoutStream.on('data', handleStdout); - stdoutStream.on('error', swallowStdoutError); + stdoutStream.on('error', ignoreEmitterError); meta.listeners.push({ stream: stdoutStream, event: 'data', @@ -437,7 +435,7 @@ function patchStdioStart(): void { meta.listeners.push({ stream: stdoutStream, event: 'error', - handler: swallowStdoutError, + handler: ignoreEmitterError, }); } diff --git a/src/server-proxy.ts b/src/server-proxy.ts index 119a287..dc8d369 100644 --- a/src/server-proxy.ts +++ b/src/server-proxy.ts @@ -281,11 +281,11 @@ export function createServerProxy( } const base: ServerProxy = { - call: async (toolName: string, options?: ToolCallOptions) => { - const result = await runtime.callTool(serverName, toolName, options ?? {}); + call: async (toolName: string, callOptions?: ToolCallOptions) => { + const result = await runtime.callTool(serverName, toolName, callOptions ?? {}); return createCallResult(result); }, - listTools: (options) => runtime.listTools(serverName, options), + listTools: (listOptions) => runtime.listTools(serverName, listOptions), }; return new Proxy(base as ServerProxy & Record, { diff --git a/tests/cli-auth-retry.test.ts b/tests/cli-auth-retry.test.ts index 24f1a3a..b4d5826 100644 --- a/tests/cli-auth-retry.test.ts +++ b/tests/cli-auth-retry.test.ts @@ -20,7 +20,7 @@ describe('handleAuth retry logic', () => { registerDefinition: vi.fn(), getDefinition: vi.fn().mockReturnValue(baseDefinition), listTools, - } as unknown as Awaited>; + } as unknown as Awaited>; await expect(handleAuth(runtime, ['adhoc-server'])).resolves.toBeUndefined(); expect(listTools).toHaveBeenCalledTimes(2); @@ -33,7 +33,7 @@ describe('handleAuth retry logic', () => { registerDefinition: vi.fn(), getDefinition: vi.fn().mockReturnValue(baseDefinition), listTools, - } as unknown as Awaited>; + } as unknown as Awaited>; await expect(handleAuth(runtime, ['adhoc-server'])).rejects.toThrow(/Failed to authorize/); expect(listTools).toHaveBeenCalledTimes(2); diff --git a/tests/cli-auth.test.ts b/tests/cli-auth.test.ts index b4d4ce0..b40d1b9 100644 --- a/tests/cli-auth.test.ts +++ b/tests/cli-auth.test.ts @@ -22,7 +22,7 @@ const createRuntimeDouble = () => { getDefinition, getDefinitions: () => Array.from(definitions.values()), listTools, - } as unknown as Awaited>; + } as unknown as Awaited>; return { runtime, listTools }; }; @@ -59,7 +59,7 @@ describe('mcporter auth ad-hoc support', () => { registerDefinition, listTools, getDefinition: () => definition, - } as unknown as Awaited>; + } as unknown as Awaited>; await handleAuth(runtime, ['https://mcp.vercel.com']); @@ -78,7 +78,7 @@ describe('mcporter auth ad-hoc support', () => { registerDefinition: vi.fn(), listTools: vi.fn().mockRejectedValue(new Error('fetch failed: connect ECONNREFUSED 127.0.0.1:9000')), getDefinition: () => definition, - } as unknown as Awaited>; + } as unknown as Awaited>; const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); diff --git a/tests/cli-call-errors.test.ts b/tests/cli-call-errors.test.ts index 982d8b9..3baaf51 100644 --- a/tests/cli-call-errors.test.ts +++ b/tests/cli-call-errors.test.ts @@ -10,7 +10,7 @@ describe('CLI call error reporting', () => { const runtime = { callTool, close: vi.fn().mockResolvedValue(undefined), - } as unknown as Awaited>; + } as unknown as Awaited>; const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); @@ -31,7 +31,7 @@ describe('CLI call error reporting', () => { const runtime = { callTool, close: vi.fn().mockResolvedValue(undefined), - } as unknown as Awaited>; + } as unknown as Awaited>; const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); diff --git a/tests/cli-call-execution.test.ts b/tests/cli-call-execution.test.ts index 3a5c0e2..6d4ca8c 100644 --- a/tests/cli-call-execution.test.ts +++ b/tests/cli-call-execution.test.ts @@ -196,7 +196,7 @@ describe('CLI call execution behavior', () => { callTool, listTools, close: vi.fn().mockResolvedValue(undefined), - } as unknown as Awaited>; + } as unknown as Awaited>; const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); @@ -220,7 +220,7 @@ describe('CLI call execution behavior', () => { callTool, listTools, close: vi.fn().mockResolvedValue(undefined), - } as unknown as Awaited>; + } as unknown as Awaited>; const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); @@ -311,7 +311,7 @@ function createRuntimeStub( >, options: { definitions?: ServerDefinition[] } = {} ): { - runtime: Awaited>; + runtime: Awaited>; callTool: ReturnType; listTools: ReturnType; } { @@ -343,6 +343,6 @@ function createRuntimeStub( listTools, callTool, close, - } as unknown as Awaited>; + } as unknown as Awaited>; return { runtime, callTool, listTools }; } diff --git a/tests/cli-generate-cli.integration.test.ts b/tests/cli-generate-cli.integration.test.ts index 8c6193b..8e5869d 100644 --- a/tests/cli-generate-cli.integration.test.ts +++ b/tests/cli-generate-cli.integration.test.ts @@ -659,12 +659,12 @@ await new Promise((resolve) => { expect(stats.isFile()).toBe(true); const { stdout } = await new Promise<{ stdout: string; stderr: string }>((resolve, reject) => { - execFile(binaryPath, [], { env: process.env }, (error, stdout, stderr) => { + execFile(binaryPath, [], { env: process.env }, (error, childStdout, childStderr) => { if (error) { reject(error); return; } - resolve({ stdout, stderr }); + resolve({ stdout: childStdout, stderr: childStderr }); }); }); expect(stdout).toContain('echo - Return the provided text'); diff --git a/tests/cli-list-classification.test.ts b/tests/cli-list-classification.test.ts index 16bfb81..d6d5d70 100644 --- a/tests/cli-list-classification.test.ts +++ b/tests/cli-list-classification.test.ts @@ -52,7 +52,7 @@ describe('CLI list classification and routing', () => { return Promise.resolve([]); } }, - } as unknown as Awaited>; + } as unknown as Awaited>; const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); @@ -99,7 +99,7 @@ describe('CLI list classification and routing', () => { }), getDefinitions: () => Array.from(definitions.values()), listTools: vi.fn().mockRejectedValue(new Error('SSE error: Non-200 status code (401)')), - } as unknown as Awaited>; + } as unknown as Awaited>; const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); @@ -130,7 +130,7 @@ describe('CLI list classification and routing', () => { registerDefinition, getDefinition: () => definition, listTools, - } as unknown as Awaited>; + } as unknown as Awaited>; await handleList(runtime, ['https://mcp.vercel.com']); @@ -153,7 +153,7 @@ describe('CLI list classification and routing', () => { registerDefinition, getDefinition: () => definition, listTools, - } as unknown as Awaited>; + } as unknown as Awaited>; await handleList(runtime, ['https://www.shadcn.io/api/mcp.getComponents']); @@ -175,7 +175,7 @@ describe('CLI list classification and routing', () => { registerDefinition: vi.fn(), getDefinition: () => definition, listTools, - } as unknown as Awaited>; + } as unknown as Awaited>; await handleList(runtime, ['shadcn.io/api/mcp.getComponents']); @@ -195,7 +195,7 @@ describe('CLI list classification and routing', () => { const runtime = { getDefinitions: () => [definition], listTools, - } as unknown as Awaited>; + } as unknown as Awaited>; await handleList(runtime, []); @@ -223,7 +223,7 @@ describe('CLI list classification and routing', () => { }, listTools, registerDefinition, - } as unknown as Awaited>; + } as unknown as Awaited>; const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); @@ -250,7 +250,7 @@ describe('CLI list classification and routing', () => { getDefinition, getDefinitions: () => [definition], listTools, - } as unknown as Awaited>; + } as unknown as Awaited>; const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); @@ -274,7 +274,7 @@ describe('CLI list classification and routing', () => { }, getDefinitions: () => [definition], listTools, - } as unknown as Awaited>; + } as unknown as Awaited>; const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); diff --git a/tests/cli-list-flags.test.ts b/tests/cli-list-flags.test.ts index c755eb8..e9638d2 100644 --- a/tests/cli-list-flags.test.ts +++ b/tests/cli-list-flags.test.ts @@ -66,7 +66,7 @@ describe('CLI list flag parsing', () => { new Promise((resolve) => { setTimeout(() => resolve([{ name: 'ok' }]), 50); }), - } as unknown as Awaited>; + } as unknown as Awaited>; const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); diff --git a/tests/cli-list-formatting.test.ts b/tests/cli-list-formatting.test.ts index e3c6457..737feae 100644 --- a/tests/cli-list-formatting.test.ts +++ b/tests/cli-list-formatting.test.ts @@ -41,7 +41,7 @@ describe('CLI list formatting', () => { command: { kind: 'http', url: new URL('https://example.com/mcp') }, }), listTools: listToolsSpy, - } as unknown as Awaited>; + } as unknown as Awaited>; const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); @@ -94,7 +94,7 @@ describe('CLI list formatting', () => { } return Promise.reject(new Error('HTTP error 500: upstream unavailable')); }, - } as unknown as Awaited>; + } as unknown as Awaited>; const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); await handleList(runtime, ['--json']); const payload = JSON.parse(logSpy.mock.calls.at(-1)?.[0] ?? '{}'); @@ -133,7 +133,7 @@ describe('CLI list formatting', () => { getDefinitions: () => [definition], getDefinition: () => definition, registerDefinition: vi.fn(), - } as unknown as Awaited>; + } as unknown as Awaited>; const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); @@ -156,7 +156,7 @@ describe('CLI list formatting', () => { const runtime = { getDefinition: () => linearDefinition, listTools: listToolsSpy, - } as unknown as Awaited>; + } as unknown as Awaited>; const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); @@ -183,7 +183,7 @@ describe('CLI list formatting', () => { const runtime = { getDefinition: () => linearDefinition, listTools: listToolsSpy, - } as unknown as Awaited>; + } as unknown as Awaited>; const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); @@ -224,7 +224,7 @@ describe('CLI list formatting', () => { const runtime = { getDefinition: () => linearDefinition, listTools: listToolsSpy, - } as unknown as Awaited>; + } as unknown as Awaited>; const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); @@ -247,7 +247,7 @@ describe('CLI list formatting', () => { const runtime = { getDefinition: () => linearDefinition, listTools: listToolsSpy, - } as unknown as Awaited>; + } as unknown as Awaited>; const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); @@ -306,7 +306,7 @@ describe('CLI list formatting', () => { const runtime = { getDefinition: () => linearDefinition, listTools: listToolsSpy, - } as unknown as Awaited>; + } as unknown as Awaited>; const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); const nowSpy = vi.spyOn(Date, 'now').mockReturnValue(1_700_000_000_000); diff --git a/tests/cli-regenerate.test.ts b/tests/cli-regenerate.test.ts index 2ae4282..b533430 100644 --- a/tests/cli-regenerate.test.ts +++ b/tests/cli-regenerate.test.ts @@ -197,7 +197,7 @@ async function writeMetadataFixture(kind: ArtifactKind): Promise { kind: 'http' as const, url: 'https://mcp.vercel.com', headers: { - // biome-ignore lint/suspicious/noTemplateCurlyInString: metadata preserves env-placeholder format + // Metadata preserves env-placeholder format. Authorization: 'Bearer ${VERCEL_TOKEN}', }, }, diff --git a/tests/config-imports.test.ts b/tests/config-imports.test.ts index af52566..cbcb417 100644 --- a/tests/config-imports.test.ts +++ b/tests/config-imports.test.ts @@ -79,7 +79,7 @@ describe('config imports', () => { }); const homeDir = ensureFakeHomeDir(); - const names = servers.map((server) => server.name).sort(); + const names = servers.map((server) => server.name).toSorted(); expect(names).toEqual([ 'claude-home', 'claude-local', diff --git a/tests/config-layered.test.ts b/tests/config-layered.test.ts index e75817f..10295f1 100644 --- a/tests/config-layered.test.ts +++ b/tests/config-layered.test.ts @@ -95,7 +95,7 @@ describe('loadServerDefinitions with layered configs', () => { ); const servers = await loadServerDefinitions({ rootDir: projectDir }); - const names = servers.map((server) => server.name).sort(); + const names = servers.map((server) => server.name).toSorted(); expect(names).toEqual(['fromHome', 'fromProject', 'overrideMe']); const merged = Object.fromEntries(servers.map((server) => [server.name, server])); diff --git a/tests/runtime-compose.test.ts b/tests/runtime-compose.test.ts index f8ae725..cba6349 100644 --- a/tests/runtime-compose.test.ts +++ b/tests/runtime-compose.test.ts @@ -271,9 +271,9 @@ describe('stdio transport environment', () => { cwd: '/repo', }, env: { - // biome-ignore lint/suspicious/noTemplateCurlyInString: placeholders resolve against process env at runtime + // Placeholders resolve against process env at runtime. OBSIDIAN_API_KEY: '${OBSIDIAN_API_KEY}', - // biome-ignore lint/suspicious/noTemplateCurlyInString: placeholders resolve against process env at runtime + // Placeholders resolve against process env at runtime. OBSIDIAN_BASE_URL: '${OBSIDIAN_BASE_URL:-https://127.0.0.1:27124}', EMPTY_VAR: '', },