Skill: design a module¶
For: an AI agent asked to design an RF module — a transmitter, a receiver, or a transmit/receive front end — on someone's behalf.
The shape of the job: one HTTP call returns a complete module. There is no session, no job id and nothing to poll. Your work is on either side of that call: collecting inputs only your user can give you, and reporting a result without smoothing it over.
This is the agent-facing procedure. The same design is told as a narrative for a human reader in Design a 9.4 GHz T/R module from Claude Code — read that one if you want the story; read this one if you are making the call.
Every figure here was re-measured against the production API on 2026-09-09.
Step 0 — pick the engine¶
Transmit chain, receiver, T/R module and link budget are four different engines with four different endpoints. Pick the engine is one page and settles it. The rest of this skill uses the T/R module as the worked case, because it is the one with the most decisions in it; the procedure is identical for the others.
Step 1 — collect what only your user can tell you¶
These engines compute faithfully from whatever you send. They have no way to tell whether a number came from your user or from you. A value you invented produces a precise, authoritative-looking, wrong answer — and it is wrong because of your input, not our maths.
For a T/R module, exactly two fields are required:
| Field | Unit | Why only your user knows it |
|---|---|---|
frequencyGhz |
GHz | The band is the requirement |
targetOutputPowerDbm |
dBm | Transmit power is a system decision |
Everything else is optional, and every optional field you leave out is disclosed
rather than assumed. That is a feature. Leave out transmitDutyCyclePercent and
the engine does not silently pick one — it says so, in analysis.disclosures:
"No transmit duty cycle was stated, so the transmit stages and the shared element assume continuous wave. A pulsed design dissipates less in both, in proportion to its duty cycle. The receive branch does not derate — its bias is continuous."
That is a question for your user, and it moves the thermal answer.
Worth asking for, because each one changes an answer:
application(radar,wifi, …) — moves both the part selection and the power verdict.transmitDutyCyclePercent— a pulsed design derates; without it you get a CW assumption, disclosed.antennaReturnLossDb— see step 4. This one field flips a verdict.prfHzandpulseWidthS— without both, thetimingbudget is absent entirely frombudgets. Notnull, notnot_evaluated: absent. Measured — the response below carriestopology,isolation,leakageandstress, and notimingkey at all. AddprfHz: 1000, pulseWidthS: 1e-6to the same design point andtimingappears,verdict: "ok", with a 0.9989 ms receive window and the switching time taken from the selected switch's own record (txRxSwitchingBasis: "switching_speed_ns"). When a part states no switching speed, the budget comes backnot_evaluatedwith the missing input named rather than assumed.
If you do not have a required field, ask. The refusal already names it for you:
curl -X POST https://api.pcbgenerator.com/api/tr-module/design \
-H "Content-Type: application/json" -d '{}'
Supply the frequency alone and the next refusal explains itself too:
422 {"detail": "targetOutputPowerDbm is required: the transmit half of a T/R
module is designed to a stated output power, and every safety budget below
is measured against it"}
Those are sentences you can hand straight to your user. They are never a reason to pick a frequency or a power yourself.
Step 2 — make the call¶
curl -X POST https://api.pcbgenerator.com/api/tr-module/design \
-H "Content-Type: application/json" \
-d '{
"frequencyGhz": 9.4,
"targetOutputPowerDbm": 40,
"application": "radar",
"transmitDutyCyclePercent": 10
}'
No account, no API key, no Authorization header. Rate limit 15 requests per
minute per IP.
Step 3 — read what came back¶
Top-level keys: ok, topology, frequencyGhz, transmit, receive,
sharedParts, budgets, sharedPathLoss, needsPartFlags, warnings,
analysis, bom.
It chose a topology and recorded the alternative it rejected. In
budgets.topology: selected: "tr_switch", a reason, and a rejected block
naming circulator_limiter and why it lost. The choice is auditable rather than
magic — quote the reason, not just the name.
⚠ Dated: recorded before 2026-10-09. It shows QPC2040 as the 9.4 GHz T/R switch, with the figures that went with it. Since 2026-10-09 (issue 90) the engine picks FW2001 by default for this design, so a fresh run shows a different switch and different leakage figures. Not re-recorded (owner decision, issue 369).
It returned 15 catalogue parts across three groups:
| Group | Parts |
|---|---|
| Shared | QPC2040 (the T/R switch) |
| Transmit | HMC465 → QPA2811 → ABF-9G+ → SMA-J-P-H-ST-EM1 |
| Receive | TGL2209-SM, HMC8412, B095MB1S, CMD183C4, ADF5611, CBP2-1090C+, PHA-23LN+, HMC1119, SMA-J-P-H-ST-EM1, RO3003-50ohm-MS |
needsPartFlags: [] — every slot filled from the catalogue. Nothing invented.
Headline figures:
| Figure | Field | Value |
|---|---|---|
| Transmit target | transmit.targetOutputDbm |
40.0 dBm |
| Transmit, as built | transmit.asBuiltOutputDbm |
40.65 dBm |
| Receive gain | receive.totalGainDb |
25.65 dB |
| Receive noise figure | receive.cascadedNfDb |
2.597 dB |
| Shared-path insertion loss | sharedPathLoss.insertionLossDb |
1.035 dB |
| Delivered at the antenna | sharedPathLoss.txDeliveredAtAntennaDbm |
39.615 dBm |
| Receive NF at the antenna | sharedPathLoss.rxCascadedNfAtAntennaDb |
3.632 dB |
| BOM | bom.summary |
29 lines, 43 items, $939.99, 15 of 29 priced |
⚠ Note sharedPathLoss.fedBack: false. The switch's loss is stated beside the
as-designed figures, not folded back into them — neither chain was re-run against
a target your user never gave. Quote both: the chain figure and the at-the-antenna
figure. They answer different questions.
Thirteen entries in warnings. They are the part worth reading, and the part
a summary usually deletes. Three of the thirteen:
- The receive budget's verdict is
lna_compressed—HMC8412compresses at 3.50 dBm input, below the 4.55 dBm reaching it during transmit. The disclosure carries the finding, a census of the alternatives ("6 of the 33 LNAs here that declare a computable input P1dB stay out of compression at this level"), an engineering judgement about severity ("the receiver is deaf, not damaged"), and the input that would settle it — a PRF and pulse width, so the timing budget's blind range can answer whether it recovers before the first range bin. TGL2209-SM"never conducts on this branch" — the limiter is present and idle, and no limiter covering this band clamps low enough to change that.- Three parts are inside their average rating but the filing states no peak/pulse rating: "The average is fine; the peak is UNVERIFIED — check the datasheet for a pulse rating."
And analysis.unresolved names heat the thermal model declines to attribute,
rather than absorbing it into a tidier total.
Read a design result is the companion skill for this step
— which field carries the power verdict, why needsPartFlags is the honesty flag,
and why a null verdict is more dangerous than a false one.
Step 4 — the second call, when a disclosure names its own missing input¶
The most useful thing these engines do is tell you what they are holding back.
The switch verdict came back safe:
| Field | Value |
|---|---|
budgets.stress.switch.powerAtThePartDbm |
40.65 dBm |
budgets.stress.switch.ratingDbm |
44.5 dBm |
budgets.stress.switch.ratingBasis |
abs_max_pulsed_85c_dbm |
budgets.stress.switch.stressDb |
−3.85 |
budgets.stress.switch.verdict |
safe |
⚠ Dated: recorded before 2026-10-09. It shows QPC2040 as the 9.4 GHz T/R switch, with the figures that went with it. Since 2026-10-09 (issue 90) the engine picks FW2001 by default for this design, so a fresh run shows a different switch and different leakage figures. Not re-recorded (owner decision, issue 369).
With a disclosure attached to it:
"No antenna return loss was supplied, so the load QPC2040 drives is unknown and the verdict above is measured against
abs_max_pulsed_85c_dbm. Its record also declaresabs_max_3to1_vswr_85c_dbm= 43.00 dBm… against that rating this design's 40.65 dBm is -2.35 dB of stress and the verdict would bemarginalrather thansafe. SupplyantennaReturnLossDb(6.02 dB or lower) to have it govern."
So supply it. One field, same design point:
| Without | With antennaReturnLossDb: 6.0 |
|
|---|---|---|
ratingBasis |
abs_max_pulsed_85c_dbm 44.5 dBm |
abs_max_3to1_vswr_85c_dbm 43.0 dBm |
stressDb |
−3.85 | −2.35 |
verdict |
safe |
marginal |
Same parts, same transmit power, asBuiltOutputDbm unchanged at 40.65. The engine
held the more binding rating the whole time and told you it was holding it,
because applying it without knowing the load would have over-derated a matched
design.
The procedure this teaches: when a disclosure names the input that would
resolve it, that is a question for your user, and often a second call. Do not
answer it yourself with a plausible number — antennaReturnLossDb: 6.0 from you
rather than from your user turns a safe into a marginal on your authority.
Step 5 — report¶
Report in this order, because it is the order a reviewer needs:
- What it built — topology, part count,
needsPartFlags. - The headline figures — as-built transmit, receive NF and gain, BOM total. Name the at-the-antenna figures separately.
- What it caught — the warnings, as findings, in the engine's own words.
- What it wants you to decide — the disclosures that name a missing input, each one as a question.
Do not compress step 3 into "the design looks good". A lna_compressed verdict
and a marginal stress verdict are handed back as findings for a human to rule
on, and that ruling is the one thing in this whole procedure that is not yours to
make.
Over MCP: the same procedure, with memory and a final check¶
Steps 0–5 hold unchanged over the MagicON MCP server
(https://api.pcbgenerator.com/mcp/, see the MCP guide).
Four things differ, and each one changes what you do.
1. Send your user's own words with every call¶
Every tool takes an optional user_request argument. Put your user's words in
it, verbatim — not your summary of them:
{
"frequencyGhz": 9.4,
"targetOutputPowerDbm": 40,
"transmitDutyCyclePercent": 10,
"user_request": "Design a 9.4 GHz radar T/R module with 40 dBm transmit power at 10% duty cycle."
}
The server checks the numbers you send against those words, and the result
comes back labelled grounding.mode: "user_stated". Leave it out and the
result says caller_asserted: your numbers were taken on your word. That is
step 1's rule — never invent an input — made visible in the result.
Keep it to the message that prompted the call: user_request is limited to
4,000 characters, and a longer one is refused with the limit named.
⚠ design_tr_module over MCP has no application argument. The HTTPS
example above sends "application": "radar"; over MCP, state the radar through
transmitDutyCyclePercent (and prfHz / pulseWidthS if your user gave them).
2. The session remembers, so do not re-send figures¶
Over HTTPS every call stands alone. Over MCP, one session remembers the results of earlier calls in it. After a design, call the analysis tools with no figures at all:
design_rf_chain(or a receiver / T/R design)run_thermal_analysis— no arguments neededrun_pdn_analysisrun_compliance_analysis
Measured on production: a run_thermal_analysis call sent nothing after a
2.4 GHz design_rf_chain took its heat from the design
(result.power_w_source: "derived_from_chain") and its layers from the solved
board (result.stackup_layers_source: "derived_from_solver").
Two limits on that memory:
- Only computed results are remembered. A number you sent as an argument is not evidence for a later call, even when a result repeats it back.
- It lasts one session. A new connection starts empty: nothing designed in an earlier session carries over.
⚠ PDN and compliance need a solved stackup and refuse without one
(needs_plane_pairs, needs_solved_stackup). After design_rf_chain the
server solves one for you and says so in autoSolvedStackup — see
Read a design result. It does not do that after a
receiver or T/R design: call run_stackup_solver yourself first.
3. Read the result one level down¶
For design_tr_module and design_rf_receiver, the fields in step 3 sit
inside a result key: result.needsPartFlags, result.warnings,
result.transmit.asBuiltOutputDbm, result.sharedPathLoss. Measured on
production for the 9.4 GHz T/R call above: result.needsPartFlags: [].
4. Check the draft before your user reads it¶
This is the one step with no HTTPS equivalent. Before you hand over a report,
a fabrication sheet or a summary, pass the draft to check_grounding. It
returns every number in the text that no engine in this session produced, and
every claim it checks that the results do not support or contradict (a solver
score called a loss, say). Its findings list gives each one's reason.
Measured on production, after a 2.4 GHz / 30 dBm design and a thermal run, on this draft:
The chain delivers 30.0 dBm against a 30 dBm target. The PA saturated output is 41.8 dBm.
The first figure came from the engine. The second did not, and it is the only
one flagged. For each flagged figure, do what its why says: quote the engine's
own value, call the tool that computes it, correct the wording, or say in the
text that it is not from a tool.
What a pass does not mean:
- It checks figures and a fixed set of claim shapes only. A claim outside them ("L3 is the power layer") is not checked.
- Run it before any design and it passes trivially, and its
notesays so: "a pass here is not evidence."
What it will not do¶
- It will not invent a part. An unfillable slot is named in
needsPartFlagsand left empty. - It will not check numbers you supply. Figures in the request are computed faithfully whether or not anyone said them. Pass through what your user actually stated.
- It will not evaluate a budget you did not ask for. Without a PRF and pulse
width the
timingbudget is absent entirely — not null, not a guess. - It will not decide for you.
lna_compressedandmarginalcome back as findings, with the evidence, for your user to rule on. - Over HTTPS, it will not remember. Every call is independent; if a second call needs a figure from the first, carry it yourself. Over MCP, one session remembers computed results — see above.
Related¶
- Pick the engine — transmitter vs receiver vs T/R vs link budget, with paths and rate limits.
- Read a design result — the field-by-field reading guide.
- Design a 9.4 GHz T/R module from Claude Code — the same design, told for a human reader.
- API reference for LLMs — every path, limit and error code.