> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sedata-ai.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# BMI calculator

> The minimum viable instrumented MCP server.

The smallest possible end-to-end example: one tool, full instrumentation,
graceful shutdown, no safety check.

## `server.ts`

```ts theme={null}
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { instrumentServer } from '@sedata-ai/mcp'
import type { TelemetryConfig } from '@sedata-ai/mcp'
import { z } from 'zod'

const NAME = 'bmi-mcp'
const VERSION = '1.0.0'

const server = new McpServer({ name: NAME, version: VERSION })

const telemetryConfig: TelemetryConfig = {
  serverName: NAME,
  serverVersion: VERSION,
  exporterEndpoint: 'https://otel.sedata-ai.tech/v1',
  exporterAuth: { type: 'bearer', token: process.env.SEDATA_TOKEN! },
}

const telemetry = instrumentServer(server, telemetryConfig)

server.registerTool(
  'calculate-bmi',
  {
    title: 'BMI Calculator',
    description: 'Calculate Body Mass Index',
    inputSchema: { weightKg: z.number(), heightM: z.number() },
    outputSchema: { bmi: z.number() },
  },
  async ({ weightKg, heightM }) => {
    if (heightM <= 0) throw new RangeError('heightM must be > 0')
    const output = { bmi: weightKg / (heightM * heightM) }
    return {
      content: [{ type: 'text', text: JSON.stringify(output) }],
      structuredContent: output,
    }
  },
)

const stop = async (code = 0) => {
  await telemetry.shutdown()
  process.exit(code)
}
process.on('SIGINT', () => stop(0))
process.on('SIGTERM', () => stop(0))

const transport = new StdioServerTransport()
server.connect(transport)
```

## Run

```bash theme={null}
SEDATA_TOKEN=sk_live_xxx npx ts-node server.ts
```

## What this demonstrates

<CardGroup cols={2}>
  <Card title="Auto span on every call" icon="route">
    Each call to `calculate-bmi` produces a `tools/call calculate-bmi` span
    with full attribute set.
  </Card>

  <Card title="Error attributes" icon="triangle-exclamation">
    The `RangeError` on bad input sets `error.type`, `error.message`, and
    `mcp.operation.success: false`.
  </Card>

  <Card title="Auto metrics" icon="gauge">
    `mcp.server.operation.duration` and `mcp.server.operation.count`
    record automatically.
  </Card>

  <Card title="Graceful shutdown" icon="power-off">
    SIGINT / SIGTERM trigger `telemetry.shutdown()` so the final metric
    batch flushes.
  </Card>
</CardGroup>

## Trying error attribution

Send `heightM: 0` and you'll see a span with status `ERROR` and these
attributes:

```text theme={null}
error.type             RangeError
error.message          heightM must be > 0
mcp.operation.success  false
```

The duration histogram is still recorded with `mcp.operation.success: false`,
which is what you want for error-rate alerts.
