---
url: >-
  https://adk.nht.io/api/@nhtio/adk/batteries/orchestration/functions/effectiveToolMethods.md
---

# Function: effectiveToolMethods()

```ts
function effectiveToolMethods(
  ctor: ArtifactClassLike,
): readonly ArtifactMethodDescriptor[];
```

Defined in: [src/batteries/orchestration/artifact\_methods.ts:40](https://github.com/NHTIO/ADK/blob/v1.20260906.0/src/src/batteries/orchestration/artifact_methods.ts#L40)

The effective, deduped set of artifact method descriptors reachable on a class, leaf-first up
the static prototype chain.

WHY THE WALK IS NECESSARY — DO NOT "SIMPLIFY" THIS BACK TO `ctor.toolMethods`.

Core's `static toolMethods` SHADOWS rather than concatenates. `spooled_artifact.ts:232-240`
states it outright: *"Each `toolMethods` array lists **only** its own class's descriptors —
subclasses do not concatenate inherited descriptors."* So `SpooledJsonArtifact.toolMethods` is
its seven JSON descriptors and nothing else; the base seven (`artifact_head`, `artifact_tail`,
`artifact_grep`, `artifact_cat`, `artifact_byte_length`, `artifact_line_count`,
`artifact_estimate_tokens`) live on `SpooledArtifact` and are composed at a DIFFERENT layer —
`SpooledJsonArtifact.forgeTools` calls `SpooledArtifact.forgeTools(ctx)` and merges registries.

Reading the leaf static alone would therefore wrongly exclude the base methods: a consumer (or
freeze, or the transform runtime) that trusted `instance.constructor.toolMethods` would
advertise a vocabulary missing `artifact_head`/`_tail`/`_grep`/`_cat` on every JSON or Markdown
artifact, and would refuse a `transform` step that names one. There is no helper in core that
unions the chain, so this battery ships the one place the union is computed.

The walk:

1. Start at `ctor` and walk up via `Object.getPrototypeOf` until `Function.prototype` or null.
2. At each class take ONLY its own `toolMethods`, via `Object.getOwnPropertyDescriptor` — an
   inherited static must not be collected twice.
3. Collect leaf-first, deduping by descriptor `name` with NEAREST CLASS WINS (the first
   occurrence encountered wins). This matches the `Tool.onCollision = 'replace'` semantics core
   documents for the same overlap.
4. A class with no `toolMethods` anywhere yields an empty array — never throw.

Verified against the real classes: `effectiveToolMethods(SpooledJsonArtifact)` has 14 entries
and includes `artifact_head`; `SpooledMarkdownArtifact` has 15; `SpooledArtifact` itself has 7.

## Parameters

| Parameter | Type                                                            | Description                                                                                                                                   |
| --------- | --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `ctor`    | [`ArtifactClassLike`](../types/interfaces/ArtifactClassLike.md) | The artifact class whose effective descriptor set is wanted. Structural (`ArtifactClassLike`), so the battery never imports the core classes. |

## Returns

readonly [`ArtifactMethodDescriptor`](../types/interfaces/ArtifactMethodDescriptor.md)\[]

A frozen, readonly array of descriptors, leaf-first, deduped by `name` with
nearest-class-wins. Empty when no class in the chain declares `toolMethods`.
