DEV Community

Morgan Xu
Morgan Xu

Posted on

Park Live Tool URLs Behind a Practice Host

Live tool URLs should not meet first-pass agents. A practice host should absorb that first-pass traffic instead. This playbook parks live endpoints behind a named desk, then promotes work only after a short wiki run.

Tool-calling agents behave like touring theater crews. Opening night needs lights, cues, and paid seats. Tech rehearsal needs a cheap hall and a marked floor. Many teams skip the hall. They point agents at live APIs during prompt thrash. Bills rise. Logs mix. On-call gets noise from fake failures.

The practice host is that cheap hall. Free model access can draft the agent turns. A free server can host the mock tools and the router. Live URLs stay behind a clerk. That split is the whole SOP.

Disclosure: This article was prepared as part of MonkeyCode's product outreach. MonkeyCode provides free model access and a free server option. Those two pieces fit this desk when a team needs a shared practice host. The playbook still works if another practice host already exists. Strip the product name and the steps remain useful.

Current agent stacks call tools in tight loops. Each loop can fan out into HTTP, SQL, or ticket writes. First drafts of those loops are unstable. Schema guesses miss fields. Retries stampede. A practice host keeps that weather off the paid coast.

This article does not claim model names, quotas, or hardware. Availability of any free lane can change. Treat the host as a named environment, not a promise. Verify the current signup path on the project page before a team depends on it.

The one-page wiki run

Paste the block below into the team wiki. Fill the four hats before any agent batch. Two people may hold two hats. Hats still need names, not vibes.

# Practice Host Run — Agent Tool Spend
Status: OPEN | PROMOTED | BLOCKED
Batch id: tool-loop-YYYYMMDD-##
Lane clerk: @name
Tool owner: @name
Spend watcher: @name
On-call reviewer: @name

Practice host: https://practice.internal.example
Live tool URLs: LOCKED until Status=PROMOTED
Model lane: practice-free (no live billed route)
Mock map: /wiki/tool-mocks/{batch id}
Receipt path: /var/log/practice-host/{batch id}.json

Go / no-go:
- [ ] Practice host healthcheck 200
- [ ] Live URLs absent from agent env
- [ ] Mock map covers every tool name
- [ ] Receipt printer writes one file
- [ ] Spend watcher signed the card
Enter fullscreen mode Exit fullscreen mode

The card is the stage manager. Nobody starts a loop until Status reads OPEN. Nobody points at live URLs until Status reads PROMOTED. BLOCKED means the host is sick or the mock map is thin. Agents wait. Humans talk.

Roles that actually move

The lane clerk owns the practice host URL and the env file. That person rotates secrets for the desk, not for production. The tool owner writes one mock per live endpoint. A mock must fail in the same shape as the live API. Status codes matter more than witty body text.

The spend watcher is the only hat that can flip PROMOTED. That person checks the receipt, not the prompt poetry. The on-call reviewer reads the receipt before the batch hits a shared calendar. Review is a signature, not a meeting.

Think of the four hats as a loading dock. Boxes do not walk onto the truck alone. A clerk tags them. An owner confirms contents. A watcher signs the gate. A reviewer keeps the night shift from inheriting a mess.

A router that refuses live URLs

The next script is a proposed gate, not production law. Run it on the practice host. It should refuse any live tool URL while the batch is OPEN. Label this example as unexecuted until the team pins versions.

# practice_host_gate.py — proposed env gate for OPEN batches
import json, os, sys, time, urllib.parse
from pathlib import Path

LIVE_HINTS = ("api.prod.", "billing.", "stripe.", "github.com", "api.openai.")
RECEIPT = Path(os.environ.get("PRACTICE_RECEIPT", "/tmp/practice-receipt.json"))

def host_of(url: str) -> str:
    return urllib.parse.urlparse(url).hostname or ""

def assert_practice(env: dict) -> list[str]:
    failures = []
    status = env.get("BATCH_STATUS", "")
    if status not in {"OPEN", "PROMOTED", "BLOCKED"}:
        failures.append("BATCH_STATUS missing or unknown")
    base = env.get("TOOL_BASE_URL", "")
    host = host_of(base)
    if status != "PROMOTED":
        if not host.startswith("practice."):
            failures.append(f"non-practice host while {status}: {host}")
        for hint in LIVE_HINTS:
            if hint in (host or "") or hint in base:
                failures.append(f"live hint {hint} in TOOL_BASE_URL")
    if env.get("MODEL_LANE") == "live-billed" and status != "PROMOTED":
        failures.append("billed model lane before PROMOTED")
    return failures

def write_receipt(env: dict, failures: list[str]) -> None:
    payload = {
        "ts": int(time.time()),
        "batch_id": env.get("BATCH_ID"),
        "status": env.get("BATCH_STATUS"),
        "tool_base": env.get("TOOL_BASE_URL"),
        "model_lane": env.get("MODEL_LANE"),
        "ok": not failures,
        "failures": failures,
    }
    RECEIPT.write_text(json.dumps(payload, indent=2))
    print(json.dumps(payload))

