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

# Weather server

> A complete instrumented MCP server with two tools — one safe, one unsafe.

This is the canonical example shipped in the
[`sedata-ai-packages`](https://github.com/sedata-ai/sedata-ai-packages)
repo at `example-servers/typescript-mcp-server/`. It instruments two tools:

* `calculate-bmi` — pure math, no safety check.
* `text-summarizer` — accepts free-form text, wrapped with `safetyCheck`.

## Project setup

```bash theme={null}
mkdir weather-mcp-server && cd weather-mcp-server
npm init -y
npm install @sedata-ai/mcp @modelcontextprotocol/sdk zod
npm install -D typescript ts-node @types/node
npx tsc --init
```

Set your token:

```bash theme={null}
export SEDATA_TOKEN="sk_live_xxx"
```

## `server.ts`

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

const NAME = 'weather-mcp-server'
const VERSION = '1.2.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)

// 1. Math tool — no safety needed
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 }) => {
    const output = { bmi: weightKg / (heightM * heightM) }
    return {
      content: [{ type: 'text', text: JSON.stringify(output) }],
      structuredContent: output,
    }
  },
)

// 2. Text tool — wrap with safetyCheck
server.registerTool(
  'text-summarizer',
  {
    title: 'Text Summarizer',
    description: 'Summarize text content',
    inputSchema: { text: z.string() },
    outputSchema: { summary: z.string() },
  },
  safetyCheck(
    async ({ text }: any) => {
      const summary = text.substring(0, 100) + '...'
      return {
        content: [{ type: 'text', text: JSON.stringify({ summary }) }],
        structuredContent: { summary },
      }
    },
    { parameterName: 'text', output_screen: true },
  ),
)

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

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

## Run

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

Drive it with any MCP client (Claude Desktop, Inspector, your agent runtime).
After a few calls, traces appear at
[app.sedata-ai.tech](https://app.sedata-ai.tech) within \~5 seconds.

## What you'll see

| Tool              | Span                         | Notable attributes                                                |
| ----------------- | ---------------------------- | ----------------------------------------------------------------- |
| `calculate-bmi`   | `tools/call calculate-bmi`   | `mcp.tool.name`, `mcp.operation.duration`                         |
| `text-summarizer` | `tools/call text-summarizer` | Above + `mcp.safety_check.flagged`, `mcp.safety_check.latency_ms` |

If the safety API flags an input, the response includes
`structuredContent.summary === '🚫 CONTENT BLOCKED: ...'` and the original
handler is **not** called.

## Variations

<CardGroup cols={2}>
  <Card title="BMI calculator only" icon="calculator" href="/examples/bmi-calculator">
    A minimal version with one tool, no safety.
  </Card>

  <Card title="Summarizer with safety" icon="shield-check" href="/examples/text-summarizer-with-safety">
    Focused walkthrough of the safety wrapper.
  </Card>
</CardGroup>
