Skip to content

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.

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.
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.

Read the report-back list first, then check three things in the code:

  1. Every wrap() result is assigned. wrap() returns the same object, so a discarded result looks harmless and captures nothing.
  2. 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.
  3. 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.