@mediansh/agent-tools
v0.4.0
Published
Server-side Median helpers: define agent tools and serve them from one route, derive the browser-safe widget key, sign user identities and site sign-ins.
Readme
@mediansh/agent-tools
Server-side helpers for Median. Define tools the support agent can call in your own codebase, serve them from one route, and derive the keys the widget needs. Zero dependencies, WebCrypto only, Node 18+.
Tools in one route
import { p, median } from "@mediansh/agent-tools";
export const { GET, POST } = median({
tools: {
orderStatus: {
description: "Look up the status of an order by its number.",
input: { orderNumber: p.string("The order number, like ORD-1042.") },
async execute({ orderNumber }) {
return db.orders.findByNumber(orderNumber);
},
},
},
});Set MEDIAN_KEY in your server env, deploy, and connect the route once:
curl -H "authorization: Bearer $MEDIAN_KEY" https://example.com/api/medianEvery registration requires this header, including localhost and routes with
MEDIAN_TOOLS_URL set. Anonymous requests return 401 without tool metadata.
Manifest reads and tool calls require a valid Median signature. Responses
are private and must not be cached. risk: "high" on a tool makes
every call wait for a teammate's approval in the inbox.
Works as a Next route handler, and anywhere else that speaks fetch Request
and Response.
The widget key
import { publicKeyFromMedianKey } from "@mediansh/agent-tools";
const apiKey = publicKeyFromMedianKey(process.env.MEDIAN_KEY!);Derives the browser-safe median_pk_ id for the
@mediansh/widget
component without exposing the secret half.
Identity verification
// app/api/median/identity/route.ts
import { auth } from "@clerk/nextjs/server";
import { medianIdentity } from "@mediansh/agent-tools";
export const { GET } = medianIdentity(async () => (await auth()).userId);A whole route. It signs the id with MEDIAN_KEY, adds the browser-safe key,
and refuses to be cached, so the widget can tie a conversation to a person
across devices. Point it at the route and that is the install:
<MedianSupport identity="/api/median/identity" />signMedianUser(id) signs one id on its own, for an app that already has the
session where the widget is rendered.
Site sign-in
The same route signs visitors in to your public site. Set Site → Sign-in to Your app and paste the route's URL. Return an email with the id:
// app/api/median/identity/route.ts
import { currentUser } from "@clerk/nextjs/server";
import { medianIdentity } from "@mediansh/agent-tools";
export const { GET } = medianIdentity(
async () => {
const user = await currentUser();
if (!user) return null;
return {
id: user.id,
email: user.primaryEmailAddress?.emailAddress,
name: user.fullName ?? undefined,
};
},
{ signIn: (returnTo) => `/sign-in?redirect_url=${encodeURIComponent(returnTo)}` },
);signIn is where a visitor goes when they are not signed in to your app.
medianSiteRedirect(request, user) builds the same redirect for servers that
do not use Web Request and Response.
Docs
Tool patterns, risk levels, and the full reference: docs.median.sh, or the source at github.com/mediansh/median.
