Skip to main content

Advanced: Custom MCP and Skills

Attach your own capabilities to an agent — register a running MCP server into Bailian (custom MCP), and package the team's workflow for upload (Skills) — including runtime mechanics and versioning.

Overview

In the earlier pages the agent used "platform-given" capabilities: the built-in toolkit (getting started), marketplace MCP services (production). This page goes the other way — attaching what you already have: register your running MCP server into Bailian (custom MCP), and package your team's workflow for upload (Skills).

Scenario: Luming Press's review assistant

"Luming Press" is a fictional book publisher whose content-ops team processes a large volume of submissions monthly. They hold two ready-made assets:
  1. An editorial-standards knowledge base — long since built on Bailian knowledge indexing (RAG), exposing a standard MCP endpoint. The pain: only application-side code could query it; a Managed Agent could not.
  2. A manuscript health-check workflow — a senior editor's "three looks": heading hierarchy, paragraph length, absolutist wording. Previously a manual checklist; now wanted as automatic agent behavior.
The ask is clear: the knowledge base's MCP endpoint is ready — let the agent query it; the check workflow is ready — let the agent follow it. These map exactly onto this page's two topics: custom MCP (register an existing service) and custom Skills (upload your own workflow). The capability map first — an agent's external capabilities come in three tiers:
TierSourceMaintained byWhere covered
Built-in toolkit builtin_toolkitPlatform (bash / file I/O / web_search etc.)BailianGetting started
Marketplace MCP type: officialMCP marketplace servicesBailian marketplaceProduction, multi-agent
Your own assets type: customerYour MCP server, your Skill packagesYouThis page

Custom MCP: register an existing MCP server into Bailian

Marketplace and custom

Bailian's MCP management has two tabs: marketplace (Bailian-hosted, ready to use — WebSearch lives here) and custom (your own MCP servers or third-party services). Custom supports four registration types: plugin, script deployment, AI gateway, and Alibaba Cloud OpenAPI. This page's focus: the MCP server is already running (Luming's knowledge-base RAG endpoint) and only needs "one registration entry in Bailian". Choose script deployment with the HTTP deployment form (installation mode http) — note this step only registers (tells Bailian the endpoint address and auth method); it deploys nothing and incurs no deployment fee. Open Managed Agent → MCP, switch to the custom tab, and click 创建MCP服务 (Create MCP Service) in the top right. In the type selector, choose 脚本部署 (script deployment).
After choosing script deployment, the form's installation mode offers npx / uvx / http. npx / uvx genuinely deploy the service to Function Compute and bill by call duration and count; http is pure registration — it registers an existing endpoint only; the deployment-mode and region fields collapse, and no deployment fee accrues.
Select http as the installation mode.

Example: register a Bailian RAG knowledge base MCP endpoint

Luming's editorial-standards knowledge base is built on Bailian knowledge indexing; its MCP endpoint is a fixed workspace-level address. Fill in the service name and description, and register the connection config in the MCP服务配置 (MCP service config) JSON:
{
  "mcpServers": {
    "rag_mcp": {
      "type": "streamableHttp",
      "url": "https://${workspaceId}.cn-beijing.maas.aliyuncs.com/api/v1/indices/rag/mcp",
      "headers": {
        "Authorization": "Bearer ${DASHSCOPE_API_KEY}"
      }
    }
  }
}
Reading the fields:
  • type takes streamableHttp (the MCP standard HTTP transport), a single URL, stateless calls; sse is also supported;
  • ${workspaceId} in url is your Bailian workspace ID (visible top-right in the console, shaped like llm-xxxx);
  • headers carries auth — the RAG endpoint uses a plain API Key (Bearer). Note: the key entered at registration is stored in Bailian's MCP configuration and attached by the platform when the agent calls it — no need to keep another copy in the agent's system prompt or a vault.
This example also dispels a common misconception: "custom MCP" does not require writing an MCP server from scratch. Any already-running service exposing an MCP endpoint — your business system's API gateway, a third-party SaaS MCP interface, even another cloud product (like Bailian knowledge indexing) — can be registered this way. Bailian does "registration + proxied calls", not "hosted runtime".
After submitting, the new service appears in the custom list. The detail page shows the service's overview, tools, and external-invocation information.

API mounting: type: customer

Once registered, note the service's service ID (of the form mcp-xxxxxxxx; the ID row on the card in the MCP management list). Fill name with the service ID, not the display name — the name field of type: customer resolves by ID despite its name; filling a display name returns AGENT_010 MCP Server lookup error. The mcp_servers field is the same as marketplace services, just with type: "customer":
curl -s -X POST "$AGENTSTUDIO_URL/agents" \
  -H "Authorization: Bearer $DASHSCOPE_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "name": "luming-editor",
    "model": {"id": "qwen3.8-max"},
    "mcp_servers": [
      {"type": "customer", "name": "mcp-xxxxxxxx"}
    ]
  }'
