CLAUDE LABJP
AUTO — Auto mode becomes the default in Claude Code today, August 14, across the Pro, Max, and Team plansPRICE — Sonnet 5 promo pricing of $2/$10 per Mtok is now permanent; the increase to $3/$15 planned for September 1 will not happenFIX — Version 2.1.231, released August 13, fixes MCP OAuth sign-in failing with a redirect URI mismatch on servers that use a pre-registered client, such as SlackSUNSET — The legacy Workbench and the experimental prompt tool APIs retire on August 17, now three days awayMCP — Support for the new MCP 2026-07-28 spec is rolling out, bringing a stateless core, stronger OAuth and OIDC authorization, and versioned extensions for Apps and TasksBOOST — The temporary 50% weekly usage boost for Claude Code subscribers runs through August 19AUTO — Auto mode becomes the default in Claude Code today, August 14, across the Pro, Max, and Team plansPRICE — Sonnet 5 promo pricing of $2/$10 per Mtok is now permanent; the increase to $3/$15 planned for September 1 will not happenFIX — Version 2.1.231, released August 13, fixes MCP OAuth sign-in failing with a redirect URI mismatch on servers that use a pre-registered client, such as SlackSUNSET — The legacy Workbench and the experimental prompt tool APIs retire on August 17, now three days awayMCP — Support for the new MCP 2026-07-28 spec is rolling out, bringing a stateless core, stronger OAuth and OIDC authorization, and versioned extensions for Apps and TasksBOOST — The temporary 50% weekly usage boost for Claude Code subscribers runs through August 19
Articles/API & SDK
API & SDK/2026-08-14Intermediate

Move the Prompt Tools API Into Your Own Scripts Before Workbench Closes on August 17

The legacy Workbench and three experimental prompt endpoints shut down on August 17, 2026. Here is how to count what actually depends on them, plus working local replacements for templatize_prompt and improve_prompt with real output.

Claude API117WorkbenchMigration4Prompt Design3Deprecation2

I ran a grep across the repositories I maintain, got zero hits, and felt relieved for about ten seconds.

Then I stopped. The prompts themselves were never the thing living in my code — the ones that matter most for other people are saved on the Console side. A clean code search does not prove you are unaffected.

Three days are left. So before the migration itself, let's settle a more useful question: what do you count, and what do you take back into your own hands?

What actually stops on August 17

Anthropic announced this on July 17, 2026 — 31 days of notice — and two things end on the same day.

WhatDateBehavior afterward
Legacy Workbench (platform.claude.com/workbench)August 17, 2026Access ends. Saved prompts, variables, and evals are not supported in the updated Workbench
/v1/experimental/generate_promptAugust 17, 2026Requests return an error
/v1/experimental/improve_promptAugust 17, 2026Same
/v1/experimental/templatize_promptAugust 17, 2026Same

No successor endpoint has been named. That detail changes the shape of the work: this is not "point the client at a new URL." It is "bring the behavior in-house."

Saved data can be exported from the banner in the Console and from Organizational Settings. Of everything on this list, that is the only step you cannot redo later. Do it first.

A code search only tells you half the story

Start by counting. These endpoints are almost always called by URL string, so one line covers it.

grep -rIn -E "experimental/(generate|improve|templatize)_prompt" \
  --include='*.{py,ts,tsx,js,mjs,sh,yml,yaml,json}' .

As an indie developer I run four sites from one set of repositories, and running this across all of their source produced zero hits. My prompts live as text files inside each repository and are loaded by scripts, so there was nothing to find.

But that zero was luck, not hygiene. If you drafted prompts in the Workbench and copied them out by hand, your code carries no trace of the dependency at all. The dependency exists in a different form: the wording only exists there.

So pair the code search with two more checks.

  1. Export the usage CSV from the Usage page in the Console. It breaks traffic down by API key and model. Config files lie when someone forgets to update them; actual traffic does not.
  2. Move every prompt saved in the Workbench into your repository as a file. Once it is a file, the next tool that shuts down cannot take it with it.

The second one matters more. What ends this month is not really a feature — it is the bill for having let someone else hold the storage.

templatize_prompt fits in about twenty lines

templatize_prompt turned a concrete prompt into a reusable template with {{VARIABLE}} placeholders. That transformation is deterministic, so there is no reason to ask a model for it. You can keep it locally.

#!/usr/bin/env python3
"""Turn a concrete prompt into a reusable template."""
import argparse, json, re, sys
 
