
Step Parts
- 5.4k installs
- 11k repo stars
- Updated July 11, 2026
- earthtojake/text-to-cad
step-parts searches step.parts catalog, downloads verified STEP files for purchasable parts, and hands off to cad-viewer.
About
The step-parts skill finds and downloads common purchasable CAD parts from the hosted step.parts catalog before creating placeholder geometry. Use https://api.step.parts as canonical API origin. Workflow interprets parts into q search tokens plus optional category, family, standard, or tag facets, searches /v1/parts, presents ambiguous matches, prefers exact actuator STEP downloads when found, and verifies sha256 checksums on download. Network failures are inconclusive: retry with network permission before reporting unavailable. Named servos and motors need alias retries like STS3215 versus ST3215 and family facets. Bundled scripts/download_step_part.py supports search, --id download, facet filters, --all bulk download, and JSON stdout. After creating or updating local STEP files, always hand explicit paths to cad-viewer when installed. API reference in references/step-parts-api.md covers parts index, schema, and OpenAPI. Search guidance notes ANDed query tokens, ORed facet values, and millimeter attributes like thread and lengthMm.
- Hosted step.parts API at api.step.parts; do not scrape HTML or use stale local files.
- Search with q plus category family standard tag facets; retry aliases for actuators.
- download_step_part.py for search download checksum verify with JSON stdout.
- Prefer exact off-the-shelf STEP over placeholder envelopes when match found.
- Mandatory cad-viewer handoff with explicit file path after STEP create or update.
Step Parts by the numbers
- 5,426 all-time installs (skills.sh)
- +705 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #62 of 2,742 Automation & Workflows skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
step-parts capabilities & compatibility
- Capabilities
- step.parts api search with facet filters · alias retry for actuator and servo model numbers · step download with sha256 verification · download_step_part.py bundled script · cad viewer handoff after file creation
- Use cases
- orchestration
What step-parts says it does
Use the hosted step.parts machine endpoints instead of scraping HTML or relying on local repository files.
npx skills add https://github.com/earthtojake/text-to-cad --skill step-partsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 5.4k |
|---|---|
| repo stars | ★ 11k |
| Security audit | 2 / 3 scanners passed |
| Last updated | July 11, 2026 |
| Repository | earthtojake/text-to-cad ↗ |
How do I find and download standard purchasable CAD parts from step.parts instead of placeholder geometry?
Search step.parts catalog, resolve purchasable CAD parts, download verified STEP files, and hand off to cad-viewer.
Who is it for?
CAD workflows needing catalog screws, bearings, actuators, and connectors as verified STEP files.
Skip if: Skip when you need custom organic modeling without catalog parts or offline-only CAD with no API access.
When should I use this skill?
User needs step.parts search, standard part download, actuator model lookup, or STEP checksum verification.
What you get
Selected part record with local STEP path, sha256 verification, and cad-viewer handoff when available.
- Downloaded STEP files with checksums
- Selected part metadata JSON
- cad-viewer handoff path
By the numbers
- Canonical API api.step.parts
- sha256 verification on download
- Alias search for actuator model numbers
Files
CAD Parts
Provenance: maintained in earthtojake/text-to-cad. Use the installed local skill files as the runtime source of truth; the repository link is only for provenance and release review.
Overview
Use the hosted step.parts machine endpoints instead of scraping HTML or relying on local repository files. Treat https://api.step.parts as the canonical API origin and https://www.step.parts as the site/static-asset origin unless the user provides a different hosted mirror. Network/DNS failures are inconclusive: if api.step.parts cannot be reached from the sandbox, retry once with network permission before reporting a miss or using placeholder geometry. Do not describe a part as unavailable unless the API was reachable and returned no relevant candidates.
When a CAD assembly includes named off-the-shelf actuators, servos, motors, electronics boards, connectors, or other purchasable components, search step.parts before creating simplified placeholder geometry. For named servos, motors, and actuators, search both exact model strings and common aliases/vendor spellings before giving up. For example, STS3215 may also appear as ST3215, 3215, Waveshare Feetech ST3215, or under family=feetech. If the API was reachable and no exact or near-exact match is available, record the search miss and then use a documented envelope or simplified stand-in.
Quick Workflow
1. Interpret the requested part into search terms and optional facets:
qfor fuzzy tokens, standards, aliases, dimensions, source/product URLs, and attribute names/values.category,family,standard, ortagwhen the user gives an exact facet.
2. Search /v1/parts and inspect items, total, and facets. For actuator model numbers, retry likely aliases, dropped letters, vendor names, and relevant family facets before treating an empty result as a miss. 3. If results are ambiguous, present the best few options with id, name, standard, and key attributes before choosing. If one result clearly matches, return the selected record details without downloading unless the user asked for a local STEP file. 4. When an exact or near-exact off-the-shelf actuator model is found, prefer downloading and using its STEP file unless there is a clear assembly-time reason to use a simplified envelope. Record that choice explicitly. 5. When the user asks to download or save a STEP file, download its stepUrl, then verify the file with the record's sha256 when present. 6. Return the local path when downloaded, plus the selected part id and page/API URLs so the user can trace provenance.
CAD Viewer Handoff
After completing step.parts work that creates or updates a local .step or .stp file, you must ALWAYS hand the explicit file path to $cad-viewer when that skill is installed. $cad-viewer must start CAD Viewer if it is not already running and return link(s) to the relevant created or updated file(s); if $cad-viewer is unavailable or startup fails, report that instead of silently omitting the handoff.
Bundled Downloader
Use scripts/download_step_part.py for deterministic search, download, and checksum verification:
python scripts/download_step_part.py "M3 socket head 12" --download
python scripts/download_step_part.py --id iso4762_socket_head_cap_screw_m3x12 --download
python scripts/download_step_part.py "bearing 608zz" --limit 5Useful options:
--origin: overridehttps://api.step.partsonly when the user provides another hosted API origin.--tag,--category,--family,--standard: repeatable facet filters.--out-dir: override the download directory when the user asks for a specific destination.--all: with--download, download every result on the returned page as individual STEP downloads.--overwrite: replace an existing output file.
The script prints JSON to stdout. For searches, it prints matched records. For downloads, it prints saved file paths, checksums, and source URLs.
API Reference
Read references/step-parts-api.md when you need endpoint details, field meanings, or query semantics. Prefer:
/v1/partsfor filtered search with absolute asset URLs./v1/parts/{id}for one enriched record.- Returned
stepUrlfor STEP downloads. /v1/catalog/parts.index.jsonfor a compact discovery index./v1/catalog/schemafor field and family attribute meanings./v1/openapi.jsonwhen generating a client or tool.
Search Guidance
- Query tokens are ANDed by the API, so start specific but not overconstrained. For example, use
M3 SHCS 12before adding exact family and standard filters. - Values within one facet are ORed together, and selected
tag,category,family, andstandardfields are ANDed together. Use exact facets to narrow within known categories, then rank manually by name and attributes. - Standards can be queried as
ISO 4762,ISO4762, or the exactstandard.designation. - The
attributesobject contains family-specific facts such asthread,lengthMm,bore1Mm,material,profileSeries,slotSizeMm, and dimensions in millimeters. - Part, GLB, and PNG URL patterns are predictable on
https://www.step.parts; STEP URLs are environment-aware and may resolve to GitHub LFS media in production. Use catalog/APIstepUrlfor downloads.
interface:
display_name: "STEP Parts"
short_description: "Find and download STEP files from step.parts"
default_prompt: "Use $step-parts to find and download a STEP file from step.parts."
MIT License
Copyright (c) 2026 earthtojake
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
step.parts API Reference
Origins
Default to the API origin https://api.step.parts. Use a different origin only when the user supplies another hosted step.parts-compatible API domain. Static HTML pages live on https://www.step.parts; GLB and PNG preview URLs in API records point directly at Vercel Blob. STEP URLs are environment-aware and production records use commit-pinned GitHub LFS media. If the API domain does not resolve, treat the hosted service as unavailable and ask for a deployed origin when a live download is required.
Machine Endpoints
| Endpoint | Use |
|---|---|
https://www.step.parts/llms.txt | Human-readable agent guide with endpoint summary and examples. |
/v1/parts | Search, filter, paginate, and retrieve absolute asset URLs. |
/v1/parts/{id} | Fetch one enriched part record by stable id. |
/v1/catalog/schema | JSON Schema, field semantics, result ordering, and family attribute meanings. |
/v1/catalog/parts.index.json | Compact id/name/facet discovery index for cheap lookups before fetching details. |
/v1/openapi.json | OpenAPI 3.1 contract for generating clients/tools. |
/v1/parts Query Parameters
q: tokenized metadata search across id, name, description, category, family, stepSource, productPage, tags, aliases, standard fields, attribute keys, and attribute values. Every token must match.tag,category,family,standard: repeatable filters. Values may also be comma-separated. Values within one facet are ORed, and selected facet fields are ANDed together.page: 1-based page number.pageSize: default100, max500.
Unfiltered results start with a fixed 100-part showcase, then continue in stable source catalog order. Filtered results are ordered by stable source catalog order.
Examples:
https://api.step.parts/v1/parts?q=M3&tag=screw&page=2
https://api.step.parts/v1/parts?pageSize=100
https://api.step.parts/v1/parts?category=fastener&family=socket-head-cap-screw&standard=ISO%204762
https://api.step.parts/v1/parts?q=lengthMm%2012Response Fields
/v1/parts returns:
catalog: part count, last modified timestamp, catalog checksum, and URLs for schema/OpenAPI.items: enriched part records with absolutepageUrl,apiUrl,stepUrl,glbUrl, andpngUrl.page,pageSize,total,totalPages,hasNextPage,hasPreviousPage.facets: availabletags,categories,families, andstandardswith counts.filters: parsed active filters.
Each part record contains:
id: stable snake_case identifier and asset filename base.name,description,category,family,tags,aliases.standard: optional{ body, number, designation }.attributes: family-specific scalar facts.stepUrl,glbUrl,pngUrl.byteSize,sha256.
Asset URL Patterns
Use returned URLs when possible. Patterns are:
https://www.step.parts/step/{id}.step
Use the absolute `glbUrl` and `pngUrl` returned by the API record. Preview assets are served from Vercel Blob.
https://www.step.parts/parts/{id}The /step/{id}.step route serves local checked-out STEP bytes in local/dev mode and redirects to commit-pinned GitHub LFS media in production.
Download And Verification
When downloading a STEP file:
1. Fetch a part record from /v1/parts or /v1/parts/{id}. 2. Download stepUrl. 3. Compare the file SHA-256 to the part record's sha256 when it is not null. 4. Keep the original .step extension and preserve the source id in the filename unless the user asks otherwise.
#!/usr/bin/env python3
"""Search step.parts for common standard parts and download STEP files."""
from __future__ import annotations
import argparse
import hashlib
import json
import sys
import tempfile
import urllib.error
import urllib.parse
import urllib.request
from pathlib import Path
from typing import Any
DEFAULT_ORIGIN = "https://api.step.parts"
DEFAULT_OUT_DIR = tempfile.gettempdir()
USER_AGENT = "step-parts-skill/1.0"
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description=(
"Search the step.parts hosted catalog for low-level common standard parts "
"(screws, bolts, bearings, electronics parts, motors, connectors, etc.) "
"and optionally download canonical STEP files."
),
)
parser.add_argument("query", nargs="?", help="Fuzzy search query, for example 'M3 socket head 12'.")
parser.add_argument("--id", dest="part_id", help="Fetch a specific part id instead of searching.")
parser.add_argument("--origin", default=DEFAULT_ORIGIN, help=f"API origin. Default: {DEFAULT_ORIGIN}")
parser.add_argument("--download", action="store_true", help="Download the selected STEP file.")
parser.add_argument("--all", action="store_true", help="With --download, download every result on the returned page.")
parser.add_argument("--out-dir", default=DEFAULT_OUT_DIR, help="Directory for downloaded STEP files. Default: active temp directory.")
parser.add_argument("--filename", help="Filename to use when downloading one selected part.")
parser.add_argument("--overwrite", action="store_true", help="Overwrite an existing downloaded file.")
parser.add_argument("--limit", type=int, default=10, help="Search page size. The API caps this at 500.")
parser.add_argument("--page", type=int, default=1, help="1-based search page.")
parser.add_argument("--tag", action="append", default=[], help="Repeatable tag filter.")
parser.add_argument("--category", action="append", default=[], help="Repeatable category filter.")
parser.add_argument("--family", action="append", default=[], help="Repeatable family filter.")
parser.add_argument("--standard", action="append", default=[], help="Repeatable standard filter, for example 'ISO 4762'.")
parser.add_argument("--timeout", type=float, default=30.0, help="HTTP timeout in seconds.")
return parser.parse_args()
def origin_url(origin: str) -> str:
parsed = urllib.parse.urlparse(origin)
if not parsed.scheme or not parsed.netloc:
raise SystemExit(f"Invalid origin: {origin!r}")
return origin.rstrip("/")
def build_url(origin: str, path: str, params: list[tuple[str, str]] | None = None) -> str:
url = f"{origin_url(origin)}{path}"
if params:
return f"{url}?{urllib.parse.urlencode(params)}"
return url
def request(url: str, timeout: float) -> bytes:
req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
try:
with urllib.request.urlopen(req, timeout=timeout) as response:
return response.read()
except urllib.error.HTTPError as exc:
detail = exc.read().decode("utf-8", errors="replace")
raise SystemExit(f"HTTP {exc.code} for {url}: {detail}") from exc
except urllib.error.URLError as exc:
raise SystemExit(f"Failed to fetch {url}: {exc.reason}") from exc
def fetch_json(url: str, timeout: float) -> Any:
data = request(url, timeout)
try:
return json.loads(data)
except json.JSONDecodeError as exc:
raise SystemExit(f"Expected JSON from {url}: {exc}") from exc
def search_parts(args: argparse.Namespace) -> dict[str, Any]:
params: list[tuple[str, str]] = [
("page", str(max(1, args.page))),
("pageSize", str(max(1, args.limit))),
]
if args.query:
params.append(("q", args.query))
for key in ("tag", "category", "family", "standard"):
for value in getattr(args, key):
params.append((key, value))
return fetch_json(build_url(args.origin, "/v1/parts", params), args.timeout)
def get_part(args: argparse.Namespace, part_id: str) -> dict[str, Any]:
safe_id = urllib.parse.quote(part_id, safe="")
return fetch_json(build_url(args.origin, f"/v1/parts/{safe_id}"), args.timeout)
def selected_parts(args: argparse.Namespace) -> list[dict[str, Any]]:
if args.part_id:
return [get_part(args, args.part_id)]
result = search_parts(args)
items = result.get("items", [])
if not items:
raise SystemExit("No parts matched the query.")
if args.download and args.all:
return items
return [items[0]]
def filename_for(part: dict[str, Any], requested_filename: str | None, allow_requested: bool) -> str:
if requested_filename and allow_requested:
return requested_filename
step_url = str(part.get("stepUrl") or "")
name = Path(urllib.parse.urlparse(step_url).path).name
if name:
return name
return f"{part['id']}.step"
def step_download_url(part: dict[str, Any], origin: str) -> str:
step_url = part.get("stepUrl")
if not step_url:
raise SystemExit(f"Part {part.get('id', '<unknown>')} does not include stepUrl.")
return urllib.parse.urljoin(f"{origin_url(origin)}/", str(step_url))
def write_download(part: dict[str, Any], args: argparse.Namespace, allow_requested_filename: bool) -> dict[str, Any]:
step_url = step_download_url(part, args.origin)
out_dir = Path(args.out_dir)
out_dir.mkdir(parents=True, exist_ok=True)
path = out_dir / filename_for(part, args.filename, allow_requested_filename)
if path.exists() and not args.overwrite:
raise SystemExit(f"Refusing to overwrite existing file: {path}")
data = request(step_url, args.timeout)
path.write_bytes(data)
actual_sha256 = hashlib.sha256(data).hexdigest()
expected_sha256 = part.get("sha256")
checksum_ok = expected_sha256 is None or expected_sha256 == actual_sha256
if not checksum_ok:
raise SystemExit(
f"Checksum mismatch for {path}: expected {expected_sha256}, got {actual_sha256}",
)
return {
"id": part.get("id"),
"name": part.get("name"),
"path": str(path),
"stepUrl": step_url,
"pageUrl": part.get("pageUrl"),
"apiUrl": part.get("apiUrl"),
"byteSize": len(data),
"sha256": actual_sha256,
"checksumVerified": expected_sha256 is not None,
}
def compact_part(part: dict[str, Any]) -> dict[str, Any]:
return {
"id": part.get("id"),
"name": part.get("name"),
"category": part.get("category"),
"family": part.get("family"),
"standard": part.get("standard"),
"attributes": part.get("attributes"),
"stepUrl": part.get("stepUrl"),
"pageUrl": part.get("pageUrl"),
"apiUrl": part.get("apiUrl"),
"sha256": part.get("sha256"),
}
def main() -> int:
args = parse_args()
if args.filename and (not args.download or args.all):
raise SystemExit("--filename can only be used with --download for one selected part.")
if not args.part_id and not args.query and not any([args.tag, args.category, args.family, args.standard]):
raise SystemExit("Provide a query, --id, or at least one facet filter.")
if not args.download:
if args.part_id:
output: Any = compact_part(get_part(args, args.part_id))
else:
result = search_parts(args)
result["items"] = [compact_part(part) for part in result.get("items", [])]
output = result
json.dump(output, sys.stdout, indent=2)
sys.stdout.write("\n")
return 0
parts = selected_parts(args)
downloads = [
write_download(part, args, allow_requested_filename=len(parts) == 1)
for part in parts
]
json.dump({"downloads": downloads}, sys.stdout, indent=2)
sys.stdout.write("\n")
return 0
if __name__ == "__main__":
raise SystemExit(main())
Related skills
How it compares
step-parts fetches catalog STEP files from step.parts, not generative CAD modeling from text alone.
FAQ
Who is step-parts for?
CAD developers searching step.parts for purchasable parts and downloading verified STEP files.
When should I use step-parts?
Before creating placeholder geometry when assemblies need named motors, screws, bearings, or connectors.
Is step-parts safe to install?
Review the Security Audits panel; downloads files from api.step.parts and www.step.parts.