VHGENGINE
REMOTE · ENGINE-PRODUCTION-3BDC.UP.RAILWAY.APP · SCANNED AUG 3
Agents-first viral-hook engine: generate, score, and remix short-form hooks over MCP.
Available components
How this component scores in each security and reliability category. Every signal is checked automatically against the live server, and we only credit what we can confirm. How we score →
Endpoint Security63
- The endpoint's TLS certificate is valid, in date, and uses a strong key. View diagnostics → Pass
- Authorisation check failed: no authorisation is required to call this server, and it exposes a tool marked destructive (delete_account). See how to fix → View diagnostics → Fail
- HTTPS is enforced; there's no plaintext access path. View diagnostics → Pass
- The HSTS (Strict-Transport-Security) header is present. View diagnostics → Pass
- DNSSEC check failed: this domain isn't protected by DNSSEC. See how to fix → View diagnostics → Fail
Transport & Reachability100
- Verified streamable-http transport via a live MCP handshake. View diagnostics → Pass
Schema Quality & AI Usability53
- 43% of prompts and resources have a non-trivial description (not blank, and not just the item's name).Partial
- AI-judged instruction clarity (excellent).Pass
- Context-footprint check failed: tool/resource definitions use about 14279 tokens (~274/item across 52 items; 38 tools + 14 resources), over budget; trim descriptions and params. See how to fix → Fail
- Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management27
- Stability observed for 8 of 30 days with no destabilising changes; credit accrues until the full window elapses.Partial
Tool Coverage100
- 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
- 100% of tool parameters carry a description.Pass
- Structured output schemas are declared (100% of tools); any adoption earns full credit.Pass
Capabilities100
- Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.Pass
Add this component to your MCP client. Where a client-specific snippet is available, pick your client below and copy it straight into your config; otherwise use the connection detail shown.
remote · engine-production-3bdc.up.railway.app
claude mcp add --transport http app-railway-up-engine-production-3bdc-vhgengine https://engine-production-3bdc.up.railway.app/mcp
[mcp_servers.app-railway-up-engine-production-3bdc-vhgengine] url = "https://engine-production-3bdc.up.railway.app/mcp"
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"app-railway-up-engine-production-3bdc-vhgengine": {
"type": "remote",
"url": "https://engine-production-3bdc.up.railway.app/mcp",
"enabled": true
}
}
} openclaw mcp add app-railway-up-engine-production-3bdc-vhgengine --url https://engine-production-3bdc.up.railway.app/mcp --transport streamable-http
mcp_servers:
app-railway-up-engine-production-3bdc-vhgengine:
url: "https://engine-production-3bdc.up.railway.app/mcp" {
"mcpServers": {
"app-railway-up-engine-production-3bdc-vhgengine": {
"type": "http",
"url": "https://engine-production-3bdc.up.railway.app/mcp"
}
}
} The mcpServers block is a cross-client convention. Remote transports vary, so check your client's docs.
Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.
- 3 Aug 26 +6
- The server rewrote its instructions, which are the text every model session reads security
- New tool “cancel_job”, which the server declares destructive security
- Tool “delete_account” rewrote its description, which is the text the model reads security
- Tool “generate_hooks” rewrote its description, which is the text the model reads security
- Tool “generate_hooks_batch” rewrote its description, which is the text the model reads security
- Tool “get_account” rewrote its description, which is the text the model reads security
- Tool “get_usage” rewrote its description, which is the text the model reads security
- Tool “health” rewrote its description, which is the text the model reads security
- Tool “list_hooks” rewrote its description, which is the text the model reads security
- Tool “remix_hook” rewrote its description, which is the text the model reads security
- Tool “score_hook” rewrote its description, which is the text the model reads security
- Tool “signup” rewrote its description, which is the text the model reads security
- Tool “start_generate_job” rewrote its description, which is the text the model reads security
- Schema quality: 138 → 274 ▼ functional
- Schema quality: 0% → 43% ▲ functional
- Tool coverage: 0% → 100% ▲ functional
- Schema quality: good → excellent functional
- New resource “client-guide-claude-desktop” functional
- New resource “client-guide-codex” functional
- New resource “client-guide-cursor” functional
- New resource “client-guide-rest” functional
- New resource “sample” functional
- New resource “client-guide-chatgpt” functional
- New resource “client-guide-claude-code” functional
- New tool “quote” functional
- New tool “export_usage” functional
- New tool “list_billing_events” functional
- New tool “list_runs” functional
- New tool “wait_for_job” functional
- “get_usage” added an optional parameter “offset” cosmetic
- “remix_hook” added an optional parameter “archetype” cosmetic
- “remix_hook” added an optional parameter “hook_id” cosmetic
- “remix_hook” added an optional parameter “platform” cosmetic
- “remix_hook” added an optional parameter “topic” cosmetic
- “score_hook” added an optional parameter “topic” cosmetic
- “score_hooks_batch” added an optional parameter “topic” cosmetic
- “add_credits” reworded the description of “admin_key” cosmetic
- “add_credits” reworded the description of “amount” cosmetic
- “add_credits” reworded the description of “api_key” cosmetic
- “add_credits” reworded the description of “idempotency_key” cosmetic
- “create_checkout” reworded the description of “api_key” cosmetic
- “create_checkout” reworded the description of “cancel_url” cosmetic
- “create_checkout” reworded the description of “pack” cosmetic
- “create_checkout” reworded the description of “success_url” cosmetic
- “create_key” reworded the description of “api_key” cosmetic
- “create_key” reworded the description of “daily_credit_cap” cosmetic
- “create_key” reworded the description of “name” cosmetic
- “create_key” reworded the description of “scopes” cosmetic
- “delete_account” reworded the description of “api_key” cosmetic
- “delete_account” reworded the description of “confirm” cosmetic
- “delete_webhook” reworded the description of “api_key” cosmetic
- “generate_hooks” reworded the description of “api_key” cosmetic
- “generate_hooks” reworded the description of “archetypes” cosmetic
- “generate_hooks” reworded the description of “count” cosmetic
- “generate_hooks” reworded the description of “deadline_ms” cosmetic
- “generate_hooks” reworded the description of “idempotency_key” cosmetic
- “generate_hooks” reworded the description of “mode” cosmetic
- “generate_hooks” reworded the description of “platform” cosmetic
- “generate_hooks” reworded the description of “style” cosmetic
- “generate_hooks” reworded the description of “tags” cosmetic
- “generate_hooks” reworded the description of “topic” cosmetic
- “generate_hooks” reworded the description of “verbosity” cosmetic
- “generate_hooks_batch” reworded the description of “api_key” cosmetic
- “generate_hooks_batch” reworded the description of “idempotency_key” cosmetic
- “generate_hooks_batch” reworded the description of “mode” cosmetic
- “generate_hooks_batch” reworded the description of “platform” cosmetic
- “generate_hooks_batch” reworded the description of “requests” cosmetic
- “generate_hooks_batch” reworded the description of “tags” cosmetic
- “generate_hooks_batch” reworded the description of “verbosity” cosmetic
- “get_account” reworded the description of “api_key” cosmetic
- “get_activity” reworded the description of “api_key” cosmetic
- “get_activity” reworded the description of “recent_limit” cosmetic
- “get_hook” reworded the description of “api_key” cosmetic
- “get_hook” reworded the description of “hook_id” cosmetic
- “get_job” reworded the description of “api_key” cosmetic
- “get_job” reworded the description of “job_id” cosmetic
- “get_usage” reworded the description of “api_key” cosmetic
- “get_usage” reworded the description of “key_prefix” cosmetic
- “get_usage” reworded the description of “recent_limit” cosmetic
- “get_usage” reworded the description of “request_id” cosmetic
- “get_usage” reworded the description of “tag” cosmetic
- “get_webhook” reworded the description of “api_key” cosmetic
- “list_hooks” reworded the description of “api_key” cosmetic
- “list_hooks” reworded the description of “limit” cosmetic
- “list_hooks” reworded the description of “mode” cosmetic
- “list_hooks” reworded the description of “offset” cosmetic
- “list_hooks” reworded the description of “since” cosmetic
- “list_hooks” reworded the description of “tag” cosmetic
- “list_hooks” reworded the description of “topic” cosmetic
- “list_hooks” reworded the description of “unreported” cosmetic
- “list_jobs” reworded the description of “api_key” cosmetic
- “list_jobs” reworded the description of “limit” cosmetic
- “list_jobs” reworded the description of “offset” cosmetic
- “list_keys” reworded the description of “api_key” cosmetic
- “list_outcomes” reworded the description of “api_key” cosmetic
- “list_outcomes” reworded the description of “hook_id” cosmetic
- “list_outcomes” reworded the description of “limit” cosmetic
- “list_outcomes” reworded the description of “offset” cosmetic
- “list_outcomes” reworded the description of “platform” cosmetic
- “list_outcomes” reworded the description of “since” cosmetic
- “list_webhook_deliveries” reworded the description of “api_key” cosmetic
- “list_webhook_deliveries” reworded the description of “limit” cosmetic
- “list_webhook_deliveries” reworded the description of “offset” cosmetic
- “list_webhook_deliveries” reworded the description of “status” cosmetic
- “redrive_webhook_delivery” reworded the description of “api_key” cosmetic
- “redrive_webhook_delivery” reworded the description of “delivery_id” cosmetic
- “remix_hook” reworded the description of “api_key” cosmetic
- “remix_hook” reworded the description of “count” cosmetic
- “remix_hook” reworded the description of “idempotency_key” cosmetic
- “remix_hook” reworded the description of “tags” cosmetic
- “remix_hook” reworded the description of “target_archetype” cosmetic
- “remix_hook” reworded the description of “text” cosmetic
- “remix_hook” reworded the description of “verbosity” cosmetic
- “report_outcome” reworded the description of “api_key” cosmetic
- “report_outcome” reworded the description of “hook_id” cosmetic
- “report_outcome” reworded the description of “idempotency_key” cosmetic
- “report_outcome” reworded the description of “likes” cosmetic
- “report_outcome” reworded the description of “platform” cosmetic
- “report_outcome” reworded the description of “posted_at” cosmetic
- “report_outcome” reworded the description of “retention_pct” cosmetic
- “report_outcome” reworded the description of “url” cosmetic
- “report_outcome” reworded the description of “views” cosmetic
- “revoke_key” reworded the description of “api_key” cosmetic
- “revoke_key” reworded the description of “prefix” cosmetic
- “score_hook” reworded the description of “api_key” cosmetic
- “score_hook” reworded the description of “idempotency_key” cosmetic
- “score_hook” reworded the description of “platform” cosmetic
- “score_hook” reworded the description of “tags” cosmetic
- “score_hook” reworded the description of “text” cosmetic
- “score_hook” reworded the description of “verbosity” cosmetic
- “score_hooks_batch” reworded the description of “api_key” cosmetic
- “score_hooks_batch” reworded the description of “compare” cosmetic
- “score_hooks_batch” reworded the description of “idempotency_key” cosmetic
- “score_hooks_batch” reworded the description of “platform” cosmetic
- “score_hooks_batch” reworded the description of “sets” cosmetic
- “score_hooks_batch” reworded the description of “tags” cosmetic
- “score_hooks_batch” reworded the description of “texts” cosmetic
- “score_hooks_batch” reworded the description of “verbosity” cosmetic
- “set_webhook” reworded the description of “api_key” cosmetic
- “set_webhook” reworded the description of “url” cosmetic
- “signup” reworded the description of “email” cosmetic
- “signup” reworded the description of “idempotency_key” cosmetic
- “signup” reworded the description of “name” cosmetic
- “start_generate_job” reworded the description of “api_key” cosmetic
- “start_generate_job” reworded the description of “archetypes” cosmetic
- “start_generate_job” reworded the description of “count” cosmetic
- “start_generate_job” reworded the description of “idempotency_key” cosmetic
- “start_generate_job” reworded the description of “mode” cosmetic
- “start_generate_job” reworded the description of “platform” cosmetic
- “start_generate_job” reworded the description of “style” cosmetic
- “start_generate_job” reworded the description of “tags” cosmetic
- “start_generate_job” reworded the description of “topic” cosmetic
- “start_generate_job” reworded the description of “topics” cosmetic
- “remix_hook” made “target_archetype” optional cosmetic
- “remix_hook” made “text” optional cosmetic
- 1 Aug 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 17 to 20. That category is still filling its 30-day observation window: 5 days of observed history at the previous scan, 6 at this one. The score rises as the window fills, whether or not the server changes.
- 31 Jul 26 +4
- We updated how we score, so this day's move reflects our rubric, not a change to the server See what changed → functional
- 29 Jul 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 7 to 10. That category is still filling its 30-day observation window: 2 days of observed history at the previous scan, 3 at this one. The score rises as the window fills, whether or not the server changes.
- 28 Jul 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 3 to 7. That category is still filling its 30-day observation window: 1 days of observed history at the previous scan, 2 at this one. The score rises as the window fills, whether or not the server changes.
- 27 Jul 26 0
- We updated how we score, so this day's move reflects our rubric, not a change to the server See what changed → functional
- 26 Jul 26 52
First indexed and scored.
Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.
Captured 3 Aug 2026 · Probed https://engine-production-3bdc.up.railway.app/mcp
TLS valid
Negotiated TLS 1.3 with TLS_AES_128_GCM_SHA256 .
| Subject | Issuer | Valid from | Valid until | Key | Signature | Serial |
|---|---|---|---|---|---|---|
| CN=*.up.railway.app | CN=YE1,O=Let's Encrypt,C=US | 29 Jul 2026 | 27 Oct 2026 | ECDSA 256 | ECDSA-SHA384 | 6da79bb561da3efeb0e751ca21abd3999fe |
| SANs: *.up.railway.app, up.railway.app | ||||||
| CN=YE1,O=Let's Encrypt,C=US (CA) | CN=Root YE,O=ISRG,C=US | 3 Sept 2025 | 2 Sept 2028 | ECDSA 384 | ECDSA-SHA384 | 5ddd70dd31f801c85c186a7a04b80afe |
| CN=Root YE,O=ISRG,C=US (CA) | CN=ISRG Root X2,O=Internet Security Research Group,C=US | 13 May 2026 | 2 Sept 2032 | ECDSA 384 | ECDSA-SHA384 | 872165fc34b6e5fba8add5b3705fb53a |
| CN=ISRG Root X2,O=Internet Security Research Group,C=US (CA) | CN=ISRG Root X1,O=Internet Security Research Group,C=US | 13 May 2026 | 2 Sept 2032 | ECDSA 384 | SHA256-RSA | 6c8f1dc727c7117f7baf853ac980f9cd |
DNSSEC insecure
Validation of engine-production-3bdc.up.railway.app. — Not signed
| Zone | DS | Keys | Algorithms | Outcome |
|---|---|---|---|---|
| . | trust_anchor | 20326, 38696 | 8, 8 | Verified |
| app. | present | 23684 | 8 | Verified |
| railway.app. | absent | Unsigned (proven) parent-signed NSEC/NSEC3 proves an unsigned delegation |
Authentication No authorisation required
The endpoint answered without asking for a token. Anyone who knows the URL can reach it.
| Result | No authorisation required |
|---|---|
| HTTP status | 200 |
| Header | Value |
|---|---|
| strict-transport-security | max-age=31536000 |
| x-content-type-options | nosniff |
Transports 2 probes
| Transport | URL | Outcome | Status | Location |
|---|---|---|---|---|
| streamable-http | https://engine-production-3bdc.up.railway.app/mcp | Verified | 200 | |
| http (plaintext) | http://engine-production-3bdc.up.railway.app/mcp | HTTPS enforced | 301 | https://engine-production-3bdc.up.railway.app/mcp |
The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability.
score_hooks_batch ~688
Score many hooks ranked with `best`, or compare named SETS. 1 credit per text. All-or-nothing charge. Plain: texts (1-25, each 3-300 chars). Self-test (E13): compare=true + sets (2-4 named lists, <=25 texts total) INSTEAD of texts -> per-set rankings + avg_score + winner + an honest winner_summary (same heuristic scorer on every set, never view prediction). Also: platform, tags, verbosity, api_key, idempotency_key. Returns {results, best, ...} or {sets, winner, winner_summary, ...}. Errors: unauthorized, invalid_request, insufficient_credits, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| compare | boolean | — | Switch on compare mode, which requires `sets` and returns per-set rankings + a winner instead of one flat ranking. Leave false for the ordinary texts ranking. |
| idempotency_key | — | — | Caller-chosen replay key (any string, unique per intended effect). A repeat call with the SAME key returns the stored result and is NEVER charged twice; the same key with different arguments is an id… |
| platform | string | — | Target platform, which selects the length/format conventions the hooks are written and scored against. Defaults to tiktok when omitted. |
| sets | — | — | Compare mode input: 2-4 NAMED variant lists, e.g. {"curiosity": ["..."], "contrarian": ["..."]}, each 1-25 texts and <=25 texts across all sets. Requires compare=true and excludes `texts`. Every set… |
| tags | — | — | 1-5 lowercase slug tags ([a-z0-9_-], <=40 chars) stamped on this call's usage event so a fleet can attribute spend per campaign. Omit for no tagging. Filter later with get_usage(tag=...) / list_hooks… |
| texts | — | — | 1-25 hook lines (3-300 chars each) to score and rank. Use this OR sets, never both: texts is the plain ranked mode, sets is the compare mode. 1 credit per text either way. |
| topic | — | — | What the hooks are about, 3-200 chars. Supply it to score these lines the way they were generated: the scorer penalises a line that only echoes its own topic back, and it cannot apply that penalty to… |
| verbosity | string | — | How much of the response envelope to return: minimal (ids/text/totals only), standard (the default), full (adds per-dimension score attribution and timings). Shapes the RESPONSE only, never what is g… |
| Name | Type | Req | Description |
|---|---|---|---|
| best | — | — | Plain mode: the highest-scoring entry. |
| credits_charged | number|null | — | Credits this call actually cost. |
| credits_remaining | number|null | — | Your balance AFTER this charge. |
| replayed | boolean|null | — | true when an idempotency_key replayed a stored result, so nothing was charged again. The credits_charged below is what the ORIGINAL call cost. |
| replayed_at_charge | boolean|null | — | true when the replay was detected at the charge boundary rather than up front; either way you are billed exactly once. |
| request_id | string|null | — | Id of this call. Keep it: get_usage(request_id=...) itemises exactly what it charged, and it identifies the call in a support question. |
| results | array|null | — | Plain mode: one scored entry per text, ranked. |
| sets | object|null | — | Compare mode: per-set rankings and avg_score, keyed by your set names. |
| winner | string|null | — | Compare mode: the winning set name. |
| winner_summary | string|null | — | Compare mode: an honest reading of the win. The same heuristic scorer runs on every set, so this is a craft comparison, never a view prediction. |
No examples provided.
set_webhook ~264
Register (or replace) this account's webhook; the secret is returned ONCE. Admin scope. VHGENGINE POSTs signed job.succeeded/job.failed/credits.low (WP-M). `url` https, no creds, public host; re-registering ROTATES the secret (a repeat call is not a no-op). Verify via X-VHG-Signature: sha256=HMAC_SHA256(secret, raw_body); a webhook.test pings. Args: url (1-2048), api_key (admin). Cost=free. Errors: unauthorized, forbidden, invalid_request, rate_limited. Returns {url, secret, created_at, events, note, test_delivery_id}.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| url | string | yes | https:// endpoint that will receive signed event POSTs. Must be a public host with no embedded credentials. Re-registering ANY url rotates the signing secret, so this is not safe to blind-retry. |
| Name | Type | Req | Description |
|---|---|---|---|
| created_at | string|null | — | Registration time, ISO-8601 UTC. |
| events | array|null | — | Event types that will be delivered. |
| note | string|null | — | Human-readable summary, including the rotation warning. |
| secret | string|null | — | HMAC signing secret, shown exactly once. Verify deliveries with X-VHG-Signature: sha256=HMAC_SHA256(secret, raw_body). |
| test_delivery_id | string|null | — | A webhook.test ping enqueued immediately; look it up with list_webhook_deliveries to prove your receiver works. |
| test_note | string|null | — | What that test delivery is and how to check it. |
| url | string|null | — | The endpoint now registered. |
No examples provided.
signup ~241
Create an account and return your API key exactly once. No auth needed. The entry point: an agent with nothing calls this and is productive immediately (no CAPTCHA); new accounts get 500 credits. An idempotent replay returns the SAME account with replayed:true and a NULL api_key (the plaintext is never stored), so branch on `replayed`, never on the empty key. Cost: free. Errors: invalid_request, idempotency_conflict, rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| — | — | OPTIONAL contact address. Never required, never verified, and never used to authenticate; the api_key is the only credential. | |
| idempotency_key | — | — | Caller-chosen replay key (any string, unique per intended effect). A repeat call with the SAME key returns the stored result and is NEVER charged twice; the same key with different arguments is an id… |
| name | string | — | Display name for the account, 1-100 chars. Purely a label (it is never checked for uniqueness); defaults to 'agent'. |
| Name | Type | Req | Description |
|---|---|---|---|
| account_id | string|null | — | Your account id. Not a credential. |
| api_key | string|null | — | The plaintext key, returned EXACTLY once. NULL on an idempotent replay (the plaintext is never stored), so store it on first sight: it cannot be re-read. |
| api_key_prefix | string|null | — | First 12 chars of the key, safe to log and to pass to revoke_key. |
| created_at | string|null | — | Account creation time, ISO-8601 UTC. |
| credits | number|null | — | Starting balance (500 on a fresh account). |
| docs | object|null | — | Where to read more, keyed by topic. |
| memory_card | — | — | A compact block worth persisting into your own memory. |
| next_steps | array|null | — | Ordered suggestions for what to call next. |
| note | string|null | — | Human-readable summary of the above. |
| replayed | boolean|null | — | true when this response replays an earlier signup with the same idempotency_key (and therefore carries no api_key); false on a fresh one. |
No examples provided.
start_generate_job ~667
Submit a generate as an async job; poll get_job for the result. Same args as generate_hooks (tags persist); charge runs on the worker; results are always full-verbosity; prefer for research. EITHER topic OR topics (1-20 subjects, ONE job + one atomic charge, result = the batch envelope); both or neither is invalid_request. A job only runs where a job worker runs: when none does the response carries worker_alive:false + a warning, so call generate_hooks instead. Errors: unauthorized, invalid_request, insufficient_credits, idempotency_conflict.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| archetypes | — | — | Restrict generation to these archetype ids (see list_archetypes). Omit to let the engine spread across archetypes, which is what you want unless you are deliberately narrowing a deck. |
| count | integer | — | Hooks per topic, 1-25. With `topics` this applies to every subject, so the job's cost scales with count * len(topics). |
| idempotency_key | — | — | Caller-chosen replay key (any string, unique per intended effect). A repeat call with the SAME key returns the stored result and is NEVER charged twice; the same key with different arguments is an id… |
| mode | — | — | Generation engine and therefore the price: instant (0 base + 1/hook, deterministic templates, sub-second), smart (0 + 2/hook, one LLM call, seconds), research (10 + 4/hook, brief->draft->judge, tens… |
| platform | string | — | Target platform, which selects the length/format conventions the hooks are written and scored against. Defaults to tiktok when omitted. |
| style | — | — | Voice/tone to match, <=200 chars. Honored as a real instruction by smart and research; on instant it only varies which deterministic template fillers are drawn, so it cannot change the voice there. O… |
| tags | — | — | 1-5 lowercase slug tags ([a-z0-9_-], <=40 chars) stamped on this call's usage event so a fleet can attribute spend per campaign. Omit for no tagging. Filter later with get_usage(tag=...) / list_hooks… |
| topic | — | — | Single subject for the job, 3-200 chars. Pass EITHER this or `topics`, never both and never neither. |
| topics | — | — | 1-20 subjects run as ONE job with ONE atomic charge; the result is the batch envelope instead of a single generate body. Pass EITHER this or `topic`. |
| Name | Type | Req | Description |
|---|---|---|---|
| estimated_seconds | number|null | — | Queue-aware estimate of total time to a result. |
| expires_at | string|null | — | When an unexecuted job is abandoned, ISO-8601 UTC. |
| job_id | string|null | — | Pass this to get_job. |
| poll_after_seconds | number|null | — | Wait at least this long before the first get_job. Honor it. |
| replayed | boolean|null | — | true when an idempotency_key returned an EXISTING job rather than queuing a new one. Poll the returned job_id either way. |
| requeued | boolean|null | — | true when an idempotent resubmit revived an existing job. |
| status | string|null | — | Lifecycle state; 'queued' immediately after submit. |
| status_url | string|null | — | REST URL for the same status (HTTP clients only). |
| warning | string|null | — | Present only alongside worker_alive:false; says what to do instead. |
| worker_alive | boolean|null | — | Present and FALSE only when no job worker will ever run this job. Then read `warning` and use generate_hooks instead of polling. |
No examples provided.
wait_for_job ~312
Block until a job is terminal, then return it. Free, bounded, no poll loop. Returns the EXACT get_job body plus timed_out, waited_ms and polls, so branch on `status` exactly as you would with get_job. timed_out:true is NOT a failure, it means the budget ran out: call again with the SAME job_id. Waiting neither cancels nor charges; the worker charges when it runs the job either way. It returns IMMEDIATELY with worker_alive:false + `warning` when no worker exists here. Errors: unauthorized, not_found (unknown or foreign job_id), rate_limited.
| Name | Type | Req | Description |
|---|---|---|---|
| api_key | — | — | API key for this call. Omit to fall back to the Authorization: Bearer / X-API-Key request header (streamable-HTTP only), then the VHGENGINE_API_KEY env var (the stdio default). No key resolvable -> u… |
| job_id | string | yes | The job to wait on, as returned by start_generate_job (or by a generate_hooks call that auto-jobbed on deadline_ms). Account-scoped: an id you do not own reads as not_found. |
| timeout_seconds | integer | — | How long to block, 1-300 seconds. Keep it BELOW your own MCP client's request timeout, or the client gives up before this tool answers. Running out is not an error: you get timed_out:true plus the jo… |
| Name | Type | Req | Description |
|---|---|---|---|
| elapsed_ms | number|null | — | Milliseconds since the job started running. |
| error | object|null | — | On failure: the same typed error envelope a synchronous call returns. details.cancelled true means YOU cancelled it with cancel_job, not a fault. |
| eta_seconds | number|null | — | Estimated seconds still remaining. |
| job_id | string|null | — | The job waited on. |
| poll_after_seconds | number|null | — | How long to wait before polling again; 0 once finished. |
| polls | number|null | — | How many get_job reads this call made on your behalf. |
| progress_pct | number|null | — | 0-100 progress within the run. |
| result | object|null | — | On success: the FULL generate (or batch) body, always full-verbosity. |
| stage | string|null | — | The real engine stage reached (e.g. brief, draft, judge). |
| status | string|null | — | queued, running, succeeded (see `result`) or failed (see `error`). With timed_out:false this is always succeeded or failed unless worker_alive is false. |
| timed_out | boolean|null | — | true when timeout_seconds elapsed before the job finished. NOT a failure: call wait_for_job again with the SAME job_id, or poll get_job. |
| waited_ms | number|null | — | How long this call actually blocked. |
| warning | string|null | — | Present only alongside worker_alive:false; says what to do instead. |
| worker_alive | boolean|null | — | Present and FALSE only when the wait returned immediately because no job worker will ever run this job. Then read `warning`. |
No examples provided.