Skip to content
Merged
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
39 changes: 39 additions & 0 deletions src/mcp/tools/result.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { expect, test } from 'bun:test';
import {
EVIDENCE_KEYS,
MAX_LIST_ITEMS,
mimeTypeFor,
steeringNote,
TRUNCATED_KEY,
} from './result.js';

test('MAX_LIST_ITEMS is 20', () => {
expect(MAX_LIST_ITEMS).toBe(20);
});

test('EVIDENCE_KEYS contains exactly screenshot and evidence', () => {
expect(EVIDENCE_KEYS).toEqual(new Set(['screenshot', 'evidence']));
});

test('mimeTypeFor returns the expected MIME for each known extension', () => {
expect(mimeTypeFor('/tmp/x.png')).toBe('image/png');
expect(mimeTypeFor('/tmp/x.jpg')).toBe('image/jpeg');
expect(mimeTypeFor('/tmp/x.jpeg')).toBe('image/jpeg');
expect(mimeTypeFor('/tmp/x.mp4')).toBe('video/mp4');
expect(mimeTypeFor('/tmp/x.log')).toBe('text/plain');
});

test('mimeTypeFor returns undefined for an unknown extension', () => {
expect(mimeTypeFor('/tmp/x.txt')).toBeUndefined();
});

test('TRUNCATED_KEY is "truncated"', () => {
expect(TRUNCATED_KEY).toBe('truncated');
});

test('steeringNote contains the literal phrases for the capped fields', () => {
const note = steeringNote(['steps', 'bugs']);
expect(note).toContain('Truncated steps, bugs to the first 20 items');
expect(note).toContain('see "truncated" for the real totals');
expect(note).toContain('fields=["steps", "bugs"]');
});
8 changes: 4 additions & 4 deletions src/mcp/tools/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ function isRecord(value: unknown): value is Record<string, unknown> {
}

/** Cap on a top-level array field before it gets truncated for the response. */
const MAX_LIST_ITEMS = 20;
export const MAX_LIST_ITEMS = 20;

/** Keys whose string value — when an absolute path — is evidence worth linking. */
const EVIDENCE_KEYS = new Set(['screenshot', 'evidence']);
export const EVIDENCE_KEYS = new Set(['screenshot', 'evidence']);

type ResourceLinkContent = Extract<CallToolResult['content'][number], { type: 'resource_link' }>;

/** Guess a MIME type from an evidence path's extension; omit when unknown. */
function mimeTypeFor(path: string): string | undefined {
export function mimeTypeFor(path: string): string | undefined {
if (path.endsWith('.png')) return 'image/png';
if (path.endsWith('.jpg') || path.endsWith('.jpeg')) return 'image/jpeg';
if (path.endsWith('.mp4')) return 'video/mp4';
Expand Down Expand Up @@ -117,7 +117,7 @@ function capLists(data: Record<string, unknown>): {
}

/** Steering text appended when one or more lists got capped. */
function steeringNote(truncated: string[]): string {
export function steeringNote(truncated: string[]): string {
const fields = truncated.map((f) => `"${f}"`).join(', ');
return (
`Truncated ${truncated.join(', ')} to the first ${MAX_LIST_ITEMS} items (see "${TRUNCATED_KEY}" ` +
Expand Down
Loading