Documentation Index Fetch the complete documentation index at: https://sedataai.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
The auto-instrumentation gives you per-tool spans for free. For everything
else — cache lookups, upstream HTTP calls, business outcomes — use the
helpers on the ObservabilityInstance returned by instrumentServer.
Setup once, reuse everywhere
Create your instruments once at module scope so you’re not reallocating
histograms or counters on the hot path.
import { instrumentServer } from '@sedata-ai/mcp'
export const telemetry = instrumentServer ( server , telemetryConfig )
export const recordCacheLookup = telemetry . getHistogram ( 'cache.lookup.duration' , {
description: 'Cache lookup duration' ,
unit: 'ms' ,
})
export const incCacheOutcome = telemetry . getIncrementCounter ( 'cache.outcome.count' , {
description: 'Cache hit/miss outcome' ,
})
Then import where you need them:
import { telemetry , recordCacheLookup , incCacheOutcome } from './instruments'
Pattern: time + count an operation
async function fetchUser ( id : string ) {
return telemetry . startActiveSpan (
'user.fetch' ,
{ 'user.id' : id },
async ( span ) => {
const t0 = Date . now ()
try {
const user = await db . user . findUnique ({ where: { id } })
span . setAttribute ( 'user.found' , !! user )
return user
} catch ( err ) {
span . recordException ( err as Error )
span . setStatus ({ code: 2 , message: ( err as Error ). message })
throw err
} finally {
span . setAttribute ( 'user.fetch.duration_ms' , Date . now () - t0 )
span . end ()
}
},
)
}
The new span becomes a child of the auto-generated tools/call <toolName>
span if you call this from inside a tool handler.
const t0 = Date . now ()
const value = cache . get ( key )
recordCacheLookup ( Date . now () - t0 , {
'cache.hit' : !! value ,
'cache.key.kind' : 'user' ,
})
incCacheOutcome ( 1 , { outcome: value ? 'hit' : 'miss' })
Pattern: business outcomes
Counters are great for outcome tracking even when nothing is timed:
const incPaymentOutcome = telemetry . getIncrementCounter ( 'payment.outcome.count' , {
description: 'Payment outcome' ,
})
incPaymentOutcome ( 1 , { result: 'authorized' , method: 'card' })
incPaymentOutcome ( 1 , { result: 'declined' , method: 'card' , code: 'insufficient_funds' })
Pattern: distributed trace context
If a tool calls an upstream HTTP service, propagate the active context so the
upstream’s spans hang under your trace:
import { context , propagation } from '@opentelemetry/api'
await telemetry . startActiveSpan (
'github.repos.get' ,
{ 'http.method' : 'GET' , 'http.target' : '/repos/x/y' },
async ( span ) => {
const headers : Record < string , string > = {}
propagation . inject ( context . active (), headers )
const res = await fetch ( 'https://api.github.com/repos/x/y' , { headers })
span . setAttribute ( 'http.status_code' , res . status )
span . end ()
},
)
The upstream service (if it’s also instrumented with OTel) will continue your
trace.
Pattern: feature-flag the SDK
If you want to gate instrumentation:
const enabled = process . env . ENABLE_SEDATA === 'true'
const telemetry = enabled
? instrumentServer ( server , config )
: noopTelemetry ()
function noopTelemetry () {
const noop = () => {}
return {
startActiveSpan : ( _n : string , _a : any , fn : any ) => fn ({ end: noop , setAttribute: noop , setStatus: noop , recordException: noop }),
getHistogram : () => noop ,
getIncrementCounter : () => noop ,
processTelemetryAttributes : ( d : any ) => d ,
shutdown : async () => {},
}
}
Pattern: async iterators
If your tool streams events, record one span per batch + a counter per event:
const incEvent = telemetry . getIncrementCounter ( 'stream.event.count' , {
description: 'Stream event count' ,
})
for await ( const evt of stream ) {
incEvent ( 1 , { type: evt . type })
}
For a span-per-event you’d lose more in overhead than you’d gain in
visibility — counters are usually the right shape for high-frequency events.
Pattern: shut down on signal
const stop = async ( code = 0 ) => {
await telemetry . shutdown ()
process . exit ( code )
}
process . on ( 'SIGINT' , () => stop ( 0 ))
process . on ( 'SIGTERM' , () => stop ( 0 ))
process . on ( 'uncaughtException' , ( err ) => {
console . error ( err )
stop ( 1 )
})
See also
Automatic metrics What the package records without any code.
Data processors Mutate every attribute set in one place.