Custom proxy contract
When you call configureClient({ proxyUrl }), the SDK serialises every
function invocation to JSON and POSTs it to your endpoint. Your endpoint
returns a JSON envelope holding the LLM result.
Request
Section titled “Request”POST /api/neuro HTTP/1.1Content-Type: application/jsoninterface NeuroProxyRequest { /** e.g. "Array.prototype.map", "Math.random", "globalThis.parseInt". */ functionId: string;
/** Natural-language prompt as supplied by the application. */ prompt: string;
/** * Receiver / `this` value, JSON-serialised by the SDK with safe handling * of Date, Map, Set, RegExp, BigInt, TypedArrays, and circular refs. * `null` for static methods. */ instanceData: string | null;
/** * Named arguments map. Keys mirror the original JavaScript built-in's * parameter names (e.g. `callbackfn`, `searchString`, `fromIndex`). * Variadic items live under their declared rest-parameter name * (e.g. `items`, `values`, `codes`). */ args: Record<string, unknown>;
/** Original parameter signature for the LLM. */ signatureHint: { name: string; type: string }[];
/** * Frozen, generated system prompt for this method. Forwarding it * verbatim is recommended. `neuro-ts` ships the same string in * `prompts.json` so consumers can audit. */ systemPrompt: string;
/** Model the SDK requested (already merged with NeuroClient defaults). */ model: string;}What instanceData looks like
Section titled “What instanceData looks like”instanceData is the receiver value (this), serialised by the SDK
before transport. The serialiser handles Date, Map, Set, RegExp,
BigInt, every TypedArray, and circular references; the result is always
a string the model can read.
// neuro.array.map({ array: [1, 2, 3], callbackfn: (n) => n * 2, prompt: '...' })// functionId -> "Array.prototype.map"// instanceData -> "[1,2,3]"
// neuro.string.toUpperCase({ string: 'hi mum', prompt: '...' })// functionId -> "String.prototype.toUpperCase"// instanceData -> "\"hi mum\""
// neuro.map.get({ map: new Map([['a', 1]]), key: 'a', prompt: '...' })// functionId -> "Map.prototype.get"// instanceData -> "{\"__type\":\"Map\",\"entries\":[[\"a\",1]]}"
// neuro.math.random({ prompt: '...' })// functionId -> "Math.random"// instanceData -> null (static methods have no receiver)For prototype methods the receiver is whatever the caller passed under
the receiver-named key (array, string, map, etc.). For static
methods (Math.*, JSON.*, Promise.*) and globals (parseInt,
encodeURI) the value is always null.
Response
Section titled “Response”The SDK accepts three shapes (in order of preference):
// 1. Wrapped result. Recommended.{ "result": <any JSON value> }
// 2. Wrapped text (will be parsed as JSON; falls back to raw string).{ "text": "[1, 4, 9]" }
// 3. Bare value.[1, 4, 9]Non-2xx responses surface as NeuroClientError on the caller side, with
the response body included in error.message.
Minimal Node.js implementation
Section titled “Minimal Node.js implementation”import OpenAI from 'openai';const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
export async function POST(req) { const body = await req.json(); const argLines = Object.entries(body.args ?? {}) .map(([k, v]) => `- ${k} = ${JSON.stringify(v)}`) .join('\n'); const completion = await openai.chat.completions.create({ model: body.model, temperature: 0.2, messages: [ { role: 'system', content: body.systemPrompt }, { role: 'user', content: `## User intent\n${body.prompt}\n\n` + `## Function\n\`${body.functionId}\`\n\n` + `## Receiver / \`this\` value\n${body.instanceData ?? 'null'}\n\n` + `## Named arguments\n${argLines || '(none)'}`, }, ], }); const text = completion.choices[0]?.message?.content ?? ''; let result = text; try { result = JSON.parse(text); } catch { /* leave raw */ } return Response.json({ result });}Drop-in handler
Section titled “Drop-in handler”neuro-ts/proxy ships a Web-standard (req: Request) => Response
handler that implements this contract:
import { createNeuroProxy } from 'neuro-ts/proxy';
export default { fetch: createNeuroProxy({ apiKey: process.env.OPENAI_API_KEY, defaultModel: 'gpt-4o', allowedFunctionIds: ['Array.prototype.map', 'Math.random'], }),};allowedFunctionIds — required for production
Section titled “allowedFunctionIds — required for production”allowedFunctionIds is an explicit allowlist of the functionId values
your proxy will serve. If omitted, every function id is accepted.
That means a caller could send functionId: "eval" or any other string
and your proxy would forward it to the LLM with your API key. In
production, always set an explicit allowlist:
createNeuroProxy({ apiKey: process.env.OPENAI_API_KEY, allowedFunctionIds: [ 'Array.prototype.map', 'Array.prototype.filter', 'JSON.parse', 'Math.random', ],});Requests for unlisted function ids receive a 403 response. The full
list of valid functionId values is the set of keys in prompts.json,
available at runtime via:
import prompts from 'neuro-ts/prompts';const valid = Object.values(prompts).map((e) => e.functionId);Rate-limiting and abuse mitigations
Section titled “Rate-limiting and abuse mitigations”Because every request includes prompt, instanceData, and the named
args, your proxy is the right layer to:
- Authenticate the caller (cookie session, OAuth, API key).
- Cap request size. The SDK already bounds
instanceDataat 8 KiB; the prompt is not bounded. - Log
functionIdplus caller for billing and abuse review. - Apply per-user rate limits.