阿里云百炼:自主构建和部署MCP服务,轻松集成智能体

手把手教你如何将阿里云百炼工作流封装成MCP服务并集成到智能体,实现自定义功能扩展。

原文标题:自主构建MCP,轻松实现云端部署!

原文作者:阿里云开发者

冷月清谈:

本文介绍了如何通过阿里云百炼平台,将工作流封装成MCP(Model Context Protocol)服务并集成到智能体中。文章详细阐述了使用Node.js和TypeScript搭建MCP服务的步骤,包括创建Node.js项目、配置package.json和tsconfig.json、以及编写核心的index.ts代码。重点是如何将阿里云百炼的智能体应用API封装为MCP服务,通过调用DashScope API实现市场研究功能。文章还讲解了如何将项目打包并发布到npm平台,并在阿里云百炼中创建自定义MCP服务,最后在智能体中引用该服务。未来的百炼平台将简化这一流程,允许用户通过简单的配置将工作流转化为MCP服务,并无缝集成到智能体中,提升开发效率和智能化水平。

怜星夜思:

1、文章中提到将百炼应用API封装为MCP服务,然后集成到智能体中。这种方式相比直接在智能体内部调用API,有什么优势?
2、文章中提到使用Node.js和TypeScript搭建MCP服务。如果我更熟悉Python或其他语言,是否也可以实现类似的功能?
3、文章提到未来百炼平台将集成工作流的MCP服务,用户只需简单配置即可将工作流转化为MCP服务。你觉得这种“零代码”或“低代码”的MCP服务构建方式,会带来哪些影响?

原文内容

本文以阿里云百炼上的工作流为例,将其封装成MCP服务并部署到阿里云百炼,随后引入智能体中,从而可以在智能体内使用自定义的MCP服务。今天我们先介绍其中一种方式。

1. 编写代码封装MCP服务。

2. 将封装后的服务发布到npm官方平台。

3. 在阿里云百炼平台中创建自定义的MCP服务。

4. 在智能体中引用自定义的MCP服务。

一、搭建MCP服务

通过Nodejs+TypeScript实现

1、创建Nodejs项目

在创建Node.js项目之前,请确保你已安装Node.js,如果尚未安装,可以访问以下链接进行下载:https://nodejs.org

在电脑上创建一个文件夹,名称可自定义。例如,我将其命名为 bailian-mcp-workflow-server

你可以使用VSCode打开该文件夹,或者直接通过命令行进入此文件夹进行操作。

1.1、在命令行当中运行
初始化Nodejs项目
npm init -y

执行成功后自动创建package.json修改package.json当中的内容:

package.json
{
  "name": "bailian-mcp-workflow-server",
  "version": "0.0.1",
  "description": "Bailian MCP server",
  "license": "MIT",
  "author": "Anthropic, PBC (https://anthropic.com)",
  "homepage": "https://modelcontextprotocol.io",
  "bugs": "https://github.com/modelcontextprotocol/servers/issues",
  "type": "module",
  "bin": {
    "mcp-server-brave-search": "dist/index.js"
  },
  "files": [
    "dist"
  ],
  "scripts": {
    "build": "tsc && shx chmod +x dist/*.js",
    "prepare": "npm run build",
    "watch": "tsc --watch"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "1.0.1"
  },
  "devDependencies": {
    "@types/node": "^22",
    "shx": "^0.3.4",
    "typescript": "^5.6.2"
  }
}
1.2、在package.json的同级下创建tsconfig.json
{
    "compilerOptions": {
        "target": "ES2022",
        "module": "Node16",
        "moduleResolution": "Node16",
        "strict": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true,
        "resolveJsonModule": true,
        "outDir": "./dist",
        "rootDir": "."
    },
    "include": [
        "./**/*.ts"
    ],
    "exclude": [
        "node_modules"
    ]
}
1.3、同级目录下创建 index.ts内容为空就行。
1.4、设置好上述内容之后我们安装一下对应的依赖。
npm install

2、将百炼的智能体应用API封装为MCP

1.1、在官方文档中,查看应用调用的API

查看应用调用API参考文档:https://bailian.console.aliyun.com/?tab=api#/api/?type=app,官方API实现对百炼应用的调用如下:

