@roll-agent/sdk 0.2.1 → 0.3.0

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/context.d.ts CHANGED
@@ -15,6 +15,13 @@ export interface AgentLLM {
15
15
  export interface AgentContext {
16
16
  readonly llm: AgentLLM;
17
17
  readonly logger: AgentLogger;
18
+ /**
19
+ * 当前 MCP 请求的取消信号(per-request)。
20
+ *
21
+ * 客户端超时或主动取消时触发。工具(尤其是排队等待共享资源的工具)
22
+ * 应在开始不可逆操作前检查该信号,避免执行已被客户端放弃的"幽灵操作"。
23
+ */
24
+ readonly signal?: AbortSignal;
18
25
  }
19
26
  /**
20
27
  * 创建一个基于 stderr 的 Agent Logger。
@@ -25,5 +25,5 @@ type McpTextToolResult = {
25
25
  }
26
26
  ];
27
27
  };
28
- export declare function executeToolForMcp(tool: AnyToolDefinition, ctx: AgentContext, params: Record<string, unknown>): Promise<McpTextToolResult>;
28
+ export declare function executeToolForMcp(tool: AnyToolDefinition, ctx: AgentContext, params: Record<string, unknown>, signal?: AbortSignal): Promise<McpTextToolResult>;
29
29
  export {};
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{createServer as e}from"node:http";import{randomUUID as n}from"node:crypto";import{McpServer as t}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as r}from"@modelcontextprotocol/sdk/server/stdio.js";import{StreamableHTTPServerTransport as o}from"@modelcontextprotocol/sdk/server/streamableHttp.js";var s={debug:0,info:1,warn:2,error:3};function i(e,n="info"){const t=s[n],r=(n,r)=>{if(s[n]<t)return;const o=(new Date).toISOString(),i=n.toUpperCase().padEnd(5);console.error(`${o} [${i}] [${e}] ${r}`)};return{debug:e=>r("debug",e),info:e=>r("info",e),warn:e=>r("warn",e),error:e=>r("error",e)}}import{z as a}from"zod";var c=8;function f(e){let n=e;for(let e=0;e<c;e+=1)if(n instanceof a.ZodEffects)n=n.innerType();else if(n instanceof a.ZodOptional||n instanceof a.ZodNullable||n instanceof a.ZodBranded||n instanceof a.ZodReadonly)n=n.unwrap();else if(n instanceof a.ZodDefault)n=n.removeDefault();else{if(!(n instanceof a.ZodCatch))return n;n=n.removeCatch()}return n}async function d(e,n){const t=await e.safeParseAsync(n);if(!t.success)throw new Error(`Tool input validation failed: ${p(t.error)}`);return t.data}function p(e){return e.issues.map(e=>{const n=e.path.join(".");return n.length>0?`${n}: ${e.message}`:e.message}).join("; ")}var l=class extends Error{payload;constructor(e){super(e.message),this.name="StructuredToolError",this.payload=e}};function u(e){return e instanceof l||e instanceof Error&&"payload"in e&&y(e.payload)}function y(e){if("object"!=typeof e||null===e||Array.isArray(e))return!1;const n=e,t=n.details;return"string"==typeof n.code&&"string"==typeof n.message&&(void 0===t||"object"==typeof t&&null!==t&&!Array.isArray(t))}function m(e){return e??w()??"info"}function w(){const e=process.env.ROLL_AGENT_LOG_LEVEL;if("debug"===e||"info"===e||"warn"===e||"error"===e)return e}function g(e,n,t){return{llm:{generateText:async e=>{try{const n=h((await t.server.createMessage({messages:[{role:"user",content:{type:"text",text:e}}],maxTokens:1024})).content);if(void 0===n)throw new Error("Sampling response did not include text content");return n}catch(e){throw new Error("LLM sampling unavailable. Ensure roll-core client enables sampling capability.",{cause:e})}}},logger:i(e,n)}}function h(e){if("object"==typeof e&&null!==e&&"type"in e&&"text"===e.type&&"text"in e){const n=e.text;return"string"==typeof n?n:void 0}if(Array.isArray(e))for(const n of e){const e=h(n);if(void 0!==e)return e}}function v(e,n={}){return{...e,listen:async t=>{const r=t?.transport??{type:"stdio"},o=m(n.logLevel);"http"===r.type?await T(e,o,r.port,r.host,n.onShutdown):await x(e,o,n.onShutdown)}}}function S(e){let n;const t=t=>{n??=e(t).catch(()=>{process.exit(1)})};process.once("SIGTERM",()=>{t("SIGTERM")}),process.once("SIGINT",()=>{t("SIGINT")})}async function x(e,n,o){const s=new t({name:e.name,version:"0.0.1"}),i=g(e.name,n,s);for(const n of e.tools)$(s,n,i);const a=new r;await s.connect(a),i.logger.info("MCP Server running on stdio");S(async e=>{i.logger.info(`${e} received, shutting down...`),o&&await o(),await s.close(),process.exit(0)})}async function T(r,s,a,c,f){const d=i(r.name,s),p=new Map,l=e(async(e,i)=>{if("/mcp"!==new URL(e.url??"/",`http://${e.headers.host??"localhost"}`).pathname)return i.writeHead(404,{"Content-Type":"application/json"}),void i.end(JSON.stringify({error:"Not found"}));const a="string"==typeof e.headers["mcp-session-id"]?e.headers["mcp-session-id"]:void 0;if(a){const n=p.get(a);if(n){try{await n.transport.handleRequest(e,i)}catch(e){d.error(`Error handling request for session ${a}: ${String(e)}`),i.headersSent||(i.writeHead(500,{"Content-Type":"application/json"}),i.end(JSON.stringify({error:"Internal server error"})))}return}return i.writeHead(404,{"Content-Type":"application/json"}),void i.end(JSON.stringify({error:"Session not found"}))}try{const a=new o({sessionIdGenerator:()=>n()}),c=new t({name:r.name,version:"0.0.1"}),f=g(r.name,s,c);for(const e of r.tools)$(c,e,f);a.onclose=()=>{const e=a.sessionId;e&&(p.delete(e),d.info(`Session ${e} closed`))},a.onerror=e=>{d.error(`Transport error (session ${a.sessionId??"unknown"}): ${e.message}`)},await c.connect(a),await a.handleRequest(e,i),a.sessionId&&p.set(a.sessionId,{transport:a,server:c})}catch(e){d.error(`Error creating new session: ${String(e)}`),i.headersSent||(i.writeHead(500,{"Content-Type":"application/json"}),i.end(JSON.stringify({error:"Failed to create session"})))}});l.on("error",e=>{d.error(`HTTP server error: ${e.message}`)});const u=c??"127.0.0.1";l.listen(a,u,()=>{d.info(`MCP Server running on http://${u}:${a}/mcp`)});S(async e=>{d.info(`${e} received, shutting down HTTP server...`),await new Promise(e=>{l.close(()=>e())});const n=[];for(const[,e]of p)n.push(e.server.close());await Promise.all(n),p.clear(),f&&await f(),process.exit(0)})}function $(e,n,t){e.registerTool(n.name,{description:n.description,inputSchema:f(n.input)},async e=>await E(n,t,e))}async function E(e,n,t){const r=await d(e.input,t);try{const t=await e.execute(r,n);return{content:[{type:"text",text:JSON.stringify(t)}]}}catch(e){if(u(e))return{isError:!0,content:[{type:"text",text:JSON.stringify(e.payload)}]};throw e}}function I(e){return e}export{l as StructuredToolError,i as createAgentLogger,v as defineAgent,I as defineTool,u as isStructuredToolError};
1
+ import{createServer as e}from"node:http";import{randomUUID as n}from"node:crypto";import{McpServer as t}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as r}from"@modelcontextprotocol/sdk/server/stdio.js";import{StreamableHTTPServerTransport as o}from"@modelcontextprotocol/sdk/server/streamableHttp.js";var s={debug:0,info:1,warn:2,error:3};function i(e,n="info"){const t=s[n],r=(n,r)=>{if(s[n]<t)return;const o=(new Date).toISOString(),i=n.toUpperCase().padEnd(5);console.error(`${o} [${i}] [${e}] ${r}`)};return{debug:e=>r("debug",e),info:e=>r("info",e),warn:e=>r("warn",e),error:e=>r("error",e)}}import{z as a}from"zod";var c=8;function f(e){let n=e;for(let e=0;e<c;e+=1)if(n instanceof a.ZodEffects)n=n.innerType();else if(n instanceof a.ZodOptional||n instanceof a.ZodNullable||n instanceof a.ZodBranded||n instanceof a.ZodReadonly)n=n.unwrap();else if(n instanceof a.ZodDefault)n=n.removeDefault();else{if(!(n instanceof a.ZodCatch))return n;n=n.removeCatch()}return n}async function d(e,n){const t=await e.safeParseAsync(n);if(!t.success)throw new Error(`Tool input validation failed: ${l(t.error)}`);return t.data}function l(e){return e.issues.map(e=>{const n=e.path.join(".");return n.length>0?`${n}: ${e.message}`:e.message}).join("; ")}var p=class extends Error{payload;constructor(e){super(e.message),this.name="StructuredToolError",this.payload=e}};function u(e){return e instanceof p||e instanceof Error&&"payload"in e&&y(e.payload)}function y(e){if("object"!=typeof e||null===e||Array.isArray(e))return!1;const n=e,t=n.details;return"string"==typeof n.code&&"string"==typeof n.message&&(void 0===t||"object"==typeof t&&null!==t&&!Array.isArray(t))}function m(e){return e??g()??"info"}function g(){const e=process.env.ROLL_AGENT_LOG_LEVEL;if("debug"===e||"info"===e||"warn"===e||"error"===e)return e}function w(e,n,t){return{llm:{generateText:async e=>{try{const n=h((await t.server.createMessage({messages:[{role:"user",content:{type:"text",text:e}}],maxTokens:1024})).content);if(void 0===n)throw new Error("Sampling response did not include text content");return n}catch(e){throw new Error("LLM sampling unavailable. Ensure roll-core client enables sampling capability.",{cause:e})}}},logger:i(e,n)}}function h(e){if("object"==typeof e&&null!==e&&"type"in e&&"text"===e.type&&"text"in e){const n=e.text;return"string"==typeof n?n:void 0}if(Array.isArray(e))for(const n of e){const e=h(n);if(void 0!==e)return e}}function v(e,n={}){return{...e,listen:async t=>{const r=t?.transport??{type:"stdio"},o=m(n.logLevel);"http"===r.type?await T(e,o,r.port,r.host,n.onShutdown):await x(e,o,n.onShutdown)}}}function S(e){let n;const t=t=>{n??=e(t).catch(()=>{process.exit(1)})};process.once("SIGTERM",()=>{t("SIGTERM")}),process.once("SIGINT",()=>{t("SIGINT")})}async function x(e,n,o){const s=new t({name:e.name,version:"0.0.1"}),i=w(e.name,n,s);for(const n of e.tools)$(s,n,i);const a=new r;await s.connect(a),i.logger.info("MCP Server running on stdio");S(async e=>{i.logger.info(`${e} received, shutting down...`),o&&await o(),await s.close(),process.exit(0)})}async function T(r,s,a,c,f){const d=i(r.name,s),l=new Map,p=e(async(e,i)=>{if("/mcp"!==new URL(e.url??"/",`http://${e.headers.host??"localhost"}`).pathname)return i.writeHead(404,{"Content-Type":"application/json"}),void i.end(JSON.stringify({error:"Not found"}));const a="string"==typeof e.headers["mcp-session-id"]?e.headers["mcp-session-id"]:void 0;if(a){const n=l.get(a);if(n){try{await n.transport.handleRequest(e,i)}catch(e){d.error(`Error handling request for session ${a}: ${String(e)}`),i.headersSent||(i.writeHead(500,{"Content-Type":"application/json"}),i.end(JSON.stringify({error:"Internal server error"})))}return}return i.writeHead(404,{"Content-Type":"application/json"}),void i.end(JSON.stringify({error:"Session not found"}))}try{const a=new o({sessionIdGenerator:()=>n()}),c=new t({name:r.name,version:"0.0.1"}),f=w(r.name,s,c);for(const e of r.tools)$(c,e,f);a.onclose=()=>{const e=a.sessionId;e&&(l.delete(e),d.info(`Session ${e} closed`))},a.onerror=e=>{d.error(`Transport error (session ${a.sessionId??"unknown"}): ${e.message}`)},await c.connect(a),await a.handleRequest(e,i),a.sessionId&&l.set(a.sessionId,{transport:a,server:c})}catch(e){d.error(`Error creating new session: ${String(e)}`),i.headersSent||(i.writeHead(500,{"Content-Type":"application/json"}),i.end(JSON.stringify({error:"Failed to create session"})))}});p.on("error",e=>{d.error(`HTTP server error: ${e.message}`)});const u=c??"127.0.0.1";p.listen(a,u,()=>{d.info(`MCP Server running on http://${u}:${a}/mcp`)});S(async e=>{d.info(`${e} received, shutting down HTTP server...`),await new Promise(e=>{p.close(()=>e())});const n=[];for(const[,e]of l)n.push(e.server.close());await Promise.all(n),l.clear(),f&&await f(),process.exit(0)})}function $(e,n,t){e.registerTool(n.name,{description:n.description,inputSchema:f(n.input)},async(e,r)=>await E(n,t,e,r.signal))}async function E(e,n,t,r){const o=await d(e.input,t),s=void 0===r?n:{...n,signal:r};try{const n=await e.execute(o,s);return{content:[{type:"text",text:JSON.stringify(n)}]}}catch(e){if(u(e))return{isError:!0,content:[{type:"text",text:JSON.stringify(e.payload)}]};throw e}}function I(e){return e}export{p as StructuredToolError,i as createAgentLogger,v as defineAgent,I as defineTool,u as isStructuredToolError};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roll-agent/sdk",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",