Instrument a repository
The full pass: install the SDK, wrap every client, name the functions, wire up configuration, and report back on the calls it could not reach. Paste one of the blocks below into Claude Code, Codex, Cursor or whatever you use, from inside the repository.
Pick your language. The two prompts are written separately so the agent never reads instructions for a stack you do not have.
Each prompt links out rather than restating facts. That is deliberate. The earlier single-page version of these prompts copied configuration details by hand, which meant every factual correction had to be re-pasted into every repository that had already run it.
Python
Section titled “Python”Instrument this Python repository's LLM costs with Metergraph(https://github.com/PioneerSquareLabs/metergraphsdk).
INSTALL pip install metergraph, or whichever installer this repo uses (poetry, uv, pipenv). No runtime dependencies.
WRAP EVERY CLIENT Find every construction of OpenAI(), AsyncOpenAI(), Anthropic(), AsyncAnthropic() or genai.Client() and wrap it in place:
import metergraph client = metergraph.wrap(OpenAI())
wrap() returns the same object, so assign it. Call sites, arguments, streaming and async all keep working untouched.
A client pointed at a gateway needs no special handling, but it does have secrets you must not touch. Read https://www.metergraph.dev/docs/instrument/vercel-ai-gateway/ before changing any client whose base_url is not the provider's own.
NAME THINGS Attribution is automatic: the SDK walks the stack to the nearest function under the app root. Add @metergraph.track to the functions that matter, so the name survives a refactor.
Group calls by product surface where the surface is obvious:
with metergraph.route("ticket-classifier"): ...
Wrap multi-call workflows in metergraph.trace("checkout").
What each of these five labels means, and which question each one answers, is at https://www.metergraph.dev/docs/concepts/identity-model/
SCOPE REQUEST IDENTITY If the application has a per-request or per-job session ID or tags, scope the whole operation:
with metergraph.context( session_id=session_id, tags={"customer_tier": "pro"}, ): ...
Use set_default_tags() only for deliberate service-wide labels. Pass thread-pool executors through metergraph.wrap_executor() so the active scope follows submitted work; see https://www.metergraph.dev/docs/instrument/threads/
CONFIGURE Add the SDK's environment variables to .env.example or the deployment config, and never commit a real token. The authoritative list of variables, their types and their defaults is at https://www.metergraph.dev/docs/reference/configuration/ Read it rather than guessing, and do not invent variable names.
Leave content capture at its default. Turn it off only if the deployment owner has explicitly chosen metadata-only capture; the controls and their consequences are at https://www.metergraph.dev/docs/guides/keep-content-private/ Report any global, route or trace content opt-out already present in the repository rather than changing it.
Configure repository identity once, by whichever of the supported options suits this repo: https://www.metergraph.dev/docs/concepts/identity-model/ If a committed .metergraph/config.json is used, it is read-only to the SDK; do not generate it at runtime.
FINISH THE WORK A script or one-shot job that exits should call metergraph.shutdown() after its work, which sends queued telemetry and stops background work. In serverless handlers where the runtime may freeze but the process stays active, call metergraph.flush() before returning. Long-running servers should call shutdown() during graceful termination. The rules per runtime are at https://www.metergraph.dev/docs/instrument/serverless/
DO NOT Do not add try/except around wrapping or wrapped calls. The SDK is fail-open and never raises into the application. Do not refactor, rename or reformat anything you are not instrumenting. Do not add a dependency to make any of this easier.
REPORT BACK List every client you wrapped with file and line. Then list every LLM call you found that is NOT captured: raw HTTP calls, clients you could not reach, workers with their own setup path. Those are invisible to Metergraph and I need to know about them.TypeScript
Section titled “TypeScript”Instrument this TypeScript repository's LLM costs with Metergraph(https://github.com/PioneerSquareLabs/metergraphsdk).
INSTALL npm install metergraph, or whichever package manager this repo uses. No runtime dependencies.
WRAP EVERY CLIENT Find every construction of new OpenAI(), new Anthropic() or new GoogleGenAI() and wrap it in place:
import * as mg from "metergraph"; const client = mg.wrap(new OpenAI());
wrap() returns the same object, so assign it. Call sites, arguments, streaming and async all keep working untouched.
Vercel AI SDK models are NOT wrapped this way. Use middleware:
wrapLanguageModel({ model, middleware: mg.vercelAISDKMiddleware() })
The version flag and the multi-step tool-loop behaviour are at https://www.metergraph.dev/docs/instrument/vercel-ai-sdk/
A client pointed at a gateway needs no special handling, but it does have secrets you must not touch. Read https://www.metergraph.dev/docs/instrument/vercel-ai-gateway/ before changing any client whose baseURL is not the provider's own.
NAME THINGS, THIS IS NOT OPTIONAL Bundlers and minifiers destroy function names, so stack attribution is only a fallback. Wrap EVERY function that calls a wrapped client:
export const classify = mg.track("billing.classify", () => ...);
The stable name comes first. Then group by product surface where the surface is obvious:
mg.route("ticket-classifier", () => ...)
Wrap multi-call workflows in mg.trace("checkout", fn).
What each of these five labels means, and which question each one answers, is at https://www.metergraph.dev/docs/concepts/identity-model/
SCOPE REQUEST IDENTITY If the application has a per-request or per-job session ID or tags, wrap the whole operation:
mg.withContext( { sessionId, tags: { customerTier: "pro" } }, async () => { ... }, )
Use setDefaultTags() only for deliberate service-wide labels.
CONFIGURE Add the SDK's environment variables to .env.example or the deployment config, and never commit a real token. The authoritative list of variables, their types and their defaults is at https://www.metergraph.dev/docs/reference/configuration/ Read it rather than guessing, and do not invent variable names.
Leave content capture at its default. Turn it off only if the deployment owner has explicitly chosen metadata-only capture; the controls and their consequences are at https://www.metergraph.dev/docs/guides/keep-content-private/ Report any global, route or trace content opt-out already present in the repository rather than changing it.
Configure repository identity once, by whichever of the supported options suits this repo: https://www.metergraph.dev/docs/concepts/identity-model/ If a committed .metergraph/config.json is used, it is read-only to the SDK; do not generate it at runtime.
FINISH THE WORK A script or one-shot job that exits should await mg.shutdown() after its work, which sends queued telemetry and stops background work. Serverless runtimes freeze between requests, so delivery has to be forced: wrap the handler with mg.wrapHandler(handler), or call mg.bindWaitUntil(ctx) once per request, or await mg.flush() before returning. Long-running servers should call shutdown() during graceful termination. The rules per runtime are at https://www.metergraph.dev/docs/instrument/serverless/
DO NOT Do not add try/catch around wrapping or wrapped calls. The SDK is fail-open and never raises into the application. Do not refactor, rename or reformat anything you are not instrumenting. Do not add a dependency to make any of this easier.
REPORT BACK List every client and AI SDK model you instrumented with file and line, and every function you wrapped in track(). Then list every LLM call you found that is NOT captured: raw fetch calls, clients you could not reach, workers with their own setup path. Those are invisible to Metergraph and I need to know about them.After it finishes
Section titled “After it finishes”Read the report-back list first, then check three things in the code:
- Every
wrap()result is assigned.wrap()returns the same object, so a discarded result looks harmless and captures nothing. - The token is set wherever the code runs, including CI, workers and preview environments. Capture is off and silent without it. See Install the SDK.
- A short-lived process ends with
shutdown(), and a serverless handler forces delivery. See Serverless and short-lived jobs.
Then send some traffic and confirm rows arrive: Send your first trace.