curl -X POST https://dashscope.aliyuncs.com/api/v1/apps/YOUR_APP_ID/completion \
--header "Authorization: Bearer $DASHSCOPE_API_KEY" \
--header 'Content-Type: application/json' \
--data '{
    "input": {
        "prompt": "你是谁?"
    },
    "parameters":  {},
    "debug": {}
}'

我们只需要将上述的API封装为MCP即可。

1.2、将应用的API封装为MCP应用,参考如下代码

index.ts当中写入如下内容

#!/usr/bin/env node

import { Server } from “@modelcontextprotocol/sdk/server/index.js”;
import { StdioServerTransport } from “@modelcontextprotocol/sdk/server/stdio.js”;
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from “@modelcontextprotocol/sdk/types.js”;
const MARKET_RESEARCH_ASSISTANT = {
  name: “market_research_tool”,
  description:  “This is an intelligent market research report generation assistant, specifically designed to efficiently and professionally build research plans.”,
  inputSchema: {
    type: “object”,
    properties: {
      query: {
        type: “string”,
        description: “Search query (max 400 chars, 50 words)”
      }
    },
    required: [“query”],
  },
};
// Server implementation
const server = new Server(
    {
      name: “bailian-mcp-workflow-server”,
      version: “0.1.0”,
    },
    {
      capabilities: {
        tools: {},
      },
    }
);
// Check for API key
const DASHSCOPE_API_KEY = process.env.DASHSCOPE_API_KEY!;
if (!DASHSCOPE_API_KEY) {
  console.error(“Error: DASHSCOPE_API_KEY environment variable is required”);
  process.exit(1);
}
const APP_ID = process.env.APP_ID!;
if (!APP_ID) {
  console.error(“Error: APP_ID environment variable is required”);
  process.exit(1);
}
async function performWebMarketResearch(query: any){
  const url = ‘https://dashscope.aliyuncs.com/api/v1/apps/‘+APP_ID+’/completion’;
  // 构造请求体xxxx
  const requestBody = {
    “input”: {
        “prompt”: query
    },
    “parameters”:  {},
    “debug”: {}
  };
  const response = await fetch(url, {
    method: ‘POST’, // 修改为 POST 请求
    headers: {
      ‘Content-Type’: ‘application/json’, // 指定请求体为 JSON 格式
      ‘Authorization’: "Bearer "+DASHSCOPE_API_KEY
    },
    body: JSON.stringify(requestBody) // 将请求体序列化为 JSON 字符串
  });

  if (!response.ok) {
    thrownew Error(Bailian API error: ${response.status} ${response.statusText}\n${await response.text()});
  }
  const descriptionsData = await response.json(); // 解析响应 JSON 数据
  const strjson = JSON.stringify(descriptionsData)
  return strjson;
}
// Tool handlers
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [MARKET_RESEARCH_ASSISTANT],
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  try {
    const { name, arguments: args } = request.params;
    if (!args) {
      thrownew Error(“No arguments provided”);
    }
    switch (name) {
      case"market_research_tool": {
        const { query } = args;
        const results = await performWebMarketResearch(query);
        return {
          content: [{ type: “text”, text: results }],
          isError: false,
        };
      }
      default:
        return {
          content: [{ type: “text”, text: Unknown tool: ${name} }],
          isError: true,
        };
    }
  } catch (error) {
    return {
      content: [
        {
          type: “text”,
          text: Error: ${error instanceof Error ? error.message : String(error)},
        },
      ],
      isError: true,
    };
  }
});
async function runServer(){
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error(“Bailian Mcp Workflow Server running on stdio”);
}
runServer().catch((error) => {
  console.error(“Fatal error running server:”, error);
  process.exit(1);
});

1.3、解释一下上述代码

核心功能

  • 提供一个名为 market_research_tool 的市场研究工具。

  • 支持通过 API 调用执行市场研究任务。

  • 基于用户输入的查询(Query),调用阿里云 DashScope API 获取市场研究结果。

  • 以 JSON 格式返回研究结果,便于后续处理。

初始化 Server

代码首先创建了一个 Server 实例,该实例基于 @modelcontextprotocol/sdk/server 模块。服务器的配置如下:

const server = new Server(
  {
    name: "bailian-mcp-workflow-server",
    version: "0.1.0",
  },
  {
    capabilities: {
      tools: {},
    },
  }
);
  • name  version 定义了服务器的基本信息。

  • capabilities.tools 表示服务器支持的工具集合(目前为空,后续会动态注册)。

