mirror of
https://github.com/ChromeDevTools/chrome-devtools-mcp.git
synced 2026-11-04 05:02:31 +00:00
refactor(response): pass live ReadonlyServerConfig to McpResponse (#3258)
- Passes `ReadonlyServerConfig` directly to `McpResponse` instead of a static `ParsedArguments` snapshot, and removes `McpResponse.prototype.setRedactNetworkHeaders`. - Reads `this.#config.resolved` dynamically in `ToolHandler` so unqueued tools (`concurrency: 'none'`) use reloaded configuration options when building responses.
This commit is contained in:
1 parent
f8f18ac253
commit
6042490600
10 files changed
+121
-83
No files matched your search
+11
-16
@@ -6,7 +6,7 @@
|
||||
|
||||
import type {WebMCPTool} from 'puppeteer-core';
|
||||
|
||||
import type {ParsedArguments} from './config/ConfigParser.js';
|
||||
import type {ReadonlyServerConfig} from './config/ServerConfig.js';
|
||||
import {
|
||||
CommentFormatter,
|
||||
type StructuredCommentThread,
|
||||
@@ -149,9 +149,8 @@ export class McpResponse implements Response {
|
||||
#listWebMcpTools?: boolean;
|
||||
#devToolsData?: DevToolsData;
|
||||
#tabId?: string;
|
||||
#args: ParsedArguments;
|
||||
#config: ReadonlyServerConfig;
|
||||
#page?: McpPage;
|
||||
#redactNetworkHeaders = true;
|
||||
#error?: Error;
|
||||
#attachedWaitForResult?: WaitForEventsResult;
|
||||
#reconnectNotice = false;
|
||||
@@ -161,18 +160,14 @@ export class McpResponse implements Response {
|
||||
return this.#page?.viewport?.isMobile ? 'PHONE' : 'DESKTOP';
|
||||
}
|
||||
|
||||
constructor(args: ParsedArguments) {
|
||||
this.#args = args;
|
||||
constructor(config: ReadonlyServerConfig) {
|
||||
this.#config = config;
|
||||
}
|
||||
|
||||
setPage(page: McpPage): void {
|
||||
this.#page = page;
|
||||
}
|
||||
|
||||
setRedactNetworkHeaders(value: boolean): void {
|
||||
this.#redactNetworkHeaders = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Surfaces a one-time note that the browser reconnected and page ids changed.
|
||||
* Set by the tool handler when the context reports a pending reconnect notice.
|
||||
@@ -192,7 +187,7 @@ export class McpResponse implements Response {
|
||||
setIncludePages(value: boolean): void {
|
||||
this.#includePages = value;
|
||||
|
||||
if (this.#args.categoryExtensions) {
|
||||
if (this.#config.resolved.categoryExtensions) {
|
||||
this.#includeExtensionServiceWorkers = value;
|
||||
this.#includeExtensionPages = value;
|
||||
}
|
||||
@@ -577,7 +572,7 @@ export class McpResponse implements Response {
|
||||
responseFilePath: this.#attachedNetworkRequestOptions?.responseFilePath,
|
||||
saveFile: (data, filename, extension) =>
|
||||
context.saveFile(data, filename, extension),
|
||||
redactNetworkHeaders: this.#redactNetworkHeaders,
|
||||
redactNetworkHeaders: this.#config.resolved.redactNetworkHeaders,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -622,7 +617,7 @@ export class McpResponse implements Response {
|
||||
|
||||
async #handleThirdPartyDeveloperTools(): Promise<ToolGroups | undefined> {
|
||||
if (
|
||||
this.#args.categoryExperimentalThirdParty &&
|
||||
this.#config.resolved.categoryExperimentalThirdParty &&
|
||||
this.#listThirdPartyDeveloperTools &&
|
||||
this.#page
|
||||
) {
|
||||
@@ -633,7 +628,7 @@ export class McpResponse implements Response {
|
||||
|
||||
async #handleWebMCP(): Promise<WebMCPTool[] | undefined> {
|
||||
if (
|
||||
this.#args.categoryExperimentalWebmcp &&
|
||||
this.#config.resolved.categoryExperimentalWebmcp &&
|
||||
this.#listWebMcpTools &&
|
||||
this.#page
|
||||
) {
|
||||
@@ -742,7 +737,7 @@ export class McpResponse implements Response {
|
||||
fetchData: false,
|
||||
saveFile: (data, filename, extension) =>
|
||||
context.saveFile(data, filename, extension),
|
||||
redactNetworkHeaders: this.#redactNetworkHeaders,
|
||||
redactNetworkHeaders: this.#config.resolved.redactNetworkHeaders,
|
||||
}),
|
||||
),
|
||||
);
|
||||
@@ -979,7 +974,7 @@ export class McpResponse implements Response {
|
||||
if (this.#reconnectNotice) {
|
||||
structuredContent.reconnected = true;
|
||||
response.push(
|
||||
`Note: the browser was restarted or reconnected since the last call. Page ids have changed. Call ${listPages(this.#args).name} to see open pages.`,
|
||||
`Note: the browser was restarted or reconnected since the last call. Page ids have changed. Call ${listPages(this.#config.resolved).name} to see open pages.`,
|
||||
);
|
||||
}
|
||||
if (this.#textResponseLines.length) {
|
||||
@@ -1089,7 +1084,7 @@ export class McpResponse implements Response {
|
||||
: '';
|
||||
response.push(`# Open dialog
|
||||
${dialog.type()}: ${dialog.message()}${defaultValueIfNeeded}.
|
||||
Call ${handleDialog(this.#args).name} to handle it before continuing.`);
|
||||
Call ${handleDialog(this.#config.resolved).name} to handle it before continuing.`);
|
||||
structuredContent.dialog = {
|
||||
type: dialog.type(),
|
||||
message: dialog.message(),
|
||||
|
||||
+13
-18
@@ -353,7 +353,6 @@ export class ToolHandler {
|
||||
*/
|
||||
async #runBrowserlessTool(
|
||||
tool: BrowserlessToolDefinition,
|
||||
serverArgs: ParsedArguments,
|
||||
params: Record<string, unknown>,
|
||||
response: McpResponse,
|
||||
context: McpContext,
|
||||
@@ -364,12 +363,14 @@ export class ToolHandler {
|
||||
} catch (err) {
|
||||
response.setError(err);
|
||||
}
|
||||
return await response.handle(context, this.#dataFormat(serverArgs));
|
||||
return await response.handle(
|
||||
context,
|
||||
this.#dataFormat(this.#config.resolved),
|
||||
);
|
||||
}
|
||||
|
||||
async #runBrowserTool(
|
||||
tool: ToolDefinition | DefinedPageTool,
|
||||
serverArgs: ParsedArguments,
|
||||
params: Record<string, unknown>,
|
||||
response: McpResponse,
|
||||
context: McpContext,
|
||||
@@ -395,7 +396,7 @@ export class ToolHandler {
|
||||
try {
|
||||
await validateToolFiles(tool, params, context, isLocalBrowser(browser));
|
||||
if (isPageScopedTool(tool)) {
|
||||
const pageId = routedPageId(tool, serverArgs, params);
|
||||
const pageId = routedPageId(tool, this.#config.resolved, params);
|
||||
page =
|
||||
pageId !== undefined
|
||||
? context.getPageById(pageId)
|
||||
@@ -429,7 +430,10 @@ export class ToolHandler {
|
||||
await context.getDevToolsData(page),
|
||||
context.getSelectedMcpPageUrl(page),
|
||||
);
|
||||
return await response.handle(context, this.#dataFormat(serverArgs));
|
||||
return await response.handle(
|
||||
context,
|
||||
this.#dataFormat(this.#config.resolved),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -482,7 +486,6 @@ export class ToolHandler {
|
||||
}
|
||||
|
||||
const tool = this.#tool;
|
||||
const serverArgs = this.#config.resolved;
|
||||
const parseResult = this.#registeredInputSchema.safeParse(rawArgs);
|
||||
if (!parseResult.success) {
|
||||
return errorResult(formatValidationError(tool.name, parseResult.error));
|
||||
@@ -496,20 +499,12 @@ export class ToolHandler {
|
||||
try {
|
||||
logger?.(`${tool.name} request: ${JSON.stringify(params, null, ' ')}`);
|
||||
const response = isSlimTool(tool)
|
||||
? new SlimMcpResponse(serverArgs)
|
||||
: new McpResponse(serverArgs);
|
||||
response.setRedactNetworkHeaders(serverArgs.redactNetworkHeaders);
|
||||
? new SlimMcpResponse(this.#config)
|
||||
: new McpResponse(this.#config);
|
||||
const {content, structuredContent} = isBrowserlessTool(tool)
|
||||
? await this.#runBrowserlessTool(
|
||||
tool,
|
||||
serverArgs,
|
||||
params,
|
||||
response,
|
||||
context,
|
||||
)
|
||||
? await this.#runBrowserlessTool(tool, params, response, context)
|
||||
: await this.#runBrowserTool(
|
||||
tool,
|
||||
serverArgs,
|
||||
params,
|
||||
response,
|
||||
context,
|
||||
@@ -527,7 +522,7 @@ export class ToolHandler {
|
||||
result.isError = true;
|
||||
}
|
||||
success = true;
|
||||
if (serverArgs.experimentalStructuredContent) {
|
||||
if (this.#config.resolved.experimentalStructuredContent) {
|
||||
result.structuredContent = structuredContent as Record<string, unknown>;
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -6,7 +6,7 @@ exports[`McpContext > should include detailed network request in structured cont
|
||||
"url": "http://example.com/detail",
|
||||
"status": "pending",
|
||||
"requestHeaders": {
|
||||
"content-size": "<redacted>"
|
||||
"content-size": "10"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ exports[`McpResponse > add network request when attached 1`] = `
|
||||
## Request http://example.com
|
||||
Status: pending
|
||||
### Request Headers
|
||||
- content-size:<redacted>
|
||||
- content-size:10
|
||||
## Network requests
|
||||
Showing 1-1 of 1 (Page 1 of 1).
|
||||
reqid=1 GET http://example.com [pending]
|
||||
@@ -16,7 +16,7 @@ exports[`McpResponse > add network request when attached 2`] = `
|
||||
"url": "http://example.com",
|
||||
"status": "pending",
|
||||
"requestHeaders": {
|
||||
"content-size": "<redacted>"
|
||||
"content-size": "10"
|
||||
}
|
||||
},
|
||||
"pagination": {
|
||||
@@ -44,7 +44,7 @@ exports[`McpResponse > add network request when attached with POST data 1`] = `
|
||||
## Request http://example.com
|
||||
Status: 200
|
||||
### Request Headers
|
||||
- content-size:<redacted>
|
||||
- content-size:10
|
||||
### Request Body
|
||||
{"request":"body"}
|
||||
### Response Headers
|
||||
@@ -64,7 +64,7 @@ exports[`McpResponse > add network request when attached with POST data 2`] = `
|
||||
"url": "http://example.com",
|
||||
"status": "200",
|
||||
"requestHeaders": {
|
||||
"content-size": "<redacted>"
|
||||
"content-size": "10"
|
||||
},
|
||||
"requestBody": "{\\"request\\":\\"body\\"}",
|
||||
"responseHeaders": {
|
||||
|
||||
+33
-32
@@ -57,6 +57,7 @@ import {
|
||||
createMockObjectInfo,
|
||||
createMockParsedArguments,
|
||||
createMockPuppeteerTarget,
|
||||
createMockServerConfig,
|
||||
createMockWebMCPTool,
|
||||
type MockMcpPage,
|
||||
} from './mocks.js';
|
||||
@@ -102,7 +103,7 @@ describe('McpResponse', () => {
|
||||
const context = createMockMcpContext({selectedPage: page});
|
||||
context.getPages.returns([page]);
|
||||
context.isPageSelected.returns(true);
|
||||
const response = new McpResponse(createMockParsedArguments());
|
||||
const response = new McpResponse(createMockServerConfig());
|
||||
response.setIncludePages(true);
|
||||
|
||||
const result = await response.handle(context);
|
||||
@@ -137,7 +138,7 @@ describe('McpResponse', () => {
|
||||
);
|
||||
context.getPages.returns([openedPage, unrelatedPage]);
|
||||
context.isPageSelected.returns(false);
|
||||
const response = new McpResponse(createMockParsedArguments());
|
||||
const response = new McpResponse(createMockServerConfig());
|
||||
response.attachWaitForResult({
|
||||
openedPages: new Set([openedPage.target]),
|
||||
});
|
||||
@@ -574,7 +575,7 @@ describe('McpResponse', () => {
|
||||
page.emulationSettings = {};
|
||||
page.getNetworkRequests.returns([]);
|
||||
|
||||
const responseWithPreserved = new McpResponse(createMockParsedArguments());
|
||||
const responseWithPreserved = new McpResponse(createMockServerConfig());
|
||||
responseWithPreserved.setPage(page);
|
||||
responseWithPreserved.setIncludeNetworkRequests(true, {
|
||||
includePreservedRequests: true,
|
||||
@@ -583,7 +584,7 @@ describe('McpResponse', () => {
|
||||
|
||||
sinon.assert.calledOnceWithExactly(page.getNetworkRequests, true);
|
||||
|
||||
const responseDefault = new McpResponse(createMockParsedArguments());
|
||||
const responseDefault = new McpResponse(createMockServerConfig());
|
||||
responseDefault.setPage(page);
|
||||
responseDefault.setIncludeNetworkRequests(true);
|
||||
await responseDefault.handle(context);
|
||||
@@ -1242,7 +1243,7 @@ describe('third-party developer tools', () => {
|
||||
},
|
||||
]);
|
||||
const context = createMockMcpContext({selectedPage: page});
|
||||
const response = new McpResponse(args);
|
||||
const response = new McpResponse(createMockServerConfig(args));
|
||||
response.setPage(page);
|
||||
response.setListThirdPartyDeveloperTools();
|
||||
|
||||
@@ -1258,7 +1259,7 @@ describe('third-party developer tools', () => {
|
||||
|
||||
it('lists no third-party developer tools if there are none', async t => {
|
||||
const response = new McpResponse(
|
||||
createMockParsedArguments({categoryExperimentalThirdParty: true}),
|
||||
createMockServerConfig({categoryExperimentalThirdParty: true}),
|
||||
);
|
||||
const page = createMockMcpPage();
|
||||
page.discoverThirdPartyDeveloperTools.resolves([]);
|
||||
@@ -1308,7 +1309,7 @@ describe('third-party developer tools', () => {
|
||||
context.getPages.returns([page]);
|
||||
context.getPageById.withArgs(1).returns(page);
|
||||
context.newPage.resolves(page);
|
||||
const response = new McpResponse(args);
|
||||
const response = new McpResponse(createMockServerConfig(args));
|
||||
|
||||
await handlerAction(response, context, args, page);
|
||||
|
||||
@@ -1420,7 +1421,7 @@ describe('webmcp', () => {
|
||||
context.getPages.returns([page]);
|
||||
context.isPageSelected.returns(true);
|
||||
context.getPageById.withArgs(1).returns(page);
|
||||
const response = new McpResponse(args);
|
||||
const response = new McpResponse(createMockServerConfig(args));
|
||||
|
||||
await handlerAction(response, context, args, page);
|
||||
|
||||
@@ -1478,7 +1479,7 @@ describe('webmcp', () => {
|
||||
});
|
||||
const page = createMockMcpPage();
|
||||
const context = createMockMcpContext({selectedPage: page});
|
||||
const response = new McpResponse(args);
|
||||
const response = new McpResponse(createMockServerConfig(args));
|
||||
response.setPage(page);
|
||||
response.setListWebMcpTools();
|
||||
|
||||
@@ -1543,7 +1544,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
DevTools.I18n.i18n.registerLocaleDataForTest('en-US', {});
|
||||
|
||||
it('formats stats, staticData, nativeContextSizes, and retainedByContextSummary', async () => {
|
||||
const response = new McpResponse(createMockParsedArguments());
|
||||
const response = new McpResponse(createMockServerConfig());
|
||||
const stats = createMockHeapSnapshotStats();
|
||||
const staticData = createMockHeapSnapshotStaticData();
|
||||
const nativeContextSizes = {
|
||||
@@ -1602,7 +1603,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
});
|
||||
|
||||
it('formats aggregate data with shallow size and pagination summaries', async () => {
|
||||
const response = new McpResponse(createMockParsedArguments());
|
||||
const response = new McpResponse(createMockServerConfig());
|
||||
const aggregates = {
|
||||
aggregates: [
|
||||
createMockHeapSnapshotAggregate({
|
||||
@@ -1653,7 +1654,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
});
|
||||
|
||||
it('sorts nodes descending by retainedSize and formats pagination summaries', async () => {
|
||||
const response = new McpResponse(createMockParsedArguments());
|
||||
const response = new McpResponse(createMockServerConfig());
|
||||
const nodeSmall = createMockHeapSnapshotNode({
|
||||
id: 1,
|
||||
name: 'SmallNode',
|
||||
@@ -1704,7 +1705,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
});
|
||||
|
||||
it('formats edge nodes correctly', async () => {
|
||||
const response = new McpResponse(createMockParsedArguments());
|
||||
const response = new McpResponse(createMockServerConfig());
|
||||
const edge = createMockHeapSnapshotEdge({
|
||||
name: 'myProp',
|
||||
type: 'property',
|
||||
@@ -1726,7 +1727,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
});
|
||||
|
||||
it('formats retaining paths with truncation note when limits are reached', async () => {
|
||||
const response = new McpResponse(createMockParsedArguments());
|
||||
const response = new McpResponse(createMockServerConfig());
|
||||
const retainingPaths = {
|
||||
paths: [
|
||||
{
|
||||
@@ -1764,7 +1765,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
});
|
||||
|
||||
it('formats empty retaining paths with no paths message', async () => {
|
||||
const response = new McpResponse(createMockParsedArguments());
|
||||
const response = new McpResponse(createMockServerConfig());
|
||||
response.setHeapSnapshotRetainingPaths({
|
||||
paths: [],
|
||||
limitsReached: {depth: false, nodes: false, siblings: false},
|
||||
@@ -1778,7 +1779,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
});
|
||||
|
||||
it('formats dominator chain and handles empty dominators', async () => {
|
||||
const response = new McpResponse(createMockParsedArguments());
|
||||
const response = new McpResponse(createMockServerConfig());
|
||||
const dominators = [
|
||||
{
|
||||
nodeId: 10,
|
||||
@@ -1797,7 +1798,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
assert.ok(text.includes('### Dominator Chain'));
|
||||
assert.ok(text.includes('10,DomClass'));
|
||||
|
||||
const emptyResponse = new McpResponse(createMockParsedArguments());
|
||||
const emptyResponse = new McpResponse(createMockServerConfig());
|
||||
emptyResponse.setHeapSnapshotDominators([]);
|
||||
const emptyResult = await emptyResponse.handle(context);
|
||||
const emptyText = getTextContent(emptyResult.content[0]);
|
||||
@@ -1806,7 +1807,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
});
|
||||
|
||||
it('formats class diff summary and detailed diff headers', async () => {
|
||||
const response = new McpResponse(createMockParsedArguments());
|
||||
const response = new McpResponse(createMockServerConfig());
|
||||
response.setHeapSnapshotClassDiffs(createMockHeapSnapshotDiff());
|
||||
|
||||
const context = createMockMcpContext();
|
||||
@@ -1816,7 +1817,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
assert.ok(text.includes('### Heap Snapshot Diff'));
|
||||
assert.ok(text.includes('TestClass'));
|
||||
|
||||
const detailedResponse = new McpResponse(createMockParsedArguments());
|
||||
const detailedResponse = new McpResponse(createMockServerConfig());
|
||||
const detailedDiff = createMockDetailedClassDiff();
|
||||
detailedResponse.setHeapSnapshotDetailedClassDiff(detailedDiff);
|
||||
|
||||
@@ -1827,7 +1828,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
});
|
||||
|
||||
it('paginates detailed class diff objects below the class header', async () => {
|
||||
const response = new McpResponse(createMockParsedArguments());
|
||||
const response = new McpResponse(createMockServerConfig());
|
||||
const objects = [
|
||||
createMockDiffObject({id: 101, change: 'added'}),
|
||||
createMockDiffObject({id: 102, change: 'added'}),
|
||||
@@ -1855,7 +1856,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
});
|
||||
|
||||
it('paginates class diff summary and keeps global indices', async () => {
|
||||
const response = new McpResponse(createMockParsedArguments());
|
||||
const response = new McpResponse(createMockServerConfig());
|
||||
const classDiffs = [
|
||||
createMockClassDiff({index: 0, className: 'ClassA'}),
|
||||
createMockClassDiff({index: 1, className: 'ClassB'}),
|
||||
@@ -1888,7 +1889,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
|
||||
it('truncates class diff names in compact format', async () => {
|
||||
const context = createMockMcpContext();
|
||||
const toonDiffResponse = new McpResponse(createMockParsedArguments());
|
||||
const toonDiffResponse = new McpResponse(createMockServerConfig());
|
||||
toonDiffResponse.setHeapSnapshotClassDiffs(createMockHeapSnapshotDiff(), {
|
||||
maxNameLength: 4,
|
||||
});
|
||||
@@ -1897,7 +1898,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
assert.ok(toonDiffText.includes('Test...'));
|
||||
|
||||
const detailedDiff = createMockDetailedClassDiff();
|
||||
const toonDetailedResponse = new McpResponse(createMockParsedArguments());
|
||||
const toonDetailedResponse = new McpResponse(createMockServerConfig());
|
||||
toonDetailedResponse.setHeapSnapshotDetailedClassDiff(detailedDiff, {
|
||||
maxNameLength: 4,
|
||||
});
|
||||
@@ -1910,7 +1911,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
});
|
||||
|
||||
it('formats duplicate strings with pagination and object details', async () => {
|
||||
const response = new McpResponse(createMockParsedArguments());
|
||||
const response = new McpResponse(createMockServerConfig());
|
||||
const duplicateStrings = [
|
||||
{
|
||||
value: 'duplicated-string-value',
|
||||
@@ -1936,7 +1937,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
assert.ok(text.includes('duplicated-string-value'));
|
||||
assert.ok(text.includes('Showing 1-1 of 1 (Page 1 of 1).'));
|
||||
|
||||
const objectInfoResponse = new McpResponse(createMockParsedArguments());
|
||||
const objectInfoResponse = new McpResponse(createMockServerConfig());
|
||||
const objectInfo = createMockObjectInfo();
|
||||
objectInfoResponse.setHeapSnapshotObjectDetails(objectInfo);
|
||||
|
||||
@@ -1949,7 +1950,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
|
||||
it('truncates object details name in compact format', async () => {
|
||||
const context = createMockMcpContext();
|
||||
const toonResponse = new McpResponse(createMockParsedArguments());
|
||||
const toonResponse = new McpResponse(createMockServerConfig());
|
||||
const longNameObjectInfo = {
|
||||
...createMockObjectInfo(),
|
||||
name: 'Object_' + 'x'.repeat(150),
|
||||
@@ -1965,7 +1966,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
});
|
||||
|
||||
it('renders the context field usage section for the filtered contexts', async () => {
|
||||
const response = new McpResponse(createMockParsedArguments());
|
||||
const response = new McpResponse(createMockServerConfig());
|
||||
response.setHeapSnapshotContextAnalysis(createMockContextAnalysisResult(), {
|
||||
retainedSize: parseByteSizeRange('1000'),
|
||||
});
|
||||
@@ -1984,7 +1985,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
|
||||
describe('extension service workers', () => {
|
||||
it('lists only service workers, excluding dedicated and shared workers', async () => {
|
||||
const response = new McpResponse(createMockParsedArguments());
|
||||
const response = new McpResponse(createMockServerConfig());
|
||||
response.setIncludeExtensionServiceWorkers(true);
|
||||
|
||||
const context = createMockMcpContext();
|
||||
@@ -2050,7 +2051,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
}
|
||||
|
||||
it('includes note when DevTools comments regenerate a standard text snapshot', async () => {
|
||||
const response = new McpResponse(createMockParsedArguments());
|
||||
const response = new McpResponse(createMockServerConfig());
|
||||
const page = createMockMcpPage();
|
||||
page.textSnapshot = null;
|
||||
const standardSnapshot = createSnapshotWithNodes(
|
||||
@@ -2086,7 +2087,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
});
|
||||
|
||||
it('includes note when DevTools comments regenerate a verbose text snapshot', async () => {
|
||||
const response = new McpResponse(createMockParsedArguments());
|
||||
const response = new McpResponse(createMockServerConfig());
|
||||
const page = createMockMcpPage();
|
||||
page.textSnapshot = null;
|
||||
const verboseSnapshot = createSnapshotWithNodes(
|
||||
@@ -2122,7 +2123,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
});
|
||||
|
||||
it('does not include note when DevTools comments do not regenerate the snapshot', async () => {
|
||||
const response = new McpResponse(createMockParsedArguments());
|
||||
const response = new McpResponse(createMockServerConfig());
|
||||
const page = createMockMcpPage();
|
||||
const existingSnapshot = createSnapshotWithNodes(
|
||||
[{id: '1_1', backendNodeId: 42}],
|
||||
@@ -2151,7 +2152,7 @@ describe('McpResponse heap snapshot formatting', () => {
|
||||
});
|
||||
|
||||
it('does not include note when comments have no associated nodes', async () => {
|
||||
const response = new McpResponse(createMockParsedArguments());
|
||||
const response = new McpResponse(createMockServerConfig());
|
||||
const page = createMockMcpPage();
|
||||
page.textSnapshot = null;
|
||||
response.setPage(page);
|
||||
|
||||
@@ -8,11 +8,11 @@ import assert from 'node:assert';
|
||||
import {describe, it} from 'node:test';
|
||||
|
||||
import {SlimMcpResponse} from '../src/SlimMcpResponse.js';
|
||||
import {createMockMcpContext, createMockParsedArguments} from './mocks.js';
|
||||
import {createMockMcpContext, createMockServerConfig} from './mocks.js';
|
||||
|
||||
describe('SlimMcpResponse', () => {
|
||||
it('preserves response lines when there is no error', async () => {
|
||||
const response = new SlimMcpResponse(createMockParsedArguments());
|
||||
const response = new SlimMcpResponse(createMockServerConfig());
|
||||
response.appendResponseLine('Navigation complete');
|
||||
|
||||
const {content} = await response.handle(createMockMcpContext());
|
||||
@@ -22,7 +22,7 @@ describe('SlimMcpResponse', () => {
|
||||
});
|
||||
|
||||
it('includes stored errors in the response text', async () => {
|
||||
const response = new SlimMcpResponse(createMockParsedArguments());
|
||||
const response = new SlimMcpResponse(createMockServerConfig());
|
||||
response.appendResponseLine('Navigation started');
|
||||
response.setError(new Error('Navigation failed'));
|
||||
|
||||
|
||||
@@ -2312,5 +2312,50 @@ describe('ToolHandler', () => {
|
||||
assert.strictEqual(listingChanged, false);
|
||||
assert.strictEqual(toolHandler.disabled, true);
|
||||
});
|
||||
|
||||
it('uses reloaded config options when building response for an unqueued tool', async () => {
|
||||
const config = parseConfig();
|
||||
const {promise: unblockTool, resolve: finishTool} =
|
||||
Promise.withResolvers<void>();
|
||||
const {promise: started, resolve: markStarted} =
|
||||
Promise.withResolvers<void>();
|
||||
|
||||
const unqueuedTool: BrowserlessToolDefinition = defineBrowserlessTool(
|
||||
() => ({
|
||||
name: 'unqueued_tool',
|
||||
description: 'Runs outside the task runner',
|
||||
annotations: {
|
||||
category: ToolCategory.NAVIGATION,
|
||||
readOnlyHint: true,
|
||||
},
|
||||
schema: {},
|
||||
blockedByDialog: false,
|
||||
verifyFilesSchema: {},
|
||||
concurrency: 'none',
|
||||
handler: async (_request, response) => {
|
||||
markStarted();
|
||||
await unblockTool;
|
||||
response.appendResponseLine('done');
|
||||
},
|
||||
}),
|
||||
)(config.resolved);
|
||||
|
||||
const toolHandler = new ToolHandler(
|
||||
unqueuedTool,
|
||||
config,
|
||||
async () => createMockMcpContext(),
|
||||
sinon.spy(),
|
||||
);
|
||||
|
||||
const callPromise = toolHandler.handle({});
|
||||
await started;
|
||||
|
||||
config.commit(parseConfig(['--experimental-structured-content']));
|
||||
|
||||
finishTool();
|
||||
const result = await callPromise;
|
||||
|
||||
assert.deepStrictEqual(result.structuredContent, {message: 'done'});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
listConsoleMessages,
|
||||
} from '../../src/tools/console.js';
|
||||
import {installExtension} from '../../src/tools/extensions.js';
|
||||
import {createMockServerConfig} from '../mocks.js';
|
||||
import {serverHooks} from '../server.js';
|
||||
import {
|
||||
getTextContent,
|
||||
@@ -62,7 +63,7 @@ describe('console', () => {
|
||||
const sw = swList.find(s => s.target === swTarget);
|
||||
assert(sw, 'Service worker not found in context list');
|
||||
|
||||
const response2 = new McpResponse(args);
|
||||
const response2 = new McpResponse(createMockServerConfig(args));
|
||||
|
||||
await context.triggerExtensionAction(extensionId);
|
||||
const worker = await swTarget.worker();
|
||||
@@ -267,7 +268,7 @@ describe('console', () => {
|
||||
// setContent resolves, so poll until the collector has all three.
|
||||
let textContent = '';
|
||||
await waitExecutionFor(async () => {
|
||||
const pollResponse = new McpResponse(args);
|
||||
const pollResponse = new McpResponse(createMockServerConfig(args));
|
||||
pollResponse.setPage(page);
|
||||
await listConsoleMessages(args).handler(
|
||||
{params: {}, page},
|
||||
@@ -465,7 +466,7 @@ describe('console', () => {
|
||||
response,
|
||||
context,
|
||||
);
|
||||
const response2 = new McpResponse(args);
|
||||
const response2 = new McpResponse(createMockServerConfig(args));
|
||||
response2.setPage(context.getSelectedMcpPage());
|
||||
await getConsoleMessage(args).handler(
|
||||
{params: {msgid: 1}, page: context.getSelectedMcpPage()},
|
||||
@@ -521,7 +522,7 @@ describe('console', () => {
|
||||
response,
|
||||
context,
|
||||
);
|
||||
const response2 = new McpResponse(args);
|
||||
const response2 = new McpResponse(createMockServerConfig(args));
|
||||
response2.setPage(context.getSelectedMcpPage());
|
||||
await getConsoleMessage(args).handler(
|
||||
{params: {msgid: id}, page: context.getSelectedMcpPage()},
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
createHandlerMocks,
|
||||
createMockDialog,
|
||||
createMockElementHandle,
|
||||
createMockServerConfig,
|
||||
} from '../mocks.js';
|
||||
import {serverHooks} from '../server.js';
|
||||
import {
|
||||
@@ -942,7 +943,7 @@ describe('input', () => {
|
||||
);
|
||||
|
||||
// Fill email
|
||||
const response1 = new McpResponse(args);
|
||||
const response1 = new McpResponse(createMockServerConfig(args));
|
||||
await fill(args).handler(
|
||||
{
|
||||
params: {
|
||||
@@ -960,7 +961,7 @@ describe('input', () => {
|
||||
);
|
||||
|
||||
// Fill password
|
||||
const response2 = new McpResponse(args);
|
||||
const response2 = new McpResponse(createMockServerConfig(args));
|
||||
await fill(args).handler(
|
||||
{
|
||||
params: {
|
||||
@@ -1045,7 +1046,7 @@ describe('input', () => {
|
||||
},
|
||||
page: context.getSelectedMcpPage(),
|
||||
},
|
||||
new McpResponse(args),
|
||||
new McpResponse(createMockServerConfig(args)),
|
||||
context,
|
||||
);
|
||||
|
||||
@@ -1103,7 +1104,7 @@ describe('input', () => {
|
||||
},
|
||||
page: context.getSelectedMcpPage(),
|
||||
},
|
||||
new McpResponse(args),
|
||||
new McpResponse(createMockServerConfig(args)),
|
||||
context,
|
||||
);
|
||||
|
||||
|
||||
+1
-1
@@ -231,7 +231,7 @@ export async function withMcpContext(
|
||||
allowUnrestrictedPaths: options.allowUnrestrictedPaths,
|
||||
}),
|
||||
});
|
||||
const response = new McpResponse(parsedArgs);
|
||||
const response = new McpResponse(serverConfig);
|
||||
const heapSnapshotManager = new HeapSnapshotManager(new TaskRunner());
|
||||
if (context) {
|
||||
context.dispose();
|
||||
|
||||
Reference in new issue
Block a user