fivebar

Server event examples

Copy page
View Markdown
Open with
Connect MCP
Cursor VS Code

Code to send an event for each call to your service, from curl, Python, Node or an MCP server.

Each sends one call’s event with the token from the FIVEBAR_TOKEN environment variable, and gives up after 2 seconds. Send it once your service has answered, never waiting for it. Server events says how to make the token, what each field holds and what each answer means.

curl

curl https://fiveb.ar/api/events \
  -H "Authorization: Bearer $FIVEBAR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"site": "example.com", "name": "/v1/postcodes/:code", "ip": "203.0.113.7"}' \
  --max-time 2 -w '%{http_code}\n'

Python

With the standard library alone, in a thread of its own:

import json, os, threading, urllib.request

def send_event(name, **caller):
    req = urllib.request.Request(
        'https://fiveb.ar/api/events',
        data=json.dumps({'site': 'example.com', 'name': name, **caller}).encode(),
        headers={
            'Authorization': 'Bearer ' + os.environ['FIVEBAR_TOKEN'],
            'Content-Type': 'application/json',
        },
    )

    def send():
        try:
            urllib.request.urlopen(req, timeout=2).close()
        except OSError:
            pass

    threading.Thread(target=send).start()

send_event('/v1/postcodes/:code', ip='203.0.113.7')

Node

For Node 18 or later. sendEvent() doesn’t wait for fivebar’s answer. With Express, req.route.path is the route as written, req.ip is the caller’s address only where Express’s trust proxy setting names the proxies in front of your service, as a proxy’s own address is refused, bad ip, and lookUp() stands for your own code:

export function sendEvent(name, caller = {}) {
  fetch('https://fiveb.ar/api/events', {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + process.env.FIVEBAR_TOKEN,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ site: 'example.com', name, ...caller }),
    signal: AbortSignal.timeout(2000),
  }).catch(() => {});
}

app.get('/v1/postcodes/:code', async (req, res) => {
  res.json(await lookUp(req.params.code));
  sendEvent(req.route.path, { ip: req.ip, user_agent: req.get('User-Agent') });
});

An MCP server

With version 1 of the MCP TypeScript SDK, @modelcontextprotocol/sdk, and sendEvent() above, each tools/call is counted once it has its answer: the tool as its name, the session as its caller and the app’s clientInfo.name as its source, read as What is counted says. server is the SDK’s Server, and callTool() your own:

import { CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';

server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
  const answer = await callTool(request.params.name, request.params.arguments);
  sendEvent(request.params.name, {
    id: extra.sessionId,
    client: server.getClientVersion()?.name,
  });
  return answer;
});

A session has an id only on a server that keeps sessions, over Streamable HTTP with a sessionIdGenerator. A stateless server, or one over stdio, has none, and an event with neither id nor ip is refused: send another id there, such as the account calling, as fivebar’s own MCP server does.