create_price_watch
Register a webhook to monitor AI model price changes. Specify model, price field, and trigger for threshold breaches or any change. Get HMAC-signed POST alerts to your callback URL.
Instructions
Register a webhook watch on a model price change. Costs 1 credit. Watch lives 90 days. Each fire is an HMAC-signed POST to callback_url.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| model | Yes | Model name (e.g. "Claude Opus 4.7") | |
| field | Yes | Which price field to watch | |
| op | Yes | Trigger: lt = below threshold, gt = above, changes = any change | |
| threshold | No | Required when op is lt or gt; ignored for changes | |
| callback_url | Yes | HTTPS URL to POST to when the watch fires | |
| secret | No | Optional shared secret used to HMAC-sign delivery bodies |
Implementation Reference
- mcp-server/src/index.ts:821-839 (handler)The async handler function that executes the create_price_watch tool logic: builds the request body with spec (type: 'price', model, field, op, and optional threshold) and callback_url, optionally includes secret, then calls fetchJSON('/premium/watches', {method: 'POST', body, auth: true}) and returns the created watch ID and credits remaining.
async ({ model, field, op, threshold, callback_url, secret }) => { const body: Record<string, unknown> = { spec: { type: 'price', model, field, op, ...(typeof threshold === 'number' ? { threshold } : {}) }, callback_url, }; if (secret !== undefined) body.secret = secret; const data = (await fetchJSON('/premium/watches', { method: 'POST', body, auth: true })) as { watch: { id: string; expires_at: string }; billing?: { credits_remaining?: number }; }; return { content: [ { type: 'text' as const, text: `Created watch ${data.watch.id} (expires ${data.watch.expires_at}). Credits remaining: ${data.billing?.credits_remaining ?? '?'}`, }, ], }; }, - mcp-server/src/index.ts:813-820 (schema)Zod schema for create_price_watch inputs: model (string), field (enum: inputPrice/outputPrice/blended), op (enum: lt/gt/changes), threshold (optional number), callback_url (string), secret (optional string).
{ model: z.string().describe('Model name (e.g. "Claude Opus 4.7")'), field: z.enum(['inputPrice', 'outputPrice', 'blended']).describe('Which price field to watch'), op: z.enum(['lt', 'gt', 'changes']).describe('Trigger: lt = below threshold, gt = above, changes = any change'), threshold: z.number().optional().describe('Required when op is lt or gt; ignored for changes'), callback_url: z.string().describe('HTTPS URL to POST to when the watch fires'), secret: z.string().optional().describe('Optional shared secret used to HMAC-sign delivery bodies'), }, - mcp-server/src/index.ts:810-840 (registration)Registration of the 'create_price_watch' tool with server.tool(), including its description and the full handler.
server.tool( 'create_price_watch', 'Register a webhook watch on a model price change. Costs 1 credit. Watch lives 90 days. Each fire is an HMAC-signed POST to callback_url.', { model: z.string().describe('Model name (e.g. "Claude Opus 4.7")'), field: z.enum(['inputPrice', 'outputPrice', 'blended']).describe('Which price field to watch'), op: z.enum(['lt', 'gt', 'changes']).describe('Trigger: lt = below threshold, gt = above, changes = any change'), threshold: z.number().optional().describe('Required when op is lt or gt; ignored for changes'), callback_url: z.string().describe('HTTPS URL to POST to when the watch fires'), secret: z.string().optional().describe('Optional shared secret used to HMAC-sign delivery bodies'), }, async ({ model, field, op, threshold, callback_url, secret }) => { const body: Record<string, unknown> = { spec: { type: 'price', model, field, op, ...(typeof threshold === 'number' ? { threshold } : {}) }, callback_url, }; if (secret !== undefined) body.secret = secret; const data = (await fetchJSON('/premium/watches', { method: 'POST', body, auth: true })) as { watch: { id: string; expires_at: string }; billing?: { credits_remaining?: number }; }; return { content: [ { type: 'text' as const, text: `Created watch ${data.watch.id} (expires ${data.watch.expires_at}). Credits remaining: ${data.billing?.credits_remaining ?? '?'}`, }, ], }; }, ); - mcp-server/src/index.ts:19-59 (helper)The fetchJSON helper function used by the handler to make authenticated HTTP requests to the TensorFeed API. It handles GET/POST/DELETE, attaches Bearer token when auth:true, and throws on 402/401/other errors.
async function fetchJSON(path: string, opts: FetchOptions = {}): Promise<unknown> { const headers: Record<string, string> = { 'User-Agent': `TensorFeed-MCP/${SDK_VERSION}`, }; if (opts.body !== undefined) headers['Content-Type'] = 'application/json'; if (opts.auth) { const token = process.env.TENSORFEED_TOKEN; if (!token) { throw new Error( 'TENSORFEED_TOKEN env var is not set. Premium MCP tools require a bearer token. ' + 'Buy credits at https://tensorfeed.ai/developers/agent-payments and pass the returned tf_live_... token via the TENSORFEED_TOKEN env var in your MCP client config.', ); } headers['Authorization'] = `Bearer ${token}`; } const res = await fetch(`${API_BASE}${path}`, { method: opts.method ?? 'GET', headers, ...(opts.body !== undefined ? { body: JSON.stringify(opts.body) } : {}), }); if (!res.ok) { let errPayload: unknown; try { errPayload = await res.json(); } catch { errPayload = await res.text().catch(() => ''); } if (res.status === 402) { throw new Error( `Payment required (402). Your token may be out of credits. Top up at https://tensorfeed.ai/developers/agent-payments. Detail: ${JSON.stringify(errPayload)}`, ); } if (res.status === 401) { throw new Error( `Token rejected (401). Check that TENSORFEED_TOKEN is set to a valid tf_live_... token. Detail: ${JSON.stringify(errPayload)}`, ); } throw new Error(`API error ${res.status}: ${JSON.stringify(errPayload)}`); } return res.json(); }