From 6779b9af0ee8f9a32885169e9d79d1a113fbfb01 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Tue, 1 Sep 2026 15:54:52 -0700 Subject: [PATCH 1/2] Add a run-tool skill for direct integration tool calls `sim tools execute ` runs one built-in integration tool without a workflow, resolving the credential server-side. Nothing in the skill set covered it, so an agent asked for a single action against a connected service built a graph instead. The payload is the part an agent gets wrong: which auth path a tool wants is decided by the `visibility` its catalog entry publishes per parameter, not by guessing. `user-only` takes a `{{VAR_NAME}}` reference resolved server-side, `hidden` is Sim's to fill, and a tool declaring `oauth.required` wants a `--credential-id` rather than a key. Getting that wrong reads as an upstream 401 that names nothing. Also cross-references it from build-workflow, which otherwise implies a graph is the only way to reach an integration. --- scripts/validate-skills.ts | 1 + skills/build-workflow/SKILL.md | 4 +++ skills/run-tool/SKILL.md | 59 ++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 skills/run-tool/SKILL.md diff --git a/scripts/validate-skills.ts b/scripts/validate-skills.ts index 7482da5..02419d8 100644 --- a/scripts/validate-skills.ts +++ b/scripts/validate-skills.ts @@ -14,6 +14,7 @@ const expectedSkillNames = [ "build-workflow", "deploy-workflow", "knowledge-base", + "run-tool", "run-workflow", "table", ] as const; diff --git a/skills/build-workflow/SKILL.md b/skills/build-workflow/SKILL.md index ee14f31..9726701 100644 --- a/skills/build-workflow/SKILL.md +++ b/skills/build-workflow/SKILL.md @@ -8,6 +8,10 @@ description: Create or modify Sim workflows through the sim CLI. Use when transl Build the smallest valid graph that satisfies the request, using the Sim CLI as the source of truth for available resources and accepted shapes. +A request that is one action against one connected service needs no graph at all — call the tool +directly with `sim tools execute` (see the `run-tool` skill). Build a workflow when the task needs +more than one call, branching, or a schedule. + ## Establish context - Use the profile the user named. If none was named, inspect configured profiles and current context; diff --git a/skills/run-tool/SKILL.md b/skills/run-tool/SKILL.md new file mode 100644 index 0000000..444f13d --- /dev/null +++ b/skills/run-tool/SKILL.md @@ -0,0 +1,59 @@ +--- +name: run-tool +description: Call one Sim integration tool directly through the sim CLI, using credentials Sim already holds. Use for a single action against a connected service — send a message, create an issue, scrape a page; not for multi-step logic or anything scheduled, which belongs in a workflow. +--- + +# Run a Sim Integration Tool + +Sim holds the credentials. Read what the tool declares, bind auth from that declaration, and run it. +Never put a live credential in the command. + +## Preflight + +- `tools execute` requires a personal API-key profile; a workspace API key is refused. +- Find the tool with `sim --output json tools list --search `, then read it with + `sim --output json tools get `. Never guess a tool id or a parameter name. +- An unversioned name resolves to the newest version visible in the workspace, and the response + echoes the id that answered. Use that id in the call. + +## Bind auth from the declaration, not from habit + +`tools get` labels every parameter with a `visibility`, and the label says where its value comes +from: + +- `user-or-llm` — you supply it in `--input`. +- `user-only` — you supply it, but pass a reference instead of the secret: `{{VAR_NAME}}` as the + whole value, resolved server-side. List the available names with + `sim --output json secrets list`; values are never returned. Any other value is sent verbatim. +- `hidden` — Sim fills it. Never put it in `--input`. + +Then bind the credential by the tool's own shape: + +- The tool declares `oauth.required` — find the credential with + `sim --output json credentials list --provider-id ` and pass `--credential-id`. + Omitting it fails with a message naming the provider. +- `hostedApiKey` is `always`, or `conditional` and this call matches — omit the key entirely; Sim + supplies its own and bills the workspace. +- Otherwise the tool takes its own `user-only` key parameter; pass a `{{VAR_NAME}}` reference. + +## Run and read the outcome + +```bash +sim --output json tools execute --credential-id --input '{"...": "..."}' +``` + +`--input` also accepts `@path` or `@-`, which is how a payload too large or too awkward to quote +reaches the command. + +A tool that ran and refused exits non-zero with `status: "failed"` and the reason in `error.message`: +the call reached the service and the service declined. Report that reason. A `403` carrying +`error.details.code` `INTEGRATION_NOT_ALLOWED` is a workspace policy decision, not a fixable +argument — say so and stop. + +## Invariants + +- Never print a resolved secret, an OAuth token, or a profile credential. You hold a credential id + and a variable name; that is all the call needs. +- Do not retry a failed write blind. A tool call is not idempotent, and a retry can duplicate the + message, issue, or record the first attempt created. +- Build a workflow instead when the task needs more than one call, branching, or a schedule. From 442fb58458c1c3edd2ff1773e5238f3ea918eee0 Mon Sep 17 00:00:00 2001 From: Vikhyath Mondreti Date: Tue, 1 Sep 2026 18:37:14 -0700 Subject: [PATCH 2/2] run-tool: cover tools that declare the credential selector as a parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sixty-eight tools — Snowflake among them — declare `oauthCredential` or `credential` as a required `user-only` parameter with no `oauth` block. The skill's rule for `user-only` ("pass a `{{VAR_NAME}}` reference in --input") would have sent an agent straight into the endpoint's refusal: a credential under any spelling in --input is a 400 pointing at the top-level field. Name that shape beside `oauth.required` as a second trigger for `--credential-id`, and state the endpoint's actual contract once: --input accepts exactly what `tools get` publishes as the caller's to send, and refuses the rest by name. --- skills/run-tool/SKILL.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/skills/run-tool/SKILL.md b/skills/run-tool/SKILL.md index 444f13d..1687b49 100644 --- a/skills/run-tool/SKILL.md +++ b/skills/run-tool/SKILL.md @@ -25,17 +25,25 @@ from: - `user-only` — you supply it, but pass a reference instead of the secret: `{{VAR_NAME}}` as the whole value, resolved server-side. List the available names with `sim --output json secrets list`; values are never returned. Any other value is sent verbatim. -- `hidden` — Sim fills it. Never put it in `--input`. + One exception: a parameter named `credential` or `oauthCredential` is the credential selector, + not a secret — see below. It is refused in `--input`. +- `hidden` — Sim fills it. Never put it in `--input`; it is refused. Then bind the credential by the tool's own shape: -- The tool declares `oauth.required` — find the credential with - `sim --output json credentials list --provider-id ` and pass `--credential-id`. - Omitting it fails with a message naming the provider. +- The tool declares `oauth.required`, **or** declares a required `credential` or `oauthCredential` + parameter (Snowflake and others do the latter, with no `oauth` block) — find the credential with + `sim --output json credentials list --provider-id ` and pass `--credential-id`. It is + the one place a credential is named; Sim places it where the tool expects it. Omitting it fails + naming `credentialId`; putting it in `--input` under any spelling is refused. - `hostedApiKey` is `always`, or `conditional` and this call matches — omit the key entirely; Sim supplies its own and bills the workspace. - Otherwise the tool takes its own `user-only` key parameter; pass a `{{VAR_NAME}}` reference. +`--input` accepts exactly what `tools get` publishes as yours to send. An undeclared key, a +`hidden` one, or a credential under any name is a `400` that names the offending field — read +it rather than guessing at a spelling. + ## Run and read the outcome ```bash