postar-pipe-mcp 1.0.0 → 1.0.2

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/dist/server.js CHANGED
@@ -21015,7 +21015,7 @@ function registerGitLab(manager2) {
21015
21015
  manager2.registerConfig("gitlab", {
21016
21016
  name: "gitlab",
21017
21017
  command: "npx",
21018
- args: ["-y", "@structured-world/gitlab-mcp"],
21018
+ args: ["-y", "gitlab-core-mcp"],
21019
21019
  env: {
21020
21020
  GITLAB_API_URL: gitlabUrl,
21021
21021
  GITLAB_TOKEN: gitlabToken
@@ -21057,11 +21057,21 @@ function initMCPClients() {
21057
21057
  }
21058
21058
  const GITLAB_PREFIX = "mcp_gitlab_";
21059
21059
  function getRegisteredServers() {
21060
- const servers = ["gitlab"];
21060
+ const servers = [];
21061
+ if (process.env.GITLAB_TOKEN) {
21062
+ servers.push("gitlab");
21063
+ console.error("[MCP-PIPE][DEBUG] 检测到 GITLAB_TOKEN,添加 gitlab 服务器");
21064
+ } else {
21065
+ console.error("[MCP-PIPE][DEBUG] 未检测到 GITLAB_TOKEN");
21066
+ }
21061
21067
  if (process.env.JENKINS_URL && process.env.JENKINS_USER && process.env.JENKINS_TOKEN) {
21062
21068
  servers.push("jenkins");
21069
+ console.error("[MCP-PIPE][DEBUG] 检测到默认 Jenkins 配置");
21070
+ } else {
21071
+ console.error("[MCP-PIPE][DEBUG] 未检测到默认 Jenkins 配置 (JENKINS_URL/JENKINS_USER/JENKINS_TOKEN)");
21063
21072
  }
21064
21073
  const urlPattern = /^JENKINS_([A-Z0-9_]+)_URL$/;
21074
+ console.error("[MCP-PIPE][DEBUG] 开始检测命名 Jenkins 实例...");
21065
21075
  for (const key of Object.keys(process.env)) {
21066
21076
  const match = key.match(urlPattern);
21067
21077
  if (!match) continue;
@@ -21070,11 +21080,14 @@ function getRegisteredServers() {
21070
21080
  const tokenKey = `JENKINS_${suffix}_TOKEN`;
21071
21081
  const user = process.env[userKey];
21072
21082
  const token = process.env[tokenKey];
21083
+ console.error(`[MCP-PIPE][DEBUG] 检查 ${key} -> suffix=${suffix}, user=${!!user}, token=${!!token}`);
21073
21084
  if (user && token) {
21074
21085
  const instanceName = `jenkins-${suffix.toLowerCase().replace(/_/g, "-")}`;
21075
21086
  servers.push(instanceName);
21087
+ console.error(`[MCP-PIPE][DEBUG] ✅ 添加命名 Jenkins 实例: ${instanceName}`);
21076
21088
  }
21077
21089
  }
21090
+ console.error(`[MCP-PIPE][DEBUG] 最终注册的服务器列表: ${servers.join(", ") || "(空)"}`);
21078
21091
  return servers;
21079
21092
  }
21080
21093
  function getToolPrefix(serverName) {
@@ -21097,49 +21110,59 @@ async function getProxiedTools() {
21097
21110
  const jenkinsServers = servers.filter((s) => s.startsWith("jenkins") && s !== "jenkins");
21098
21111
  const mainJenkins = servers.includes("jenkins") ? "jenkins" : null;
21099
21112
  const otherServers = servers.filter((s) => !s.startsWith("jenkins"));
21100
- if (mainJenkins) {
21101
- const prefix = getToolPrefix(mainJenkins);
21102
- try {
21103
- const tools = await manager2.listTools(mainJenkins);
21104
- for (const tool of tools.tools || []) {
21113
+ const allJenkinsServers = mainJenkins ? [mainJenkins, ...jenkinsServers] : jenkinsServers;
21114
+ let isFirstJenkins = true;
21115
+ for (const serverName of allJenkinsServers) {
21116
+ const prefix = getToolPrefix(serverName);
21117
+ if (isFirstJenkins) {
21118
+ try {
21119
+ const tools = await manager2.listTools(serverName);
21120
+ for (const tool of tools.tools || []) {
21121
+ const proxiedToolName = `${prefix}${tool.name}`;
21122
+ const proxiedTool = {
21123
+ ...tool,
21124
+ name: proxiedToolName,
21125
+ description: `[${serverName}] ${tool.description || ""}`
21126
+ };
21127
+ allTools.push(proxiedTool);
21128
+ toolSchemaCache.set(proxiedToolName, tool.inputSchema);
21129
+ }
21130
+ console.error(`[MCP-PIPE] 已从 ${serverName} 获取 ${tools.tools?.length || 0} 个工具`);
21131
+ isFirstJenkins = false;
21132
+ } catch (error2) {
21133
+ console.error(`[MCP-PIPE] 无法从 ${serverName} 获取工具列表:`, error2);
21134
+ continue;
21135
+ }
21136
+ } else {
21137
+ const sourcePrefix = mainJenkins ? getToolPrefix(mainJenkins) : getToolPrefix(allJenkinsServers[0]);
21138
+ const jenkinsTools = allTools.filter((t) => t.name.startsWith(sourcePrefix));
21139
+ for (const tool of jenkinsTools) {
21140
+ const originalToolName = tool.name.slice(sourcePrefix.length);
21141
+ const proxiedToolName = `${prefix}${originalToolName}`;
21105
21142
  const proxiedTool = {
21106
21143
  ...tool,
21107
- name: `${prefix}${tool.name}`,
21108
- description: `[${mainJenkins}] ${tool.description || ""}`
21144
+ name: proxiedToolName,
21145
+ description: `[${serverName}] ${tool.description || ""}`
21109
21146
  };
21110
21147
  allTools.push(proxiedTool);
21148
+ toolSchemaCache.set(proxiedToolName, tool.inputSchema);
21111
21149
  }
21112
- console.error(`[MCP-PIPE] 已代理 ${mainJenkins} ${tools.tools?.length || 0} 个工具`);
21113
- } catch (error2) {
21114
- console.error(`[MCP-PIPE] 无法获取 ${mainJenkins} 的工具列表:`, error2);
21115
- }
21116
- }
21117
- for (const serverName of jenkinsServers) {
21118
- const prefix = getToolPrefix(serverName);
21119
- const sourcePrefix = mainJenkins ? getToolPrefix(mainJenkins) : prefix;
21120
- const jenkinsTools = allTools.filter((t) => t.name.startsWith(sourcePrefix));
21121
- for (const tool of jenkinsTools) {
21122
- const originalToolName = tool.name.slice(sourcePrefix.length);
21123
- const proxiedTool = {
21124
- ...tool,
21125
- name: `${prefix}${originalToolName}`,
21126
- description: `[${serverName}] ${tool.description || ""}`
21127
- };
21128
- allTools.push(proxiedTool);
21150
+ console.error(`[MCP-PIPE] 已为 ${serverName} 生成 ${jenkinsTools.length} 个工具别名`);
21129
21151
  }
21130
- console.error(`[MCP-PIPE] 已为 ${serverName} 生成 ${jenkinsTools.length} 个工具别名`);
21131
21152
  }
21132
21153
  for (const serverName of otherServers) {
21133
21154
  const prefix = getToolPrefix(serverName);
21134
21155
  try {
21135
21156
  const tools = await manager2.listTools(serverName);
21136
21157
  for (const tool of tools.tools || []) {
21158
+ const proxiedToolName = `${prefix}${tool.name}`;
21137
21159
  const proxiedTool = {
21138
21160
  ...tool,
21139
- name: `${prefix}${tool.name}`,
21161
+ name: proxiedToolName,
21140
21162
  description: `[${serverName}] ${tool.description || ""}`
21141
21163
  };
21142
21164
  allTools.push(proxiedTool);
21165
+ toolSchemaCache.set(proxiedToolName, tool.inputSchema);
21143
21166
  }
21144
21167
  console.error(`[MCP-PIPE] 已代理 ${serverName} 的 ${tools.tools?.length || 0} 个工具`);
21145
21168
  } catch (error2) {
@@ -21148,9 +21171,40 @@ async function getProxiedTools() {
21148
21171
  }
21149
21172
  return allTools;
21150
21173
  }
21174
+ function fixArgumentTypes(args, toolSchema) {
21175
+ if (!args || typeof args !== "object") return args;
21176
+ const fixedArgs = { ...args };
21177
+ if (!toolSchema?.properties) {
21178
+ console.warn(`[MCP-PIPE] 警告: 工具缺少 schema,跳过类型修复`);
21179
+ return fixedArgs;
21180
+ }
21181
+ for (const [key, schema] of Object.entries(toolSchema.properties)) {
21182
+ if (key in fixedArgs && typeof fixedArgs[key] === "string") {
21183
+ const propSchema = schema;
21184
+ if (propSchema.type === "number" || propSchema.type === "integer") {
21185
+ const num = Number(fixedArgs[key]);
21186
+ if (!isNaN(num)) {
21187
+ fixedArgs[key] = num;
21188
+ console.debug(`[MCP-PIPE] 类型转换: ${key} "${fixedArgs[key]}" → ${num} (number)`);
21189
+ }
21190
+ }
21191
+ if (propSchema.type === "boolean") {
21192
+ const str = fixedArgs[key].toLowerCase();
21193
+ if (str === "true" || str === "false") {
21194
+ fixedArgs[key] = str === "true";
21195
+ console.debug(`[MCP-PIPE] 类型转换: ${key} "${str}" → ${fixedArgs[key]} (boolean)`);
21196
+ }
21197
+ }
21198
+ }
21199
+ }
21200
+ return fixedArgs;
21201
+ }
21202
+ const toolSchemaCache = /* @__PURE__ */ new Map();
21151
21203
  async function handleProxiedTool(name, args) {
21152
21204
  const manager2 = getMCPClientManager();
21153
21205
  const servers = getRegisteredServers();
21206
+ const toolSchema = toolSchemaCache.get(name);
21207
+ const fixedArgs = fixArgumentTypes(args, toolSchema);
21154
21208
  if (name.startsWith("mcp_jenkins_")) {
21155
21209
  let targetMCP = null;
21156
21210
  const otherJenkinsServers = servers.filter((s) => s.startsWith("jenkins-"));
@@ -21168,7 +21222,7 @@ async function handleProxiedTool(name, args) {
21168
21222
  const prefix = getToolPrefix(targetMCP);
21169
21223
  const originalToolName = name.slice(prefix.length);
21170
21224
  try {
21171
- const result = await manager2.callTool(targetMCP, originalToolName, args);
21225
+ const result = await manager2.callTool(targetMCP, originalToolName, fixedArgs);
21172
21226
  return result;
21173
21227
  } catch (error2) {
21174
21228
  return {
@@ -21183,7 +21237,7 @@ async function handleProxiedTool(name, args) {
21183
21237
  if (name.startsWith(prefix)) {
21184
21238
  const originalToolName = name.slice(prefix.length);
21185
21239
  try {
21186
- const result = await manager2.callTool(serverName, originalToolName, args);
21240
+ const result = await manager2.callTool(serverName, originalToolName, fixedArgs);
21187
21241
  return result;
21188
21242
  } catch (error2) {
21189
21243
  return {
@@ -21199,13 +21253,26 @@ const skillCache = /* @__PURE__ */ new Map();
21199
21253
  const skillMetadataCache = /* @__PURE__ */ new Map();
21200
21254
  let remoteSkillsList = null;
21201
21255
  function parseSkillSource(url, index) {
21202
- const rawUrl = url.replace("/tree/", "/raw/").replace(/\/$/, "");
21203
- const match = url.match(/\/([^\/]+)\/[^\/]+\/tree\//);
21256
+ const questionMarkIndex = url.indexOf("?");
21257
+ let cleanUrl = url;
21258
+ let skillsList;
21259
+ if (questionMarkIndex !== -1) {
21260
+ cleanUrl = url.substring(0, questionMarkIndex);
21261
+ const queryString = url.substring(questionMarkIndex + 1);
21262
+ const params = new URLSearchParams(queryString);
21263
+ const skillsParam = params.get("skills");
21264
+ if (skillsParam) {
21265
+ skillsList = skillsParam.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
21266
+ }
21267
+ }
21268
+ const rawUrl = cleanUrl.replace("/tree/", "/raw/").replace(/\/$/, "");
21269
+ const match = cleanUrl.match(/\/([^\/]+)\/[^\/]+\/tree\//);
21204
21270
  const name = match ? match[1] : `source-${index}`;
21205
21271
  return {
21206
21272
  name,
21207
21273
  baseUrl: rawUrl,
21208
- originalUrl: url
21274
+ originalUrl: cleanUrl,
21275
+ skillsList
21209
21276
  };
21210
21277
  }
21211
21278
  function parseCommandLineArgs() {
@@ -21345,35 +21412,52 @@ async function fetchRemoteSkillsFromDirectory(config2) {
21345
21412
  }
21346
21413
  }
21347
21414
  const skillSourceMap = /* @__PURE__ */ new Map();
21415
+ const CACHE_TTL_MINUTES = process.env.SKILLS_CACHE_TTL ? parseInt(process.env.SKILLS_CACHE_TTL) : 30;
21416
+ const CACHE_TTL = CACHE_TTL_MINUTES * 60 * 1e3;
21417
+ function isCacheExpired(timestamp) {
21418
+ return Date.now() - timestamp > CACHE_TTL;
21419
+ }
21348
21420
  async function fetchRemoteSkillsList(config2) {
21349
- if (remoteSkillsList) {
21350
- return remoteSkillsList;
21421
+ if (remoteSkillsList && !isCacheExpired(remoteSkillsList.timestamp)) {
21422
+ return remoteSkillsList.data;
21351
21423
  }
21424
+ console.error(`[SKILL-LOADER] Skills 列表缓存过期或不存在,重新加载...`);
21352
21425
  const skillsListEnv = process.env.SKILLS_LIST;
21353
21426
  if (skillsListEnv) {
21354
21427
  const skills = skillsListEnv.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
21355
- remoteSkillsList = skills;
21428
+ remoteSkillsList = { data: skills, timestamp: Date.now() };
21356
21429
  console.error(`[SKILL-LOADER] 从环境变量加载 skills: ${skills.join(", ")}`);
21357
21430
  return skills;
21358
21431
  }
21359
- console.error(`[SKILL-LOADER] 未配置 SKILLS_LIST,自动遍历所有远程目录...`);
21432
+ console.error(`[SKILL-LOADER] 未配置 SKILLS_LIST,检查各源独立配置...`);
21360
21433
  const allSkills = [];
21361
21434
  skillSourceMap.clear();
21362
21435
  if (config2.sources && config2.sources.length > 0) {
21363
21436
  for (const source of config2.sources) {
21364
21437
  console.error(`[SKILL-LOADER] 扫描源: ${source.name} (${source.originalUrl})`);
21365
- const sourceConfig = {
21366
- source: "remote",
21367
- baseUrl: source.baseUrl,
21368
- originalUrl: source.originalUrl
21369
- };
21370
- const skills = await fetchRemoteSkillsFromDirectory(sourceConfig);
21371
- console.error(`[SKILL-LOADER] 源 ${source.name} 发现 skills: ${skills.join(", ")}`);
21372
- for (const skillName of skills) {
21373
- if (!allSkills.includes(skillName)) {
21374
- allSkills.push(skillName);
21438
+ if (source.skillsList && source.skillsList.length > 0) {
21439
+ console.error(`[SKILL-LOADER] 源 ${source.name} 使用独立 skills 列表: ${source.skillsList.join(", ")}`);
21440
+ for (const skillName of source.skillsList) {
21441
+ if (!allSkills.includes(skillName)) {
21442
+ allSkills.push(skillName);
21443
+ }
21444
+ skillSourceMap.set(skillName, source);
21445
+ }
21446
+ } else {
21447
+ console.error(`[SKILL-LOADER] 源 ${source.name} 未配置独立 skills,自动遍历目录...`);
21448
+ const sourceConfig = {
21449
+ source: "remote",
21450
+ baseUrl: source.baseUrl,
21451
+ originalUrl: source.originalUrl
21452
+ };
21453
+ const skills = await fetchRemoteSkillsFromDirectory(sourceConfig);
21454
+ console.error(`[SKILL-LOADER] 源 ${source.name} 发现 skills: ${skills.join(", ")}`);
21455
+ for (const skillName of skills) {
21456
+ if (!allSkills.includes(skillName)) {
21457
+ allSkills.push(skillName);
21458
+ }
21459
+ skillSourceMap.set(skillName, source);
21375
21460
  }
21376
- skillSourceMap.set(skillName, source);
21377
21461
  }
21378
21462
  }
21379
21463
  } else if (config2.baseUrl && config2.originalUrl) {
@@ -21388,7 +21472,7 @@ async function fetchRemoteSkillsList(config2) {
21388
21472
  }
21389
21473
  }
21390
21474
  console.error(`[SKILL-LOADER] 汇总所有 skills: ${allSkills.join(", ")}`);
21391
- remoteSkillsList = allSkills;
21475
+ remoteSkillsList = { data: allSkills, timestamp: Date.now() };
21392
21476
  return allSkills;
21393
21477
  }
21394
21478
  async function loadRemoteSkillContent(skillName, baseUrl) {
@@ -21474,11 +21558,14 @@ Skill 文件加载失败,请检查安装。`;
21474
21558
  }
21475
21559
  }
21476
21560
  async function getSkillContent(skillName) {
21477
- if (!skillCache.has(skillName)) {
21478
- const content = await loadSkillContent(skillName);
21479
- skillCache.set(skillName, content);
21561
+ const cached2 = skillCache.get(skillName);
21562
+ if (cached2 && !isCacheExpired(cached2.timestamp)) {
21563
+ return cached2.data;
21480
21564
  }
21481
- return skillCache.get(skillName);
21565
+ console.error(`[SKILL-LOADER] Skill 缓存过期或不存在,重新加载: ${skillName}`);
21566
+ const content = await loadSkillContent(skillName);
21567
+ skillCache.set(skillName, { data: content, timestamp: Date.now() });
21568
+ return content;
21482
21569
  }
21483
21570
  async function parseFrontmatter(content) {
21484
21571
  const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
@@ -21510,21 +21597,24 @@ async function parseFrontmatter(content) {
21510
21597
  }
21511
21598
  }
21512
21599
  async function getSkillMetadata(skillName) {
21513
- if (!skillMetadataCache.has(skillName)) {
21514
- const content = await getSkillContent(skillName);
21515
- const { metadata: metadata2 } = await parseFrontmatter(content);
21516
- const metadataObj = {
21517
- name: metadata2.name || skillName,
21518
- description: metadata2.description || `${skillName} skill`,
21519
- constraint: metadata2.constraint,
21520
- version: metadata2.version,
21521
- tools: metadata2.tools || [],
21522
- ...metadata2
21523
- // 包含其他自定义字段
21524
- };
21525
- skillMetadataCache.set(skillName, metadataObj);
21526
- }
21527
- return skillMetadataCache.get(skillName);
21600
+ const cached2 = skillMetadataCache.get(skillName);
21601
+ if (cached2 && !isCacheExpired(cached2.timestamp)) {
21602
+ return cached2.data;
21603
+ }
21604
+ console.error(`[SKILL-LOADER] Skill 元数据缓存过期或不存在,重新加载: ${skillName}`);
21605
+ const content = await getSkillContent(skillName);
21606
+ const { metadata: metadata2 } = await parseFrontmatter(content);
21607
+ const metadataObj = {
21608
+ name: metadata2.name || skillName,
21609
+ description: metadata2.description || `${skillName} skill`,
21610
+ constraint: metadata2.constraint,
21611
+ version: metadata2.version,
21612
+ tools: metadata2.tools || [],
21613
+ ...metadata2
21614
+ // 包含其他自定义字段
21615
+ };
21616
+ skillMetadataCache.set(skillName, { data: metadataObj, timestamp: Date.now() });
21617
+ return metadataObj;
21528
21618
  }
21529
21619
  async function getAllSkillsMetadata() {
21530
21620
  const skills = await getAvailableSkills();