ToolContext
The invocation context handed to every tool handler — a small capability object with no ambient authority.
Each member maps to a Trove-provided callback the SDK closes over the
short-TTL ctxToken; the blast radius of a handler is exactly its declared
secrets ∪ scopes ∪ egress.
Extends
Section titled “Extends”Properties
Section titled “Properties”| Property | Modifier | Type | Description | Inherited from |
|---|---|---|---|---|
cache? | public | ExtensionCache | A best-effort key/value cache that outlives a single run. Optional, and absent is normal — a host may provide no cache at all, so read it as ctx.cache?.get(...) and treat a miss and an absent cache the same way. Never the source of truth: anything here must be re-derivable from the upstream, because it can vanish between runs. | ExtensionContext.cache |
config | readonly | Readonly<Record<string, unknown>> | The user’s settings for this toolkit, keyed by the field names its manifest declares in config. {} for a toolkit that declares no settings, or whose owner has not filled any in — the manifest’s declared defaults are what apply. Preferences only, never credentials: a credential comes from ToolContext.secret, where it is encrypted at rest and redacted out of logs. Trove refuses a settings write that looks like a secret, so a toolkit should not go looking for one here. The same shape a source gets from ctx.config in @ontrove/extend/source. | - |
fetch | public | FetchLike | Behaves like the standard fetch, and is the ONLY way out. In Trove’s cloud every request is routed through an egress worker that permits https alone, matches the manifest’s declared hosts exactly, and refuses private and link-local addresses even when allowlisted. On the Mac it adds per-source timeouts, retry and rate-limit handling. Prefer it over global fetch everywhere: the global one is unguarded where it exists at all, and absent where it does not. | ExtensionContext.fetch |
log | public | LogChannel | Where an extension says what it is doing. Surfaced in the run transcript. | ExtensionContext.log |
trove? | readonly | TroveClient | A scoped client over the caller’s own knowledge base — present only if the manifest scopes requested trove:search and/or trove:ingest. | - |
userId | readonly | string | The authenticated Clerk user id of the caller — identity, not a credential. | - |
Methods
Section titled “Methods”fetchJson()
Section titled “fetchJson()”Call Signature
Section titled “Call Signature”fetchJson<S>(url: string | URL, opts: FetchJsonOpts & { schema: S;}): Promise<output<S>>;Fetch JSON with batteries included: routes through fetch (so the
default UA and any declarative auth apply), maps a non-2xx status to a
ToolError (4xx≠429 → non-retryable; 429/5xx/network → retryable),
guards malformed JSON, and — when opts.schema is supplied — validates the
body and returns the typed result. Omit schema to receive parsed unknown.
Keep the schema lenient (.default()/.nullish()); it is for parsing the
upstream shape, not the tool’s strict output contract.
Type Parameters
Section titled “Type Parameters”| Type Parameter |
|---|
S extends ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>> |
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
url | string | URL |
opts | FetchJsonOpts & { schema: S; } |
Returns
Section titled “Returns”Promise<output<S>>
Call Signature
Section titled “Call Signature”fetchJson(url: string | URL, opts?: FetchJsonOpts): Promise<unknown>;Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
url | string | URL |
opts? | FetchJsonOpts |
Returns
Section titled “Returns”Promise<unknown>
now(): Date;The current wall-clock time.
Injected rather than read from a global so a run is deterministic under test, and so a replayed fixture means the same thing tomorrow.
Returns
Section titled “Returns”Date
Inherited from
Section titled “Inherited from”requireSecret()
Section titled “requireSecret()”requireSecret(name: string): Promise<string>;secret, for a credential the extension genuinely cannot proceed
without: rejects, naming the credential, rather than resolving undefined.
Prefer it to if (!(await ctx.secret(n))) throw — the failure message is
the useful half, and the host writes a better one than each extension will.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
name | string |
Returns
Section titled “Returns”Promise<string>
Inherited from
Section titled “Inherited from”ExtensionContext.requireSecret
secret()
Section titled “secret()”secret(name: string): Promise<string | undefined>;A credential the manifest declared, by name — undefined when it is not
set.
Resolves whether the value was pasted by the user or is a token Trove refreshed a moment ago — the extension cannot tell, and must not need to. That is what keeps delegated authorization out of every author’s code. Async for the same reason: resolving may be a vault read or a refresh.
This is the reader for a credential the extension can work without — an optional API key that raises a rate limit, an OAuth client secret a public client does not have. When the extension cannot proceed without the value, use requireSecret and let it raise.
Until 3.3.0 this rejected, and requireSecret was documented as behaving
“identically; the name is the documentation” — so there were two names for
one behaviour and no way at all to read an optional credential. The two
sources that had one reached around the whole mechanism into the legacy
ctx.credentials bag instead.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
name | string |
Returns
Section titled “Returns”Promise<string | undefined>