@zibby/core 2.0.10 → 2.0.12

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.
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import{readFileSync as j}from"fs";import{McpServer as _}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as k}from"@modelcontextprotocol/sdk/server/stdio.js";import{Client as T}from"@modelcontextprotocol/sdk/client/index.js";import{StdioClientTransport as A}from"@modelcontextprotocol/sdk/client/stdio.js";import{StreamableHTTPClientTransport as x}from"@modelcontextprotocol/sdk/client/streamableHttp.js";import{z as a}from"zod";var c=e=>console.error(`[tool-broker] ${e}`),v=process.argv.indexOf("--spec"),f=v!==-1?process.argv[v+1]:null;f||(console.error("[tool-broker] usage: tool-broker-server.js --spec <path>"),process.exit(2));var l;try{l=JSON.parse(j(f,"utf8"))}catch(e){console.error(`[tool-broker] cannot read spec ${f}: ${e.message}`),process.exit(2)}var u=l.servers&&typeof l.servers=="object"?l.servers:{},$=new Set(Array.isArray(l.pinned)?l.pinned:[]),p=new Map;function N(e){if(e&&(e.transport==="http"||e.type==="http"||e.url&&!e.command)){let r=e.headers||e.http_headers||void 0;return new x(new URL(e.url),r?{requestInit:{headers:r}}:void 0)}if(!e||!e.command)throw new Error("config has neither a url nor a command");return new A({command:e.command,args:e.args||[],env:e.env||void 0,cwd:e.cwd||void 0,stderr:"inherit"})}async function b(e){if(p.has(e))return p.get(e);let o=u[e],r={name:e};try{let t=new T({name:"zibby-tool-broker",version:"1.0.0"},{capabilities:{}});await t.connect(N(o));let s=await t.listTools();r.client=t,r.tools=(s.tools||[]).map(n=>({name:n.name,description:n.description||"",inputSchema:n.inputSchema||{}})),c(`connected ${e} (${r.tools.length} tools)`)}catch(t){r.error=t&&t.message?t.message:String(t),r.tools=[],c(`FAILED ${e}: ${r.error}`)}return p.set(e,r),r}async function y(){await Promise.all(Object.keys(u).map(e=>b(e)))}function O(e,o=140){let r=String(e||"").split(`
3
- `).map(t=>t.trim()).find(Boolean)||"";return r.length>o?`${r.slice(0,o-1)}\u2026`:r}function w(e){let o=String(e||"").trim();if(o.includes(":")){let[t,...s]=o.split(":"),n=s.join(":"),i=p.get(t);if(!i)return{error:`no such server "${t}". Call list_tools to see what exists.`};if(i.error)return{error:`server "${t}" failed to connect: ${i.error}`};let g=i.tools.find(S=>S.name===n);return g?{server:t,entry:i,tool:g}:{error:`server "${t}" has no tool "${n}". Call list_tools({server:"${t}"}).`}}let r=[];for(let[t,s]of p)for(let n of s.tools)n.name===o&&r.push({server:t,entry:s,tool:n});return r.length===1?r[0]:r.length===0?{error:`no tool named "${o}". Call list_tools to see the catalogue.`}:{error:`"${o}" exists on ${r.map(t=>t.server).join(", ")} \u2014 address it as "<server>:${o}".`}}var h=e=>({content:[{type:"text",text:e}]}),d=e=>({content:[{type:"text",text:e}],isError:!0}),m=new _({name:"zibby-tool-broker",version:"1.0.0"},{capabilities:{tools:{}}}),E=Object.keys(u).join(", ");m.registerTool("list_tools",{description:`List every tool available to you, as name + one line each. Parameters are NOT included \u2014 call describe_tool for those. Servers: ${E||"(none)"}. Start here whenever you need a capability you have not used yet in this turn.`,inputSchema:{server:a.string().optional().describe('Limit to one server (e.g. "jira"). Omit for everything.')}},async({server:e}={})=>{await y();let o=[],r=0;for(let[t,s]of p)if(!(e&&t!==e)){if(s.error){o.push(`## ${t} \u2014 UNAVAILABLE: ${s.error}`);continue}o.push(`## ${t} (${s.tools.length})`);for(let n of s.tools)r++,o.push(`- ${t}:${n.name} \u2014 ${O(n.description)}`)}return o.length?(o.push(""),o.push(`${r} tools. describe_tool("<server>:<tool>") for parameters, then call_tool.`),h(o.join(`
4
- `))):h(e?`No server named "${e}".`:"No tools are connected.")});m.registerTool("describe_tool",{description:'Full parameter schema for ONE tool. Name it as "<server>:<tool>" (or the bare tool name if unique).',inputSchema:{name:a.string().describe('e.g. "jira:jira_list_projects"')}},async({name:e})=>{await y();let o=w(e);return o.error?d(o.error):h(JSON.stringify({name:`${o.server}:${o.tool.name}`,description:o.tool.description,inputSchema:o.tool.inputSchema},null,2))});m.registerTool("call_tool",{description:"Invoke a tool by name with its arguments. Get the argument shape from describe_tool first.",inputSchema:{name:a.string().describe('e.g. "jira:jira_list_projects"'),arguments:a.record(a.any()).optional().describe("Arguments object; omit when the tool takes none.")}},async({name:e,arguments:o})=>{await y();let r=w(e);if(r.error)return d(r.error);try{return await r.entry.client.callTool({name:r.tool.name,arguments:o||{}})}catch(t){return d(`${r.server}:${r.tool.name} threw: ${t&&t.message?t.message:String(t)}`)}});for(let e of $){if(!u[e]){c(`pinned server "${e}" is not in the spec \u2014 ignoring`);continue}let o=await b(e);if(o.error){c(`pinned server "${e}" did not connect; its tools are absent from turn 1`);continue}for(let r of o.tools)m.registerTool(r.name,{description:r.description||r.name,inputSchema:L(r.inputSchema)},async(t={})=>{try{return await o.client.callTool({name:r.name,arguments:t})}catch(s){return d(`${e}:${r.name} threw: ${s&&s.message?s.message:String(s)}`)}});c(`pinned ${e} (${o.tools.length} tools exposed directly)`)}function L(e={}){let o=e&&e.properties||{},r=new Set(Array.isArray(e?.required)?e.required:[]),t={};for(let[s,n]of Object.entries(o)){let i;Array.isArray(n?.enum)&&n.enum.length?i=a.enum(n.enum.map(String)):n?.type==="string"?i=a.string():n?.type==="number"||n?.type==="integer"?i=a.number():n?.type==="boolean"?i=a.boolean():n?.type==="array"?i=a.array(a.any()):n?.type==="object"?i=a.record(a.any()):i=a.any(),n?.description&&(i=i.describe(n.description)),r.has(s)||(i=i.optional()),t[s]=i}return t}await m.connect(new k);c(`ready \u2014 3 broker tools + ${$.size} pinned server(s) in front of ${Object.keys(u).length} server(s)`);
2
+ import{readFileSync as x}from"fs";import{McpServer as O}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as L}from"@modelcontextprotocol/sdk/server/stdio.js";import{Client as C}from"@modelcontextprotocol/sdk/client/index.js";import{StdioClientTransport as E}from"@modelcontextprotocol/sdk/client/stdio.js";import{StreamableHTTPClientTransport as z}from"@modelcontextprotocol/sdk/client/streamableHttp.js";import{z as a}from"zod";var p=e=>console.error(`[tool-broker] ${e}`),k=process.argv.indexOf("--spec"),S=k!==-1?process.argv[k+1]:null;S||(console.error("[tool-broker] usage: tool-broker-server.js --spec <path>"),process.exit(2));var u;try{u=JSON.parse(x(S,"utf8"))}catch(e){console.error(`[tool-broker] cannot read spec ${S}: ${e.message}`),process.exit(2)}var g=u.servers&&typeof u.servers=="object"?u.servers:{},T=new Set(Array.isArray(u.pinned)?u.pinned:[]),m=new Map;function I(e){if(e&&(e.transport==="http"||e.type==="http"||e.url&&!e.command)){let r=e.headers||e.http_headers||void 0;return new z(new URL(e.url),r?{requestInit:{headers:r}}:void 0)}if(!e||!e.command)throw new Error("config has neither a url nor a command");return new E({command:e.command,args:e.args||[],env:e.env||void 0,cwd:e.cwd||void 0,stderr:"inherit"})}async function A(e){if(m.has(e))return m.get(e);let o=g[e],r={name:e};try{let t=new C({name:"zibby-tool-broker",version:"1.0.0"},{capabilities:{}});await t.connect(I(o));let i=await t.listTools();r.client=t,r.tools=(i.tools||[]).map(n=>({name:n.name,description:n.description||"",inputSchema:n.inputSchema||{}})),p(`connected ${e} (${r.tools.length} tools)`)}catch(t){r.error=t&&t.message?t.message:String(t),r.tools=[],p(`FAILED ${e}: ${r.error}`)}return m.set(e,r),r}async function _(){await Promise.all(Object.keys(g).map(e=>A(e)))}function q(e,o=140){let r=String(e||"").split(`
3
+ `).map(t=>t.trim()).find(Boolean)||"";return r.length>o?`${r.slice(0,o-1)}\u2026`:r}function N(e){let o=String(e||"").trim();if(o.includes(":")){let[t,...i]=o.split(":"),n=i.join(":"),s=m.get(t);if(!s)return{error:`no such server "${t}". Call list_tools to see what exists.`};if(s.error)return{error:`server "${t}" failed to connect: ${s.error}`};let d=s.tools.find(c=>c.name===n);return d?{server:t,entry:s,tool:d}:{error:`server "${t}" has no tool "${n}". Call list_tools({server:"${t}"}).`}}let r=[];for(let[t,i]of m)for(let n of i.tools)n.name===o&&r.push({server:t,entry:i,tool:n});return r.length===1?r[0]:r.length===0?{error:`no tool named "${o}". Call list_tools to see the catalogue.`}:{error:`"${o}" exists on ${r.map(t=>t.server).join(", ")} \u2014 address it as "<server>:${o}".`}}var j=e=>({content:[{type:"text",text:e}]}),w=e=>({content:[{type:"text",text:e}],isError:!0}),y=new O({name:"zibby-tool-broker",version:"1.0.0"},{capabilities:{tools:{}}}),P=Object.keys(g).join(", ");y.registerTool("list_tools",{description:`List the tools available to you, as name + one line each. Parameters are NOT included \u2014 call describe_tool for those. Servers: ${P||"(none)"}. Start here whenever you need a capability you have not used yet in this turn.`,inputSchema:{server:a.string().optional().describe('Limit to one server (e.g. "jira"). Omit for everything.'),query:a.string().optional().describe('Keywords to narrow the list (e.g. "jira issue"). Omit to see everything.')}},async({server:e,query:o}={})=>{await _();let r=String(o||"").toLowerCase().split(/[^a-z0-9_]+/i).filter(c=>c.length>2),t=(c,l)=>!r.length||r.some($=>`${c}:${l.name} ${l.description}`.toLowerCase().includes($)),i=c=>{let l=[],$=0;for(let[h,f]of m){if(e&&h!==e)continue;if(f.error){l.push(`## ${h} \u2014 UNAVAILABLE: ${f.error}`);continue}let v=f.tools.filter(b=>c(h,b));if(v.length){l.push(`## ${h} (${v.length}${v.length===f.tools.length?"":` of ${f.tools.length}`})`);for(let b of v)$++,l.push(`- ${h}:${b.name} \u2014 ${q(b.description)}`)}}return{lines:l,count:$}},{lines:n,count:s}=i(t),d="";return r.length&&s===0&&({lines:n,count:s}=i(()=>!0),d=`Nothing matched "${o}", so here is everything. `),n.length?(n.push(""),n.push(`${d}${s} tools. describe_tool("<server>:<tool>") for parameters, then call_tool.`),j(n.join(`
4
+ `))):j(e?`No server named "${e}".`:"No tools are connected.")});y.registerTool("describe_tool",{description:'Full parameter schema for ONE tool. Name it as "<server>:<tool>" (or the bare tool name if unique).',inputSchema:{name:a.string().describe('e.g. "jira:jira_list_projects"')}},async({name:e})=>{await _();let o=N(e);return o.error?w(o.error):j(JSON.stringify({name:`${o.server}:${o.tool.name}`,description:o.tool.description,inputSchema:o.tool.inputSchema},null,2))});y.registerTool("call_tool",{description:"Invoke a tool by name with its arguments. Get the argument shape from describe_tool first.",inputSchema:{name:a.string().describe('e.g. "jira:jira_list_projects"'),arguments:a.record(a.any()).optional().describe("Arguments object; omit when the tool takes none.")}},async({name:e,arguments:o})=>{await _();let r=N(e);if(r.error)return w(r.error);try{return await r.entry.client.callTool({name:r.tool.name,arguments:o||{}})}catch(t){return w(`${r.server}:${r.tool.name} threw: ${t&&t.message?t.message:String(t)}`)}});for(let e of T){if(!g[e]){p(`pinned server "${e}" is not in the spec \u2014 ignoring`);continue}let o=await A(e);if(o.error){p(`pinned server "${e}" did not connect; its tools are absent from turn 1`);continue}for(let r of o.tools)y.registerTool(r.name,{description:r.description||r.name,inputSchema:R(r.inputSchema)},async(t={})=>{try{return await o.client.callTool({name:r.name,arguments:t})}catch(i){return w(`${e}:${r.name} threw: ${i&&i.message?i.message:String(i)}`)}});p(`pinned ${e} (${o.tools.length} tools exposed directly)`)}function R(e={}){let o=e&&e.properties||{},r=new Set(Array.isArray(e?.required)?e.required:[]),t={};for(let[i,n]of Object.entries(o)){let s;Array.isArray(n?.enum)&&n.enum.length?s=a.enum(n.enum.map(String)):n?.type==="string"?s=a.string():n?.type==="number"||n?.type==="integer"?s=a.number():n?.type==="boolean"?s=a.boolean():n?.type==="array"?s=a.array(a.any()):n?.type==="object"?s=a.record(a.any()):s=a.any(),n?.description&&(s=s.describe(n.description)),r.has(i)||(s=s.optional()),t[i]=s}return t}await y.connect(new L);p(`ready \u2014 3 broker tools + ${T.size} pinned server(s) in front of ${Object.keys(g).length} server(s)`);
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zibby/core",
3
- "version": "2.0.10",
3
+ "version": "2.0.12",
4
4
  "description": "Core test automation engine with multi-agent and multi-MCP support",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -69,9 +69,9 @@
