fix: include errors in slim responses (#3085)

Slim mode now returns rejected `evaluate` calls as one `Error: …`
response with `isError: true`, matching full responses. Successful
response text is unchanged.

Fixes #2938.

## Validation

- Passed `npm run build`, `npm run typecheck`, and `npm run
check-format` on the updated PR head.
- Passed the full `npm run test` suite on the updated PR head, including
Chrome-backed browser tests.

## AI disclosure

OpenAI Codex assisted with issue analysis, implementation, testing, and
code review.
This commit is contained in:
Som Samantray authored and GitHub committed 2026-10-15 11:57:57 +00:00
1 parent 05545a0a71
commit ee38097983
4 files changed
+56 -13

No files matched your search

+5 -1
View File
@@ -13,9 +13,13 @@ export class SlimMcpResponse extends McpResponse {
content: Array<TextContent | ImageContent>;
structuredContent: object;
}> {
const error = this.error;
const responseText = error
? [...this.responseLines, `Error: ${error.message}`].join('\n')
: this.responseLines.join('\n');
const text: TextContent = {
type: 'text',
text: this.responseLines.join('\n'),
text: responseText,
};
return {
content: [text],
+2 -6
View File
@@ -100,11 +100,7 @@ export const evaluate = definePageTool(() => ({
verifyFilesSchema: {},
handler: async (request, response) => {
const page = request.page;
try {
const result = await page.pptrPage.evaluate(request.params.script);
response.appendResponseLine(JSON.stringify(result));
} catch (err) {
response.appendResponseLine(String(err.message));
}
const result = await page.pptrPage.evaluate(request.params.script);
response.appendResponseLine(JSON.stringify(result));
},
}));
+43
View File
@@ -0,0 +1,43 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import assert from 'node:assert';
import {afterEach, describe, it} from 'node:test';
import sinon from 'sinon';
import {SlimMcpResponse} from '../src/SlimMcpResponse.js';
import {createMockMcpContext, createMockParsedArguments} from './mocks.js';
describe('SlimMcpResponse', () => {
afterEach(() => {
sinon.restore();
});
it('preserves response lines when there is no error', async () => {
const response = new SlimMcpResponse(createMockParsedArguments());
response.appendResponseLine('Navigation complete');
const {content} = await response.handle(createMockMcpContext());
assert.strictEqual(content[0].type, 'text');
assert.strictEqual(content[0].text, 'Navigation complete');
});
it('includes stored errors in the response text', async () => {
const response = new SlimMcpResponse(createMockParsedArguments());
response.appendResponseLine('Navigation started');
response.setError(new Error('Navigation failed'));
const {content} = await response.handle(createMockMcpContext());
assert.strictEqual(content[0].type, 'text');
assert.strictEqual(
content[0].text,
'Navigation started\nError: Navigation failed',
);
});
});
+6 -6
View File
@@ -33,18 +33,18 @@ describe('slim', () => {
sinon.assert.calledOnceWithExactly(response.appendResponseLine, '10');
});
it('handles errors', async () => {
it('propagates evaluation errors for response handling', async () => {
const {page, context, response, args} = createHandlerMocks();
const script = "throw new Error('test error')";
page.pptrPage.evaluate.rejects(new Error('test error'));
await evaluate(args).handler({params: {script}, page}, response, context);
await assert.rejects(
evaluate(args).handler({params: {script}, page}, response, context),
{message: 'test error'},
);
sinon.assert.calledOnceWithExactly(page.pptrPage.evaluate, script);
sinon.assert.calledOnceWithExactly(
response.appendResponseLine,
'test error',
);
sinon.assert.notCalled(response.appendResponseLine);
});
it('navigates to correct page', async t => {