Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .changeset/skill-document-action-tools.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
'@objectstack/mcp': patch
---

fix(mcp): the generated SKILL.md now documents the business-action tools

`renderSkillMarkdown()` listed only the 7 object-CRUD tools; the MCP surface
exposes 9 — `list_actions` / `run_action` (business actions) were missing, so
agents installing the skill never learned they can run approvals, conversions,
or flow triggers directly. The skill now covers the full native tool surface
and teaches action preference: when `list_actions` offers a matching action,
call it instead of hand-editing the records it would have touched (actions
carry the app's validation and side effects), confirming destructive or
confirmation-flagged actions with the user first.

Prerequisite for the distribution shells (#2714 Phase 0): every shell repo
copies this rendered content, so the gap had to close before fan-out.
10 changes: 9 additions & 1 deletion packages/mcp/src/skill.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe('renderSkillMarkdown', () => {
expect(md).toContain('Authorization: ApiKey');
});

it('lists the object-CRUD tools and a discover-first instruction', () => {
it('lists the full native tool surface (CRUD + business actions) and a discover-first instruction', () => {
const md = renderSkillMarkdown();
for (const tool of [
'list_objects',
Expand All @@ -61,12 +61,20 @@ describe('renderSkillMarkdown', () => {
'create_record',
'update_record',
'delete_record',
'list_actions',
'run_action',
]) {
expect(md).toContain(tool);
}
expect(md.toLowerCase()).toContain('discover');
});

it('teaches action preference — run a matching business action instead of hand-editing records', () => {
const md = renderSkillMarkdown();
expect(md).toContain('Prefer actions over hand-edits');
expect(md).toContain('run_action({ actionName, objectName?, recordId?, params? })');
});

it('is generic — it does not enumerate any concrete schema', () => {
// The skill must not bake in tenant object/field names; it points to live
// discovery instead. Sanity: no stray "objectName: <something concrete>".
Expand Down
36 changes: 28 additions & 8 deletions packages/mcp/src/skill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,21 @@ description: ${OBJECTSTACK_SKILL_DESCRIPTION}

# ObjectStack

${intro} An ObjectStack environment exposes its data **objects** (tables) as
tools over the Model Context Protocol (MCP). Every operation runs **as you** —
${intro} An ObjectStack environment exposes its data **objects** (tables) and
its business **actions** (registered app logic: approvals, conversions, flow
triggers) as tools over the Model Context Protocol (MCP). Every operation runs **as you** —
under your account's permissions and row-level security — so you may see a
subset of rows, or get a permission error on a write. That is expected
governance, not a failure.

## When to use

Use these tools whenever the user wants to **inspect or change data** in their
ObjectStack app: look up records, filter/report, create or update entries, or
clean up data. Prefer these tools over guessing — the environment is the source
of truth.
ObjectStack app — look up records, filter/report, create or update entries,
clean up data — or **run a business action** the app defines (approve a
request, convert a lead, kick off a flow). Prefer these tools over guessing —
the environment is the source of truth, and an action is always better than
hand-editing the records it would have touched.

## Connect

Expand Down Expand Up @@ -112,6 +115,8 @@ always current even as the app evolves:
1. \`list_objects\` — see what objects exist.
2. \`describe_object({ objectName })\` — get an object's fields (name, type,
required) before querying or writing it.
3. \`list_actions\` — see what business actions you may run (each entry
includes its parameters and whether it needs a \`recordId\`).

Always discover the relevant object's shape before constructing a filter or a
create/update payload.
Expand All @@ -128,6 +133,14 @@ create/update payload.
- **update_record({ objectName, recordId, data })** — change fields on a record.
- **delete_record({ objectName, recordId })** — delete a record (destructive —
confirm with the user first).
- **list_actions()** — list the business actions you are permitted to run, with
each action's declared parameters, whether it operates on a record, and
whether it is flagged destructive.
- **run_action({ actionName, objectName?, recordId?, params? })** — invoke a
business action by name. This executes the app's registered logic (can
mutate data or trigger flows) under your permissions and RLS. Pass
\`recordId\` for record-scoped actions and \`params\` for declared inputs;
\`objectName\` only disambiguates a name shared by multiple objects.

## Conventions & gotchas

Expand All @@ -139,13 +152,20 @@ create/update payload.
- **Writes are real and immediate.** There is no implicit dry-run. Confirm
destructive actions (\`delete_record\`, bulk updates) with the user.
- **Page large reads.** Use \`limit\`/\`offset\` rather than asking for everything.
- **Prefer actions over hand-edits.** When \`list_actions\` offers an action for
the task (approve, convert, close, …), call it instead of updating the
records yourself — actions carry the app's validation and side effects.
Confirm first when an action is record-destructive or flagged for
confirmation.

## Recommended workflow

1. \`list_objects\` to orient.
1. \`list_objects\` (and \`list_actions\` when the task sounds like a business
operation) to orient.
2. \`describe_object\` on the target object.
3. \`query_records\` to read / verify current state.
4. \`create_record\` / \`update_record\` / \`delete_record\` to make changes,
confirming destructive steps with the user.
4. \`run_action\` when a matching business action exists; otherwise
\`create_record\` / \`update_record\` / \`delete_record\` — confirming
destructive steps with the user.
`;
}