if __name__ == "__main__":
    fails = assert_practice(os.environ)
    write_receipt(os.environ, fails)
    sys.exit(1 if fails else 0)
Enter fullscreen mode Exit fullscreen mode

A shell wrapper keeps humans from exporting live URLs by habit. Put it in the same wiki page as the card. The wrapper is also a proposal until the team tests it on the practice host.

#!/usr/bin/env bash
# pin-practice-host.sh — proposed wrapper, not a blessed binary
set -euo pipefail
: "${BATCH_ID:?}"
: "${BATCH_STATUS:=OPEN}"
export BATCH_STATUS TOOL_BASE_URL="${TOOL_BASE_URL:-https://practice.internal.example}"
export MODEL_LANE="${MODEL_LANE:-practice-free}"
export PRACTICE_RECEIPT="/tmp/${BATCH_ID}.json"

if [[ "$BATCH_STATUS" != "PROMOTED" ]]; then
  case "$TOOL_BASE_URL" in
    *api.prod.*|*billing.*) echo "live URL blocked"; exit 2 ;;
  esac
fi
python3 practice_host_gate.py
Enter fullscreen mode Exit fullscreen mode

The receipt is the dock ticket. It records host, lane, and failures. Later debugging starts there, not in chat scrollback. If the file is missing, the batch did not start. If ok is false, the clerk left a live hint in the env.

Handoff between desks

Promotion is a file, not a vibe. The spend watcher copies the receipt into the wiki card. The tool owner pastes the mock map diff. The lane clerk rotates the practice secret if the batch leaked a token into logs. Then Status becomes PROMOTED. Only then may TOOL_BASE_URL point at a live host.

A compact handoff object keeps that story portable. Agents should not parse it. Humans and CI should.

{
  "batch_id": "tool-loop-20260923-01",
  "from": "practice",
  "to": "live",
  "status": "PROMOTED",
  "receipt": "/var/log/practice-host/tool-loop-20260923-01.json",
  "mocks_covered": ["create_ticket", "search_docs", "post_comment"],
  "live_urls_unlocked": ["https://api.internal.example/v1"],
  "watcher": "spend-watcher",
  "reviewer": "oncall"
}
Enter fullscreen mode Exit fullscreen mode

Refuse promotion when mocks_covered is shorter than the tool list. Refuse promotion when the receipt says ok false. Refuse promotion when the reviewer hat is empty. Empty hats are how live URLs sneak back in.

What the practice host must mimic

A practice host is not a toy. It must speak the same content types as the live tools. JSON keys should match. Error envelopes should match. Timeouts should be injected on purpose. Agents learn from those bruises. They should not learn them on a billed endpoint.

A thin mock that always returns 200 trains a liar. The tool owner should script at least one 409, one 429, and one 5xx per tool. Keep those fixtures next to the wiki card. Name them after the batch id so leftovers do not haunt the next crew.

# proposed fixture sketch — unexecuted until pinned
FIXTURES = {
    "create_ticket": [
        {"status": 201, "body": {"id": "T-1"}},
        {"status": 409, "body": {"error": "duplicate"}},
        {"status": 429, "body": {"error": "rate"}},
    ]
}
Enter fullscreen mode Exit fullscreen mode

Free model access belongs on the OPEN side of this split. The model is drafting tool arguments against mocks. It should not hold production credentials. The free server belongs there too. It holds the router, the fixtures, and the receipt printer. Live billed routes wait for PROMOTED.

Limitations

This SOP is a traffic dock, not a security boundary. A determined process can still paste a live URL. The gate only catches env mistakes and common host hints. It will not stop a tool that embeds a hardcoded production client.

Shared practice hosts are the wrong place for customer secrets. Do not load production dumps onto a free server. Do not point the practice model lane at private datasets. If the batch needs real PII, stop. Build an isolated staging account instead.

The playbook also fails when mocks lie about side effects. A mock create_ticket that never stores state will hide idempotency bugs. Those bugs then bloom after PROMOTED. The tool owner should keep a tiny store, even a JSON file, so retries collide on purpose.

Do not treat free model access as a capacity plan. Do not treat a free server as a permanent plant. Teams should re-read the current product terms before a quarter-long dependency. This article does not publish quotas, uptime, or model catalogs.

Who should not run this

Solo scripts with no billed tools do not need four hats. A local mock on localhost is enough. Air-gapped shops cannot use a shared free server. They should print the same card against an internal host.

Regulated pipelines that already forbid mixed environments should not add a new shared desk. Follow the existing promotion lane. Teams without a wiki will lose the card in chat. Start with a repo file if the wiki is theater.

If the agent never calls an HTTP tool, this dock is ceremony. Skip it. Spend the energy on tests for the actual side effect.

A short close

Park live tool URLs behind a practice host. Name the four hats. Print a receipt. Promote only on a signed card. The cheap hall exists so opening night is dull.

Teams that already keep a wiki runbook can seat MonkeyCode's free model access and free server as that hall, then keep the same clerk path when the batch finally earns a live URL.

Top comments (0)