diff --git a/mateclaw-server/src/main/java/vip/mate/agent/AgentAuthoringTool.java b/mateclaw-server/src/main/java/vip/mate/agent/AgentAuthoringTool.java
new file mode 100644
index 00000000..003b455c
--- /dev/null
+++ b/mateclaw-server/src/main/java/vip/mate/agent/AgentAuthoringTool.java
@@ -0,0 +1,313 @@
+package vip.mate.agent;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.chat.model.ToolContext;
+import org.springframework.ai.tool.annotation.Tool;
+import org.springframework.ai.tool.annotation.ToolParam;
+import org.springframework.lang.Nullable;
+import org.springframework.stereotype.Component;
+import vip.mate.agent.binding.service.AgentBindingService;
+import vip.mate.agent.context.ChatOrigin;
+import vip.mate.agent.model.AgentEntity;
+import vip.mate.exception.MateClawException;
+import vip.mate.skill.model.SkillEntity;
+import vip.mate.skill.repository.SkillMapper;
+import vip.mate.tool.model.AvailableToolDTO;
+import vip.mate.tool.service.AvailableToolService;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Agent-callable employee authoring tool.
+ *
+ *
Lets an agent design and persist a new specialized employee (Agent)
+ * from a plain-language role spec, then bind a focused capability set to
+ * it. Pairs with the workflow drafting tool so a single chat turn can plan
+ * a team of employees and chain them into a workflow:
+ * design roles → {@link #create_employee} for each → workflow drafting tool
+ * referencing the just-created employees.
+ *
+ *
Workspace is taken from {@link ChatOrigin} on the active
+ * {@link ToolContext}; the LLM can never write into a foreign workspace
+ * even if its prompt tried to forge one. Mirrors the create-then-bind
+ * sequence used when applying an agent template.
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class AgentAuthoringTool {
+
+ private final AgentService agentService;
+ private final AgentBindingService agentBindingService;
+ private final SkillMapper skillMapper;
+ private final AvailableToolService availableToolService;
+ private final ObjectMapper objectMapper;
+
+ /** Cap on names listed per catalog section so the tool result stays small. */
+ private static final int CATALOG_MAX_PER_SECTION = 200;
+
+ @Tool(description = """
+ Create a new specialized employee (Agent) in the current workspace from a role spec, \
+ and optionally bind a focused set of skills and tools to it. \
+ Use this when a task needs a role that does not exist yet — design the role, then create it. \
+ Returns the new agentId (string) and a short summary. \
+ Leave skillNames/toolNames empty to make a generalist that inherits all globally-enabled capabilities. \
+ Call list_capability_catalog first to learn the exact skill and tool names you can assign. \
+ The created employee is enabled immediately and can be referenced by the workflow drafting tool.""")
+ public String create_employee(
+ @ToolParam(description = "Employee name, unique within the workspace, e.g. \"market-research-analyst\".")
+ String name,
+ @ToolParam(description = "One-line description of the employee's role and responsibility. Shown in pickers and used by the workflow planner to route work.")
+ String description,
+ @ToolParam(description = "System prompt that defines the employee's persona, expertise, and working style. Be specific about its specialty.")
+ String systemPrompt,
+ @ToolParam(description = "Agent type: \"react\" (single-loop reasoning, default) or \"plan_execute\" (decompose then execute). Leave empty for react.", required = false)
+ String agentType,
+ @ToolParam(description = "Optional model name override (must match an enabled model). Leave empty to use the workspace default model.", required = false)
+ String modelName,
+ @ToolParam(description = "Skills to bind, as a JSON array of skill names or a comma-separated list, e.g. [\"sql_query\",\"make_plan\"]. Empty = inherit all globally-enabled skills. Names must come from list_capability_catalog.", required = false)
+ String skillNames,
+ @ToolParam(description = "Tools to bind, as a JSON array of tool names or a comma-separated list, e.g. [\"web_search\",\"read_file\"]. Empty = inherit all globally-enabled tools. Names must come from list_capability_catalog.", required = false)
+ String toolNames,
+ @Nullable ToolContext ctx) {
+
+ ChatOrigin origin = ChatOrigin.from(ctx);
+ Long workspaceId = origin.workspaceId();
+ if (workspaceId == null || workspaceId <= 0) {
+ return "[error] Cannot determine the current workspace; invoke this tool within a workspace context.";
+ }
+ if (name == null || name.isBlank()) {
+ return "[error] Employee name is required.";
+ }
+
+ AgentEntity agent = new AgentEntity();
+ agent.setName(name.trim());
+ agent.setDescription(blankToNull(description));
+ if (systemPrompt != null && !systemPrompt.isBlank()) {
+ agent.setSystemPrompt(systemPrompt);
+ }
+ agent.setAgentType(normalizeAgentType(agentType));
+ agent.setModelName(blankToNull(modelName));
+ agent.setWorkspaceId(workspaceId);
+ agent.setCreatorUserId(parseUserId(origin.requesterId()));
+
+ AgentEntity created;
+ try {
+ created = agentService.createAgent(agent);
+ } catch (MateClawException e) {
+ // Duplicate name / blank name surface here as a friendly message
+ // so the planner can rename and retry instead of aborting.
+ return "[error] Failed to create employee: " + e.getMessage();
+ }
+
+ List requestedSkills = parseNameList(skillNames);
+ List requestedTools = parseNameList(toolNames);
+
+ List boundSkills = bindSkills(created, workspaceId, requestedSkills);
+ List boundTools = bindTools(created, requestedTools);
+
+ Map result = new LinkedHashMap<>();
+ result.put("agentId", String.valueOf(created.getId()));
+ result.put("name", created.getName());
+ result.put("agentType", created.getAgentType());
+ result.put("skillsBound", boundSkills.isEmpty() ? "(inherits global defaults)" : boundSkills);
+ result.put("toolsBound", boundTools.isEmpty() ? "(inherits global defaults)" : boundTools);
+ result.put("note", "Employee created and enabled. Reference it by name in the workflow drafting tool to chain it into a workflow.");
+ try {
+ return objectMapper.writeValueAsString(result);
+ } catch (Exception e) {
+ return "Employee created: id=" + created.getId() + " name=" + created.getName();
+ }
+ }
+
+ @Tool(description = """
+ List the capabilities you can assign when creating an employee: the enabled skill names \
+ and the bindable tool names in the current workspace. \
+ Call this before create_employee so you assign real, resolvable names rather than guessing.""")
+ public String list_capability_catalog(@Nullable ToolContext ctx) {
+ ChatOrigin origin = ChatOrigin.from(ctx);
+ Long workspaceId = origin.workspaceId();
+
+ // Skills: builtin (global) + skills owned by this workspace, enabled only.
+ List skills = skillMapper.selectList(new LambdaQueryWrapper()
+ .eq(SkillEntity::getEnabled, true)
+ .eq(SkillEntity::getDeleted, 0)
+ .orderByAsc(SkillEntity::getName));
+ long effectiveWs = workspaceId == null ? 1L : workspaceId;
+ List