Runtime
Serve your tools over the JSON-RPC /mcp runtime, verify inbound HMAC, and test in-process.
serve
serve runs the JSON-RPC /mcp runtime — /health, /manifest, and an HMAC-verified POST /mcp — interchangeable with the Python/Go author SDKs behind the same gateway.
import {
defineTool,
ToolServer,
ToolResult,
serve,
} from "@convilyn/sdk-author";
import { z } from "zod";
const echo = defineTool({
name: "echo",
description: "Echo the input text back.",
input: z.object({ text: z.string().min(1) }),
idempotent: true,
handler: (args) =>
ToolResult.ok({ echoed: args.text }, `Echoed ${args.text.length} chars`),
});
const server = new ToolServer({
name: "echo-server",
version: "1.0.0",
description: "demo",
});
server.register(echo);
serve(server, { port: 8080 }); // CONVILYN_HMAC_SECRET / CONVILYN_PORT come from env via SDKConfig| Endpoint | Purpose |
|---|---|
GET /health | Liveness probe |
GET /manifest | Serves the server manifest |
POST /mcp | HMAC-verified JSON-RPC tool invocation |
Inbound HMAC verification
The gateway signs every call it makes to your tool server; verify the signature to reject forgeries. The built-in runtime (serve) does this for you — use verifySignature directly only if you run your own HTTP framework.
import { verifySignature, InvalidSignatureError } from "@convilyn/sdk-author";
try {
// `verifySignature` reads the `x-convilyn-signature` / `-timestamp` headers.
verifySignature(
process.env.CONVILYN_HMAC_SECRET!,
rawBodyBytes,
req.headers as Record<string, string | undefined>,
);
} catch (err) {
if (err instanceof InvalidSignatureError) {
// err.reason ∈ missing_secret | missing_header | invalid_timestamp | timestamp_out_of_range | signature_mismatch
res.writeHead(401).end();
}
}InvalidSignatureError.reason is one of missing_secret | missing_header | invalid_timestamp | timestamp_out_of_range | signature_mismatch.
signRequest
signRequest(secret, body, unixSeconds) produces the matching { signature, timestamp } pair — for tests, or for calling a developer-hosted server directly.
import { signRequest } from "@convilyn/sdk-author";
const { signature, timestamp } = signRequest(
secret,
body,
Math.floor(Date.now() / 1000),
);Local testing harness
invokeTool drives a ToolServer in-process (no HTTP), returning the same wire envelope the gateway would receive — ideal for unit tests.
import { invokeTool } from "@convilyn/sdk-author";
const wire = await invokeTool(server, "echo", { text: "hi" });
expect(wire.status).toBe("ok");
expect(wire.data).toEqual({ echoed: "hi" });Authentication at a glance
| Secret (env) | Used for |
|---|---|
CONVILYN_API_KEY | Bearer auth to the platform API (ConvilynClient) |
CONVILYN_HMAC_SECRET | Verifying inbound POST /mcp calls from the gateway (serve / verifySignature) |
CONVILYN_TOOL_CONFIRMATION_SECRET | Minting / verifying confirmation tokens |
Where to go next
Take your tool server live and connect it to the gateway.
Author SDK · TypeScriptTool ServersDefine tools with defineTool and register them on a ToolServer.
Run the runtime via convilyn-author dev.
Mint confirmation tokens and call the platform API.
Author SDKAuthor SDK overviewBack up to the Author SDK landing page.