For $AGENTSTUDIO_URL and $DASHSCOPE_API_KEY see the production page. Enabling tools works exactly as with marketplace services: add an mcp_toolkit to tools, set mcp_server_name to the same service ID (it must match mcp_servers[].name), and enable each tool in configs[ ] (forgetting per-tool enabling leaves the tool silently absent at runtime — the pit is covered in the production page). One important difference — the two types validate at completely different times:
type: official (marketplace)type: customer (custom)
Validation at declarationNo existence check — a name absent from the marketplace still creates successfullyMandatory check — an unregistered name fails with 400 at creation
Failure modeTool silently absent at runtime; the agent says it has no toolsFails at creation: AGENT_010 "MCP Server 校验失败: 查询MCP详情异常"
Troubleshooting difficultyHigh (no error; the tool is simply missing and hard to notice)Low (fails at creation)
So the order is fixed for custom MCP: register in the console first, then reference it in the agents configuration. Incidentally, registering, renaming, and deleting custom MCP currently happens in the console (there is no API endpoint for it).

Skills: package the team's workflow for upload

MCP solves "the agent can call your services"; Skills solve "the agent works by your process". A Skill is a zip package: a manual (SKILL.md) plus bundled resources (scripts, templates, reference files) — zipped, uploaded, reviewed, and attached to an agent. The full SKILL.md authoring spec is in Agent Skills.

Packaging

The zip is at most 10 MB and must have SKILL.md at the root. The front matter declares name and description — the agent decides when to invoke the skill from the description. Luming's "manuscript health check" skill, two files: SKILL.md (the manual):
---
name: manuscript-check
description: 稿件体检技能。当用户提交稿件要求"体检"、"审读"或"出版前检查"时使用:将稿件保存为文本文件后运行 manuscript_check.py,对稿件做标题层级跳跃、段落超长(>400 字)、绝对化禁用词三类检查,并将 JSON 输出整理为审读意见清单。
---

# 稿件体检

对稿件执行出版前体检时按以下流程操作:

1. 将用户提交的稿件内容保存为 `/tmp/manuscript.txt`
2. 运行 `python3 manuscript_check.py /tmp/manuscript.txt`
3. 将 JSON 输出整理为审读意见:逐项列出问题类型、所在段落与建议

## 检查项

- 标题层级跳跃(如 H1 直接到 H3)
- 段落超长(超过 400 字)
- 绝对化禁用词("最好"、"第一"、"绝对"、"百分之百"、"史无前例"、"独一无二")
manuscript_check.py (bundled resource, excerpt):
#!/usr/bin/env python3
"""Manuscript health check: heading hierarchy, paragraph length, banned words. Outputs a JSON review list."""
import json, re, sys

BANNED = ["最好", "第一", "绝对", "百分之百", "史无前例", "独一无二"]

def check(path):
    text = open(path, encoding="utf-8").read()
    issues = [ ]
    # Heading skips: scan line by line (adjacent headings need no blank line;
    # paragraph-splitting misses them)
    prev_level = 0
    for lineno, line in enumerate(text.splitlines(), 1):
        m = re.match(r"^(#{1,6})\s", line)
        if m:
            level = len(m.group(1))
            if prev_level and level > prev_level + 1:
                issues.append({"issue": "HEADING_SKIP",
                               "detail": f"第 {lineno} 行标题从 H{prev_level} 跳到 H{level}"})
            prev_level = level
    # Paragraph length and banned words: split on blank lines
    paras = [p for p in re.split(r"\n\s*\n", text) if p.strip()]
    for i, para in enumerate(paras, 1):
        if len(para) > 400:
            issues.append({"issue": "PARA_TOO_LONG",
                           "detail": f"第{i}段 {len(para)} 字(上限 400)"})
        for w in BANNED:
            if w in para:
                issues.append({"issue": "BANNED_WORD", "detail": f"第{i}段出现禁用词「{w}」"})
    return {"paragraphs": len(paras), "issues": issues}

if __name__ == "__main__":
    print(json.dumps(check(sys.argv[1]), ensure_ascii=False, indent=2))
Package it (SKILL.md at the root — no extra directory layer):
zip manuscript-check.zip SKILL.md manuscript_check.py

The upload chain (API)

Step one: upload as a plain file first (POST /files, multipart), take the file_id:
curl -s -X POST "$AGENTSTUDIO_URL/files" \
  -H "Authorization: Bearer $DASHSCOPE_API_KEY" \
  -F "file=@manuscript-check.zip"
{"id": "file_zq9kyvmkkprtg0b32afk8rto", "filename": "manuscript-check.zip",
 "mime_type": "application/octet-stream", "size_bytes": 1695,
 "status": "checking", "created_at": "2026-09-02T12:56:45+08:00", ...}
The zip itself enters review (status: checking). Step two: create the skill with the file_id (POST /skills; the request body has exactly this one field):
curl -s -X POST "$AGENTSTUDIO_URL/skills" \
  -H "Authorization: Bearer $DASHSCOPE_API_KEY" -H "Content-Type: application/json" \
  -d '{"file_id": "file_zq9kyvmkkprtg0b32afk8rto"}'
{
  "id": "skill_MWY3ZTY5ZDE3MGIzNDFkZWI2Zj",
  "name": "manuscript-check",
  "description": "稿件体检技能。当用户提交稿件要求\"体检\"、\"审读\"或\"出版前检查\"时使用:...",
  "source": "customer",
  "status": "checking",
  "latest_version": "1.0",
  "created_at": "2026-09-02T04:57:01Z",
  ...
}
Two notable points:
  1. name and description are parsed server-side from SKILL.md — the request body has neither field. Whatever the front matter says is what the skill is called; SKILL.md's name becomes the directory name in the sandbox later.
  2. The first version number is auto-assigned as "1.0".
Step three: wait for review. A reference timeline:
12:57:01  POST /skills         → status: checking
12:57:29 ~ 13:00:21  GET poll   → checking (5 polls)
13:01:00  approved             → status: active   ← 3 min 59 s total
State machine: checking → active (mountable) / rejected (not mountable; the version detail lists issues by file path) → deleted. Review is not second-scale — scripted uploads should not assume "upload then immediately mount": either poll GET /skills/{skill_id} until active, or make upload an async pipeline (upload → notify → mount). Step four: attach to the agent:
curl -s -X POST "$AGENTSTUDIO_URL/agents" \
  -H "Authorization: Bearer $DASHSCOPE_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "name": "luming-editor",
    "model": {"id": "qwen3.8-max"},
    "system": "你是鹿鸣出版的内容审读助手。收到稿件时必须使用 manuscript-check 技能执行体检,并将检查结果整理为审读意见。",
    "tools": [{"type": "builtin_toolkit", "default_config": {"enabled": true},
      "configs": [{"name": "bash", "enabled": true}, {"name": "read", "enabled": true},
                  {"name": "write", "enabled": true}, {"name": "glob", "enabled": true},
                  {"name": "grep", "enabled": true}]}],
    "skills": [{"type": "customer", "skill_id": "skill_MWY3ZTY5ZDE3MGIzNDFkZWI2Zj", "version": "1.0"}]
  }'
Each skills[ ] entry has three fields: type (customer), skill_id, and version (must lock exactly one existing version; latest is not supported). Mount-validation error samples:
  • Mounting a checking-state skill → 400 AGENT_010 "Skill 不存在或正在接受安全扫描: skill_xxx@1.0"
  • Mounting an active skill with version 9.9 → the same 400 (the message joins skill_id@version, pinpointing the mismatched pair)

Runtime: how a skill gets "used"