环境变量检查

为了确保服务器能够正常运行,代码检查了两个关键环境变量:

  • DASHSCOPE_API_KEY: 用于调用阿里云 DashScope API 的密钥。

  • APP_ID: 用于标识具体的应用程序。

如果这些变量缺失,服务器将报错并退出。

const DASHSCOPE_API_KEY = process.env.DASHSCOPE_API_KEY!;
if (!DASHSCOPE_API_KEY) {
  console.error("Error: DASHSCOPE_API_KEY environment variable is required");
  process.exit(1);
}
const APP_ID = process.env.APP_ID!;
if (!APP_ID) {
  console.error("Error: APP_ID environment variable is required");
  process.exit(1);
}

定义市场研究工具

MARKET_RESEARCH_ASSISTANT 是一个描述市场研究工具的对象,包含以下内容:

  • name: 工具名称,固定为 market_research_tool

  • description: 工具的功能说明。

  • inputSchema: 输入参数的 JSON Schema,定义了用户需要提供的查询字段 query,并限制其长度为最多 400 个字符或 50 个单词。

注册工具列表

通过

 server.setRequestHandler(ListToolsRequestSchema, ...) 方法,服务器向客户端提供支持的工具列表:

server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [MARKET_RESEARCH_ASSISTANT],
}));

当客户端请求工具列表时,服务器会返回 MARKET_RESEARCH_ASSISTANT 的定义。

API封装为MCP服务

利用fetch发起POST请求,调用百炼API接口并获取相应返回数据。若想将其他API接口封装为MCP,可参考代码中的requestBody,于此设置自定义请求参数。获取到返回数据后,可进行处理,亦可直接返回,因为在百炼中会自动处理返回的JSON,将其转化为便于用户阅读的Markdown格式。

const url = 'https://dashscope.aliyuncs.com/api/v1/apps/'+APP_ID+'/completion';
  // 构造请求体x`x`x`x`
  const requestBody = {
    "input": {
        "prompt": query
    },
    "parameters":  {},
    "debug": {}
  };
  const response = await fetch(url, {
    method: 'POST', // 修改为 POST 请求
    headers: {
      'Content-Type': 'application/json', // 指定请求体为 JSON 格式
      'Authorization': "Bearer "+DASHSCOPE_API_KEY
    },
    body: JSON.stringify(requestBody) // 将请求体序列化为 JSON 字符串
  });
1.4、本机通过Cline联调测试

{
  "mcpServers": {
    "bailian-mcp-workflow-server": {
      "disabled": false,
      "timeout": 300000,
      "command": "cmd",
      "args": [
        "/c",
        "node",
        "D:\\ProgramData\\bailian-mcp-workflow-server\\index.ts"
      ],
      "env": {
        "DASHSCOPE_API_KEY": "sk-212aeb2121213232",
        "APP_ID": "3621da212127b272343411337e7"
      },
      "transportType": "stdio"
    }
  }
}

DASHSCOPE_API_KEY为百炼的API-KEY,去百炼官网上申请

APP_ID 百炼的工作流应用ID,具体工作流是做什么的您可以自定义。

配置好上述内容以后,再次测试提问。

二、将项目打包并发布到npm上


1、首先我们需要注册npm账号

https://www.npmjs.com

2、在本机的npm当中登录刚刚注册的账号

登录到 npm,在项目的终端当中运行以下命令登录到 npm

npm login

系统会提示你输入用户名、密码和邮箱地址。如果登录成功,你会看到类似以下的输出。

Logged in as <your-username> on https://registry.npmjs.org/.

3、检查包名是否唯一

在发布之前,建议检查你选择的包名是否已经被占用。你可以访问 npmjs.com 并搜索你的包名,或者直接尝试发布。

如果包名已被占用,你需要更改 package.json 中的 name 字段为一个唯一的名称。

4、项目打包

在项目的终端当中运行以下命令打包:

npm run build

如果一切正常,你会看到类似以下的输出

运行成功后,项目中会自动创建dist目录,这意味着打包成功

5、发布包

在项目根目录下运行以下命令发布包:

npm publish

如果一切正常,你会看到类似以下的输出:

此时,你的包就已经成功发布到 npm 上了!

https://www.npmjs.com/package/bailian-mcp-workflow-server