69
69
  "@modelcontextprotocol/sdk": "^1.29.0",
70
70
  "@openai/codex-sdk": "^0.144.6",
71
71
  "@playwright/mcp": "^0.0.70",
72
- "@zibby/agent-workflow": "2.0.10",
73
- "@zibby/mcp-browser": "2.0.10",
74
- "@zibby/skill-ids": "2.0.10",
72
+ "@zibby/agent-workflow": "2.0.12",
73
+ "@zibby/mcp-browser": "2.0.12",
74
+ "@zibby/skill-ids": "2.0.12",
75
75
  "acorn": "^8.15.0",
76
76
  "acorn-walk": "^8.3.5",
77
77
  "axios": "^1.15.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zibby/core",
3
- "version": "2.0.10",
3
+ "version": "2.0.12",
4
4
  "description": "Core test automation engine with multi-agent and multi-MCP support",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -69,9 +69,9 @@
69
69
  "@modelcontextprotocol/sdk": "^1.29.0",
70
70
  "@openai/codex-sdk": "^0.144.6",
71
71
  "@playwright/mcp": "^0.0.70",
72
- "@zibby/agent-workflow": "2.0.10",
73
- "@zibby/mcp-browser": "2.0.10",
74
- "@zibby/skill-ids": "2.0.10",
72
+ "@zibby/agent-workflow": "2.0.12",
73
+ "@zibby/mcp-browser": "2.0.12",
74
+ "@zibby/skill-ids": "2.0.12",
75
75
  "acorn": "^8.15.0",
76
76
  "acorn-walk": "^8.3.5",
77
77
  "axios": "^1.15.0",