def templatize(prompt: str, values: dict[str, str]) -> tuple[str, list[str]]:
    # Replace longer values first, so a short value never eats part of a long one.
    ordered = sorted(values.items(), key=lambda kv: len(kv[1]), reverse=True)
    template, used = prompt, []
    for name, value in ordered:
        if not value:
            continue
        if value not in template:
            print(f"warning: value for {name} not found in prompt: {value!r}", file=sys.stderr)
            continue
        template = template.replace(value, "{{" + name + "}}")
        used.append(name)
    return template, used
 
def fill(template: str, values: dict[str, str]) -> str:
    return re.sub(r"\{\{(\w+)\}\}", lambda m: values.get(m.group(1), m.group(0)), template)
 
def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("prompt_file")
    ap.add_argument("values_file", help='JSON: {"VARIABLE_NAME": "literal text in the prompt"}')
    args = ap.parse_args()
    prompt = open(args.prompt_file, encoding="utf-8").read()
    values = json.load(open(args.values_file, encoding="utf-8"))
 
    template, used = templatize(prompt, values)
    # If filling the template does not reproduce the original, a replacement went wrong
    if fill(template, values) != prompt:
        print("round-trip check failed: template does not restore the original", file=sys.stderr)
        return 1
    print(template)
    print(f"\n--- variables: {', '.join(used)} ---", file=sys.stderr)
    return 0
 
if __name__ == "__main__":
    raise SystemExit(main())

Give it this prompt:

You are a support agent for an online store.
A customer asks about "Wireless Earbuds Model A" and reports that delivery is late.
Reply politely in no more than 3 sentences. The order number is ORD-20260814-0031.

with these values:

{
  "PRODUCT_NAME": "Wireless Earbuds Model A",
  "ISSUE": "delivery is late",
  "ORDER_ID": "ORD-20260814-0031",
  "MAX_SENTENCES": "3"
}

and the run produces:

You are a support agent for an online store.
A customer asks about "{{PRODUCT_NAME}}" and reports that {{ISSUE}}.
Reply politely in no more than {{MAX_SENTENCES}} sentences. The order number is {{ORDER_ID}}.
 
--- variables: PRODUCT_NAME, ORDER_ID, ISSUE, MAX_SENTENCES ---

Replacement order changed the result

When I started writing this, I assumed iterating over the dictionary and calling replace would be enough. Then I tried a different prompt:

Summarize the Q3 2026 sales report in 3 sentences.

With MAX_SENTENCES set to "3", PERIOD set to "Q3 2026", and no ordering, you get this:

Summarize the Q{{MAX_SENTENCES}} 2026 sales report in {{MAX_SENTENCES}} sentences.

The digit inside the quarter label became a variable, and the period variable never got substituted at all. The template still looks plausible at a glance, and it produces broken text the moment you fill it back in. Sorting by length removes the failure entirely — the problem is that nothing prompts you to notice it.

That is why the round-trip check sits at the end. Fill the template back with the same values, compare against the original, and fail loudly when they differ. Five lines catch a silent corruption at the exit. And when a value is only one or two characters long, consider not making it a variable at all.

improve_prompt lasts longer as a meta-prompt you own

improve_prompt was, in essence, a prompt that rewrites prompts. Call the Messages API yourself and you get to decide the editing criteria — which were a black box while the experimental endpoint held them.

#!/usr/bin/env python3
"""Rewrite a prompt with Claude. Replaces improve_prompt."""
import argparse, json, os, urllib.error, urllib.request
 
ENDPOINT = "https://api.anthropic.com/v1/messages"
META_PROMPT = """You are a prompt editor. Rewrite the prompt you are given,
changing only the following. Do not change its meaning.
 
1. Reorder instructions as: role, then input, then constraints, then output format
2. Replace vague wording ("appropriately", "nicely") with checkable conditions
3. State the output format explicitly, but only if none was specified
4. Leave placeholders exactly as they are
 
Output only the rewritten prompt. No preamble, no explanation."""
 
def build_payload(prompt: str, model: str) -> dict:
    # Do not send temperature / top_p / top_k. Non-default values return 400
    # on Claude Opus 4.7 and later, and on Claude Sonnet 5.
    return {
        "model": model,
        "max_tokens": 2000,
        "system": META_PROMPT,
        "messages": [{"role": "user", "content": prompt}],
    }
 