6、通过发布的npm包在Cline当中引入测试

本机再次测试一下

Windows配置

{
  "mcpServers": {
    "bailian-mcp-workflow-server": {
      "disabled": false,
      "timeout": 300000,
      "command": "cmd",
      "args": [
        "/c",
        "npx",
        "-y",
        "bailian-mcp-workflow-server"
      ],
      "env": {
        "DASHSCOPE_API_KEY": "sk-212aeb2121213232",
        "APP_ID": "3621da212127b272343411337e7"
      },
      "transportType": "stdio"
    }
  }
}

MacOS/Linux 配置

{
  "mcpServers": {
    "bailian-mcp-workflow-server": {
      "disabled": false,
      "timeout": 300000,
      "command": "npx",
      "args": [
        "-y",
        "bailian-mcp-workflow-server"
      ],
      "env": {
        "DASHSCOPE_API_KEY": "sk-212aeb2121213232",
        "APP_ID": "3621da212127b272343411337e7"
      },
      "transportType": "stdio"
    }
  }
}

三、将刚发布的项目集成到阿里云百炼的自定义MCP中

1、创建自定义MCP

MCP服务配置

{
"mcpServers": {
"bailian-mcp-workflow-server": {
"disabled": false,
"timeout": 300000,
"command": "npx",
"args": [
"-y",
"bailian-mcp-workflow-server"
      ],
"env": {
"DASHSCOPE_API_KEY": "sk-212aeb2121213232",
"APP_ID": "3621da212127b272343411337e7"
      },
"transportType": "stdio"
    }
  }
}

点击提交部署


2、部署成功以后我们找到对应的工具测试一下

选择【工具】进行测试,如下结果则表示测试成功

3、创建智能体,添加MCP服务

4、运行测试

百炼未来将集成工作流的MCP服务,用户只需在平台中进行简单配置,即可将工作流转化为MCP服务,并引入智能体。


楼上说的有道理,我再补充一点,使用MCP服务还可以进行统一的权限管理和安全控制。通过MCP服务,可以对API的访问进行鉴权和限流,防止滥用和恶意攻击。这对于保证智能体的安全性和稳定性非常重要。

这种方式大大降低了MCP服务构建的门槛,让更多的人可以参与到智能体的开发中来。以前需要专业的程序员才能完成的工作,现在通过简单的配置就可以实现,这无疑会极大地加速智能体的普及和应用。

我理解是,MCP服务就像一个翻译器,把智能体听不懂的“百炼API语言”翻译成智能体能理解的“通用语言”。这样,智能体就可以更灵活地使用各种外部能力,不用关心底层API的具体实现。有点像搭积木,把各种功能模块拼在一起,快速构建强大的应用。

从软件工程的角度来看,将API封装成MCP服务,实现了服务间的解耦。智能体不再直接依赖于特定的API,而是依赖于MCP接口。这样,如果API发生变化,只需要修改MCP服务,而不需要修改智能体本身。提高了系统的可维护性和可扩展性。此外,MCP服务可以被多个智能体复用,提高了代码的复用率。

当然可以!MCP协议本身是语言无关的,只要你选择的语言有相应的SDK或者库,能够实现MCP客户端和服务端之间的通信,就可以使用任何你喜欢的语言来实现MCP服务。Node.js和TypeScript只是文章中的一个示例。

我觉得这种方式更侧重于“效率”和“易用性”,但同时也可能会牺牲一定的“灵活性”和“可定制性”。对于简单的应用场景来说,零代码或低代码可能足够了,但对于复杂的、需要深度定制的场景,可能还是需要专业的开发人员来完成。

零代码/低代码平台,关键在于找到一个平衡点,在易用性和可扩展性之间做好权衡。如果平台能够提供足够的扩展接口和定制选项,让开发者可以在必要的时候进行深度定制,那么这种方式将会非常有竞争力。

Python也是一个不错的选择,有很多成熟的Web框架(比如Flask、Django)可以用来快速搭建MCP服务。而且Python在数据处理和机器学习方面有很多优势,如果你的MCP服务涉及到这些方面,Python可能会更方便。

理论上来说,只要支持网络通信和数据序列化的语言都可以。不过不同语言的成熟度和生态支持可能不一样,需要根据具体情况进行选择。比如,如果你的团队主要使用Java,那么用Java来实现MCP服务可能更容易维护和集成。