Commit b5a79303 by lijiabin

测试MCP

parent 6fefc62d
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "enterprise-culture",
version: "2.0.0",
});
const BASE_URL =
process.env.ENTERPRISE_CULTURE_BASE_URL || "http://192.168.2.55:8089";
const TOKEN = process.env.ENTERPRISE_CULTURE_TOKEN || process.env.CODE_LOGIN_TOKEN || "";
function buildUrl(path) {
const normalizedBase = BASE_URL.replace(/\/+$/, "");
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
return `${normalizedBase}${normalizedPath}`;
}
async function requestCultureApi({ path, method = "POST", body }) {
const headers = {
"Content-Type": "application/json",
};
if (TOKEN) headers.Authorization = TOKEN;
const response = await fetch(buildUrl(path), {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
});
let payload = null;
try {
payload = await response.json();
} catch {
payload = null;
}
if (!response.ok) {
return {
ok: false,
message: payload?.message || payload?.error || `HTTP ${response.status}`,
payload,
};
}
const bizError = payload?.error || payload?.msg;
if (bizError && !payload?.data && !payload?.list) {
return { ok: false, message: bizError, payload };
}
return { ok: true, payload };
}
function getMaybeList(payload) {
if (Array.isArray(payload?.list)) return payload.list;
if (Array.isArray(payload?.data?.list)) return payload.data.list;
if (Array.isArray(payload?.data?.records)) return payload.data.records;
if (Array.isArray(payload?.records)) return payload.records;
return [];
}
function okText(title, value) {
return {
content: [
{
type: "text",
text: `${title}\n${typeof value === "string" ? value : JSON.stringify(value, null, 2)}`,
},
],
};
}
function failText(message, payload) {
return {
content: [
{
type: "text",
text: `请求失败: ${message}\n${JSON.stringify(payload ?? {}, null, 2)}`,
},
],
};
}
const listActions = {
listArticles: {
path: "/api/cultureArticle/listByPage",
buildBody: ({ current, size, title, type }) => ({ current, size, title, type }),
format: (payload) => {
const list = getMaybeList(payload);
if (!list.length) return "未查询到文章。";
return list
.map(
(item, idx) =>
`${idx + 1}. 标题: ${item.title || "-"} | ID: ${item.id || item.articleId || "-"} | 发布人: ${item.showName || item.createUserName || "-"}`
)
.join("\n");
},
},
getArticleDetail: {
path: "/api/cultureArticle/getArticleDetail",
method: "POST",
useQuery: ({ articleId }) => `?articleId=${encodeURIComponent(String(articleId))}`,
format: (payload) => JSON.stringify(payload?.data || payload, null, 2),
},
};
const mutateActions = {
publishArticle: {
path: "/api/cultureArticle/addOrUpdateArticle",
},
deleteArticle: {
path: "/api/personalCenter/deleteArticle",
buildBody: ({ id }) => ({ id }),
},
updateRecommend: {
path: "/api/cultureArticle/isRecommend",
},
};
const interactActions = {
likeArticle: {
path: ({ articleId }) => `/api/cultureArticle/likeArticle?articleId=${encodeURIComponent(String(articleId))}`,
method: "POST",
noBody: true,
},
collectArticle: {
path: ({ articleId }) => `/api/cultureArticle/collectArticle?articleId=${encodeURIComponent(String(articleId))}`,
method: "POST",
noBody: true,
},
rewardArticle: {
path: "/api/culture/action/record/reward",
},
};
async function runAction(actionMap, action, params) {
const config = actionMap[action];
if (!config) return failText(`不支持的 action: ${action}`, { supported: Object.keys(actionMap) });
const path = typeof config.path === "function" ? config.path(params) : config.path;
const query = config.useQuery ? config.useQuery(params) : "";
const body = config.noBody ? undefined : config.buildBody ? config.buildBody(params) : params;
const res = await requestCultureApi({ path: `${path}${query}`, method: config.method || "POST", body });
if (!res.ok) return failText(res.message, res.payload);
return okText(`action=${action} 执行成功`, config.format ? config.format(res.payload) : res.payload);
}
server.registerTool(
"article_list",
{
description: "文章查询类能力:分页查询、详情查询",
inputSchema: {
action: z.enum(["listArticles", "getArticleDetail"]).describe("查询动作"),
params: z
.object({
current: z.number().int().positive().optional(),
size: z.number().int().positive().optional(),
title: z.string().optional(),
type: z.number().optional(),
articleId: z.union([z.number(), z.string()]).optional(),
})
.describe("动作参数"),
},
},
async ({ action, params }) => runAction(listActions, action, params)
);
server.registerTool(
"article_mutate",
{
description: "文章写操作:发布/更新、删除、推荐设置",
inputSchema: {
action: z.enum(["publishArticle", "deleteArticle", "updateRecommend"]).describe("写操作动作"),
params: z.record(z.string(), z.any()).describe("请求参数,按前端 src/api 对应接口传值"),
},
},
async ({ action, params }) => runAction(mutateActions, action, params)
);
server.registerTool(
"article_interact",
{
description: "文章互动操作:点赞、收藏、打赏",
inputSchema: {
action: z.enum(["likeArticle", "collectArticle", "rewardArticle"]).describe("互动动作"),
params: z
.object({
articleId: z.union([z.number(), z.string()]).optional(),
ayabi: z.number().optional(),
})
.passthrough()
.describe("互动参数"),
},
},
async ({ action, params }) => runAction(interactActions, action, params)
);
// 兼容你旧的单一工具名
server.registerTool(
"get_article_list_by_page",
{
description: "根据传参获取企业文化文章列表分页(兼容旧调用)",
inputSchema: {
current: z.number().int().positive().describe("当前页码"),
size: z.number().int().positive().default(10).describe("每页条数"),
},
},
async ({ current, size }) => {
return runAction(listActions, "listArticles", { current, size });
}
);
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Enterprise Culture MCP Server running on stdio");
console.error(`BASE_URL=${BASE_URL}`);
console.error(`TOKEN=${TOKEN ? "configured" : "missing"}`);
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});
......@@ -23,6 +23,7 @@
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.2",
"@modelcontextprotocol/sdk": "^1.29.0",
"@types/crypto-js": "^4.2.2",
"@vueuse/components": "^14.0.0",
"@vueuse/core": "^14.0.0",
......@@ -40,7 +41,8 @@
"ssh2": "^1.17.0",
"vue": "^3.5.22",
"vue-router": "^4.6.3",
"xlsx": "^0.18.5"
"xlsx": "^0.18.5",
"zod": "^4.3.6"
},
"devDependencies": {
"@iconify-json/ep": "^1.2.3",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment