[PR #42] feat(comfy-sdk): a single-file skill for building apps on the Comfy SDK and Router #42

Open
opened 2026-10-26 22:34:20 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/Comfy-Org/comfy-skills/pull/42
Author: @mattmillerai
Created: 10/8/2026
Status: 🔄 Open

Base: main ← Head: mattmiller/be-19739-comfy-sdk-skill


📝 Commits (7)

  • 412949b feat(comfy-sdk): a single-file skill for building apps on the Comfy SDK and Router
  • a82fead fix(comfy-sdk): smoke-check every fenced example, don't persist checkout creds
  • da52987 chore(comfy-sdk): drop the committed .pyc and ignore Python bytecode
  • 06e67d0 fix(comfy-sdk): TypeScript run errors are ComfyError, and a 429 is not retried
  • 2f6a5b1 fix(comfy-sdk): queued TypeScript client failures are ComfyError, not RouterError
  • 675527f fix(comfy-sdk): scope the error bucket to Router responses; name transport and abort errors
  • 05fc5b1 fix(comfy-sdk): TypeScript models.run reports an unreadable reply as unexpected_response

📊 Changes

7 files changed (+382 additions, -3 deletions)

View changed files

📝 .claude-plugin/marketplace.json (+7 -1)
➕ .github/scripts/sdk_skill_smoke.py (+69 -0)
➕ .github/workflows/sdk-skill-smoke.yml (+106 -0)
📝 .gitignore (+2 -0)
📝 README.md (+14 -2)
➕ claude-code-sdk/.claude-plugin/plugin.json (+10 -0)
➕ claude-code-sdk/skills/comfy-sdk/SKILL.md (+174 -0)

📄 Description

ELI5

A coding agent asked to "add image generation to my app with the Comfy SDK" had nothing telling it how the SDK and Comfy Router actually behave, so it would guess: put the API key in the browser, retry a generation itself (and pay twice), pick the wrong client, or catch the wrong kind of error. This adds a one-file skill that teaches the agent the stable rules and tells it to look up anything that changes (models, schemas, prices, timeout defaults) at run time.

What changed

  • claude-code-sdk/ — new comfy-sdk plugin: .claude-plugin/plugin.json (metadata only, no mcpServers) and one skills/comfy-sdk/SKILL.md. No references/ directory, so comfy skills install --skill <path> and copy-it-raw users (Cursor rules, AGENTS.md) get the whole thing.
  • .claude-plugin/marketplace.json — registers comfy-sdk next to comfy-cloud.
  • README.md — install block for comfy-sdk, an "Other agents" line, and the repo-layout entry. The existing authoring rule is unchanged, and the skill follows it (no model ids, prices or schemas).
  • .github/workflows/sdk-skill-smoke.yml and .github/scripts/sdk_skill_smoke.py — run on push to main, on PRs, and weekly. They install the latest comfy-sdk, comfy-cli and @comfyorg/sdk, pull the fenced python/ts blocks out of the skill (CommonMark fences: backtick or tilde, any length; an unclosed fence fails), fill in the placeholders, then run ast.parse and tsc --noEmit. They also confirm that models.run/submit/handle and router_exceptions exist in both SDKs, and type-check the TypeScript error surface the skill names (ComfyError.code from run, routerErrors.RouterError.errorType from the queued path, retryAfter on both), and run comfy skills validate, a grep that fails on a model id or price, and claude plugin validate ./claude-code-sdk. Both checkouts set persist-credentials: false. Nothing calls the network.

Judgment calls

  • I checked the skill against the published SDKs (0.4.0 when drafted, re-checked against 0.5.0) instead of copying the draft as-is. Changes from the draft:
    • Errors are split by language and call. Python raises RouterError subclasses (exc.error_type) everywhere. TypeScript comfy.models.run throws ComfyError subclasses read via err.code. On the queued path (submit / handle / subscribe), a Router error response throws routerErrors.* with err.errorType, and TS callers catch ComfyError on both paths.
    • Client-side failures carry no bucket and are listed separately for both languages: SDK-raised ones are a plain ComfyError (an unreadable reply is invalid_response in Python and on the TS queued path but unexpected_response from TS run; TS queued request_timeout), a transport failure after retries are spent is httpx.HTTPError (Python) or the native fetch TypeError (TS), Python subscribe raises TimeoutError, and a TS caller's own abort is rethrown unchanged.
    • Rate limits: Python run retries a 429 that names Retry-After; no TypeScript path does, so the skill tells TS callers to wait err.retryAfter themselves.
    • Discovery points TS users at comfy.models.list() / comfy.models.schema(). Python 0.4.0 has no wrapper for these, so Python users get the raw routes with a Bearer key.
    • Added a catch-all branch for unrecognised buckets (RouterError in Python and for Router responses on the TS queued path, ComfyError from TS run and SDK-raised client-side failures).
    • Spelled out that TS results have kind: "json" | "binary".
  • No hard-coded timeout numbers. The draft said "ten-minute" (Python run default and Router's deadline) and "twenty minutes" (TS default). Those are 600 s / 1,200,000 ms today, and an open upstream SDK change is moving the Python default. The skill now gives what each timeout bounds and its unit, and tells the agent to read the default off the installed SDK.
  • submit / handle / subscribe "need 0.3.0+": confirmed. comfy-sdk==0.2.0 doesn't have them; 0.3.0 does.
  • The draft's frontmatter had been turned into a Markdown heading on the way in. It's restored to real YAML (name: comfy-sdk, which matches the directory).

Provenance

  • Authored by: agent-work loop
  • Verified:
    • claude plugin validate (plugin and marketplace) and comfy skills validate on the skill pass, and a local marketplace install of comfy-sdk@comfy-skills installs the skill
    • Error and retry guidance checked against the comfy-sdk / @comfyorg/sdk 0.5.0 source: TS run maps buckets onto ComfyError subclasses; queued client timeouts throw ComfyError request_timeout, an unreadable reply is unexpected_response from TS run and invalid_response from the TS queued path and Python, while transport TypeErrors and caller aborts are rethrown as-is; Python transport errors stay httpx.HTTPError and subscribe raises TimeoutError; neither TS path retries below 500 except the 409/504 collect
    • Smoke block extraction, the model-id/price grep and comfy skills validate pass locally on the current head
    • Smoke type-check replayed locally against @comfyorg/sdk 0.5.0 (skill blocks + error surface): tsc --noEmit passes, and a planted ComfyError.errorType read fails as intended
    • Block extractor follows CommonMark fences (fixture-tested), and no compiled bytecode is tracked
    • CI on 05fc5b1: smoke and plugin-validate pass, with no merge-queue run yet
  • Deviations:
    • Draft content changed where it didn't match the published SDKs or would have hard-coded a timeout (see Judgment calls)

Residual

  • Install from GitHub not exercised. I ran the /plugin install comfy-sdk@comfy-skills acceptance against a local checkout of this branch. Installing from Comfy-Org/comfy-skills on GitHub only works after merge, so re-check it then.
  • Some claims come from the SDK docstrings and the investigation behind this skill, not from a live Router call. No calls were made because they cost money and need a key:
    • "no webhooks or push events"
    • subscribe's best-effort cancel on timeout
    • COMPLETED-with-error_type semantics
    • 409 invalid_input on key reuse with a different body
    • the docs URLs under "Read next"
  • Timeout guidance waits on the open Python SDK change that raises the run timeout above Router's deadline. The skill states no number, so it should not go stale, but its Python sentence ("sized to Router's deadline") should be re-read once that change lands.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/Comfy-Org/comfy-skills/pull/42 **Author:** [@mattmillerai](https://github.com/mattmillerai) **Created:** 10/8/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `mattmiller/be-19739-comfy-sdk-skill` --- ### 📝 Commits (7) - [`412949b`](https://github.com/Comfy-Org/comfy-skills/commit/412949b7dc7693aabf9feb25cc60a8dd067bfa71) feat(comfy-sdk): a single-file skill for building apps on the Comfy SDK and Router - [`a82fead`](https://github.com/Comfy-Org/comfy-skills/commit/a82fead1b0368743418c3f572821fefbc4d63a77) fix(comfy-sdk): smoke-check every fenced example, don't persist checkout creds - [`da52987`](https://github.com/Comfy-Org/comfy-skills/commit/da5298736d6c4129328a7dcad5cb4fba6c337eae) chore(comfy-sdk): drop the committed .pyc and ignore Python bytecode - [`06e67d0`](https://github.com/Comfy-Org/comfy-skills/commit/06e67d00d8b919f6b241536f2aa7ea91a992d4e2) fix(comfy-sdk): TypeScript run errors are ComfyError, and a 429 is not retried - [`2f6a5b1`](https://github.com/Comfy-Org/comfy-skills/commit/2f6a5b169b92a62665b401bac5c93967c5445f11) fix(comfy-sdk): queued TypeScript client failures are ComfyError, not RouterError - [`675527f`](https://github.com/Comfy-Org/comfy-skills/commit/675527fe633a8f1ace5761acac0f1718a94f13a7) fix(comfy-sdk): scope the error bucket to Router responses; name transport and abort errors - [`05fc5b1`](https://github.com/Comfy-Org/comfy-skills/commit/05fc5b18cbade9bba0b33bf86ae48f134fbbfc2e) fix(comfy-sdk): TypeScript models.run reports an unreadable reply as unexpected_response ### 📊 Changes **7 files changed** (+382 additions, -3 deletions) <details> <summary>View changed files</summary> 📝 `.claude-plugin/marketplace.json` (+7 -1) ➕ `.github/scripts/sdk_skill_smoke.py` (+69 -0) ➕ `.github/workflows/sdk-skill-smoke.yml` (+106 -0) 📝 `.gitignore` (+2 -0) 📝 `README.md` (+14 -2) ➕ `claude-code-sdk/.claude-plugin/plugin.json` (+10 -0) ➕ `claude-code-sdk/skills/comfy-sdk/SKILL.md` (+174 -0) </details> ### 📄 Description ## ELI5 A coding agent asked to "add image generation to my app with the Comfy SDK" had nothing telling it how the SDK and Comfy Router actually behave, so it would guess: put the API key in the browser, retry a generation itself (and pay twice), pick the wrong client, or catch the wrong kind of error. This adds a one-file skill that teaches the agent the stable rules and tells it to look up anything that changes (models, schemas, prices, timeout defaults) at run time. ## What changed - `claude-code-sdk/` — new `comfy-sdk` plugin: `.claude-plugin/plugin.json` (metadata only, no `mcpServers`) and one `skills/comfy-sdk/SKILL.md`. No `references/` directory, so `comfy skills install --skill <path>` and copy-it-raw users (Cursor rules, `AGENTS.md`) get the whole thing. - `.claude-plugin/marketplace.json` — registers `comfy-sdk` next to `comfy-cloud`. - `README.md` — install block for `comfy-sdk`, an "Other agents" line, and the repo-layout entry. The existing authoring rule is unchanged, and the skill follows it (no model ids, prices or schemas). - `.github/workflows/sdk-skill-smoke.yml` and `.github/scripts/sdk_skill_smoke.py` — run on push to main, on PRs, and weekly. They install the latest `comfy-sdk`, `comfy-cli` and `@comfyorg/sdk`, pull the fenced `python`/`ts` blocks out of the skill (CommonMark fences: backtick or tilde, any length; an unclosed fence fails), fill in the placeholders, then run `ast.parse` and `tsc --noEmit`. They also confirm that `models.run/submit/handle` and `router_exceptions` exist in both SDKs, and type-check the TypeScript error surface the skill names (`ComfyError.code` from `run`, `routerErrors.RouterError.errorType` from the queued path, `retryAfter` on both), and run `comfy skills validate`, a grep that fails on a model id or price, and `claude plugin validate ./claude-code-sdk`. Both checkouts set `persist-credentials: false`. Nothing calls the network. ## Judgment calls - **I checked the skill against the published SDKs (0.4.0 when drafted, re-checked against 0.5.0) instead of copying the draft as-is.** Changes from the draft: - Errors are split by language and call. Python raises `RouterError` subclasses (`exc.error_type`) everywhere. TypeScript `comfy.models.run` throws `ComfyError` subclasses read via `err.code`. On the queued path (`submit` / `handle` / `subscribe`), a Router error response throws `routerErrors.*` with `err.errorType`, and TS callers catch `ComfyError` on both paths. - Client-side failures carry no bucket and are listed separately for both languages: SDK-raised ones are a plain `ComfyError` (an unreadable reply is `invalid_response` in Python and on the TS queued path but `unexpected_response` from TS `run`; TS queued `request_timeout`), a transport failure after retries are spent is `httpx.HTTPError` (Python) or the native `fetch` `TypeError` (TS), Python `subscribe` raises `TimeoutError`, and a TS caller's own abort is rethrown unchanged. - Rate limits: Python `run` retries a `429` that names `Retry-After`; no TypeScript path does, so the skill tells TS callers to wait `err.retryAfter` themselves. - Discovery points TS users at `comfy.models.list()` / `comfy.models.schema()`. Python 0.4.0 has no wrapper for these, so Python users get the raw routes with a Bearer key. - Added a catch-all branch for unrecognised buckets (`RouterError` in Python and for Router responses on the TS queued path, `ComfyError` from TS `run` and SDK-raised client-side failures). - Spelled out that TS results have `kind: "json" | "binary"`. - **No hard-coded timeout numbers.** The draft said "ten-minute" (Python run default and Router's deadline) and "twenty minutes" (TS default). Those are 600 s / 1,200,000 ms today, and an open upstream SDK change is moving the Python default. The skill now gives what each timeout bounds and its unit, and tells the agent to read the default off the installed SDK. - `submit` / `handle` / `subscribe` "need 0.3.0+": confirmed. `comfy-sdk==0.2.0` doesn't have them; 0.3.0 does. - The draft's frontmatter had been turned into a Markdown heading on the way in. It's restored to real YAML (`name: comfy-sdk`, which matches the directory). ## Provenance - **Authored by:** agent-work loop - **Verified:** - `claude plugin validate` (plugin and marketplace) and `comfy skills validate` on the skill pass, and a local marketplace install of `comfy-sdk@comfy-skills` installs the skill - Error and retry guidance checked against the comfy-sdk / @comfyorg/sdk 0.5.0 source: TS `run` maps buckets onto `ComfyError` subclasses; queued client timeouts throw `ComfyError` `request_timeout`, an unreadable reply is `unexpected_response` from TS `run` and `invalid_response` from the TS queued path and Python, while transport `TypeError`s and caller aborts are rethrown as-is; Python transport errors stay `httpx.HTTPError` and `subscribe` raises `TimeoutError`; neither TS path retries below 500 except the `409`/`504` collect - Smoke block extraction, the model-id/price grep and `comfy skills validate` pass locally on the current head - Smoke type-check replayed locally against @comfyorg/sdk 0.5.0 (skill blocks + error surface): `tsc --noEmit` passes, and a planted `ComfyError.errorType` read fails as intended - Block extractor follows CommonMark fences (fixture-tested), and no compiled bytecode is tracked - CI on 05fc5b1: `smoke` and `plugin-validate` pass, with no merge-queue run yet - **Deviations:** - Draft content changed where it didn't match the published SDKs or would have hard-coded a timeout (see Judgment calls) ## Residual - **Install from GitHub not exercised.** I ran the `/plugin install comfy-sdk@comfy-skills` acceptance against a local checkout of this branch. Installing from `Comfy-Org/comfy-skills` on GitHub only works after merge, so re-check it then. - **Some claims come from the SDK docstrings and the investigation behind this skill, not from a live Router call.** No calls were made because they cost money and need a key: - "no webhooks or push events" - `subscribe`'s best-effort cancel on timeout - `COMPLETED`-with-`error_type` semantics - `409 invalid_input` on key reuse with a different body - the docs URLs under "Read next" - **Timeout guidance waits on the open Python SDK change that raises the run timeout above Router's deadline.** The skill states no number, so it should not go stale, but its Python sentence ("sized to Router's deadline") should be re-read once that change lands. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Comfy-Org/comfy-skills#42
No description provided.