Visitar URL original
feat: run independent tool calls in parallel with a TaskRunner by OrKoN · Pull Request #3243 · ChromeDevTools/chrome-devtools-mcp · GitHub
Skip to content

feat: run independent tool calls in parallel with a TaskRunner - #3243

Merged
OrKoN merged 1 commit into
mainfrom
tool-locks-1-keyed-lock
Oct 20, 2026
Merged

OrKoN merged 1 commit into
mainfrom
tool-locks-1-keyed-lock

Conversation

@OrKoN

@OrKoN OrKoN commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

ToolHandler held one Puppeteer Mutex for every tool call. This PR replaces it with a central TaskRunner that creates TaskQueue objects and runs every task from one ordered list. A queue has an optional parent queue. A task on a queue starts when every earlier task on the same queue, an ancestor, or a descendant has finished. Tasks on unrelated queues run at the same time. Waiting tasks count, so a waiting task blocks later related tasks and no task starves.

TaskRunner (root)
├── context queue (McpContext)
│   ├── page 1 queue
│   └── page 2 queue
└── heap queue (HeapSnapshotManager)
    ├── file a queue
    └── file b queue

User-visible changes

  • Heap snapshot tools run in parallel with browser tools.
  • close_heapsnapshot waits for running reads of the same file.
  • With --pageIdRouting, page tools that declare concurrency: 'page' and target different pages run in parallel. No tool declares it in this PR; PR 3 of the stack does.
  • get_devtools_comments runs at the same time as any other tool. Before, only its handler ran unlocked.
  • A tool call that waits on a page that closes fails with Page N is closed.
  • Two first tool calls that wait for the initial roots/list share one request. Before, the second call validated paths against no roots and failed with "Access denied".

API

src/utils/TaskRunner.ts (new, ~170 lines):

API Behavior
runner.createQueue(parent?) Creates a queue. Without parent, only pause() relates to it.
runner.pause() Enters the list ahead of waiting tasks and relates to every queue. Resolves with a guard when running tasks have finished. Disposing the guard resumes.
queue.scheduleTask() Resolves with a guard when no earlier task on a related queue runs. Callers write using guard = await queue.scheduleTask();. Rejects when the queue is disposed while the task waits.
queue.dispose(reason) Rejects waiting tasks with reason and rejects later scheduleTask() calls. The current holder keeps the queue.

Every hold is a Disposable, released by using. A second dispose of a guard throws. There are no lock modes: holding a queue is exclusive on that queue and implicitly shared on its ancestors.

Tool definitions declare a concurrency mode (ToolConcurrency):

Mode Queue Tools
'exclusive' (default) context queue every tool that touches browser-global state
'page' pageTaskQueue(pageId); the context queue without page id routing page tools that touch only the target page
'none' none browserless tools; get_devtools_comments

yieldsToolLock is gone. get_devtools_comments only reads page state and waits for comment events, so it runs outside the runner with concurrency: 'none'. The type system limits the modes per definition kind: defineTool tools cannot take 'page' or 'none', browserless tools cannot take 'page'.

Changes

  • ToolHandler.handle() resolves the context, picks the queue from rawArgs.pageId and tool.concurrency, and holds it with using guard = await queue.scheduleTask() for the rest of the upstream body, which is unchanged. The disabled check, input parsing, ensureBrowser(), page lookup, handler, and response formatting all run while the queue is held. A rejected scheduleTask() (disposed queue) returns a tool error result.
  • McpContext takes a taskRunner option, owns taskQueue, and manages page queues keyed by page id in pageTaskQueue(pageId). #disposeMcpPage() disposes the page and its queue in one place.
  • McpContext.createPagesSnapshot() runs under an internal Mutex because two page tasks can call it at the same time.
  • HeapSnapshotManager takes the TaskRunner, owns a heap queue, and creates one child queue per absolute file path in #scheduleTask(filePath?). Every read method starts with using guard = await this.#scheduleTask(filePath); the two-file diff methods hold the heap queue; disposeSnapshot() holds the file queue.
  • McpServer owns the TaskRunner, passes it to McpContext and HeapSnapshotManager, and holds using guard = await taskRunner.pause() in applyConfig() and in the --watchConfig reload instead of the mutex.
  • McpServer.#getContext() stores the initial roots/list request as a promise.
  • defineBrowserlessTool sets concurrency: 'none'.
  • McpPage is unchanged.

