Metafetch calls Claude, GPT, or any model API you describe in a recipe file
One recipe format for any model API. Claude and OpenAI are built in, keys stay in Obsidian's keychain, and every plugin can share them.
Why Care?
A Tooling note's links come from its homepage, but some fields need a reader: a one-line zinger, a pricing model, the right category from your own list. Those need a model, and which model is your call. Claude this month, GPT next, a local model for a private vault, whatever a router offers next year.
No Lossless plugin had a good answer to "use any model". Each one hard-wired its providers with a plaintext key field per provider. Metafetch now has the answer, built to be copied into the rest.
What's New?
from: [model]fields in folder profiles, with adescribe:instruction and optionaltype: enum/type: listwithvalues. One call per note, after the free fields, reading the page's text.Four providers built in: Claude (
claude-opus-5-5, reply held to a JSON schema, server-side fallback on a decline), OpenAI (gpt-6-astra, strict JSON schema), TrustedRouter, and any OpenAI-compatible endpoint.Any other API as a recipe file in
zz-cf-lib/recipes/. Create example model recipe writes one for Google Gemini's native API.Keys in Obsidian's keychain. Settings store the secret's name, never the key, and the keychain is vault-wide.
How a recipe stays safe
A recipe is a vault file, and vaults get shared. So:
A recipe is data. The only computation is filling
{{model}},{{system}},{{prompt}},{{schema}}, and{{baseUrl}}into the body and reading one path out of the reply.A bundled recipe's key goes only to its own host. A recipe file's key goes nowhere until you list its host under Approved hosts in settings, so a file someone drops into a shared vault can't send your keys elsewhere.
Plain http is refused, except to localhost.
An error quotes the provider's message, never the request.
The model's answer is checked against the profile before anything is written. A value outside an
enumis dropped, and URLs are never asked of a model.
Proof
One live call each, on TinaCMS's homepage text, through the same engine:
claude-opus-5-5, 4.4 s: "Open-source, Git-backed headless CMS with visual editing for developers' Markdown sites."gpt-6-astra, 2.7 s: "A Git-backed CMS that lets developers edit website content visually."
Under the hood
src/services/modelRecipes.ts: the recipe type, the bundled recipes,parseRecipe()for vault files, andcallModel(). Its helpers:substitute()(a whole-string{{schema}}keeps its object type),readPath()([0],[-1],[?type=text]), andparseJsonObject()(bare, fenced, or wrapped in prose).src/services/modelProviderSettings.ts: the settings rows (aSecretComponentper provider, model, base URL, approved hosts), plusmergeProviderSettings()so a provider added later gets its defaults.frontmatterProfiles.ts:modelSchema()makes every field nullable and required, as strict schema modes need;modelPrompt();acceptModelValue().Both service files depend only on Obsidian's API, and the fence is
cf-reciperather than a Metafetch name, so other plugins can copy the files and share the recipe folder.21 new tests (158 total).
What's Next
The other plugins: see the blueprint's migration table. Perplexed's Perplexity and Anthropic calls fit as-is; it also needs streaming, which recipes don't do yet.
Moving each plugin's plaintext key fields into the keychain is worth doing now, ahead of the full move to recipes.