Skip to content

feat(agent-toolkit): agents subgraph tools expansion#343

Open
NadavAvraham wants to merge 30 commits into
masterfrom
feat/agent-tools-expansion
Open

feat(agent-toolkit): agents subgraph tools expansion#343
NadavAvraham wants to merge 30 commits into
masterfrom
feat/agent-tools-expansion

Conversation

@NadavAvraham
Copy link
Copy Markdown
Collaborator

monday item

https://monday.monday.com/boards/10045819454/pulses/10045820228

Summary

  • Adds 6 new tools to @mondaydotcomorg/agent-toolkit covering the full agents subgraph surface
  • get_agent_catalog — discovers available trigger types and skills account-wide (READ)
  • manage_agent_triggers — list, add, and remove triggers on an agent (WRITE)
  • manage_agent_skills — create custom skills, attach and detach skills from an agent (WRITE)
  • update_agent — update an agent's name, role, description, or execution plan (WRITE)
  • manage_agent_state — activate, deactivate, or manually run an agent (WRITE)
  • manage_agent_knowledge — list, grant, update, and revoke an agent's access to boards and docs (WRITE)
  • Bumps package version to 5.11.0

Design decisions

  • Tool descriptions encode catalog-first workflows so agents know to call get_agent_catalog before adding triggers or skills
  • manage_agent_triggers and manage_agent_knowledge include a list action to support the lookup-before-mutate pattern (get node_id / inspect current state before remove/update)
  • All tools use versionOverride: 'dev' — the agents subgraph is still on the dev API version
  • manage_agent_skills consolidates skill creation (create_agent_skill mutation) alongside attach/detach in one tool since they're all skill management operations
  • GraphQL operations are co-located with each tool's directory rather than centralised in shared/

Test plan

  • npm test passes (1013 tests across 55 suites)
  • npm run build compiles cleanly
  • Each tool has happy-path, validation-error, null-response, and API-error-propagation coverage

🤖 Generated with Claude Code

NadavAvraham and others added 26 commits May 13, 2026 17:30
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements the get_agent_catalog READ tool that fetches the account-wide catalog of available trigger types or skills for monday platform agents via the dev API version.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…d test assertion

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements list/add/remove actions for agent triggers, using the dev API versionOverride. All 10 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rigger actions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements the manage_agent_skills tool supporting add/remove actions, with full test coverage for happy paths, request shape verification, and error propagation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…null response tests

Use explicit if/else branching in executeInternal and add tests verifying the ?? false fallback when the API returns null for add_skill_to_agent or remove_skill_from_agent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements the update_agent WRITE tool that patches an existing monday platform agent's profile or execution plan, including only the provided fields in the mutation input.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Throw explicit error when update_agent returns no data instead of falling back to {}
- Strengthen agent_model describe string to match create_agent's STRONGLY DISCOURAGED wording
- Add profile.name assertion to happy-path test to confirm response forwarding

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds manage_agent_state tool supporting activate, deactivate, and run actions for monday platform agents. Includes 11 unit tests covering happy paths, error propagation, versionOverride, default inactive_reason, and null fallbacks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ety, and test coverage

Restructure try/catch blocks so return statements are outside the try, add explicit null check for run_agent response, and add test coverage for null run_agent case.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements list/add/update/remove actions for managing a monday platform
agent's access to boards and docs, following the restructured try/catch
pattern from manage-agent-state-tool.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…agent_knowledge test coverage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…safety checks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…s list note

Move return statements inside try blocks (using const) for all branches in manage-agent-state-tool.ts and manage-agent-knowledge-tool.ts, eliminating non-null assertions. Add a note to manage_agent_skills description that there is no list action because the platform does not yet expose that query.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ills tool

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- create_agent: guide calling manage_agent_state(activate) after creation instead of redirecting to the monday.com UI
- manage_agent_state: narrow inactive_reason to DEACTIVATED_BY_USER only (remove internal ACCOUNT_LEVEL_BLOCKING); describe when to pass it vs omit
- manage_agent_knowledge: document that files in the list response cannot be managed via this tool and redirect to the platform UI
- manage_agent_skills: clarify create→add flow — use the id returned by action:create directly, no catalog lookup needed
- update-agent-tool.test: add missing null-response test for update_agent returning no data
- manage-agent-state-tool.test: update test to reflect removed ACCOUNT_LEVEL_BLOCKING enum value

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown

@RanEldann RanEldann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strong addition overall — the 6 new tools follow established patterns in the toolkit, tests cover happy paths plus most error/null/validation cases, and the GraphQL operations are correctly wired. Auth model is fine: JWT-scoped, every mutation goes through authorizeAgentAction('edit', agentId), no security concerns from the toolkit perspective.

Three blocking issues

  1. Prettier will fail CIcreate-agent-tool.ts:166-171 has an 8-space indent in a 6-space block. Looks like an editor mishap during the latest edits.
  2. .optional() + manual throw instead of z.discriminatedUnion across all four manage_* tools. The repo already uses discriminated unions in 4 places (update-doc-tool.schema.ts, create-doc-tool.ts). The current pattern means LLMs see all action-conditional fields as optional in the JSON schema and only learn requirements via runtime errors.
  3. field_values: z.record(z.unknown()) in manage_agent_triggers provides no schema guidance to the LLM. The description tells it to call get_agent_catalog and follow field_schemas, but the JSON schema is fully untyped — three different shape conventions hide behind one record.

Theme: description ↔ code drift

Several tool descriptions claim or imply behaviors the code doesn't deliver:

  • inactive_reason's "omit if ambiguous" is meaningless because the code defaults to the same value either way (it's also a 1-value enum, so the field is pure noise)
  • update_agent's "profile or execution plan" overpromises — avatar_url, background_color, goal, user_prompt aren't in UpdateAgentInput
  • agent_model's "degrades the agent's performance" is wrong — invalid values are rejected by the server, not degraded
  • manage_agent_triggers doesn't mention that the catalog excludes 3rd-party (OAuth) triggers — LLMs will look up Slack/Salesforce and be confused they're missing
  • manage_agent_knowledge's "call list first" is overstated — only needed for update/remove, not add

Theme: cross-tool inconsistency

  • update_agent returns { content: agent } while siblings return { content: { message, agent } } — LLM has to learn per-tool shapes
  • agent_id (new tools) vs id (existing update/get/delete-agent) — same concept, two names
  • count field on most list responses but missing from knowledge list

Nits

  • Missing CHANGELOG.md entry for 5.11.0 (the agent-toolkit/CLAUDE.md mandates this — though to be fair, CHANGELOG was already stale at 5.7.1)
  • update-agent-tool.test.ts has 3 expect()s in a single it() — violates the "one assert per test" rule used elsewhere
  • rethrowWithContext strings are verbose and inconsistent — 4 of them have double-"agent" ("list agent knowledge for monday platform agent", etc.). The "monday platform agent" suffix is wasted tokens in every error
  • Domain validation errors (e.g. throw new Error('create_agent_skill returned no data')) get double-wrapped to "Failed to X: <inner>" because the throw is inside the try block
  • trigger_uuid returned by manage_agent_state action:run has no companion query yet — the description should say "store for future correlation; no run-status query exists yet"
  • manage_agent_knowledge list returns a files array the tool can't act on; the description has to apologize. Either drop from the GraphQL selection or split into a separate read tool

Recommendations

  1. Fix Prettier first (item 1) — that's CI.
  2. Convert the four manage_* schemas to z.discriminatedUnion('action', [...]). This single change fixes findings 2, 4, 8, 13, plus removes the manual cross-field throws.
  3. Fix the description honesty issues (medium-severity but cheap) — they directly affect tool-call success rate in production.
  4. Align update_agent's return shape and rename idagent_id for consistency.
  5. Optional but recommended: type agent_model as z.nativeEnum(AgentModel) so wrong values fail at zod time with a clear list of valid options, instead of producing a confusing GraphQL error.

Nice work overall — happy to clarify or drop any of the smaller items.

Comment thread packages/agent-toolkit/package.json
NadavAvraham and others added 2 commits May 14, 2026 13:16
- Fix Prettier indent in create-agent-tool.ts (CI blocker)
- update_agent: rename id→agent_id for consistency with new tools,
  use z.nativeEnum(AgentModel), fix description to list only updatable
  fields, add empty-input guard, fix return shape to { message, agent }
- manage_agent_state: remove inactive_reason (1-value enum always
  defaulted), hardcode DeactivatedByUser, clarify trigger_uuid has no
  companion run-status query
- manage_agent_knowledge: scope "call list first" to update/remove,
  add count field to list response
- manage_agent_triggers: narrow field_values to typed union, add OAuth
  exclusion note, clarify add returns no node_id
- get_agent_catalog: add .min(1) to block_reference_ids
- Update tests: split 3-expect test, agent_id naming, new return shape,
  simplified inactive_reason coverage
- Add CHANGELOG.md entry for 5.11.0

Co-authored-by: Cursor <cursoragent@cursor.com>
…_skills

create_agent_skill (account-catalog CRUD) and manage_agent_skills
(attach/detach on a specific agent) operate on different domain
entities, so they now live in separate tools. manage_agent_skills
action enum shrinks to [add, remove]; all create logic and the
createAgentSkill GraphQL mutation move to the new create_agent_skill
tool.

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown

@RanEldann RanEldann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nadav 👑 you're a king. Genuinely impressive turnaround — almost every finding addressed, several solved structurally (extracting create_agent_skill, dropping inactive_reason entirely), and descriptions are now honest about what each tool actually does. This is a major enhancement — agents can now manage the full agent lifecycle end-to-end with schemas they can actually act on. Approving 🎉

One thought on the discriminated-union pushback before we move on — I think the blocker is smaller than it looked, and the LLM ergonomics win is real:

The actual constraint is just Input extends ZodRawShape in BaseMondayApiTool plus 3–4 z.object(inputSchema) sites in toolkit.ts. ~15 lines, fully backwards-compatible:

// base-monday-api-tool.ts
export type ToolInputSchema = ZodRawShape | ZodTypeAny | undefined;

export abstract class BaseMondayApiTool<
  Input extends ToolInputSchema,
  Output extends Record<string, unknown> = never,
>
// toolkit.ts — small helper
function buildZodSchema(input: any): ZodTypeAny | undefined {
  if (!input) return undefined;
  if (typeof input.safeParse === 'function') return input;  // already a Zod schema
  return z.object(input);  // ZodRawShape — existing tools
}
// then replace the 4 z.object(inputSchema) sites with buildZodSchema(inputSchema)

Then each manage_* schema becomes z.discriminatedUnion('action', [...]) with one z.object per variant. zodToJsonSchema produces oneOf with per-action required arrays — the LLM sees "for action=add, resource_id + scope_type + permission_type are required" in the schema itself instead of parsing it out of prose.

LLMs miss conditional prose ("Required for action:X") reasonably often. Discriminated oneOf cuts misuse-on-first-try meaningfully on real workloads. Bonus: drops the manual if (!input.x) throw blocks and TypeScript narrows the input type per case branch in executeInternal.

Fully backwards-compatible — every existing ZodRawShape tool keeps working unchanged. Not blocking, just smaller than the "follow-up" reply implied. Up to you whether to do it here or a separate PR 🙂

Great work 🚀

NadavAvraham and others added 2 commits May 17, 2026 19:06
- Fix CHANGELOG entry to list actual tool names (manage_agent,
  manage_agent_capabilities, manage_agent_knowledge, agent_catalog)
  instead of stale names from an earlier design iteration
- Refactor ManageAgentKnowledgeTool.executeInternal from if/else chain
  to switch with dedicated private handler methods, consistent with all
  other tools in the PR
- Document readOnlyMode limitation in AgentCatalogTool description
- Clarify zero-field create behavior and strengthen delete warning in
  ManageAgentTool description
- Clarify list-skills gap note in ManageAgentCapabilitiesTool to
  distinguish catalog browsing from per-agent skill listing

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown

@RanEldann RanEldann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did another focused pass on the restructure (consolidation from 9 → 4 tools), specifically on tool grouping and per-tool API legibility. Three concerns surfaced that I didn't catch in the May 14 approval — plus a security regression that re-opens something we'd already mitigated. Walking back the approval; details below.

Headline: tool count is the wrong optimization target; per-tool clarity is the right one. Several of the bundles are bundling unrelated things — and the bundling causes real costs (mixed destructiveHint, mixed READ/WRITE classification, all-optional schemas the LLM can't introspect).

High

🔴 inactive_reason re-exposes ACCOUNT_LEVEL_BLOCKING to LLMs (security regression). Prior version Zod-restricted to DEACTIVATED_BY_USER / hardcoded the value. The platform-side resolver runs blockApp() for the entire account on ACCOUNT_LEVEL_BLOCKING without an admin gate (verified in microservices/monday-agents/.../update-agent-state.ts:73; REST has the MANAGE_ACCOUNT_AGENTS gate, GraphQL doesn't). A user with edit on one agent can now ask the LLM to "block this for the account" and the call goes through. Inline comment below.

Medium — structural (the actual focus of this round)

These are the consolidation choices that look efficient (1 tool replaces N) but cost more than they save in LLM-correctness:

  • agent_catalog is WRITE because create_skill is bundled with list_triggers/list_skills. Description self-acknowledges: "In deployments with readOnlyMode enabled, catalog browsing is therefore unavailable." Read-only LLM sessions can't even discover trigger types / skill IDs they need before any add operation. The split was correct in commit 68dffd0; merging back was a regression.
  • manage_agent mixes 3 unrelated categories (CRUD: create/get/update/delete; state: activate/deactivate; execution: run). 13 mostly-optional fields under 7 actions. delete poisons the whole tool with destructiveHint: true. Splitting into 3 (manage_agent for CRUD, set_agent_state, run_agent) buys per-tool honesty in annotations, ~5-field tools instead of 13, no false-positive destructive warnings on non-destructive actions.
  • manage_agent_capabilities mixes triggers + skills (orthogonal concepts) and ends up with an asymmetric capability surface — list_triggers exists per-agent, list_skills per-agent doesn't (it's in agent_catalog, account-wide). LLM has to remember the gap. The original split into manage_agent_triggers + manage_agent_skills was structurally cleaner.

Medium — regressions vs my May 14 approval

  • agent_model: z.string() + as AgentModel cast — was z.nativeEnum(AgentModel) after the prior fix. Invalid values now bypass Zod and fail at the GraphQL server with confusing errors. The "STRONGLY DISCOURAGED" prose is back unnecessarily.
  • id vs agent_id inconsistency — now between sibling tools. manage_agent uses id; manage_agent_capabilities and manage_agent_knowledge use agent_id. Same concept, two names within the new toolkit.
  • destructiveHint: true on a 7-action tool — per-tool not per-action; MCP clients warn on every get/update/activate/run.

Medium — API ergonomics

  • create action has two sub-modes (AI: prompt+agent_model; manual: 7 profile fields). Same action name, different inputs, runtime guard "Do not mix both." — that warning exists because the API invites mixing.
  • get action is two operations: with id returns one agent; without id lists owned. Different output shapes behind one action name.
  • Discriminated-union concern is now bigger. manage_agent has 13 conditional fields under 7 actions — the JSON schema still gives no per-action required. The base-class blocker is real, but the ~15-line fix sketched in my May 14 approval comment becomes higher-leverage as the consolidation grows.

Minor

  • Cross-tool list_triggers collision — exists in both manage_agent_capabilities (per-agent attached) and agent_catalog (account-wide available). Disambiguation by tool name only.
  • agent_catalog name implies browse-only but the tool also creates skills.
  • Cross-tool workflow chains (agent_catalog lookup → manage_agent_capabilities add) are described in prose only, not in schema.

What I'd actually change

If I were doing this myself, in order:

  1. Fix inactive_reason (security) — must.
  2. Split manage_agentmanage_agent (CRUD) + set_agent_state + run_agent. The destructiveHint, sub-mode create, and two-op get issues all dissolve.
  3. Split agent_catalog back into agent_catalog (READ) + create_agent_skill (WRITE). Restores readOnlyMode and the name fits.
  4. Split manage_agent_capabilities back into manage_agent_triggers + manage_agent_skills. Removes the asymmetry.
  5. Restore z.nativeEnum(AgentModel) and agent_id naming.
  6. Discriminated-union refactor is still worth a separate PR — concrete sketch in the prior approval thread.

End state: ~7 tools, each with a tight coherent API. The original 9-tool layout was structurally close to this — the consolidation overshot. The 6-tool plan above hits a similar reduction goal without the bundling costs.

Keep manage_agent_knowledge exactly as-is — it's the model the others should follow.

Appreciate the iterations on this PR — the description quality and test coverage are great, this is just structural feedback. Happy to chat through any of it.

plan: z.string().trim().min(1).optional().describe('Used with action:"update". New step-by-step execution plan in markdown.'),
// deactivate
inactive_reason: z
.enum(['DEACTIVATED_BY_USER', 'ACCOUNT_LEVEL_BLOCKING'])
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 [High — security regression] Re-introducing ACCOUNT_LEVEL_BLOCKING to the LLM-callable enum re-opens the platform's GraphQL authz hole. Verified in the prior security pass: microservices/monday-agents/src/agents/services/agent-management-service/update-agent-state.ts:73 calls PlatformApiManagementHttpClient.blockApp(...) with no admin gate when inactiveReason === ACCOUNT_LEVEL_BLOCKING. The REST middleware (permission-middleware.ts:119–150) gates this with MANAGE_ACCOUNT_AGENTS; the GraphQL path doesn't. The previous version of this PR mitigated by Zod-restricting to DEACTIVATED_BY_USER only, then later removed the field entirely.

Fix: re-restrict to 'DEACTIVATED_BY_USER' only, or drop the field (the server defaults work). Either is fine — the Zod gate is the only defense in this layer until the platform team adds the GraphQL admin check.

.describe(
'Used with action:"create" (AI mode). Plain-language description of what the agent should do. Platform uses this to generate profile, goal, and plan via AI. Be specific about domain and tasks.',
),
agent_model: z
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Medium — type-safety regression] This was z.nativeEnum(AgentModel) after the May 14 fix — invalid values failed at Zod with a clear list of valid options. Now z.string().trim().min(1).optional() + as AgentModel cast at line 273 means any string passes Zod and fails at the GraphQL server with a confusing error.

Fix: restore z.nativeEnum(AgentModel).optional(); drop the cast at line 273; the giant "STRONGLY DISCOURAGED" paragraph can shrink to one line since invalid options now fail at parse time.

.describe(
'"create" — create a new agent (AI mode: pass "prompt" / manual mode: pass name/role/etc). "get" — fetch agents by id or list owned agents. "update" — modify mutable fields on an existing agent. "delete" — permanently delete an agent (irreversible). "activate" — transition agent to ACTIVE. "deactivate" — transition agent to INACTIVE. "run" — manually enqueue an agent run (fire-and-forget).',
),
id: z
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Medium — naming inconsistency] This tool uses id; manage_agent_capabilities and manage_agent_knowledge (sibling new tools) use agent_id. Same concept, two names within the new toolkit.

Fix: rename to agent_id here for consistency with the other 3 new tools, and remap to id only at the GraphQL variables boundary.


export const manageAgentToolSchema = {
action: z
.enum(['create', 'get', 'update', 'delete', 'activate', 'deactivate', 'run'])
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Medium — grouping] 7 actions across 3 unrelated categories: CRUD (create/get/update/delete), state (activate/deactivate), execution (run). Symptoms of the mismatch:

  1. delete forces destructiveHint: true on the whole tool — every get/update/activate/run triggers MCP destructive warnings.
  2. run is a fire-and-forget side effect that doesn't share inputs/outputs with the others.
  3. get is two operations (fetch one with id, list owned without id).
  4. create has two sub-modes (AI / manual) — different validation, different mutations, runtime guard "Do not mix both."
  5. 13 mostly-optional fields with "Used with action:X" prose. JSON schema gives no per-action required.

Suggested split:

  • manage_agentcreate | get | update | delete (CRUD; destructiveHint: true is honest)
  • set_agent_stateactivate | deactivate (idempotent state transitions)
  • run_agent → standalone tool (fire-and-forget)

Net: 3 tools instead of 1, but each ~5 fields and coherent. Items 4, 7, 8, 13 from the review all dissolve in this split.

annotations = createMondayApiAnnotations({
title: 'Manage monday Platform Agent',
readOnlyHint: false,
destructiveHint: true,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Medium — annotation regression] destructiveHint: true is per-tool, not per-action. With 7 actions in one tool, this means MCP clients that surface destructive warnings will warn on every get, update, activate, run call — even though only delete is actually destructive. False-positive friction.

Fixed by splitting delete to its own tool (or to manage_agent only), making the annotation honest.

}
}

private async handleCreate(input: ToolInputType<typeof manageAgentToolSchema>): Promise<ToolOutputType<never>> {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Medium — sub-modes footgun] create has two sub-modes:

  • AI mode: prompt (+ optional agent_model)
  • Manual mode: 7 profile fields (name, role, role_description, avatar_url, gender, background_color, user_prompt)

Same action name, completely different inputs, different mutations called (createAgentMutation vs createBlankAgentMutation), and a runtime guard at lines 175–177: "Do not mix both." The guard exists because the API invites mixing.

Fix options:

  • Split into two actions: create_with_prompt and create_blank (clearest)
  • Or two tools (create_agent + create_blank_agent)
  • Or — if keeping merged — at minimum mark the field groups in the schema description so the LLM sees the boundary

}

private async handleGet(input: ToolInputType<typeof manageAgentToolSchema>): Promise<ToolOutputType<never>> {
if (input.id !== undefined) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Medium — overloaded action] get does two things: with id returns a single agent ({ message, agent }); without id lists owned agents ({ message, count, agents: [] }). Different output shapes behind one action name.

Not a deal-breaker but worth surfacing in the action enum: split into get | list so the LLM sees two distinct ops with predictable return shapes.


export class AgentCatalogTool extends BaseMondayApiTool<typeof agentCatalogToolSchema> {
name = 'agent_catalog';
type = ToolType.WRITE;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Medium — readOnlyMode trap] ToolType.WRITE because of create_skill. The description acknowledges the cost:

"list_triggers and list_skills are read-only operations, but this tool is classified as WRITE because it also creates skills. In deployments with readOnlyMode enabled, catalog browsing is therefore unavailable."

That's a self-acknowledged regression. Read-only LLM sessions can't discover trigger types or skill IDs — meaning they can't even prepare an add_trigger/add_skill call.

This was correctly split in commit 68dffd0: agent_catalog (READ) + create_agent_skill (WRITE). Re-merging was the regression. Suggest reverting to that split — name fits each tool, readOnlyMode works.

};

export class AgentCatalogTool extends BaseMondayApiTool<typeof agentCatalogToolSchema> {
name = 'agent_catalog';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Minor — naming] "Catalog" implies a collection you browse, but this tool also authors entries (create_skill). After splitting WRITE off (per the prior comment), the name fits cleanly: agent_catalog becomes browse-only, and the authoring goes to create_agent_skill.


export const manageAgentCapabilitiesToolSchema = {
action: z
.enum(['list_triggers', 'add_trigger', 'remove_trigger', 'add_skill', 'remove_skill'])
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Medium — orthogonal concepts in one tool] Triggers (when an agent runs) and skills (what it can do) are unrelated mechanisms that happen to both attach to an agent. Mixing them creates an asymmetric capability surface:

list_triggers   ← exists (per-agent)
add_trigger     ← exists
remove_trigger  ← exists
[list_skills]   ← MISSING per-agent (lives in agent_catalog, account-wide)
add_skill       ← exists
remove_skill    ← exists

The LLM has to know:

  • "list triggers attached to this agent" → manage_agent_capabilities
  • "list skills attached to this agent" → not supported by platform
  • "list skills available to attach" → agent_catalog (different tool)

The description has to apologize: "There is no action to list which skills are currently attached to a specific agent."

Suggest reverting to the prior structure: manage_agent_triggers (list/add/remove) + manage_agent_skills (add/remove until the platform exposes per-agent listing). The original split was structurally cleaner.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants