Problem
While reviewing the OmniVoice Toolcraft CLI, I found a workaround that looks better fixed in Toolcraft/toolcraft-openapi itself.
OmniVoice needs to attach either a LAN-share PIN header (X-OmniVoice-Pin) or a bearer API key to every request sent by both generated OpenAPI commands and handwritten commands. Today the wrapper does this by monkeypatching globalThis.fetch at module load time:
OmniVoice-Studio/tools/omnivoice-toolcraft/src/auth.js:8 defines installOmniVoiceFetchAuth(...)
auth.js:16 replaces globalThis.fetch
auth.js:28 adds X-OmniVoice-Pin
auth.js:31 adds Authorization: Bearer ...
root.js:14 installs the patch before defineClientFromSpec(...)
bin.js:11 / bin.js:18 pass only services into runMCP / runCLI
That works, but it is process-global and order-sensitive. It also makes one client package mutate the fetch behavior of unrelated code in the same Node process.
Why it seems library-level
Toolcraft command handlers already receive ctx.fetch, and toolcraft-openapi runtime commands already call requestJson(..., fetch: ctx.fetch). So the plumbing is almost there.
The blocker is that the runtime entrypoints currently hard-code globalThis.fetch for the built-in handler context while also reserving fetch as a service name:
packages/toolcraft/src/cli.ts:72 reserves fetch
packages/toolcraft/src/cli.ts:2863 uses fetch: globalThis.fetch in normal runtime
packages/toolcraft/src/sdk.ts:21 reserves fetch
packages/toolcraft/src/sdk.ts:669 uses fetch: globalThis.fetch
packages/toolcraft/src/mcp.ts:34 reserves fetch
packages/toolcraft/src/mcp.ts:800 uses fetch: globalThis.fetch
Because fetch is reserved, a consumer cannot pass an authenticated fetch through services. Because the entrypoints do not expose a runtime fetch option, the consumer has to patch the global.
toolcraft-openapi auth is also currently token-oriented: AuthProvider.getToken() feeds Authorization: Bearer <token>. That does not cover APIs that require non-standard headers such as X-OmniVoice-Pin, short-lived request signing, or multiple auth headers.
Requested change
Provide a first-class way for Toolcraft consumers to customize the fetch used in command contexts across CLI, MCP, and SDK.
A minimal shape could be:
await runCLI(root, {
services,
fetch: authenticatedFetch
});
await runMCP(root, {
services,
fetch: authenticatedFetch
});
const sdk = createSDK(root, {
services,
fetch: authenticatedFetch
});
Then handlers and generated OpenAPI commands would see that as ctx.fetch. fetch can remain a reserved service name; it would just become an explicit runtime option rather than something consumers smuggle through services.
A complementary toolcraft-openapi improvement would be an auth/header provider abstraction, for example an auth provider method that can contribute request headers instead of only returning a bearer token. But exposing a runtime fetch option in Toolcraft core is enough to remove OmniVoice's global patch.
Acceptance criteria
- CLI, MCP, and SDK entrypoints accept an optional runtime fetch implementation.
ctx.fetch uses the supplied fetch when provided, and defaults to globalThis.fetch otherwise.
- Existing fixture/runtime fetch behavior keeps working.
toolcraft-openapi generated runtime commands automatically use the injected fetch because they already consume ctx.fetch.
- Consumers can implement origin-scoped header injection without mutating
globalThis.fetch.
Context from OmniVoice
Current workaround:
const originalFetch = globalThis.fetch;
globalThis.fetch = async (input, init = {}) => {
const requestUrl = new URL(
typeof input === "string" || input instanceof URL ? input : input.url,
baseUrl
);
if (requestUrl.origin !== targetOrigin) {
return originalFetch(input, init);
}
const headers = new Headers(init.headers || ...);
if (pin && !headers.has("X-OmniVoice-Pin")) {
headers.set("X-OmniVoice-Pin", pin);
}
if (apiKey && !headers.has("Authorization")) {
headers.set("Authorization", `Bearer ${apiKey}`);
}
return originalFetch(input, { ...init, headers });
};
This would become a local authenticatedFetch passed to Toolcraft entrypoints instead of a global mutation.
Problem
While reviewing the OmniVoice Toolcraft CLI, I found a workaround that looks better fixed in Toolcraft/toolcraft-openapi itself.
OmniVoice needs to attach either a LAN-share PIN header (
X-OmniVoice-Pin) or a bearer API key to every request sent by both generated OpenAPI commands and handwritten commands. Today the wrapper does this by monkeypatchingglobalThis.fetchat module load time:OmniVoice-Studio/tools/omnivoice-toolcraft/src/auth.js:8definesinstallOmniVoiceFetchAuth(...)auth.js:16replacesglobalThis.fetchauth.js:28addsX-OmniVoice-Pinauth.js:31addsAuthorization: Bearer ...root.js:14installs the patch beforedefineClientFromSpec(...)bin.js:11/bin.js:18pass onlyservicesintorunMCP/runCLIThat works, but it is process-global and order-sensitive. It also makes one client package mutate the fetch behavior of unrelated code in the same Node process.
Why it seems library-level
Toolcraft command handlers already receive
ctx.fetch, and toolcraft-openapi runtime commands already callrequestJson(..., fetch: ctx.fetch). So the plumbing is almost there.The blocker is that the runtime entrypoints currently hard-code
globalThis.fetchfor the built-in handler context while also reservingfetchas a service name:packages/toolcraft/src/cli.ts:72reservesfetchpackages/toolcraft/src/cli.ts:2863usesfetch: globalThis.fetchin normal runtimepackages/toolcraft/src/sdk.ts:21reservesfetchpackages/toolcraft/src/sdk.ts:669usesfetch: globalThis.fetchpackages/toolcraft/src/mcp.ts:34reservesfetchpackages/toolcraft/src/mcp.ts:800usesfetch: globalThis.fetchBecause
fetchis reserved, a consumer cannot pass an authenticated fetch throughservices. Because the entrypoints do not expose a runtime fetch option, the consumer has to patch the global.toolcraft-openapiauth is also currently token-oriented:AuthProvider.getToken()feedsAuthorization: Bearer <token>. That does not cover APIs that require non-standard headers such asX-OmniVoice-Pin, short-lived request signing, or multiple auth headers.Requested change
Provide a first-class way for Toolcraft consumers to customize the fetch used in command contexts across CLI, MCP, and SDK.
A minimal shape could be:
Then handlers and generated OpenAPI commands would see that as
ctx.fetch.fetchcan remain a reserved service name; it would just become an explicit runtime option rather than something consumers smuggle throughservices.A complementary
toolcraft-openapiimprovement would be an auth/header provider abstraction, for example an auth provider method that can contribute request headers instead of only returning a bearer token. But exposing a runtime fetch option in Toolcraft core is enough to remove OmniVoice's global patch.Acceptance criteria
ctx.fetchuses the supplied fetch when provided, and defaults toglobalThis.fetchotherwise.toolcraft-openapigenerated runtime commands automatically use the injected fetch because they already consumectx.fetch.globalThis.fetch.Context from OmniVoice
Current workaround:
This would become a local
authenticatedFetchpassed to Toolcraft entrypoints instead of a global mutation.