No tool schema or description changes, so npm run gen is not needed.

Stack

  1. refactor: extract errorResult() and routedPageId() in ToolHandler (refactor: extract errorResult() and routedPageId() in ToolHandler #3251).
  2. This PR: the runner. No tool sets concurrency: 'page' yet, so page tools still serialize on the context queue after this PR alone.
  3. feat: run page-scoped tools on different pages in parallel (feat: run page-scoped tools on different pages in parallel #3252): adds concurrency: 'page' to the page tools.

Related issues

No open issue is closed by this PR. Two are related:

Tests

  • tests/utils/TaskRunner.test.ts (new): unrelated queues run together, different roots run together, FIFO on one queue, parent waits for child, child waits for parent, child waits behind a waiting parent, second dispose of a guard throws, release on throw; pause (stops new tasks, resolves after running tasks finish, goes ahead of waiting tasks); dispose (rejects waiting tasks with the reason, rejects later runs, parent runs after a rejected child).
  • tests/ToolHandler.test.ts: concurrency: 'exclusive' suite (serializes exclusive tools; a 'none' page tool runs while an exclusive tool holds the context queue), concurrency: 'page' suite (different pages in parallel, same page serialized, exclusive tool queued FIFO behind page tools, page queue held until the response is built, call runs on the page queue, page tool without pageId runs on the context queue, page queue released on throw, disposed page queue fails the waiting call with Page 1 is closed.), browserless suite (does not enter the context queue, runs while an exclusive tool runs), update suite uses taskRunner.pause().
  • tests/McpContext.test.ts: one page queue per id with the context queue as parent; disposing a page rejects a waiting task with Page N is closed.; createPagesSnapshot() serializes concurrent calls.
  • tests/index.test.ts: two concurrent first calls share one roots/list request.
  • tests/processors/HeapSnapshotManager.test.ts: disposeSnapshot() waits for an in-flight read; a read after the dispose loads the file again.
  • tests/tools/comments.test.ts: get_devtools_comments has concurrency: 'none'.
  • tests/mocks.ts: createMockMcpContext() carries a real TaskRunner, taskQueue, and pageTaskQueue().

@OrKoN

This comment was marked as outdated.

@OrKoN
OrKoN force-pushed the tool-locks-1-keyed-lock branch 2 times, most recently from 26e2279 to 3f815f1 Compare October 20, 2026 08:21
@OrKoN OrKoN changed the title refactor: replace the tool mutex with a keyed lock feat: run independent tool calls in parallel with a TaskQueue tree Oct 20, 2026
@OrKoN
OrKoN force-pushed the tool-locks-1-keyed-lock branch 2 times, most recently from b1644f6 to 3d2d651 Compare October 20, 2026 09:23
@OrKoN OrKoN changed the title feat: run independent tool calls in parallel with a TaskQueue tree feat: run independent tool calls in parallel with a TaskRunner Oct 20, 2026
@OrKoN
OrKoN force-pushed the tool-locks-1-keyed-lock branch 4 times, most recently from 6105de2 to f25e9a4 Compare October 20, 2026 10:50
@OrKoN
OrKoN changed the base branch from main to task-runner-2-toolhandler-helpers October 20, 2026 10:50
@OrKoN
OrKoN force-pushed the task-runner-2-toolhandler-helpers branch from 54706c9 to cdbeb88 Compare October 20, 2026 10:57
@OrKoN
OrKoN force-pushed the tool-locks-1-keyed-lock branch from f25e9a4 to 54b76b2 Compare October 20, 2026 10:57
@OrKoN
OrKoN marked this pull request as ready for review October 20, 2026 10:59
Base automatically changed from task-runner-2-toolhandler-helpers to main October 20, 2026 11:30
Comment thread src/utils/TaskRunner.ts Resolved
Comment thread tests/utils/TaskRunner.test.ts Resolved

@Lightning00Blade Lightning00Blade left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks, good will re-review once the comments are resolved

@OrKoN
OrKoN force-pushed the tool-locks-1-keyed-lock branch from 54b76b2 to 3e2b83c Compare October 20, 2026 12:44
`ToolHandler` held one Puppeteer `Mutex` for every tool call. This
change replaces it with a central `TaskRunner` that creates `TaskQueue`
objects and schedules every task from one ordered list. A queue has an
optional parent queue. A task on a queue starts when every earlier task
on the same queue, an ancestor, or a descendant has finished. Tasks on
unrelated queues run at the same time. Waiting tasks count, so no task
starves. Callers hold a queue with
`using guard = await queue.scheduleTask()`.

Queues:
- `McpContext.taskQueue` for browser-wide state. `pageTaskQueue(id)`
  returns a child queue per page id, created on first use, so a tool
  call enters it before the browser connects. The context disposes a
  page queue with the page; waiting calls fail with `Page N is closed.`
- `HeapSnapshotManager` owns a heap queue with one child per file.
  Reads of different files run at the same time; a compare of two files
  and `disposeSnapshot()` wait for reads of the files they touch.

Tool definitions declare a `concurrency` mode:
- `'exclusive'` (default): the context queue.
- `'page'`: the page queue of `pageId`; the context queue without page
  id routing. No tool sets it yet; a later change does.
- `'none'`: no queue. `defineBrowserlessTool` sets it.
  `get_devtools_comments` (was `yieldsToolLock`) sets it too: the
  handler only reads page state and waits for comment events.

`McpServer.applyConfig()` and the config watcher call
`TaskRunner.pause()` and await `idle` instead of the mutex. New tool
calls wait, running calls finish, then the config applies.

`McpServer.#getContext()` stores the bounded initial `roots/list`
request as a promise. Browserless tools now run at the same time, and
without a shared request the second call validated its paths against
no roots and failed with "Access denied".
`McpContext.createPagesSnapshot()` runs under an internal `Mutex`
because two page tasks can call it at the same time.

User-visible changes:
- Heap snapshot tools run in parallel with browser tools.
- `close_heapsnapshot` waits for running reads of the same file.
- `get_devtools_comments` runs at the same time as any other tool.
@OrKoN
OrKoN force-pushed the tool-locks-1-keyed-lock branch from 3e2b83c to b5d43d6 Compare October 20, 2026 12:54
@OrKoN
OrKoN enabled auto-merge October 20, 2026 12:56
Comment thread tests/utils/TaskRunner.test.ts Resolved
@OrKoN
OrKoN added this pull request to the merge queue Oct 20, 2026
Merged via the queue into main with commit 8a8d877 Oct 20, 2026
30 checks passed
@OrKoN
OrKoN deleted the tool-locks-1-keyed-lock branch October 20, 2026 13:03
johan-gorter pushed a commit to johan-gorter/chrome-devtools-mcp that referenced this pull request Oct 20, 2026
…romeDevTools#3251)

## Summary

`ToolHandler` built the same `{content: [{type: 'text', text}], isError:
true}` literal in three places and decided the routed page id inline.
This PR adds two module-level helpers:

- `errorResult(text)` returns the error `CallToolResult`.
- `routedPageId(tool, serverArgs, args)` returns the page id a page tool
call targets, or `undefined` when the call targets the selected page.

`routedPageId()` requires an integer id. The input schema rejects a
non-integer before the value is used, so no behavior changes.

This is PR 1 of a stack; PR 2 calls both helpers from a second place.

## Stack

1. **This PR**.
2. `feat: run independent tool calls in parallel with a TaskRunner`
(ChromeDevTools#3243).
3. `feat: run page-scoped tools on different pages in parallel` (ChromeDevTools#3252).

## Tests

No test changes. The existing `ToolHandler` tests cover the disabled
result, the validation error result, the handler error result, and page
id routing.
johan-gorter pushed a commit to johan-gorter/chrome-devtools-mcp that referenced this pull request Oct 20, 2026
…Tools#3252)

## Summary

Page-scoped tools whose handler touches only the target page declare
`concurrency: 'page'`. With `--pageIdRouting` (the default),
`ToolHandler` runs such a call on the queue of the page named by
`pageId`, so calls on different pages run at the same time. Calls on one
page, and every `'exclusive'` tool, still serialize in FIFO order.

This is PR 3 of a stack. It only adds the annotations; the scheduling
comes from ChromeDevTools#3243.

## Stack

1. `refactor: extract errorResult() and routedPageId() in ToolHandler`
(ChromeDevTools#3251).
2. `feat: run independent tool calls in parallel with a TaskRunner`
(ChromeDevTools#3243).
3. **This PR**.

## User-visible changes

- With `--pageIdRouting`, calls that target different pages run in
parallel. Without it, every page tool targets the selected page and
serializes as before.
- `close_page`, `select_page`, `navigate_page`, and every other
`'exclusive'` tool wait for running page tools and block later page
tools until they finish.

## Explicit `concurrency`

`concurrency` is now a required field on `defineTool` and
`definePageTool` definitions, so every browser tool states its choice in
its definition (review feedback). 26 tools that touch browser-global
state, or that were not reviewed for page-only access, declare
`'exclusive'`: `comments.ts` (3), `emulation.ts` (1), `extensions.ts`
(5), `lighthouse.ts` (1), `memory.ts` (1), `pages.ts` (5),
`performance.ts` (3), `pwa.ts` (4), `screencast.ts` (2), `script.ts`
(1). `defineBrowserlessTool` still sets `'none'`.

## Tools that get `concurrency: 'page'`

`console.ts` (2), `css.ts` (1), `input.ts` (9), `network.ts` (2),
`pages.ts` (3), `screenshot.ts` (1), `slim/tools.ts` (3), `snapshot.ts`
(2), `thirdPartyDeveloper.ts` (2), `webmcp.ts` (2). Each handler reads
or writes only its target page: no page list, no selected page, no trace
state, no browser-wide emulation.

No tool schema or description changes, so `npm run gen` is not needed.

## Tests

The `concurrency: 'page'` suite in `tests/ToolHandler.test.ts` (added in
ChromeDevTools#3243) covers the scheduling with inline tool definitions. Inline tool
definitions in `tests/ToolHandler.test.ts` and
`tests/telemetry/metricsRegistry.test.ts` gain the now-required field.
No new tests: the change sets one field per tool and the type system
limits the values per tool kind.
github-merge-queue Bot pushed a commit that referenced this pull request Oct 22, 2026
🤖 I have created a release *beep* *boop*
---


##
[1.12.0](chrome-devtools-mcp-v1.11.0...chrome-devtools-mcp-v1.12.0)
(2026-10-22)


### 🎉 Features

* add --maxNavigationSaved flag to configure retained navigation history
([#3018](#3018))
([a580d13](a580d13))
* add Kimi Code plugin manifest
([#3029](#3029))
([cd00b64](cd00b64))
* Analyze heap snapshots without a browser connection
([#3156](#3156))
([78c8320](78c8320))
* **config:** add --watchConfig to reload config file changes
([#3248](#3248))
([b48561f](b48561f))
* **config:** introduce layered ServerConfig for live reload
([#3203](#3203))
([37f1fda](37f1fda))
* **config:** share ReadonlyServerConfig with McpContext and McpPage
([#3204](#3204))
([0813bf3](0813bf3))
* **config:** support reloading blockedUrlPattern and allowedUrlPattern
([#3268](#3268))
([cc850f5](cc850f5))
* **context:** release disabled feature state on config reload
([#3049](#3049))
([c0140d1](c0140d1))
* Paginate by default in heap snapshot tools
([#3201](#3201))
([fb8375b](fb8375b))
* Paginate detailed diff in compare_heapsnapshots
([#3195](#3195))
([4a9dd53](4a9dd53))
* run independent tool calls in parallel with a TaskRunner
([#3243](#3243))
([8a8d877](8a8d877))
* run page-scoped tools on different pages in parallel
([#3252](#3252))
([f8f18ac](f8f18ac))
* **server:** add McpServer.applyConfig() to reload configuration
([#3051](#3051))
([f874aeb](f874aeb))
* **slim:** support filepath in slim mode screenshot
([#3296](#3296))
([d399c4a](d399c4a))
* Sort objects for class by self size in compare_heapsnapshots
([#3205](#3205))
([f8a18c6](f8a18c6))
* **tools:** support updating ToolHandler definitions in place
([#3050](#3050))
([8064138](8064138))
* **wait_for:** support opt-in visible text matching
([#3212](#3212))
([eef92da](eef92da))


### 🛠️ Fixes

* allow paths below a filesystem-root workspace root
([#3277](#3277))
([296ccef](296ccef))
* block evaluate_script while a dialog is open
([#3196](#3196))
([545bcbb](545bcbb))
* bound take_snapshot wait and clear the geolocation override
([#3265](#3265))
([6282f54](6282f54))
* **browser:** redact WebSocket headers from connection logs
([#2901](#2901))
([3a2f91e](3a2f91e))
* capture thrown transform errors as Zod validation issues
([#3242](#3242))
([0f95c21](0f95c21))
* clear handled dialog in McpPage.getDialog()
([#3241](#3241))
([2ab93a3](2ab93a3)),
closes
[#2958](#2958)
* **cli:** disallow --slim in CLI start options
([#2987](#2987))
([#3113](#3113))
([ae3f61c](ae3f61c))
* **cli:** do not forward tool args to start
([#3114](#3114))
([fef1598](fef1598))
* **cli:** serialize object daemon arguments
([#2797](#2797))
([f73a440](f73a440))
* close the tab opened by a failed new_page
([#2928](#2928))
([c174677](c174677))
* **config:** accept native JSON objects for wsHeaders in config files
([#3262](#3262))
([371e6c2](371e6c2))
* **console:** describe Map, Set, function, DOM node, symbol and cyclic
arguments
([#3199](#3199))
([968e4e5](968e4e5))
* **console:** include the reason of unhandled promise rejections
([#3148](#3148))
([f7cdf73](f7cdf73))
* defer focus emulation until a page is explicitly targeted
([#3230](#3230))
([35abe35](35abe35))
* disable Puppeteer signal handlers for launched browsers
([#3238](#3238))
([3afec73](3afec73)),
closes
[#3154](#3154)
* discover third-party developer tools before tool lookup
([#3208](#3208))
([2777fa0](2777fa0)),
closes
[#2973](#2973)
* discover third-party developer tools in page tools
([#3207](#3207))
([cf33529](cf33529)),
closes
[#2980](#2980)
* drop InlineTextBox nodes from text snapshots
([#3202](#3202))
([f8c775a](f8c775a))
* **emulate:** keep previous viewport when setViewport fails
([#3150](#3150))
([19d883c](19d883c))
* exclude source maps from the npm package
([#3295](#3295))
([d575da4](d575da4))
* keep surrogate pairs and emoji whole when truncating titles, names and
bodies
([#3215](#3215))
([058adcf](058adcf))
* let resourceTypes match CORS preflights and prefetches
([#3217](#3217))
([e342c6e](e342c6e))
* **lighthouse:** explain unsupported URL schemes in navigation mode
([#3144](#3144))
([c6ad84a](c6ad84a))
* **lighthouse:** fail the audit when Lighthouse reports a runtime error
([#3188](#3188))
([53ea78d](53ea78d))
* **lighthouse:** restore the emulated viewport after a navigation audit
([#3189](#3189))
([c777ece](c777ece))
* **network:** list only earlier hops in a request's redirect chain
([#3198](#3198))
([4675940](4675940))
* **network:** remove unsupported websocket from list_network_requests
filter
([#2961](#2961))
([#3282](#3282))
([8b119d9](8b119d9))
* **network:** reset page network emulation before applying URL
restrictions
([#3312](#3312))
([f25f3df](f25f3df))
* **pages:** reject devtools:// URLs in navigate_page and new_page
([#2935](#2935))
([#3128](#3128))
([c94c03d](c94c03d))
* **pages:** report the actual page size after resize_page
([#2998](#2998))
([#3132](#3132))
([f003056](f003056))
* **pages:** revert reporting the page size after resize_page
([#3308](#3308))
([186ea66](186ea66)),
closes
[#3306](#3306)
* **pagination:** return all items when pageSize is omitted and pageIdx
is 0
([#2996](#2996))
([#3134](#3134))
([3ad9115](3ad9115))
* **performance:** populate structuredContent for traceInsight in
performance_analyze_insight
([#2981](#2981))
([#3115](#3115))
([c2c6cc4](c2c6cc4))
* preserve unread page fallback across snapshots and deduplicate page
disposal
([#3161](#3161))
([8604365](8604365))
* print the no-roots temp-directory warning under default flags
([#2927](#2927))
([0dd8ed9](0dd8ed9))
* reject empty, extra, and unknown parts in geolocation and viewport
transforms
([#3097](#3097))
([#3120](#3120))
([3ea31fb](3ea31fb))
* resolve error causes on the session that reported the exception
([#3220](#3220))
([b2d37e8](b2d37e8))
* resolve service worker IDs for console messages
([#2881](#2881))
([3804a9a](3804a9a))
* return message when page exposes no 3p developer tools
([#3197](#3197))
([663f3c3](663f3c3)),
closes
[#3004](#3004)
* **script:** surface non-Error thrown and rejected values in
evaluate_script
([#2951](#2951))
([#3127](#3127))
([b1e6e68](b1e6e68))
* **security:** validate filesystem paths that parse as http/ws URLs
([#3186](#3186))
([91e112d](91e112d))
* **snapshot:** omit uid for accessibility nodes without backendNodeId
([#3187](#3187))
([9054351](9054351))
* stop active screencast on McpPage.dispose()
([#3160](#3160))
([558af81](558af81))
* stop get_network_request waiting forever for an unread no-store body
([#3216](#3216))
([c556be1](c556be1))
* surface nested error causes and autoConnect hint on connection failure
([#3111](#3111))
([d7f1e78](d7f1e78))
* treat 0.0.0.0 and [::] browser endpoints as local
([#3321](#3321))
([7d5f7de](7d5f7de))
* treat IPv4-mapped IPv6 loopback as localhost
([#3255](#3255))
([6cfb34c](6cfb34c))
* truncate data: URLs in accessibility snapshots
([#3292](#3292))
([11e7d47](11e7d47))
* validate type_text submitKey before typing
([#3185](#3185))
([c8615d7](c8615d7))
* **wait_for:** find text in dynamically attached iframes
([#3210](#3210))
([5f84888](5f84888))
* warn when screenshot dimensions exceed 16384px limit
([#2952](#2952))
([#3291](#3291))
([10455b7](10455b7))


### 📄 Documentation

* add a pre-submit checklist to AGENTS.md
([#3236](#3236))
([e96ee6c](e96ee6c))
* clarify URL pattern port and hostname matching
([#3287](#3287))
([4723335](4723335))
* **config:** note that hot-reloaded URL restrictions keep open targets
attached
([#3314](#3314))
([47db2b8](47db2b8))
* correct CLI availability of extension tools
([#2852](#2852))
([a7280ee](a7280ee))
* explain how to install skills, including the CLI skill
([#3274](#3274))
([46eb546](46eb546))
* remove experimental status from CLI and promote it in README
([#3273](#3273))
([0ced16b](0ced16b))
* require a rebase only when the branch has merge conflicts
([#3239](#3239))
([d1143ac](d1143ac))
* **skills:** clarify Lighthouse score scale and contrast workflow
([#3006](#3006))
([#3218](#3218))
([bf8c6d9](bf8c6d9))


### ⚡ Performance

* **cli:** load a small third-party bundle in the chrome-devtools CLI
([#3200](#3200))
([552e1f8](552e1f8))
* load the Lighthouse bundle on first use
([#3169](#3169))
([798f03a](798f03a))


### 🏗️ Refactor

* **config:** isolate scripts and unit tests from home config discovery
([#3256](#3256))
([65e6b97](65e6b97))
* **config:** lock slim and pageIdRouting options when viaCli is set
([#3257](#3257))
([384d9aa](384d9aa))
* **config:** pass ReadonlyServerConfig to BrowserManager and add
OptionGate
([#3267](#3267))
([98302f2](98302f2))
* **config:** simplify watchConfig and start watcher inside connect
([#3259](#3259))
([878c5f6](878c5f6))
* extract errorResult() and routedPageId() in ToolHandler
([#3251](#3251))
([9a6b03a](9a6b03a))
* import types and values from a module in one statement
([#3303](#3303))
([f4eaf1f](f4eaf1f))
* **lighthouse:** move Lighthouse runner methods to McpPage and add
bundle types
([#3283](#3283))
([e1823a2](e1823a2))
* list optional properties last in inline object types
([#3302](#3302))
([dc929c0](dc929c0))
* register all tools upfront across modes
([#3163](#3163))
([58f8d1f](58f8d1f))
* **response:** pass live ReadonlyServerConfig to McpResponse
([#3258](#3258))
([6042490](6042490))
* **tools:** add explicit factory types to tool definitions
([#3284](#3284))
([4d1d7b2](4d1d7b2))
* use #private names instead of the private modifier
([#3301](#3301))
([2e13311](2e13311))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants