talentos 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  A thin [stdio MCP](https://modelcontextprotocol.io) server that wraps the TalentOS
4
4
  external HTTP API (`/api/v0/*`). Point any MCP-capable agent (Claude Code, Claude
5
5
  Desktop, Cursor, etc.) at it and the agent can read your org's workflows and
6
- nodes, then create or replace workflow drafts through the same API key.
6
+ nodes, then create or replace its own private Skills through the same API key.
7
7
 
8
8
  Published to npm as [`talentos`](https://www.npmjs.com/package/talentos) — run it
9
9
  with `npx -y talentos`, no checkout or build required.
@@ -20,14 +20,14 @@ It exposes six tools:
20
20
  agent-ready Markdown runbook, including its `**Version:**` stamp.
21
21
  - **`list_nodes`** (no arguments) — company-wide reusable nodes as name, id,
22
22
  step type, summary, and tools.
23
- - **`create_workflow`** (`{ title, steps, connections }`) — create a new draft
24
- workflow from a full plan. Steps may include `outputs`, `successCriteria`, and
25
- `runtime` contract fields. Writes always create drafts; a human publishes in
26
- the app.
27
- - **`update_workflow`** (`{ workflowId, title, steps, connections }`) —
28
- replace an existing draft workflow from a full plan. Steps may include
29
- `outputs`, `successCriteria`, and `runtime`; published workflows return a
30
- clear conflict instead of being edited.
23
+ - **`create_skill`** (`{ name, summary, instructions, successCriteria?,
24
+ requestedTools? }`) — create a new Skill from plain-text instructions.
25
+ Returns the new `skillId` and its version. The Skill is private to the API
26
+ key's owner, who curates it in their TalentOS Skills library.
27
+ - **`update_skill`** (`{ skillId, name, summary, instructions,
28
+ successCriteria?, requestedTools? }`) — replace one of the API key owner's
29
+ private Skills with a complete new body. Only that owner's private Skills are
30
+ writable; published and shared Skills stay human-owned.
31
31
 
32
32
  The server holds no state and stores nothing. Every call is a single
33
33
  authenticated request to the HTTP API; all authorization and visibility rules
@@ -56,7 +56,7 @@ npx -y talentos
56
56
  The `-y` skips the install prompt so noninteractive MCP clients don't hang. The
57
57
  first launch does a one-time network fetch to populate the npx cache (then exits
58
58
  asking for `TALENTOS_API_KEY`); run it once by hand — or pin a version like
59
- `talentos@0.2.0` — before wiring it into an offline or CI environment.
59
+ `talentos@0.3.0` — before wiring it into an offline or CI environment.
60
60
 
61
61
  ## Add it to Claude Code
62
62
 
@@ -106,21 +106,20 @@ Add this to your MCP client config (`.mcp.json`, Claude Desktop config, etc.):
106
106
  3. **Add the server** to your MCP client using a snippet above, with your real
107
107
  key (the API URL defaults to production).
108
108
 
109
- 4. **Ask the agent** to list your workflows, then create a draft:
109
+ 4. **Ask the agent** to list your workflows, then create a Skill:
110
110
 
111
111
  > List my TalentOS workflows, show me the first one as markdown, then create
112
- > a new draft workflow for candidate outreach.
112
+ > a Skill for candidate outreach.
113
113
 
114
- The agent calls `list_workflows`, `get_workflow`, and `create_workflow`. The
115
- new workflow lands as a draft in TalentOS for a human to review and publish.
114
+ The agent calls `list_workflows`, `get_workflow`, and `create_skill`. The new
115
+ Skill lands as a private Skill in TalentOS for its owner to review.
116
116
 
117
117
  ## Errors you might see
118
118
 
119
119
  - **Unauthorized (401)** — `TALENTOS_API_KEY` is missing, wrong, or revoked.
120
- - **Workflow not found (404)** — the `id` doesn't exist or it is not visible to
121
- this API key.
122
- - **Published workflow conflict (409)** — `update_workflow` only edits drafts;
123
- published workflows must be duplicated or edited in TalentOS.
120
+ - **Not found (404)** — the workflow or skill `id` doesn't exist, or it is not
121
+ visible to this API key. `update_skill` only reaches the key owner's own
122
+ private Skills.
124
123
  - **Rate limited (429)** — the key is over its requests-per-minute budget;
125
124
  wait a moment and retry.
126
125
  - **Could not reach the API** — `TALENTOS_API_URL` is wrong or unreachable;
package/dist/api.js CHANGED
@@ -7,6 +7,7 @@
7
7
  const WORKFLOWS_PATH = "/api/v0/workflows";
8
8
  const NODES_PATH = "/api/v0/nodes";
9
9
  const CONTEXT_PATH = "/api/v0/context";
10
+ const SKILLS_PATH = "/api/v0/skills";
10
11
  // A hung deployment (connection accepted, no response) would otherwise leave the
11
12
  // tool call pending forever with no error; abort so it surfaces as networkError.
12
13
  const REQUEST_TIMEOUT_MS = 15_000;
@@ -98,24 +99,24 @@ export async function getContext(config) {
98
99
  // formatting opinion belongs server-side, not in this wrapper.
99
100
  return textResult(JSON.stringify(result.body, null, 2));
100
101
  }
101
- export async function createWorkflow(config, plan) {
102
- return await writeWorkflow(config, WORKFLOWS_PATH, plan, "Created");
102
+ export async function createSkill(config, skill) {
103
+ return await writeSkill(config, SKILLS_PATH, skill, "Created");
103
104
  }
104
- export async function updateWorkflow(config, workflowId, plan) {
105
- return await writeWorkflow(config, `${WORKFLOWS_PATH}/${encodeURIComponent(workflowId)}`, plan, "Updated", workflowId);
105
+ export async function updateSkill(config, skillId, skill) {
106
+ return await writeSkill(config, `${SKILLS_PATH}/${encodeURIComponent(skillId)}`, skill, "Updated", skillId);
106
107
  }
107
108
  const URL_HINT = "The TALENTOS_API_URL may be wrong — it must point at the deployment's https://<name>.convex.site domain.";
108
- async function writeWorkflow(config, path, plan, action, workflowId) {
109
- const result = await fetchJson(config, path, path === WORKFLOWS_PATH ? "POST" : "PUT", plan, workflowId);
109
+ async function writeSkill(config, path, skill, action, skillId) {
110
+ const result = await fetchJson(config, path, path === SKILLS_PATH ? "POST" : "PUT", skill);
110
111
  if ("error" in result) {
111
112
  return result.error;
112
113
  }
113
- const id = result.body.id ?? workflowId;
114
- const status = result.body.status ?? "draft";
115
- if (!id) {
116
- return toolError(`The response was missing a workflow id. ${URL_HINT}`);
114
+ const id = result.body.skillId ?? skillId;
115
+ const version = result.body.version;
116
+ if (!id || version === undefined) {
117
+ return toolError(`The response was missing a skill id or version. ${URL_HINT}`);
117
118
  }
118
- return textResult(`${action} workflow draft (id: ${id}) · status: ${status}\nA human must publish it in TalentOS.`);
119
+ return textResult(`${action} skill (id: ${id}) · version ${version}\nPrivate to this API key's owner; they curate it in their TalentOS Skills library.`);
119
120
  }
120
121
  function textResult(text) {
121
122
  return { content: [{ text, type: "text" }] };
@@ -136,7 +137,7 @@ async function httpError(response, workflowId) {
136
137
  }
137
138
  if (response.status === 409) {
138
139
  return toolError(message ??
139
- "This workflow is published and can't be edited through the API.");
140
+ "This write conflicts with the current state. Reload and retry.");
140
141
  }
141
142
  if (response.status === 429) {
142
143
  return toolError("Rate limited (429). This API key is over its requests-per-minute budget — wait a moment and retry.");
package/dist/index.js CHANGED
@@ -5,74 +5,27 @@
5
5
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
6
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
7
  import { z } from "zod";
8
- import { createWorkflow, getContext, getWorkflow, listNodes, listWorkflows, updateWorkflow, } from "./api.js";
8
+ import { createSkill, getContext, getWorkflow, listNodes, listWorkflows, updateSkill, } from "./api.js";
9
9
  const TRAILING_SLASHES = /\/+$/;
10
10
  // The production TalentOS API origin. The server defaults to it so users only
11
11
  // need to supply TALENTOS_API_KEY; set TALENTOS_API_URL to override (e.g. a dev
12
12
  // deployment). Must be the .convex.site HTTP-action domain, not .convex.cloud.
13
13
  const DEFAULT_API_URL = "https://woozy-oyster-769.convex.site";
14
- const WORKFLOW_STEP_SCHEMA = z.object({
15
- key: z.string().describe("Short kebab-case slug, unique per step."),
16
- title: z.string().describe("Short imperative phrase, at most 8 words."),
17
- stepType: z.enum(["human", "ai_assisted", "ai_agent", "automation"]),
18
- description: z
14
+ const SKILL_SCHEMA = {
15
+ instructions: z
19
16
  .string()
20
- .optional()
21
- .describe("Complete markdown instructions for the step."),
22
- dataNeeded: z
23
- .array(z.object({
24
- label: z.string().describe("Short input name."),
25
- detail: z
26
- .string()
27
- .optional()
28
- .describe("Optional detail about the input."),
29
- }))
30
- .optional()
31
- .describe("Inputs the step needs."),
32
- outputs: z
33
- .array(z.object({
34
- label: z.string().describe("Artifact or deliverable name."),
35
- detail: z
36
- .string()
37
- .optional()
38
- .describe("Optional detail about the output."),
39
- format: z
40
- .string()
41
- .optional()
42
- .describe("Optional format hint, such as markdown, json, or url."),
43
- }))
44
- .optional()
45
- .describe("Artifacts or deliverables the step produces."),
46
- successCriteria: z
47
- .string()
48
- .optional()
49
- .describe("How a reviewer or agent knows this step is done well."),
50
- runtime: z
51
- .object({
52
- kind: z
53
- .string()
54
- .describe('Runtime the user named, such as "claude-code" or "api".'),
55
- model: z.string().optional().describe("Optional model name."),
56
- config: z
57
- .record(z.string(), z.string())
58
- .optional()
59
- .describe("Optional string config for the runtime."),
60
- })
61
- .optional()
62
- .describe("What executes this step, when the user names a runtime."),
63
- toolNames: z
17
+ .describe("Complete plain-text instructions for the Skill."),
18
+ name: z.string().describe("Short title for the Skill."),
19
+ requestedTools: z
64
20
  .array(z.string())
65
21
  .optional()
66
22
  .describe("Tool names exactly as the user named them."),
67
- });
68
- const WORKFLOW_CONNECTION_SCHEMA = z.object({
69
- fromKey: z.string().describe("The source step key."),
70
- toKey: z.string().describe("The target step key."),
71
- label: z
23
+ successCriteria: z
72
24
  .string()
73
25
  .optional()
74
- .describe("Short branch label, only when the flow branches."),
75
- });
26
+ .describe("How a reviewer knows the Skill did its job well."),
27
+ summary: z.string().describe("One-sentence summary of what the Skill does."),
28
+ };
76
29
  async function main() {
77
30
  const config = loadConfig();
78
31
  const server = new McpServer({
@@ -100,42 +53,30 @@ async function main() {
100
53
  title: "Get org context",
101
54
  description: "One compact summary of the org this API key belongs to: company profile basics plus the names of its published workflows and nodes. Call this first to prime yourself.",
102
55
  }, () => getContext(config));
103
- server.registerTool("create_workflow", {
104
- title: "Create workflow draft",
105
- description: "Create a new TalentOS workflow draft from a full title/steps/connections plan. Steps may include agent contract fields: outputs, successCriteria, and runtime. Writes always create drafts that a human publishes in the app.",
106
- inputSchema: {
107
- title: z.string().describe("Short name for the whole workflow."),
108
- steps: z
109
- .array(WORKFLOW_STEP_SCHEMA)
110
- .describe("The ordered workflow steps to materialize."),
111
- connections: z
112
- .array(WORKFLOW_CONNECTION_SCHEMA)
113
- .optional()
114
- .describe("Directed edges between the steps. Omit for no edges."),
115
- },
116
- }, ({ connections, steps, title }) => createWorkflow(config, {
117
- connections: connections ?? [],
118
- steps,
119
- title,
56
+ server.registerTool("create_skill", {
57
+ title: "Create Skill",
58
+ description: "Create a new private TalentOS Skill from a name, summary, instructions, and success criteria. The Skill is private to the API key's owner, who curates it in TalentOS.",
59
+ inputSchema: SKILL_SCHEMA,
60
+ }, ({ instructions, name, requestedTools, successCriteria, summary }) => createSkill(config, {
61
+ instructions,
62
+ name,
63
+ ...(requestedTools === undefined ? {} : { requestedTools }),
64
+ ...(successCriteria === undefined ? {} : { successCriteria }),
65
+ summary,
120
66
  }));
121
- server.registerTool("update_workflow", {
122
- title: "Replace workflow draft",
123
- description: "Replace an existing TalentOS workflow draft with a full title/steps/connections plan. Steps may include agent contract fields: outputs, successCriteria, and runtime. Drafts only: updating a published workflow returns a clear conflict message.",
67
+ server.registerTool("update_skill", {
68
+ title: "Replace Skill",
69
+ description: "Replace the API key owner's private TalentOS Skill with a complete name, summary, instructions, and success criteria.",
124
70
  inputSchema: {
125
- workflowId: z.string().describe("The workflow id from list_workflows."),
126
- title: z.string().describe("Short name for the whole workflow."),
127
- steps: z
128
- .array(WORKFLOW_STEP_SCHEMA)
129
- .describe("The complete replacement step list."),
130
- connections: z
131
- .array(WORKFLOW_CONNECTION_SCHEMA)
132
- .optional()
133
- .describe("The complete replacement edge list. Omit for no edges."),
71
+ skillId: z.string().describe("The skill id from create_skill."),
72
+ ...SKILL_SCHEMA,
134
73
  },
135
- }, ({ connections, steps, title, workflowId }) => updateWorkflow(config, workflowId, {
136
- connections: connections ?? [],
137
- steps,
138
- title,
74
+ }, ({ instructions, name, requestedTools, skillId, successCriteria, summary, }) => updateSkill(config, skillId, {
75
+ instructions,
76
+ name,
77
+ ...(requestedTools === undefined ? {} : { requestedTools }),
78
+ ...(successCriteria === undefined ? {} : { successCriteria }),
79
+ summary,
139
80
  }));
140
81
  await server.connect(new StdioServerTransport());
141
82
  }
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "talentos",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
- "description": "Stdio MCP server wrapping the TalentOS external HTTP API for workflow reads and draft writes.",
5
+ "description": "Stdio MCP server wrapping the TalentOS external HTTP API for workflow reads and private Skill writes.",
6
6
  "bin": {
7
7
  "talentos": "./dist/index.js"
8
8
  },
@@ -23,8 +23,8 @@
23
23
  "zod": "^4.4.3"
24
24
  },
25
25
  "devDependencies": {
26
- "@types/node": "^26.1.0",
27
- "tsx": "^4.22.5",
26
+ "@types/node": "^22",
27
+ "tsx": "^4.21.0",
28
28
  "typescript": "^6.0.3"
29
29
  }
30
30
  }