Send a problem manuscript (heading skips + banned words + overlong paragraphs) to the agent with the skill attached, and the event history shows the mechanism in full:
[05:03:33] tool_call        activate_skill  {"name": "manuscript-check"}
[05:03:33] tool_call_output {"name": "manuscript-check", "content": "<full SKILL.md>"}
[05:03:44] tool_call        write           manuscript written to /tmp/manuscript.txt
[05:03:47] tool_call        bash            "cd /root/workspace/skills/manuscript-check && python3 manuscript_check.py /tmp/manuscript.txt"
[05:03:47] tool_call_output the script's JSON result (6 issues)
[05:04:01] message          assistant       the consolidated review
Line by line:
  1. Skills are not pre-injected into the system prompt. On receiving a message the model itself decides to call a hidden tool activate_skill with the skill name — the SKILL.md (the manual) enters the conversation context only then. The benefit is obvious: attach ten skills without bloating the system prompt — loaded on demand.
  2. The zip is unpacked into the sandbox at /root/workspace/skills/<skill-name>/ (directory name = SKILL.md's name), and scripts referenced by the manual run directly via bash. Paths in SKILL.md's body should be written relative to the skill directory (e.g. plain manuscript_check.py); the agent cds there itself.
  3. The manual decides behavior; the script decides facts. What the agent does with the script's JSON is "consolidate + judge": the heading "第一章 绪论" (Chapter 1 Introduction) mechanically triggers the banned word "第一" (first); the agent's handling — "a chapter-numbering word triggered the match; recommend confirming with the proofreader whether ordinal uses of '第一' are exempt". The script does the scanning; the model does the judgment — the boundary most worth thinking through when designing a skill.

Versioning

Upload a new version (POST /skills/{skill_id}/versions; the body again has only file_id):
# Edit the BANNED constant in manuscript_check.py (say, add "全网最低价" — the check logic reads the hardcoded list in the script; editing SKILL.md alone has no effect) → repackage → upload
curl -s -X POST "$AGENTSTUDIO_URL/skills/skill_MWY3ZTY5ZDE3MGIzNDFkZWI2Zj/versions" \
  -H "Authorization: Bearer $DASHSCOPE_API_KEY" -H "Content-Type: application/json" \
  -d '{"file_id": "file_889syqcvxriijfzl5xe9kci9"}'
{"id": "skillver_MTRiNWIwNDNiNWZjNDc5Y2", "version": null,
 "status": "checking", "type": "skill_version", "skill_id": "skill_MWY3...", ...}
Behavior:
  • Version numbers auto-increment by 0.1: 1.0 → 1.1 → 1.2… (the creation response's version is null, but the version list shows the new one immediately);
  • New versions pass the same security scan (checking → active, same order of duration);
  • Mount locking: after 1.1 becomes active, an agent mounted on 1.0 is untouched (GET /agents shows skills[0].version == "1.0"). To move the agent to the new version you must explicitly update its configuration — the same philosophy as agent version locking (getting started, Advanced: Prompt Versioning and Rollback): running things are not disturbed by background changes;
  • Two actual behaviors of the current upgrade window (while the new version is under review, minutes in practice): latest_version advances to 1.1 as soon as the new version enters checking, not after approval; and during that window, creating an agent mounted on the older active version 1.0 is rejected (400 "Skill does not exist or is undergoing a security scan: ...@1.0"), and already-mounted agents may error while running. It recovers once the window ends, without any configuration change. When planning upgrades, avoid creating new agents in this window, or wait until the new version is active;
  • The version list GET /skills/{id}/versions sorts newest first;
  • GET /skills/{id}/versions/{version}/content returns an OSS pre-signed download URL — you can retrieve the exact package you uploaded (an audit asset: skill content is traceable).
Note: stray files in the package (say, an accidentally included SKILL_v2.md) do not block the upload and still pass review — audit your package contents before uploading; the review does not catch them for you.

Operations and cleanup

# Status: the skill list (source filter: custom/official)
curl -s "$AGENTSTUDIO_URL/skills?source=official&limit=10" -H "Authorization: Bearer $DASHSCOPE_API_KEY"   # official marketplace skills
curl -s "$AGENTSTUDIO_URL/skills" -H "Authorization: Bearer $DASHSCOPE_API_KEY"                            # all

# Delete: hard delete, unrecoverable (skills have only DELETE, no archive)
curl -s -X DELETE "$AGENTSTUDIO_URL/skills/skill_xxx" -H "Authorization: Bearer $DASHSCOPE_API_KEY"        # 200
  • Skill deletion is a hard delete — contrast agents/deployments, which are archive-only. Think before deleting: agents mounted on the old version lose their reference.
  • Custom MCP is managed in the console (no API); skills are fully API-managed (this page) — the management entry points are opposites; don't mix them up.
  • The GET /skills list also shows the health of every skill in the workspace (marketplace ones included) — glance at it during inspections.

Summary

WantUseKey action
Platform execution capability (bash / file I/O / web)builtin_toolkitGetting started
Public generic services (search, document processing…)Marketplace MCP (type: official)Production
Your own MCP server (knowledge base, business system)Custom MCP (type: customer)Console registration (script deployment → http mode, registration-only and free) → API mounting
The team's workflow (checklists, scripts, templates)Custom SkillPackage zip → API upload → review (minutes) → mount with locked version
The division of labor: MCP gives the agent hands (what services it can call); Skills give the agent an SOP (which process it follows). Luming's review assistant gets both: the RAG MCP lets it consult the editorial standards, and the manuscript-check skill makes it run the senior editor's three-look check — hands and method together. Engineering checklist (in priority order): write SKILL.md's description to say "when to use" (the model routes by it) → budget review wait in the upload pipeline (minutes) → mount with a specific locked version (do not track latest) → write script paths in the manual as relative usage → assess mounted references before deleting.

Next steps

Agent Context
Agent Governance & Evolution
Store
Application Marketplace