def call(payload: dict, api_key: str) -> str:
    req = urllib.request.Request(
        ENDPOINT,
        data=json.dumps(payload).encode("utf-8"),
        headers={
            "content-type": "application/json",
            "x-api-key": api_key,
            "anthropic-version": "2023-06-01",
        },
    )
    try:
        with urllib.request.urlopen(req, timeout=120) as res:
            body = json.load(res)
    except urllib.error.HTTPError as e:
        raise SystemExit(f"HTTP {e.code}: {e.read().decode('utf-8', 'replace')}")
    except urllib.error.URLError as e:
        raise SystemExit(f"network error: {e.reason}")
    return "".join(b.get("text", "") for b in body.get("content", []) if b.get("type") == "text")
 
def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("prompt_file")
    ap.add_argument("--model", default="claude-sonnet-5")
    ap.add_argument("--dry-run", action="store_true", help="print the request body without sending")
    args = ap.parse_args()
 
    prompt = open(args.prompt_file, encoding="utf-8").read().strip()
    payload = build_payload(prompt, args.model)
    if args.dry_run:
        print(json.dumps(payload, ensure_ascii=False, indent=2))
        return 0
 
    api_key = os.environ.get("ANTHROPIC_API_KEY")
    if not api_key:
        raise SystemExit("ANTHROPIC_API_KEY is not set")
    print(call(payload, api_key))
    return 0
 
if __name__ == "__main__":
    raise SystemExit(main())

--dry-run prints the request body without sending it. During a migration it is safer to inspect the shape first and attach a key second.

{
  "model": "claude-sonnet-5",
  "max_tokens": 2000,
  "system": "You are a prompt editor. ...",
  "messages": [
    {
      "role": "user",
      "content": "Summarize the reviews nicely."
    }
  ]
}

Without a key, the script exits before sending with ANTHROPIC_API_KEY is not set and status 1. If you plan to run this unattended, failing early like that saves cleanup later.

Copying your Workbench settings across returns 400

This is the step I expect to catch the most people. The Workbench UI had a temperature slider, and it is natural to carry that number into the code you are migrating to.

But temperature, top_p, and top_k now return a 400 error when set to a non-default value on Claude Opus 4.7 and later, and on Claude Sonnet 5. The SDK request types still define those fields for compatibility with earlier models, so the code type-checks. It breaks at runtime, most likely after you have already declared the migration finished.

That is why none of them appear in the script above. If you want to steer behavior in the same direction, express it as a condition inside the meta-prompt instead of as a parameter. If you would rather confirm the basic call shape first, the Claude API quickstart is the shortest path.

A checklist for the next three days

OrderTaskHow to confirm
1Export saved prompts, variables, and evalsConsole banner / Organizational Settings. Irreversible, so do it first
2Count calls to the three endpointsThe grep above. Keep going even at zero hits
3Check real traffic in the usage CSVConsole Usage → Export, broken down by API key and model
4Replace templatization with a local scriptThe round-trip check passes
5Replace prompt rewriting with a Messages API callInspect with --dry-run, then attach the key
6Verify you did not copy temperature and friends acrossSend one real request to the target model. Type checking will not catch it

When a deadline is this close, the only order that works is the one derived from the deadline. Finish the export today. With that done, everything else can still be recovered after August 17.

For the wider habit of preparing for announced removals, the Opus 4.7 fast mode retirement preflight (a premium article) covers how to measure behavior that shifts across a cutoff. Removals that raise an error and removals that quietly change behavior need very different preparation.

This one made me rethink where my prompts live. Thank you for reading.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

API & SDK2026-07-01
When the Model Survives but One Parameter Expires: A Dated Deprecation Calendar for Claude API Requests
Your model ID can stay valid while a parameter you pinned quietly reaches its sunset date and takes the batch down with it. Here is a design that breaks a request into parts, gives each part its own expiry date, and catches the problem before the call goes out — with working TypeScript and real operational numbers.
API & SDK2026-08-01
Swapping Tools Mid-Conversation Without Losing Your Prompt Cache
Tools can now be added and removed between turns, but the tool block sits at the very front of the cache prefix. I measured 12 mutation patterns with fingerprints and built a stable-core plus volatile-tail registry with a guard that refuses unsafe changes.
API & SDK2026-07-24
When Memory Store Listings Returned Half the Rows: Migrating to agent-memory-2026-07-22
agent-memory-2026-07-22 changed memories.list: fixed ordering, stricter depth, segment-based path_prefix matching. How an audit quietly halved, an access layer that survives it, and measured depth=1 traversal cost.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →