Creating Skills
When does making a skill yourself make sense?
The autonomous skill system handles most things on its own, but building a skill directly is worth it when:
- You need exact repeatability: the same procedure runs identically every time it’s triggered.
- You’re turning know-how into an asset: formalise training material or recurring processes as a skill.
Outside these cases, you don’t need to create skills at all; your AI employee finds and uses the right ones automatically.
Skills you create are yours alone. There’s no way to publish them to other users or share them by link (if you belong to an organization, you can share them to it). The only skills available to everyone are the official skills UpServe supports; find them in Installing Skills & Templates.
Try it first, then save what worked
UpServe’s recommended flow for creating a skill:
[1] Instruct the employee
"Scrape this site's prices every day and tabulate them"
│
▼
[2] Employee completes the task with whatever tools fit
(browser / file write / code execution, freely combined)
│
▼
[3] Happy with the result? Ask to save it as a skill
"Save the workflow you just ran as a skill"
│
▼
[4] The employee automatically:
· collects the execution code and files
· writes an AgentSkills-compatible SKILL.md (including required
fields like a summary and when to use it)
· auto-normalises spec violations (spaces, uppercase, etc.)
· creates a draft skill and posts a card in the chat
│
▼
[5] Open the card whenever you like to review and saveWhy this works well:
- Only workflows that actually worked get saved: you save a flow only after seeing it succeed, so you never end up with a skill that turns out not to run.
- The employee handles the messy parts: you just say “save the workflow you just ran as a skill”. You don’t need to know what files are created or how they’re organised.
- Refine it through chat until you’re happy: “show the results as a table”, “make the name shorter”, “handle this edge case too”. Keep asking in plain conversation.
- You can edit it later at any time: making a skill isn’t a one-shot decision. Come back and ask the employee to tweak, extend, or fix specific parts whenever you want.
If the employee can’t fill in required information (like a summary or “when should this trigger”), no draft is created at all. It’s told what’s missing and fills that in before trying again. So by the time a card shows up in chat, everything needed for review is already there.
Phrases you can use with the employee
| Situation | What to say |
|---|---|
| Save the workflow you just ran | “Save the workflow you just ran as a skill” |
| Save only one step | “Make a skill out of just the price extraction part” |
| Specify when the skill should be used | “Make a skill that fires when the user says ‘check competitor prices’” |
| Pick the name yourself | “Name it competitor-price-tracker and make it a skill” |
Reviewing the draft and saving
Creating a skill doesn’t pause the employee. The moment it builds a draft, a card appears in the chat and the employee moves straight on to whatever’s next. Open the card whenever you’re ready (web, iOS, and Android all offer a review screen). It lets you check and edit the skill name, description, category, the body (SKILL.md), and the file list before publishing. On web, the screen is organized into three steps: Basic Info → Content → Files.
- Step 1 · Basic Info: fields for name, description, category, and type, plus a read-only summary of the summary, when-to-use phrases, required connections, and (for executable skills) the entry script the employee already filled into SKILL.md. To change these, edit the settings block at the top of SKILL.md in Step 2.
- Step 2 · Content: edit the SKILL.md body directly.
- Step 3 · Files: review bundled files and choose any to exclude.
Saving is always something you do yourself; the employee can’t save on your behalf. Once you’re happy, click Save. The skill is stored as a private skill in your library: only your own employees can install and run it, and other users can’t find it.
If a bundled file looks like it contains personal information or sensitive data, the review screen flags it with a warning. You can uncheck a flagged file to exclude it from the skill. Excluded files are actually deleted once you publish. You can also run Validate before publishing to check whether the draft would pass, without committing to it yet.
Cancelling or closing the save dialog discards the draft; otherwise it stays put until you save or discard it.
Reviewing improvement proposals
When your employee hits a defect while running one of your skills and reports it, the report is filed as an improvement proposal. Multiple reports describing the same underlying issue are deduplicated into a single proposal.
Where to find them: open your skill from the Skills tab. The skill detail page shows an Improvement Proposals section further down. It is visible only to the skill’s author. Defect reports on official skills don’t appear here; they go to the UpServe team for review.
For each proposal you have four options:
| Action | Meaning |
|---|---|
| Mark as reviewed | Only marks the proposal as reviewed. It does not change the skill content and does not lead to a separate edit screen afterward. Use it when you want to close the proposal without shipping a fix. |
| Edit & publish | Edit the skill content right inside the proposal card and publish it as a new version. If you actually want to ship the fix, use this instead of Mark as reviewed. |
| Reject | For proposals that aren’t valid. You can leave an optional note explaining why. |
| Leave pending | No action taken; stays as a pending proposal until you decide. |
Automatic patches the employee applies to skills you have imported are handled separately and do not appear on this page.
Uploading a new version from a zip
If you’ve edited one of your skills on your own computer, you can upload the edited zip as a new version of the same skill. No second skill is created, and the skill keeps its history and name.
- Open the skill from the Skills tab to go to its detail page.
- Click the upload icon (Update from zip) next to the title.
- Pick your edited zip file and upload it.
After the upload, you’ll see one of these:
- The content changed: an “uploaded as a new version” message, plus the new version number and file count.
- The content matches the current version: a message saying nothing changed, and the version number stays the same. If you expected a change, check that you picked the right file.
- The name (
name) in the zip’s SKILL.md differs from this skill: the skill’s unique name stays as it is, and a warning asks you to confirm you picked the skill you meant to update. - The upload contains secrets such as API keys, or unsafe content: the upload is rejected and the current version stays in place.
Which skills show this button: only active skills you own (created or imported). The button doesn’t appear for:
- Archived skills: click Restore first, then upload.
- Drafts you haven’t saved yet, and official UpServe skills.
To import a brand-new skill from a zip, use the Import zip button in the Skills tab. Uploading a new version is currently available on the web only.
Deleting a skill
You can delete any skill you created at any time (official skills can’t be deleted). Open the skill’s card, click Delete, and confirm.
One of two things happens:
- A skill that has never run: it’s deleted permanently. This can’t be undone.
- A skill that has run at least once, been copied and edited, or used by a team: instead of erasing it, UpServe archives it. This protects traces the skill already left behind elsewhere (work files your employee produced with it, things it remembered, conversation history) from suddenly pointing at something that no longer exists.
What changes once a skill is archived?
- Its card stays in your list with an Archived badge, the same on web, iOS, and Android.
- The confirmation you see after deleting correctly says “archived,” not “deleted.”
- Your own employees stop using it. From their next run onward it drops out of their skill list, and you can’t attach it to a new employee either.
- If you change your mind, click Restore on the skill’s detail page to bring it back as a private skill you can attach to your employees again.
Import from Official skills
Instead of building from scratch, you can install a skill UpServe supports from Official skills , under the Skills tab. Click Import on the skill detail page and it’s added to your skill library as a reference to the original, ready to attach to your employees. When you refine it through chat, your own version is created at that point.
For the full installation walkthrough, see Installing Skills & Templates.
Advanced
The material below is not required for everyday use. Refer to it only when you need to inspect skill internals, hand-edit a skill imported from outside, or exchange skills with other clients.
What an employee-built skill looks like
Directory layout
my-skill/
├── SKILL.md # Required: frontmatter + body
├── scripts/ # Optional: executable code
│ ├── main.py
│ └── requirements.txt
├── references/ # Optional: docs the employee loads on demand
│ └── api-spec.md
└── assets/ # Optional: templates, schemas, lookup tables
└── output-template.mdSKILL.md frontmatter
The standard requires only two fields.
---
name: my-skill # kebab-case, 1-64 chars, must match directory name
description: Use this skill when ... # ≤1024 chars, imperative
---Optional standard fields:
| Field | Purpose |
|---|---|
license | License name or reference to a bundled LICENSE file |
compatibility | Environment requirements (≤500 chars), e.g. “Requires Python 3.14+ and uv” |
allowed-tools | Pre-approved tools the skill may use (experimental) |
metadata | Container for client-specific extensions |
UpServe extensions live under metadata:
UpServe-specific fields (connection detail bindings, action menus, when-to-use hints) go under the standard metadata: namespace. This keeps your SKILL.md valid for any other compliant client.
---
name: notion
description: Search, read, and write Notion pages and databases via OAuth. Use when the user asks to search Notion, read a page, query a database, or reference a Notion URL.
metadata:
display_name: Notion Integration
when_to_use:
- User asks to search their Notion workspace.
- User references a Notion URL or page id.
requires:
- oauth: notion # → ${SKILL_DIR}/.env will receive NOTION_TOKEN
entry_script: scripts/main.py
actions:
- search: "Search pages and databases by title/content"
- read: "Read a page's content"
files:
- scripts/main.py
load_before_use: true
---
# Notion Integration
## When to use this skill
...Progressive disclosure: the token-saving model
Skills load in three tiers.
| Tier | When loaded | Content | Token cost |
|---|---|---|---|
| 1. Catalog | Every turn | name + description only | ~50-100 tokens per skill |
| 2. Body | Once on first skill execution (re-delivered after conversation history is compacted) | Full SKILL.md body | < 5,000 tokens recommended |
| 3. Resources | When the body references a file | Individual scripts / references / assets | Variable |
Implication: with 20 skills installed, your employee’s behaviour instructions only carry 2-3KB. Only skills the employee actually uses expand to their full body.
[Loaded every turn] ~50-100 tokens/skill
┌─────────────────────────┐
│ Tier 1 · Catalog │
│ name + description │
└─────────────────────────┘
│
│ Employee decides "use this skill"
▼ (first skill execution)
┌─────────────────────────┐
│ Tier 2 · Body │ < 5,000 tokens
│ Full SKILL.md │
└─────────────────────────┘
│
│ Body says "look at scripts/main.py"
▼ (per individual file read)
┌─────────────────────────┐
│ Tier 3 · Resources │ variable
│ scripts / refs / assets│
└─────────────────────────┘Connection detail injection
When a skill needs to call an external service, declare it in metadata.requires. At skill-execution time, UpServe writes the connection details into ${SKILL_DIR}/.env automatically. When the employee packages a skill, it auto-detects which connection details the work used and fills this field for you.
| Notation | Injected as |
|---|---|
oauth: <provider> | <PROVIDER>_TOKEN=... in ${SKILL_DIR}/.env |
vault: <NAME> | <NAME>=... (a connection detail saved to the employee) |
scope: <scope> | UPSERVE_AGENT_TOKEN + UPSERVE_API_BASE (UpServe internal API) |
Scripts source the env with . ./.env && python3 scripts/main.py .... The .env file is scrubbed at the end of every turn (each time the employee finishes a response), and is automatically rewritten the next time the skill runs, so you don’t need to manage this yourself.
Script design principles the employee follows
When the employee authors skill scripts, it defaults to these nine principles. You don’t write them yourself, but you can use this checklist when reviewing a packaged skill.
| Principle | Why |
|---|---|
| Non-interactive | Employees can’t answer interactive TTY input. Accept all input via flags or env vars |
Expose --help | The primary way an employee learns your interface |
| Structured stdout | JSON / CSV / TSV, easy to parse |
| stderr for diagnostics | Keep data and progress separate |
| Helpful error messages | “What went wrong / what was expected / what to try” |
| Idempotent | “create-if-not-exists” beats “create-and-fail”, since employees retry |
| Distinct exit codes | Per-failure-mode (404 / auth / validation) |
| Predictable output size | Default cap + --offset / --limit for pagination |
| PEP 723 inline deps (Python) | uv run scripts/x.py runs anywhere with one command |
If something needs fixing, just ask in chat: “add a --limit flag to the script”, “include suggested next actions in the error message”. See the AgentSkills script guide for full detail.
Lenient normalisation
The standard is strict, but UpServe loads leniently and surfaces warnings instead of rejecting. Whether you import an external zip or have the employee author SKILL.md, minor spec violations are auto-corrected.
| Violation | Handling |
|---|---|
name contains uppercase / spaces / underscores | Auto-normalised (Notion Integration → notion-integration); original preserved as metadata.display_name |
name exceeds 64 chars | Warning + truncate |
| Unquoted colons in YAML | Lenient parse retry |
description missing | Rejected (required: without it the employee can’t tell when to use the skill) |
description exceeds 1024 chars | Rejected |
Import skills from other clients via zip
You can import skills built in Claude Code, Cursor, Goose, and any other compatible client as standard zips.
Import a skill from another client
curl -X POST https://api.upserve.app/api/skills/import \
-H "Authorization: Bearer $UPSERVE_TOKEN" \
-F "file=@my-skill.zip"Safety limits: 200 files per zip, 5MB per file, 20MB total package size. Binary files such as images, audio, and fonts are preserved byte-for-byte.
Update an existing skill to a new version
Include the id of the skill you want to update in the import request, and the upload becomes a new version of that skill instead of a new skill. This works for both zip uploads and GitHub imports. The target must be an active skill you own; requests for archived or official skills are rejected.
# zip
curl -X POST https://api.upserve.app/api/skills/import \
-H "Authorization: Bearer $UPSERVE_TOKEN" \
-F "file=@my-skill.zip" \
-F "target_skill_id=<skill-id>"
# GitHub
curl -X POST https://api.upserve.app/api/skills/import-github \
-H "Authorization: Bearer $UPSERVE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "owner/repo", "target_skill_id": "<skill-id>"}'In the response, updated_existing tells you whether an existing skill was updated, content_changed whether the content actually changed, and content_version the version number after the update. If the content is identical, content_changed is false and the version doesn’t go up.
Learn more
- Installing Skills & Templates: Use official skills
- Equipping tools: Pick the tools that pair with your skills
- AgentSkills specification : The canonical standard
- Optimising skill descriptions : Improve how accurately the skill gets picked
- Evaluating skills : Test output quality
- Script writing guide : Full script design reference