Skip to content

agent: let the model scaffold skills and register MCP servers from chat #494

@esengine

Description

@esengine

Problem

Adding a skill or an MCP server today means the user does the work, by hand:

  • Skills/skill new <name> (src/cli/ui/slash/handlers/skill.ts:9) writes a stub SKILL.md. Filling in the frontmatter (name, description, allowed-tools, runAs, model) + body is on the user. They have to know the schema.
  • MCP servers — first-run wizard or /mcp slash / reasonix mcp CLI. The user picks from the catalog or types command + args + env themselves.

So when a user types into chat "add a skill that runs typecheck before I commit" or "hook up a postgres MCP server pointed at my local db", the agent's only options are:

  1. Tell the user to run a slash command and write the file themselves.
  2. write_file a SKILL.md by guessing the path + schema, then hope the loader picks it up.

Both are bad. (1) defeats the point of having an agent. (2) bypasses validation — invalid frontmatter, conflict with existing skill name, wrong scope (project vs global), etc. all fail silently or at next launch.

What I want

Give the model first-class tools so "add a skill that does X" / "add an MCP server for Y" works as a normal chat request. Two new tools, both gated by the same permission flow as everything else:

create_skill

Wraps SkillStore.create() (src/skills.ts). Args:

  • name (required) — validated against VALID_SKILL_NAME (skills.ts:13).
  • scope\"project\" | \"global\". Default project when there is one, else global.
  • description, allowed_tools[], run_as (inline | subagent), model — pre-fills frontmatter so the model doesn't have to write raw YAML and risk indentation bugs.
  • body — markdown body of the skill prompt.

Returns the resolved path + scope. Reuses the existing name-collision / validation paths so we don't reinvent error handling.

add_mcp_server

Wraps the same persistence path the wizard / /mcp CLI uses (src/cli/commands/mcp.ts + config.ts). Args:

  • name (required, unique per scope).
  • scope\"project\" | \"global\".
  • command, args[], env{} for stdio servers; url + transport: \"sse\" | \"streamable-http\" for remote.
  • from_catalog — optional shortcut: name out of MCP_CATALOG (src/mcp/catalog.ts) → fill the rest from the bundled entry.

Runs the same preflight (src/mcp/preflight.ts) the wizard runs, so the model gets immediate feedback if the command fails to spawn / handshake. Persists via writeConfig only on preflight pass.

Why tools, not a slash-command shim

Asking the model to "emit a /skill new foo command for the user to run" is the wrong shape — the user typed in chat because they wanted the agent to do it. We already give the model write_file, run_command, etc.; skill + MCP scaffolding is no more privileged than those.

Both tools should be in the default allow-list of skill runAs: subagent too — init-style skills that bootstrap a project ought to be able to drop in their own helpers.

Out of scope

  • Editing / removing existing skills + MCP servers from chat. Add separately if needed; create-only covers the common ask.
  • A natural-language "do you mean…" disambiguator for catalog matches. The model can negotiate that itself before calling the tool.
  • Auto-detecting which skills the project "should" have. The user (or init) drives that.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions