@roll-agent/sdk 0.1.5 → 0.2.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.
@@ -1,5 +1,5 @@
1
- import type { AgentDefinition, RunnableAgent } from "./types/index.ts";
2
- import type { LogLevel } from "./context.ts";
1
+ import type { AgentDefinition, RunnableAgent, AnyToolDefinition } from "./types/index.ts";
2
+ import type { AgentContext, LogLevel } from "./context.ts";
3
3
  /** defineAgent 额外选项 */
4
4
  export interface DefineAgentOptions {
5
5
  /** 最低日志级别,默认 "info" */
@@ -15,3 +15,14 @@ export interface DefineAgentOptions {
15
15
  * - http:启动 Streamable HTTP 服务,适合浏览器 Agent 等需要持久进程的场景
16
16
  */
17
17
  export declare function defineAgent(definition: AgentDefinition, options?: DefineAgentOptions): RunnableAgent;
18
+ type McpTextToolResult = {
19
+ isError?: true;
20
+ content: [
21
+ {
22
+ type: "text";
23
+ text: string;
24
+ }
25
+ ];
26
+ };
27
+ export declare function executeToolForMcp(tool: AnyToolDefinition, ctx: AgentContext, params: Record<string, unknown>): Promise<McpTextToolResult>;
28
+ export {};
package/dist/index.d.ts CHANGED
@@ -3,4 +3,6 @@ export type { DefineAgentOptions } from "./define-agent.ts";
3
3
  export { defineTool } from "./define-tool.ts";
4
4
  export { createAgentLogger } from "./context.ts";
5
5
  export type { AgentContext, AgentLogger, AgentLLM, LogLevel } from "./context.ts";
6
+ export { StructuredToolError, isStructuredToolError } from "./tool-error.ts";
7
+ export type { StructuredToolErrorPayload } from "./tool-error.ts";
6
8
  export type { AnyToolDefinition, ToolDefinition, AgentDefinition, RunnableAgent, TransportConfig, ListenOptions, } from "./types/index.ts";
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 o}from"@modelcontextprotocol/sdk/server/stdio.js";import{StreamableHTTPServerTransport as r}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import"zod";var s={debug:0,info:1,warn:2,error:3};function i(e,n="info"){const t=s[n],o=(n,o)=>{if(s[n]<t)return;const r=(new Date).toISOString(),i=n.toUpperCase().padEnd(5);console.error(`${r} [${i}] [${e}] ${o}`)};return{debug:e=>o("debug",e),info:e=>o("info",e),warn:e=>o("warn",e),error:e=>o("error",e)}}function a(e,n,t){return{llm:{generateText:async e=>{try{const n=c((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 c(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=c(n);if(void 0!==e)return e}}function p(e,n={}){return{...e,listen:async t=>{const o=t?.transport??{type:"stdio"},r=n.logLevel??"info";"http"===o.type?await f(e,r,o.port,o.host,n.onShutdown):await l(e,r,n.onShutdown)}}}function d(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 l(e,n,r){const s=new t({name:e.name,version:"0.0.1"}),i=a(e.name,n,s);for(const n of e.tools)u(s,n,i);const c=new o;await s.connect(c),i.logger.info("MCP Server running on stdio");d(async e=>{i.logger.info(`${e} received, shutting down...`),r&&await r(),await s.close(),process.exit(0)})}async function f(o,s,c,p,l){const f=i(o.name,s),m=new Map,w=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 c="string"==typeof e.headers["mcp-session-id"]?e.headers["mcp-session-id"]:void 0;if(c){const n=m.get(c);if(n){try{await n.transport.handleRequest(e,i)}catch(e){f.error(`Error handling request for session ${c}: ${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 c=new r({sessionIdGenerator:()=>n()}),p=new t({name:o.name,version:"0.0.1"}),d=a(o.name,s,p);for(const e of o.tools)u(p,e,d);c.onclose=()=>{const e=c.sessionId;e&&(m.delete(e),f.info(`Session ${e} closed`))},c.onerror=e=>{f.error(`Transport error (session ${c.sessionId??"unknown"}): ${e.message}`)},await p.connect(c),await c.handleRequest(e,i),c.sessionId&&m.set(c.sessionId,{transport:c,server:p})}catch(e){f.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"})))}});w.on("error",e=>{f.error(`HTTP server error: ${e.message}`)});const g=p??"127.0.0.1";w.listen(c,g,()=>{f.info(`MCP Server running on http://${g}:${c}/mcp`)});d(async e=>{f.info(`${e} received, shutting down HTTP server...`),await new Promise(e=>{w.close(()=>e())});const n=[];for(const[,e]of m)n.push(e.server.close());await Promise.all(n),m.clear(),l&&await l(),process.exit(0)})}function u(e,n,t){e.registerTool(n.name,{description:n.description,inputSchema:n.input},async e=>{const o=await n.execute(e,t);return{content:[{type:"text",text:JSON.stringify(o)}]}})}function m(e){return e}export{i as createAgentLogger,p as defineAgent,m as defineTool};
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 o}from"@modelcontextprotocol/sdk/server/stdio.js";import{StreamableHTTPServerTransport as r}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],o=(n,o)=>{if(s[n]<t)return;const r=(new Date).toISOString(),i=n.toUpperCase().padEnd(5);console.error(`${r} [${i}] [${e}] ${o}`)};return{debug:e=>o("debug",e),info:e=>o("info",e),warn:e=>o("warn",e),error:e=>o("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,n,t){return{llm:{generateText:async e=>{try{const n=w((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 w(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=w(n);if(void 0!==e)return e}}function g(e,n={}){return{...e,listen:async t=>{const o=t?.transport??{type:"stdio"},r=n.logLevel??"info";"http"===o.type?await S(e,r,o.port,o.host,n.onShutdown):await v(e,r,n.onShutdown)}}}function h(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 v(e,n,r){const s=new t({name:e.name,version:"0.0.1"}),i=m(e.name,n,s);for(const n of e.tools)x(s,n,i);const a=new o;await s.connect(a),i.logger.info("MCP Server running on stdio");h(async e=>{i.logger.info(`${e} received, shutting down...`),r&&await r(),await s.close(),process.exit(0)})}async function S(o,s,a,c,f){const d=i(o.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 r({sessionIdGenerator:()=>n()}),c=new t({name:o.name,version:"0.0.1"}),f=m(o.name,s,c);for(const e of o.tools)x(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`)});h(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 x(e,n,t){e.registerTool(n.name,{description:n.description,inputSchema:f(n.input)},async e=>await T(n,t,e))}async function T(e,n,t){const o=await d(e.input,t);try{const t=await e.execute(o,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 $(e){return e}export{l as StructuredToolError,i as createAgentLogger,g as defineAgent,$ as defineTool,u as isStructuredToolError};
@@ -0,0 +1,3 @@
1
+ import { z } from "zod";
2
+ export declare function getMcpCompatibleInputSchema(schema: z.ZodType): z.ZodTypeAny;
3
+ export declare function parseToolInput(schema: z.ZodType, input: unknown): Promise<unknown>;
@@ -0,0 +1,12 @@
1
+ export type StructuredToolErrorPayload = {
2
+ readonly code: string;
3
+ readonly message: string;
4
+ readonly details?: Readonly<Record<string, unknown>>;
5
+ };
6
+ export declare class StructuredToolError extends Error {
7
+ readonly payload: StructuredToolErrorPayload;
8
+ constructor(payload: StructuredToolErrorPayload);
9
+ }
10
+ export declare function isStructuredToolError(value: unknown): value is Error & {
11
+ readonly payload: StructuredToolErrorPayload;
12
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roll-agent/sdk",
3
- "version": "0.1.5",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",