@lynq/lynq 0.8.1 → 0.8.3

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.
@@ -0,0 +1 @@
1
+ var r=new WeakMap;function n(s,e){r.set(s,e);}function t(s){let e=r.get(s);if(!e)throw new Error("No internals registered for this server instance");return e}export{n as a,t as b};
@@ -0,0 +1 @@
1
+ import {createHmac,timingSafeEqual}from'crypto';import {normalizeObjectSchema}from'@modelcontextprotocol/sdk/server/zod-compat.js';import {toJsonSchemaCompat}from'@modelcontextprotocol/sdk/server/zod-json-schema-compat.js';function h(e){if(e==null)return {type:"object"};let t=normalizeObjectSchema(e);return t?toJsonSchemaCompat(t):e}function x(e,t){let o=t[t.length-1];if(typeof o!="function")throw new TypeError(`${e}: last argument must be a handler function`);let n=t[t.length-2];if(n==null||typeof n!="object"||Array.isArray(n))throw new TypeError(`${e}: second-to-last argument must be a config object`);let r=t.slice(0,-2);for(let s of r)if(!s||typeof s!="object"||typeof s.name!="string")throw new TypeError(`${e}: each middleware must have a "name" property`);return {middlewares:r,config:n,handler:o}}function T(e,t){let o=[];for(let n of t)n.onRegister?.(e)===false&&o.push(n.name);return o}function R(e){let o=e.split(/\{[^}]+\}/).map(n=>n.replace(/[.*+?^$|()[\]\\]/g,"\\$&"));return new RegExp(`^${o.join("([^/]+)")}$`)}function y(e,t,o,n){let r=o.get(t);if(r==="disabled")return false;if(r==="enabled")return true;for(let s of e)if(!n.has(s))return false;return true}function b(e,t){let o=e.get(t);if(o)return o;for(let n of e.values())if(n.isTemplate&&n.uriPattern?.test(t))return n}function $(e,t,o){let n=`${e}:${t}`,r=createHmac("sha256",o).update(n).digest("hex");return `${n}:${r}`}function C(e,t){if(e.length<66)return null;let o=e.slice(-64);if(e[e.length-65]!==":")return null;let n=e.slice(0,-65),r=n.indexOf(":");if(r<1)return null;let s=n.slice(0,r),i=n.slice(r+1);if(!i)return null;let l=createHmac("sha256",t).update(`${s}:${i}`).digest("hex");try{if(!timingSafeEqual(Buffer.from(o,"hex"),Buffer.from(l,"hex")))return null}catch{return null}return {sessionId:s,elicitationId:i}}function M(e,t){if(!e)return false;let o=e.replace(/:\d+$/,"");return t.includes(o)}var S=["localhost","127.0.0.1","::1"];function I(e,t,o){let n=e.filter(l=>l.onCall),r=e.filter(l=>l.onResult).reverse(),s=0,i=async()=>{if(s>=n.length){let a=await o();for(let c of r)a=await c.onResult(a,t);return a}return n[s++].onCall(t,i)};return i}export{h as a,x as b,T as c,R as d,y as e,b as f,$ as g,C as h,M as i,S as j,I as k};
@@ -0,0 +1 @@
1
+ function g(s){let r=s?.maxEntries??1e4,e=new Map;return {async get(n){let t=e.get(n);if(t){if(t.expiresAt!==void 0&&Date.now()>t.expiresAt){e.delete(n);return}return t.accessedAt=Date.now(),t.value}},async set(n,t,i){if(e.size>=r){let f=Date.now();for(let[o,u]of e)u.expiresAt!==void 0&&f>u.expiresAt&&e.delete(o);if(e.size>=r){let o,u=Number.POSITIVE_INFINITY;for(let[c,d]of e)d.accessedAt<u&&(u=d.accessedAt,o=c);o!==void 0&&e.delete(o);}}e.set(n,{value:t,expiresAt:i!==void 0?Date.now()+i*1e3:void 0,accessedAt:Date.now()});},async delete(n){e.delete(n);}}}function a(s){let r=s.get("user");if(r){if(typeof r=="string")return r;if(typeof r=="object"&&r!==null){let e=r;if(typeof e.id=="string")return e.id;if(typeof e.id=="number")return String(e.id);if(typeof e.sub=="string")return e.sub}}}function p(s,r){let e=()=>{let n=a(s);if(!n){let t=s.get("user");if(t){let i=typeof t=="object"?JSON.stringify(t):typeof t;throw new Error(`userStore: session has a "user" but could not resolve an ID. Expected: string | { id: string | number } | { sub: string }. Got: ${i}`)}throw new Error("userStore requires a user in session. Call session.set('user', ...) first.")}return n};return {async get(n){return r.get(`user:${e()}:${n}`)},async set(n,t,i){await r.set(`user:${e()}:${n}`,t,i);},async delete(n){await r.delete(`user:${e()}:${n}`);}}}export{g as a,a as b,p as c};
package/dist/helpers.mjs CHANGED
@@ -1 +1 @@
1
- export{j as LOCALHOST_HOSTS,g as signState,i as validateHost,h as verifyState}from'./chunk-3BJEUP3F.mjs';
1
+ export{j as LOCALHOST_HOSTS,g as signState,i as validateHost,h as verifyState}from'./chunk-OYEWQXJU.mjs';
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { S as ServerOptions, M as MCPServer } from './types-DyAOlot0.js';
2
- export { E as Elicit, a as ElicitFormResult, b as ElicitUrlOptions, c as ElicitUrlResult, H as HttpAdapterOptions, R as ResourceConfig, d as ResourceContent, e as ResourceContext, f as ResourceHandler, g as RootInfo, h as Sample, i as SampleOptions, j as SampleRawParams, k as SampleRawResult, l as ServerInfo, m as Session, n as Store, T as TaskConfig, o as TaskContext, p as TaskControl, q as TaskHandler, r as ToolConfig, s as ToolContext, t as ToolHandler, u as ToolInfo, v as ToolMiddleware, w as ToolResponse, U as User, x as UserStore, y as error, z as image, A as json, B as text } from './types-DyAOlot0.js';
3
- export { memoryStore } from './store.js';
1
+ import { S as ServerOptions, M as MCPServer } from './types-CB6NartK.js';
2
+ export { E as Elicit, a as ElicitFormResult, b as ElicitUrlOptions, c as ElicitUrlResult, H as HttpAdapterOptions, R as ResourceConfig, d as ResourceContent, e as ResourceContext, f as ResourceHandler, g as RootInfo, h as Sample, i as SampleOptions, j as SampleRawParams, k as SampleRawResult, l as ServerInfo, m as Session, n as Store, T as TaskConfig, o as TaskContext, p as TaskControl, q as TaskHandler, r as ToolConfig, s as ToolContext, t as ToolHandler, u as ToolInfo, v as ToolMiddleware, w as ToolResponse, U as User, x as UserStore, y as error, z as image, A as json, B as text } from './types-CB6NartK.js';
3
+ export { MemoryStoreOptions, memoryStore } from './store.js';
4
4
  import '@modelcontextprotocol/sdk/types.js';
5
5
  import 'zod';
6
6
 
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import {b as b$1,c,d,a as a$1,k,f,e}from'./chunk-3BJEUP3F.mjs';import {a,c as c$2}from'./chunk-ICQVA5UM.mjs';export{a as memoryStore}from'./chunk-ICQVA5UM.mjs';import {c as c$1,d as d$1,b as b$2,a as a$2}from'./chunk-VAAZWX4U.mjs';export{c as error,d as image,b as json,a as text}from'./chunk-VAAZWX4U.mjs';import {Server}from'@modelcontextprotocol/sdk/server/index.js';import {InMemoryTaskStore}from'@modelcontextprotocol/sdk/experimental/tasks';import {ListToolsRequestSchema,CallToolRequestSchema,ListResourcesRequestSchema,ListResourceTemplatesRequestSchema,ReadResourceRequestSchema}from'@modelcontextprotocol/sdk/types.js';function z(){let t=new Map;function e(){let r=Date.now();for(let[a,d]of t)r-d.createdAt>36e5&&t.delete(a);}function o(r,a,d){return e(),new Promise(p=>{let n;try{n=d.createElicitationCompletionNotifier(r);}catch{}t.set(r,{resolver:p,completionNotifier:n,createdAt:Date.now()});})}function s(r){e();let a=t.get(r);a&&(t.delete(r),a.completionNotifier&&a.completionNotifier().catch(()=>{}),a.resolver());}function i(r){let a=t.get(r);a&&(t.delete(r),a.resolver());}return {register:o,complete:s,cancel:i}}function K(t,e,o){return {async form(s,i){let r=a$1(i),a=await t.elicitInput({message:s,requestedSchema:r});return {action:a.action,content:a.content??{}}},async url(s,i,r){let a=r?.elicitationId??crypto.randomUUID(),d;r?.waitForCompletion&&e&&(d=e(a,t));let p=await t.elicitInput({mode:"url",message:s,url:i,elicitationId:a});if(p.action==="accept"&&d){let n=r?.timeout??3e5,g,v=new Promise((T,c)=>{g=setTimeout(()=>c(new Error("Elicitation timed out")),n);});try{await Promise.race([d,v]);}catch(T){throw o&&o(a),T}finally{clearTimeout(g);}}else d&&o&&o(a);return {action:p.action}}}}function L(t){return async()=>{try{return (await t.listRoots()).roots.map(o=>{let s={uri:o.uri};return o.name!==void 0&&(s.name=o.name),s})}catch{return []}}}function Q(t){async function e(s,i){let r={messages:[{role:"user",content:{type:"text",text:s}}],maxTokens:i?.maxTokens??1024};i?.model!==void 0&&(r.modelPreferences={hints:[{name:i.model}]}),i?.system!==void 0&&(r.systemPrompt=i.system),i?.temperature!==void 0&&(r.temperature=i.temperature),i?.stopSequences!==void 0&&(r.stopSequences=i.stopSequences);let d=(await t.createMessage(r)).content;return d.type==="text"?d.text:""}async function o(s){return t.createMessage(s)}return Object.assign(e,{raw:o})}function O(t,e,o,s,i,r,a,d,p){return {toolName:s,args:i,session:o,signal:r,sessionId:e,elicit:K(t,d,p),roots:L(t),sample:Q(t),text:a$2,json:b$2,error:c$1,image:d$1,store:a,userStore:c$2(o,a)}}function b(t,e){let o=t.sessions.get(e);if(!o&&(o={data:new Map,grants:new Set,toolOverrides:new Map,resourceOverrides:new Map},t.sessions.set(e,o),t.onSessionCreate))try{Promise.resolve(t.onSessionCreate(e)).catch(()=>{});}catch{}return o}function P(t,e$1,o){let s=b(t,o);return e(e$1.hiddenByMiddlewares,e$1.name,s.toolOverrides,s.grants)}function C(t,e$1,o){let s=b(t,o);return e(e$1.hiddenByMiddlewares,e$1.uri,s.resourceOverrides,s.grants)}function x(t,e$1,o){let s=b(t,o);return e(e$1.hiddenByMiddlewares,e$1.name,s.toolOverrides,s.grants)}function _(t,e,o){(o&&t.serverBySession.get(o)||e).sendToolListChanged().catch(()=>{});}function A(t,e,o){(o&&t.serverBySession.get(o)||e).sendResourceListChanged().catch(()=>{});}function w(t,e,o){let s=b(t,o);return {get(i){return s.data.get(i)},set(i,r){s.data.set(i,r);},authorize(i){s.grants.add(i),_(t,e,o),A(t,e,o);},revoke(i){s.grants.delete(i),_(t,e,o),A(t,e,o);},enableTools(...i){for(let r of i)s.toolOverrides.set(r,"enabled");_(t,e,o);},disableTools(...i){for(let r of i)s.toolOverrides.set(r,"disabled");_(t,e,o);},enableResources(...i){for(let r of i)s.resourceOverrides.set(r,"enabled");A(t,e,o);},disableResources(...i){for(let r of i)s.resourceOverrides.set(r,"disabled");A(t,e,o);}}}function F(){let t=new Set,e=new InMemoryTaskStore;return {taskStore:new Proxy(e,{get(s,i,r){return i==="updateTaskStatus"?async(a,d,...p)=>(d==="cancelled"&&t.add(a),s.updateTaskStatus.call(s,a,d,...p)):Reflect.get(s,i,r)}}),cancelledTaskIds:t}}function N(t,e,o,s,i){t.setRequestHandler(ListToolsRequestSchema,(r,a)=>{let d=a.sessionId??"default",p=[];for(let n of e.tools.values())P(e,n,d)&&p.push({name:n.name,description:n.description,inputSchema:a$1(n.input)});for(let n of e.tasks.values())x(e,n,d)&&p.push({name:n.name,description:n.description,inputSchema:a$1(n.input),execution:{taskSupport:"required"}});return {tools:p}}),t.setRequestHandler(CallToolRequestSchema,async(r,a)=>{let{name:d,arguments:p}=r.params,n=a.sessionId??"default",g=e.tools.get(d);if(g){if(!P(e,g,n))return c$1(`Tool not available: ${d}`);let T=p??{},c=O(t,n,w(e,o,n),d,T,a.signal,e.store,(m,f)=>s.register(m,n,f),s.cancel),u=()=>Promise.resolve(g.handler(T,c));return k(g.middlewares,c,u)()}let v=e.tasks.get(d);if(v){if(!x(e,v,n))return c$1(`Tool not available: ${d}`);let T=a.taskStore;if(!T)return c$1("Task store not available");let c=await T.createTask({pollInterval:1e3}),u=c.taskId,l={progress(S,k){if(i.has(u))return;let W=k?`${S}% ${k}`:`${S}%`;T.updateTaskStatus(u,"working",W).catch(()=>{});},get cancelled(){return i.has(u)}},m=p??{},f={...O(t,n,w(e,o,n),d,m,a.signal,e.store,(S,k)=>s.register(S,n,k),s.cancel),task:l},y=async()=>((async()=>{try{let S=await v.handler(m,f);i.has(u)||await T.storeTaskResult(u,"completed",S);}catch(S){if(!i.has(u)){let k=S instanceof Error?S.message:String(S);await T.storeTaskResult(u,"failed",c$1(k)).catch(()=>{});}}})(),{task:c});return k(v.middlewares,f,y)()}return c$1(`Unknown tool: ${d}`)}),t.setRequestHandler(ListResourcesRequestSchema,(r,a)=>{let d=a.sessionId??"default",p=[];for(let n of e.resources.values())!n.isTemplate&&C(e,n,d)&&p.push({uri:n.uri,name:n.name,description:n.description,mimeType:n.mimeType});return {resources:p}}),t.setRequestHandler(ListResourceTemplatesRequestSchema,(r,a)=>{let d=a.sessionId??"default",p=[];for(let n of e.resources.values())n.isTemplate&&C(e,n,d)&&p.push({uriTemplate:n.uri,name:n.name,description:n.description,mimeType:n.mimeType});return {resourceTemplates:p}}),t.setRequestHandler(ReadResourceRequestSchema,async(r,a)=>{let{uri:d}=r.params,p=f(e.resources,d);if(!p)throw new Error(`Unknown resource: ${d}`);let n=a.sessionId??"default";if(!C(e,p,n))throw new Error(`Resource not available: ${d}`);let g=w(e,o,n),v={uri:d,session:g,sessionId:n,roots:L(t),store:e.store,userStore:c$2(g,e.store)},T=O(t,n,g,p.uri,{},a.signal,e.store,(l,m)=>s.register(l,n,m),s.cancel),c=async()=>{let l=await p.handler(d,v);return {contents:[{uri:d,mimeType:l.mimeType??p.mimeType,...l.text!=null?{text:l.text}:{},...l.blob!=null?{blob:l.blob}:{}}]}};return k(p.middlewares,T,c)()});}function G(t,e,o,s,i,r,a){let d={tools:{listChanged:true},resources:{listChanged:true},tasks:{list:{},cancel:{},requests:{tools:{call:{}}}}};function p(){let n=new Server(t,{capabilities:d,taskStore:o});return a(n,e,s,i,r),n}return function(g){let v=null;async function T(){return v||(v=(await import('@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js')).WebStandardStreamableHTTPServerTransport),v}if(g?.sessionless)return async l=>{let m=await T(),f=p(),y=new m({sessionIdGenerator:void 0,enableJsonResponse:g?.enableJsonResponse});return await f.connect(y),y.handleRequest(l)};let c=new Map,u=false;return async l=>{!u&&e.onServerStart&&(u=true,Promise.resolve(e.onServerStart()).catch(()=>{}));let m=await T(),f=l.headers.get("mcp-session-id");if(f){let S=c.get(f);return S?(g?.onRequest&&await g.onRequest(l,f,w(e,s,f)),S.transport.handleRequest(l)):new Response(JSON.stringify({jsonrpc:"2.0",error:{code:-32e3,message:"Session not found"}}),{status:404,headers:{"Content-Type":"application/json"}})}let y=p(),h=new m({sessionIdGenerator:g?.sessionIdGenerator??(()=>crypto.randomUUID()),enableJsonResponse:g?.enableJsonResponse,onsessioninitialized:S=>{c.set(S,{server:y,transport:h}),e.serverBySession.set(S,y),g?.onRequest&&g.onRequest(l,S,w(e,s,S));},onsessionclosed:S=>{c.delete(S),e.serverBySession.delete(S),e.sessions.delete(S),e.onSessionDestroy&&Promise.resolve(e.onSessionDestroy(S)).catch(()=>{});}});return await y.connect(h),h.handleRequest(l)}}}function ne(t){let e={store:t.store??a(),globalMiddlewares:[],tools:new Map,resources:new Map,tasks:new Map,sessions:new Map,serverBySession:new Map,onServerStart:t.onServerStart,onSessionCreate:t.onSessionCreate,onSessionDestroy:t.onSessionDestroy},o=z(),{taskStore:s,cancelledTaskIds:i}=F(),r=new Server(t,{capabilities:{tools:{listChanged:true},resources:{listChanged:true},tasks:{list:{},cancel:{},requests:{tools:{call:{}}}}},taskStore:s});N(r,e,r,o,i),r.onclose=()=>{let c="default";if(e.sessions.delete(c),e.onSessionDestroy)try{Promise.resolve(e.onSessionDestroy(c)).catch(()=>{});}catch{}};function a$1(c){e.globalMiddlewares.push(c);}function d$1(...c$1){let u=c$1[0],l=b$1(`tool("${u}")`,c$1.slice(1));if(typeof l.config.name=="string")throw new TypeError(`tool("${u}"): second-to-last argument must be a config object`);let m=l.config,f=[...e.globalMiddlewares,...l.middlewares],y={name:u,description:m.description,middlewares:f};e.tools.set(u,{name:u,description:m.description,input:m.input,handler:l.handler,middlewares:f,hiddenByMiddlewares:c(y,f)});}function p(...c$1){let u=c$1[0],l=b$1(`resource("${u}")`,c$1.slice(1));if(typeof l.config.name!="string")throw new TypeError(`resource("${u}"): second-to-last argument must be a config object with a "name" property`);let m=l.config,f=[...e.globalMiddlewares,...l.middlewares],y={name:m.name,description:m.description,middlewares:f},h=u.includes("{");e.resources.set(u,{uri:u,isTemplate:h,uriPattern:h?d(u):null,name:m.name,description:m.description,mimeType:m.mimeType,handler:l.handler,middlewares:f,hiddenByMiddlewares:c(y,f)});}function n(...c$1){let u=c$1[0],l=b$1(`task("${u}")`,c$1.slice(1));if(typeof l.config.name=="string")throw new TypeError(`task("${u}"): second-to-last argument must be a config object`);let m=l.config,f=[...e.globalMiddlewares,...l.middlewares],y={name:u,description:m.description,middlewares:f};e.tasks.set(u,{name:u,description:m.description,input:m.input,handler:l.handler,middlewares:f,hiddenByMiddlewares:c(y,f)});}async function g(){let{StdioServerTransport:c}=await import('@modelcontextprotocol/sdk/server/stdio.js'),u=new c;await r.connect(u),e.onServerStart&&await Promise.resolve(e.onServerStart()).catch(()=>{});}async function v(c){await r.connect(c);}let T=G(t,e,s,r,o,i,N);return {use:a$1,tool:d$1,resource:p,task:n,stdio:g,http:T,session:c=>w(e,r,c),completeElicitation:o.complete,store:e.store,connect:v,_server:r,_getSession:c=>b(e,c),_isToolVisible(c,u){let l=e.tools.get(c);return l?P(e,l,u):false},_isResourceVisible(c,u){let l=e.resources.get(c);return l?C(e,l,u):false},_isTaskVisible(c,u){let l=e.tasks.get(c);return l?x(e,l,u):false},_createSessionAPI:c=>w(e,r,c)}}export{ne as createMCPServer};
1
+ import {a as a$1}from'./chunk-CI343GXC.mjs';import {b as b$1,c,d,a as a$2,k as k$1,f,e}from'./chunk-OYEWQXJU.mjs';import {a,c as c$1}from'./chunk-YIQZZYBJ.mjs';export{a as memoryStore}from'./chunk-YIQZZYBJ.mjs';import {d as d$1,c as c$2,b as b$2,a as a$3}from'./chunk-VAAZWX4U.mjs';export{c as error,d as image,b as json,a as text}from'./chunk-VAAZWX4U.mjs';import {Server}from'@modelcontextprotocol/sdk/server/index.js';import {InMemoryTaskStore}from'@modelcontextprotocol/sdk/experimental/tasks';import {ListToolsRequestSchema,CallToolRequestSchema,McpError,ErrorCode,ListResourcesRequestSchema,ListResourceTemplatesRequestSchema,ReadResourceRequestSchema}from'@modelcontextprotocol/sdk/types.js';function K(){let t=new Map;function e(){let o=Date.now();for(let[l,c]of t)o-c.createdAt>36e5&&t.delete(l);}function r(o,l,c){return e(),new Promise(m=>{let n;try{n=c.createElicitationCompletionNotifier(o);}catch{}t.set(o,{resolver:m,completionNotifier:n,createdAt:Date.now()});})}function s(o){e();let l=t.get(o);l&&(t.delete(o),l.completionNotifier&&l.completionNotifier().catch(()=>{}),l.resolver());}function a(o){let l=t.get(o);l&&(t.delete(o),l.resolver());}return {register:r,complete:s,cancel:a}}function Y(t,e,r){return {async form(s,a){let o=a$2(a),l=await t.elicitInput({message:s,requestedSchema:o});return {action:l.action,content:l.content??{}}},async url(s,a,o){let l=o?.elicitationId??crypto.randomUUID(),c;o?.waitForCompletion&&e&&(c=e(l,t));let m=await t.elicitInput({mode:"url",message:s,url:a,elicitationId:l});if(m.action==="accept"&&c){let n=o?.timeout??3e5,v,y=new Promise((S,T)=>{v=setTimeout(()=>T(new Error("Elicitation timed out")),n);});try{await Promise.race([c,y]);}catch(S){throw r&&r(l),S}finally{clearTimeout(v);}}else c&&r&&r(l);return {action:m.action}}}}function J(t){return async()=>{try{return (await t.listRoots()).roots.map(r=>{let s={uri:r.uri};return r.name!==void 0&&(s.name=r.name),s})}catch{return []}}}function Z(t){async function e(s,a){let o={messages:[{role:"user",content:{type:"text",text:s}}],maxTokens:a?.maxTokens??1024};a?.model!==void 0&&(o.modelPreferences={hints:[{name:a.model}]}),a?.system!==void 0&&(o.systemPrompt=a.system),a?.temperature!==void 0&&(o.temperature=a.temperature),a?.stopSequences!==void 0&&(o.stopSequences=a.stopSequences);let c=(await t.createMessage(o)).content;return c.type==="text"?c.text:""}async function r(s){return t.createMessage(s)}return Object.assign(e,{raw:r})}function D(t,e,r,s,a,o,l,c,m){return {toolName:s,args:a,session:r,signal:o,sessionId:e,elicit:Y(t,c,m),roots:J(t),sample:Z(t),text:a$3,json:b$2,error:c$2,image:d$1,store:l,userStore:c$1(r,l)}}function k(t,e){let r=t.sessions.get(e);if(!r&&(r={data:new Map,grants:new Set,toolOverrides:new Map,resourceOverrides:new Map},t.sessions.set(e,r),t.onSessionCreate))try{Promise.resolve(t.onSessionCreate(e)).catch(()=>{});}catch{}return r}function q(t,e$1,r){let s=k(t,r);return e(e$1.hiddenByMiddlewares,e$1.name,s.toolOverrides,s.grants)}function b(t,e$1,r){let s=k(t,r);return e(e$1.hiddenByMiddlewares,e$1.uri,s.resourceOverrides,s.grants)}function E(t,e$1,r){let s=k(t,r);return e(e$1.hiddenByMiddlewares,e$1.name,s.toolOverrides,s.grants)}function U(t,e,r){(r&&t.serverBySession.get(r)||e).sendToolListChanged().catch(()=>{});}function $(t,e,r){(r&&t.serverBySession.get(r)||e).sendResourceListChanged().catch(()=>{});}function R(t,e,r){let s=k(t,r);return {get(a){return s.data.get(a)},set(a,o){s.data.set(a,o);},authorize(a){s.grants.add(a),U(t,e,r),$(t,e,r);},revoke(a){s.grants.delete(a),U(t,e,r),$(t,e,r);},enableTools(...a){for(let o of a)s.toolOverrides.set(o,"enabled");U(t,e,r);},disableTools(...a){for(let o of a)s.toolOverrides.set(o,"disabled");U(t,e,r);},enableResources(...a){for(let o of a)s.resourceOverrides.set(o,"enabled");$(t,e,r);},disableResources(...a){for(let o of a)s.resourceOverrides.set(o,"disabled");$(t,e,r);}}}function Q(){let t=new Set,e=new InMemoryTaskStore;return {taskStore:new Proxy(e,{get(s,a,o){return a==="updateTaskStatus"?async(l,c,...m)=>(c==="cancelled"&&t.add(l),s.updateTaskStatus.call(s,l,c,...m)):Reflect.get(s,a,o)}}),cancelledTaskIds:t}}function F(t,e,r,s,a){t.setRequestHandler(ListToolsRequestSchema,(o,l)=>{let c=l.sessionId??"default",m=[];for(let n of e.tools.values())q(e,n,c)&&m.push({name:n.name,description:n.description,inputSchema:a$2(n.input)});for(let n of e.tasks.values())E(e,n,c)&&m.push({name:n.name,description:n.description,inputSchema:a$2(n.input),execution:{taskSupport:"required"}});return {tools:m}}),t.setRequestHandler(CallToolRequestSchema,async(o,l)=>{let{name:c,arguments:m}=o.params,n=l.sessionId??"default",v=e.tools.get(c);if(v){if(!q(e,v,n))throw new McpError(ErrorCode.MethodNotFound,`Tool not available: ${c}`);let S=m??{},T=D(t,n,R(e,r,n),c,S,l.signal,e.store,(u,f)=>s.register(u,n,f),s.cancel),d=()=>Promise.resolve(v.handler(S,T));return k$1(v.middlewares,T,d)()}let y=e.tasks.get(c);if(y){if(!E(e,y,n))throw new McpError(ErrorCode.MethodNotFound,`Tool not available: ${c}`);let S=l.taskStore;if(!S)throw new McpError(ErrorCode.InternalError,"Task store not available");let T=await S.createTask({pollInterval:1e3}),d=T.taskId,i={progress(p,w){if(a.has(d))return;let P=w?`${p}% ${w}`:`${p}%`;S.updateTaskStatus(d,"working",P).catch(()=>{});},get cancelled(){return a.has(d)}},u=m??{},f={...D(t,n,R(e,r,n),c,u,l.signal,e.store,(p,w)=>s.register(p,n,w),s.cancel),task:i},g=async()=>{let p=(async()=>{try{let w=await y.handler(u,f);a.has(d)||await S.storeTaskResult(d,"completed",w);}catch(w){if(!a.has(d)){let P=w instanceof Error?w.message:String(w);await S.storeTaskResult(d,"failed",c$2(P)).catch(()=>{});}}})();return e.runningTasks.add(p),p.finally(()=>e.runningTasks.delete(p)),{task:T}};return k$1(y.middlewares,f,g)()}throw new McpError(ErrorCode.MethodNotFound,`Unknown tool: ${c}`)}),t.setRequestHandler(ListResourcesRequestSchema,(o,l)=>{let c=l.sessionId??"default",m=[];for(let n of e.resources.values())!n.isTemplate&&b(e,n,c)&&m.push({uri:n.uri,name:n.name,description:n.description,mimeType:n.mimeType});return {resources:m}}),t.setRequestHandler(ListResourceTemplatesRequestSchema,(o,l)=>{let c=l.sessionId??"default",m=[];for(let n of e.resources.values())n.isTemplate&&b(e,n,c)&&m.push({uriTemplate:n.uri,name:n.name,description:n.description,mimeType:n.mimeType});return {resourceTemplates:m}}),t.setRequestHandler(ReadResourceRequestSchema,async(o,l)=>{let{uri:c}=o.params,m=f(e.resources,c);if(!m)throw new McpError(ErrorCode.InvalidRequest,`Unknown resource: ${c}`);let n=l.sessionId??"default";if(!b(e,m,n))throw new McpError(ErrorCode.InvalidRequest,`Resource not available: ${c}`);let v=R(e,r,n),y={uri:c,session:v,sessionId:n,roots:J(t),store:e.store,userStore:c$1(v,e.store)},S=D(t,n,v,m.uri,{},l.signal,e.store,(i,u)=>s.register(i,n,u),s.cancel),T=async()=>{let i=await m.handler(c,y);return {contents:[{uri:c,mimeType:i.mimeType??m.mimeType,...i.text!=null?{text:i.text}:{},...i.blob!=null?{blob:i.blob}:{}}]}};return k$1(m.middlewares,S,T)()});}function X(t,e,r,s,a,o,l){let c=null;async function m(){return c||(c=(await import('@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js')).WebStandardStreamableHTTPServerTransport),c}let n={tools:{listChanged:true},resources:{listChanged:true},tasks:{list:{},cancel:{},requests:{tools:{call:{}}}}};function v(){let y=new Server(t,{capabilities:n,taskStore:r});return l(y,e,s,a,o),y}return function(S){if(S?.sessionless)return async i=>{let u=await m(),f=v(),g=new u({sessionIdGenerator:void 0,enableJsonResponse:S?.enableJsonResponse});return await f.connect(g),g.handleRequest(i)};let T=new Map,d=false;return async i=>{!d&&e.onServerStart&&(d=true,Promise.resolve(e.onServerStart()).catch(()=>{}));let u=await m(),f=i.headers.get("mcp-session-id");if(f){let p=T.get(f);return p?(S?.onRequest&&await S.onRequest(i,f,R(e,s,f)),p.transport.handleRequest(i)):new Response(JSON.stringify({jsonrpc:"2.0",error:{code:-32e3,message:"Session not found"}}),{status:404,headers:{"Content-Type":"application/json"}})}let g=v(),h=new u({sessionIdGenerator:S?.sessionIdGenerator??(()=>crypto.randomUUID()),enableJsonResponse:S?.enableJsonResponse,onsessioninitialized:p=>{T.set(p,{server:g,transport:h}),e.serverBySession.set(p,g),S?.onRequest&&S.onRequest(i,p,R(e,s,p));},onsessionclosed:p=>{T.delete(p),e.serverBySession.delete(p);let w=e.sessions.get(p)?.data??new Map;e.sessions.delete(p),e.onSessionDestroy&&Promise.resolve(e.onSessionDestroy(p,w)).catch(()=>{});}});return await g.connect(h),g.onclose=()=>{for(let[p,w]of T)if(w.server===g){T.delete(p),e.serverBySession.delete(p);let P=e.sessions.get(p)?.data??new Map;e.sessions.delete(p),e.onSessionDestroy&&Promise.resolve(e.onSessionDestroy(p,P)).catch(()=>{});break}},h.handleRequest(i)}}}function ce(t){let e={store:t.store??a(),globalMiddlewares:[],tools:new Map,resources:new Map,tasks:new Map,sessions:new Map,serverBySession:new Map,onServerStart:t.onServerStart,onSessionCreate:t.onSessionCreate,onSessionDestroy:t.onSessionDestroy,runningTasks:new Set},r=K(),{taskStore:s,cancelledTaskIds:a$2}=Q(),o=new Server(t,{capabilities:{tools:{listChanged:true},resources:{listChanged:true},tasks:{list:{},cancel:{},requests:{tools:{call:{}}}}},taskStore:s});F(o,e,o,r,a$2),o.onclose=()=>{let d="default",i=e.sessions.get(d)?.data??new Map;if(e.sessions.delete(d),e.onSessionDestroy)try{Promise.resolve(e.onSessionDestroy(d,i)).catch(()=>{});}catch{}};function l(d){e.globalMiddlewares.push(d);}function c$1(...d){let i=d[0],u=b$1(`tool("${i}")`,d.slice(1));if(typeof u.config.name=="string")throw new TypeError(`tool("${i}"): second-to-last argument must be a config object`);let f=u.config,g=[...e.globalMiddlewares,...u.middlewares],h={name:i,description:f.description,middlewares:g};e.tools.set(i,{name:i,description:f.description,input:f.input,handler:u.handler,middlewares:g,hiddenByMiddlewares:c(h,g)});}function m(...d$1){let i=d$1[0],u=b$1(`resource("${i}")`,d$1.slice(1));if(typeof u.config.name!="string")throw new TypeError(`resource("${i}"): second-to-last argument must be a config object with a "name" property`);let f=u.config,g=[...e.globalMiddlewares,...u.middlewares],h={name:f.name,description:f.description,middlewares:g},p=i.includes("{");e.resources.set(i,{uri:i,isTemplate:p,uriPattern:p?d(i):null,name:f.name,description:f.description,mimeType:f.mimeType,handler:u.handler,middlewares:g,hiddenByMiddlewares:c(h,g)});}function n(...d){let i=d[0],u=b$1(`task("${i}")`,d.slice(1));if(typeof u.config.name=="string")throw new TypeError(`task("${i}"): second-to-last argument must be a config object`);let f=u.config,g=[...e.globalMiddlewares,...u.middlewares],h={name:i,description:f.description,middlewares:g};e.tasks.set(i,{name:i,description:f.description,input:f.input,handler:u.handler,middlewares:g,hiddenByMiddlewares:c(h,g)});}async function v(){let{StdioServerTransport:d}=await import('@modelcontextprotocol/sdk/server/stdio.js'),i=new d;await o.connect(i),e.onServerStart&&await Promise.resolve(e.onServerStart()).catch(()=>{});}let y=X(t,e,s,o,r,a$2,F);async function S(){await Promise.allSettled(e.runningTasks);}let T={use:l,tool:c$1,resource:m,task:n,stdio:v,http:y,drain:S,session:d=>R(e,o,d),completeElicitation:r.complete,store:e.store};return a$1(T,{server:o,getSession:d=>k(e,d),isToolVisible(d,i){let u=e.tools.get(d);return u?q(e,u,i):false},isResourceVisible(d,i){let u=e.resources.get(d);return u?b(e,u,i):false},isTaskVisible(d,i){let u=e.tasks.get(d);return u?E(e,u,i):false},createSessionAPI:d=>R(e,o,d)}),T}export{ce as createMCPServer};
@@ -1,4 +1,4 @@
1
- import { v as ToolMiddleware } from '../types-DyAOlot0.js';
1
+ import { v as ToolMiddleware } from '../types-CB6NartK.js';
2
2
  import { GuardOptions } from './guard.js';
3
3
  import '@modelcontextprotocol/sdk/types.js';
4
4
  import 'zod';
@@ -1,4 +1,4 @@
1
- import { v as ToolMiddleware } from '../types-DyAOlot0.js';
1
+ import { v as ToolMiddleware } from '../types-CB6NartK.js';
2
2
  import '@modelcontextprotocol/sdk/types.js';
3
3
  import 'zod';
4
4
 
@@ -1,11 +1,11 @@
1
- import { v as ToolMiddleware } from '../types-DyAOlot0.js';
1
+ import { v as ToolMiddleware } from '../types-CB6NartK.js';
2
2
  import '@modelcontextprotocol/sdk/types.js';
3
3
  import 'zod';
4
4
 
5
5
  interface CacheOptions {
6
6
  /** TTL in seconds. */
7
7
  ttl: number;
8
- /** Custom cache key builder. Default: `cache:${toolName}:${JSON.stringify(args)}`. */
8
+ /** Custom cache key builder. Default: `cache:${toolName}:${stableStringify(args)}`. */
9
9
  key?: (toolName: string, args: Record<string, unknown>) => string;
10
10
  }
11
11
  declare function cache(options: CacheOptions): ToolMiddleware;
@@ -1 +1 @@
1
- function l(r){let{ttl:a}=r,n=r.key??((t,e)=>`cache:${t}:${JSON.stringify(e)}`);return {name:"cache",async onCall(t,e){let o=n(t.toolName,t.args),s=await t.store.get(o);return s||e()},async onResult(t,e){if(!t.isError){let o=n(e.toolName,e.args);await e.store.set(o,t,a);}return t}}}export{l as cache};
1
+ function i(n){if(n===null||typeof n!="object")return JSON.stringify(n);if(Array.isArray(n))return `[${n.map(i).join(",")}]`;let r=n;return `{${Object.keys(r).sort().map(t=>`${JSON.stringify(t)}:${i(r[t])}`).join(",")}}`}function c(n){let{ttl:r}=n,e=n.key??((t,o)=>`cache:${t}:${i(o)}`);return {name:"cache",async onCall(t,o){let s=e(t.toolName,t.args),a=await t.store.get(s);return a||o()},async onResult(t,o){if(!t.isError){let s=e(o.toolName,o.args);await o.store.set(s,t,r);}return t}}}export{c as cache};
@@ -1,4 +1,4 @@
1
- import { v as ToolMiddleware, s as ToolContext } from '../types-DyAOlot0.js';
1
+ import { v as ToolMiddleware, s as ToolContext } from '../types-CB6NartK.js';
2
2
  import '@modelcontextprotocol/sdk/types.js';
3
3
  import 'zod';
4
4
 
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { v as ToolMiddleware } from '../types-DyAOlot0.js';
2
+ import { v as ToolMiddleware } from '../types-CB6NartK.js';
3
3
  import '@modelcontextprotocol/sdk/types.js';
4
4
 
5
5
  interface CredentialsOptions<T extends z.ZodObject<z.ZodRawShape>> {
@@ -1,4 +1,4 @@
1
- import { v as ToolMiddleware } from '../types-DyAOlot0.js';
1
+ import { v as ToolMiddleware } from '../types-CB6NartK.js';
2
2
  import '@modelcontextprotocol/sdk/types.js';
3
3
  import 'zod';
4
4
 
@@ -1,4 +1,4 @@
1
- import { v as ToolMiddleware } from '../types-DyAOlot0.js';
1
+ import { v as ToolMiddleware } from '../types-CB6NartK.js';
2
2
  import '@modelcontextprotocol/sdk/types.js';
3
3
  import 'zod';
4
4
 
@@ -1,4 +1,4 @@
1
- import { v as ToolMiddleware } from '../types-DyAOlot0.js';
1
+ import { v as ToolMiddleware } from '../types-CB6NartK.js';
2
2
  import '@modelcontextprotocol/sdk/types.js';
3
3
  import 'zod';
4
4
 
@@ -1,4 +1,4 @@
1
- import { s as ToolContext, v as ToolMiddleware } from '../types-DyAOlot0.js';
1
+ import { s as ToolContext, v as ToolMiddleware } from '../types-CB6NartK.js';
2
2
  import '@modelcontextprotocol/sdk/types.js';
3
3
  import 'zod';
4
4
 
@@ -1,4 +1,4 @@
1
- import { s as ToolContext, v as ToolMiddleware } from '../types-DyAOlot0.js';
1
+ import { s as ToolContext, v as ToolMiddleware } from '../types-CB6NartK.js';
2
2
  import '@modelcontextprotocol/sdk/types.js';
3
3
  import 'zod';
4
4
 
@@ -1,4 +1,4 @@
1
- import { v as ToolMiddleware } from '../types-DyAOlot0.js';
1
+ import { v as ToolMiddleware } from '../types-CB6NartK.js';
2
2
  import '@modelcontextprotocol/sdk/types.js';
3
3
  import 'zod';
4
4
 
@@ -9,11 +9,31 @@ interface RateLimitOptions {
9
9
  windowMs?: number;
10
10
  /** Error message. */
11
11
  message?: string;
12
- /** Use persistent Store for distributed rate limiting. Default: false (session-scoped). */
12
+ /**
13
+ * Use persistent Store for distributed rate limiting.
14
+ * Default: `false` (session-scoped — each session gets its own counter,
15
+ * so a client can bypass the limit by reconnecting).
16
+ * Set to `true` for production rate limiting across sessions.
17
+ */
13
18
  store?: boolean;
14
- /** Scope rate limiting per user (implies `store: true`). Default: false. */
19
+ /**
20
+ * Scope rate limiting per user (implies `store: true`).
21
+ * Default: `false`.
22
+ *
23
+ * **Warning:** Users without `session.set("user", ...)` all share a single
24
+ * `"anon"` bucket. Ensure the user is set in session before rate-limited calls
25
+ * if per-user isolation is required.
26
+ */
15
27
  perUser?: boolean;
16
28
  }
29
+ /**
30
+ * Rate limiting middleware.
31
+ *
32
+ * **Default behavior is session-scoped** — each MCP session gets its own counter.
33
+ * A client can bypass the limit by creating a new session (reconnecting).
34
+ * For production use, set `store: true` to share counters across sessions,
35
+ * or `perUser: true` to scope limits per authenticated user.
36
+ */
17
37
  declare function rateLimit(options: RateLimitOptions): ToolMiddleware;
18
38
 
19
39
  export { type RateLimitOptions, rateLimit };
@@ -1 +1 @@
1
- import {b}from'../chunk-ICQVA5UM.mjs';import {c}from'../chunk-VAAZWX4U.mjs';function g(t){let{max:a,windowMs:o=6e4}=t,w=t.store===true||t.perUser===true,d=t.perUser===true,l=t.message??`Rate limit exceeded. Max ${a} calls per ${o/1e3}s.`,f=Math.ceil(o/1e3);return {name:"rateLimit",async onCall(e,n){let i=Date.now();if(w){let u=d?`rateLimit:${b(e.session)??"anon"}:${e.toolName}`:`rateLimit:${e.toolName}`,s=await e.store.get(u);return !s||i>=s.resetAt?(await e.store.set(u,{count:1,resetAt:i+o},f),n()):s.count>=a?c(l):(await e.store.set(u,{...s,count:s.count+1},f),n())}let m=`rateLimit:${e.toolName}`,r=e.session.get(m);return !r||i>=r.resetAt?(e.session.set(m,{count:1,resetAt:i+o}),n()):r.count>=a?c(l):(e.session.set(m,{...r,count:r.count+1}),n())}}}export{g as rateLimit};
1
+ import {b}from'../chunk-YIQZZYBJ.mjs';import {c}from'../chunk-VAAZWX4U.mjs';function g(t){let{max:a,windowMs:o=6e4}=t,w=t.store===true||t.perUser===true,d=t.perUser===true,l=t.message??`Rate limit exceeded. Max ${a} calls per ${o/1e3}s.`,f=Math.ceil(o/1e3);return {name:"rateLimit",async onCall(e,n){let i=Date.now();if(w){let u=d?`rateLimit:${b(e.session)??"anon"}:${e.toolName}`:`rateLimit:${e.toolName}`,s=await e.store.get(u);return !s||i>=s.resetAt?(await e.store.set(u,{count:1,resetAt:i+o},f),n()):s.count>=a?c(l):(await e.store.set(u,{...s,count:s.count+1},f),n())}let m=`rateLimit:${e.toolName}`,r=e.session.get(m);return !r||i>=r.resetAt?(e.session.set(m,{count:1,resetAt:i+o}),n()):r.count>=a?c(l):(e.session.set(m,{...r,count:r.count+1}),n())}}}export{g as rateLimit};
@@ -1,5 +1,5 @@
1
1
  import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
- import { v as ToolMiddleware } from '../types-DyAOlot0.js';
2
+ import { v as ToolMiddleware } from '../types-CB6NartK.js';
3
3
  import 'zod';
4
4
 
5
5
  interface RetryOptions {
@@ -1,4 +1,4 @@
1
- import { v as ToolMiddleware } from '../types-DyAOlot0.js';
1
+ import { v as ToolMiddleware } from '../types-CB6NartK.js';
2
2
  import '@modelcontextprotocol/sdk/types.js';
3
3
  import 'zod';
4
4
 
@@ -1,4 +1,4 @@
1
- import { v as ToolMiddleware } from '../types-DyAOlot0.js';
1
+ import { v as ToolMiddleware } from '../types-CB6NartK.js';
2
2
  import '@modelcontextprotocol/sdk/types.js';
3
3
  import 'zod';
4
4
 
@@ -1,4 +1,4 @@
1
- import { s as ToolContext, v as ToolMiddleware } from '../types-DyAOlot0.js';
1
+ import { s as ToolContext, v as ToolMiddleware } from '../types-CB6NartK.js';
2
2
  import '@modelcontextprotocol/sdk/types.js';
3
3
  import 'zod';
4
4
 
package/dist/store.d.ts CHANGED
@@ -1,9 +1,13 @@
1
- import { n as Store, m as Session, x as UserStore } from './types-DyAOlot0.js';
1
+ import { n as Store, m as Session, x as UserStore } from './types-CB6NartK.js';
2
2
  import '@modelcontextprotocol/sdk/types.js';
3
3
  import 'zod';
4
4
 
5
- declare function memoryStore(): Store;
5
+ interface MemoryStoreOptions {
6
+ /** Maximum number of entries. When exceeded, expired entries are swept first, then least-recently-accessed entries are evicted. Default: 10000. */
7
+ maxEntries?: number;
8
+ }
9
+ declare function memoryStore(options?: MemoryStoreOptions): Store;
6
10
  declare function resolveUserId(session: Session): string | undefined;
7
11
  declare function createUserStore(session: Session, store: Store): UserStore;
8
12
 
9
- export { createUserStore, memoryStore, resolveUserId };
13
+ export { type MemoryStoreOptions, createUserStore, memoryStore, resolveUserId };
package/dist/store.mjs CHANGED
@@ -1 +1 @@
1
- export{c as createUserStore,a as memoryStore,b as resolveUserId}from'./chunk-ICQVA5UM.mjs';
1
+ export{c as createUserStore,a as memoryStore,b as resolveUserId}from'./chunk-YIQZZYBJ.mjs';
package/dist/test.d.ts CHANGED
@@ -1,7 +1,25 @@
1
1
  import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
- import { m as Session, M as MCPServer } from './types-DyAOlot0.js';
2
+ import { m as Session, M as MCPServer } from './types-CB6NartK.js';
3
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
4
  import 'zod';
4
5
 
6
+ interface SessionState {
7
+ data: Map<string, unknown>;
8
+ grants: Set<string>;
9
+ toolOverrides: Map<string, "enabled" | "disabled">;
10
+ resourceOverrides: Map<string, "enabled" | "disabled">;
11
+ }
12
+
13
+ interface InternalAccess {
14
+ server: Server;
15
+ getSession(sessionId: string): SessionState;
16
+ isToolVisible(toolName: string, sessionId: string): boolean;
17
+ isResourceVisible(uri: string, sessionId: string): boolean;
18
+ isTaskVisible(taskName: string, sessionId: string): boolean;
19
+ createSessionAPI(sessionId: string): Session;
20
+ }
21
+ declare function getInternals(server: MCPServer): InternalAccess;
22
+
5
23
  interface TestClient {
6
24
  /** List visible tool names. */
7
25
  listTools(): Promise<string[]>;
@@ -36,4 +54,4 @@ declare const matchers: {
36
54
  };
37
55
  };
38
56
 
39
- export { type TestClient, createTestClient, matchers };
57
+ export { type InternalAccess, type TestClient, createTestClient, getInternals, matchers };
package/dist/test.mjs CHANGED
@@ -1 +1 @@
1
- async function d(n){let{Client:r}=await import('@modelcontextprotocol/sdk/client/index.js'),{InMemoryTransport:c}=await import('@modelcontextprotocol/sdk/inMemory.js'),[o,a]=c.createLinkedPair(),t=new r({name:"lynq-test",version:"1.0.0"},{capabilities:{tasks:{}}}),u=n;await Promise.all([u._server.connect(a),t.connect(o)]);let l=u._createSessionAPI("default");return {async listTools(){return (await t.listTools()).tools.map(s=>s.name)},async callTool(e,s={}){return t.callTool({name:e,arguments:s})},async callToolText(e,s={}){let i=await t.callTool({name:e,arguments:s});if(i.isError){let p=i.content.find(g=>g.type==="text")?.text??"Unknown error";throw new Error(p)}return i.content.find(m=>m.type==="text")?.text??""},async listResources(){return (await t.listResources()).resources.map(s=>s.uri)},async listResourceTemplates(){return (await t.listResourceTemplates()).resourceTemplates.map(s=>s.uriTemplate)},async readResource(e){return (await t.readResource({uri:e})).contents[0]?.text??""},authorize(e){l.authorize(e);},revoke(e){l.revoke(e);},session:l,async close(){await t.close();}}}var y={toHaveTextContent(n,r){let o=n.content.filter(t=>t.type==="text").map(t=>t.text??""),a=o.some(t=>t.includes(r));return {pass:a,message:()=>a?`Expected result not to contain "${r}"`:`Expected result to contain "${r}", got: ${o.join(", ")}`}},toBeError(n){let r=n.isError===true;return {pass:r,message:()=>r?"Expected result not to be an error":"Expected result to be an error (isError: true)"}}};export{d as createTestClient,y as matchers};
1
+ import {b}from'./chunk-CI343GXC.mjs';export{b as getInternals}from'./chunk-CI343GXC.mjs';async function w(o){let{Client:r}=await import('@modelcontextprotocol/sdk/client/index.js'),{InMemoryTransport:u}=await import('@modelcontextprotocol/sdk/inMemory.js'),[n,a]=u.createLinkedPair(),t=new r({name:"lynq-test",version:"1.0.0"},{capabilities:{tasks:{}}}),m=b(o);await Promise.all([m.server.connect(a),t.connect(n)]);let l=m.createSessionAPI("default");return {async listTools(){return (await t.listTools()).tools.map(s=>s.name)},async callTool(e,s={}){return t.callTool({name:e,arguments:s})},async callToolText(e,s={}){let i=await t.callTool({name:e,arguments:s});if(i.isError){let g=i.content.find(T=>T.type==="text")?.text??"Unknown error";throw new Error(g)}return i.content.find(p=>p.type==="text")?.text??""},async listResources(){return (await t.listResources()).resources.map(s=>s.uri)},async listResourceTemplates(){return (await t.listResourceTemplates()).resourceTemplates.map(s=>s.uriTemplate)},async readResource(e){return (await t.readResource({uri:e})).contents[0]?.text??""},authorize(e){l.authorize(e);},revoke(e){l.revoke(e);},session:l,async close(){await t.close();}}}var R={toHaveTextContent(o,r){let n=o.content.filter(t=>t.type==="text").map(t=>t.text??""),a=n.some(t=>t.includes(r));return {pass:a,message:()=>a?`Expected result not to contain "${r}"`:`Expected result to contain "${r}", got: ${n.join(", ")}`}},toBeError(o){let r=o.isError===true;return {pass:r,message:()=>r?"Expected result not to be an error":"Expected result to be an error (isError: true)"}}};export{w as createTestClient,R as matchers};
@@ -38,8 +38,8 @@ interface ServerOptions extends ServerInfo {
38
38
  onServerStart?: () => void | Promise<void>;
39
39
  /** Called when a new session is created. */
40
40
  onSessionCreate?: (sessionId: string) => void | Promise<void>;
41
- /** Called when a session is destroyed (HTTP session close or transport disconnect). */
42
- onSessionDestroy?: (sessionId: string) => void | Promise<void>;
41
+ /** Called when a session is destroyed (HTTP session close or transport disconnect). Session data is provided for cleanup (e.g. resolving user ID to delete store entries). */
42
+ onSessionDestroy?: (sessionId: string, data: ReadonlyMap<string, unknown>) => void | Promise<void>;
43
43
  }
44
44
  interface Session {
45
45
  /** Get a session-scoped value. */
@@ -142,7 +142,8 @@ interface ToolMiddleware {
142
142
  name: string;
143
143
  /** Called when a tool is registered. Return false to hide the tool initially. */
144
144
  onRegister?(tool: ToolInfo): boolean | undefined;
145
- /** Called when a tool is invoked. Must call next() to continue the chain. */
145
+ /** Called when a tool is invoked. Must call and return `await next()` to continue the chain.
146
+ * Calling next() without returning its result is undefined behavior. */
146
147
  onCall?(c: ToolContext, next: () => Promise<CallToolResult>): Promise<CallToolResult>;
147
148
  /** Called after the handler returns. Runs in reverse middleware order. */
148
149
  onResult?(result: CallToolResult, c: ToolContext): CallToolResult | Promise<CallToolResult>;
@@ -153,7 +154,7 @@ interface ToolConfig<TInput = unknown> {
153
154
  input?: TInput;
154
155
  }
155
156
  type ToolHandler<TInput = unknown> = (args: InferInput<TInput>, c: ToolContext) => CallToolResult | Promise<CallToolResult>;
156
- /** @experimental */
157
+ /** @experimental Running tasks are fire-and-forget — no graceful shutdown guarantee. */
157
158
  interface TaskConfig<TInput = unknown> {
158
159
  description?: string;
159
160
  input?: TInput;
@@ -193,14 +194,29 @@ interface ResourceContext {
193
194
  userStore: UserStore;
194
195
  }
195
196
  type ResourceHandler = (uri: string, c: ResourceContext) => ResourceContent | Promise<ResourceContent>;
197
+ /**
198
+ * Options for the HTTP adapter returned by `server.http()`.
199
+ *
200
+ * **Security note:** Session routing relies on the `Mcp-Session-Id` header.
201
+ * Session IDs are UUIDs (hard to guess) but can leak via logs or proxies.
202
+ * Use `onRequest` to add additional validation (e.g., Bearer token check,
203
+ * IP binding) or `sessionIdGenerator` to produce HMAC-signed session tokens.
204
+ */
196
205
  interface HttpAdapterOptions {
197
206
  /** Disable session management. Default: false. */
198
207
  sessionless?: boolean;
199
- /** Custom session ID generator. Default: crypto.randomUUID(). */
208
+ /**
209
+ * Custom session ID generator. Default: `crypto.randomUUID()`.
210
+ * Can be used to produce HMAC-signed session tokens for additional security.
211
+ */
200
212
  sessionIdGenerator?: () => string;
201
213
  /** Return JSON instead of SSE streams. Default: false. */
202
214
  enableJsonResponse?: boolean;
203
- /** Called on each HTTP request after session is resolved. Use to inject auth headers into sessions. */
215
+ /**
216
+ * Called on each HTTP request after session is resolved.
217
+ * Use to inject HTTP headers (e.g., Bearer tokens) into MCP sessions,
218
+ * or to validate that the requester owns the session (e.g., IP binding, token check).
219
+ */
204
220
  onRequest?: (req: Request, sessionId: string, session: Session) => void | Promise<void>;
205
221
  }
206
222
  interface MCPServer {
@@ -228,6 +244,8 @@ interface MCPServer {
228
244
  completeElicitation(elicitationId: string): void;
229
245
  /** The persistent store instance. */
230
246
  store: Store;
247
+ /** @experimental Wait for all running tasks to settle. For graceful shutdown. */
248
+ drain(): Promise<void>;
231
249
  }
232
250
 
233
251
  export { json as A, text as B, type Elicit as E, type HttpAdapterOptions as H, type MCPServer as M, type ResourceConfig as R, type ServerOptions as S, type TaskConfig as T, type User as U, type ElicitFormResult as a, type ElicitUrlOptions as b, type ElicitUrlResult as c, type ResourceContent as d, type ResourceContext as e, type ResourceHandler as f, type RootInfo as g, type Sample as h, type SampleOptions as i, type SampleRawParams as j, type SampleRawResult as k, type ServerInfo as l, type Session as m, type Store as n, type TaskContext as o, type TaskControl as p, type TaskHandler as q, type ToolConfig as r, type ToolContext as s, type ToolHandler as t, type ToolInfo as u, type ToolMiddleware as v, type ToolResponse as w, type UserStore as x, error as y, image as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lynq/lynq",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
4
4
  "description": "Lightweight MCP server framework. Tool visibility control through middleware.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -1 +0,0 @@
1
- import {createHmac,timingSafeEqual}from'crypto';import {normalizeObjectSchema}from'@modelcontextprotocol/sdk/server/zod-compat.js';import {toJsonSchemaCompat}from'@modelcontextprotocol/sdk/server/zod-json-schema-compat.js';function h(e){if(e==null)return {type:"object"};let t=normalizeObjectSchema(e);return t?toJsonSchemaCompat(t):e}function T(e,t){let o=t[t.length-1];if(typeof o!="function")throw new TypeError(`${e}: last argument must be a handler function`);let n=t[t.length-2];if(n==null||typeof n!="object"||Array.isArray(n))throw new TypeError(`${e}: second-to-last argument must be a config object`);let r=t.slice(0,-2);for(let s of r)if(!s||typeof s!="object"||typeof s.name!="string")throw new TypeError(`${e}: each middleware must have a "name" property`);return {middlewares:r,config:n,handler:o}}function R(e,t){let o=[];for(let n of t)n.onRegister?.(e)===false&&o.push(n.name);return o}function x(e){let o=e.split(/\{[^}]+\}/).map(n=>n.replace(/[.*+?^$|()[\]\\]/g,"\\$&"));return new RegExp(`^${o.join("(.+)")}$`)}function y(e,t,o,n){let r=o.get(t);if(r==="disabled")return false;if(r==="enabled")return true;for(let s of e)if(!n.has(s))return false;return true}function b(e,t){let o=e.get(t);if(o)return o;for(let n of e.values())if(n.isTemplate&&n.uriPattern?.test(t))return n}function $(e,t,o){let n=`${e}:${t}`,r=createHmac("sha256",o).update(n).digest("hex");return `${n}:${r}`}function C(e,t){let o=e.split(":");if(o.length!==3)return null;let[n,r,s]=o,i=createHmac("sha256",t).update(`${n}:${r}`).digest("hex");try{if(!timingSafeEqual(Buffer.from(s,"hex"),Buffer.from(i,"hex")))return null}catch{return null}return {sessionId:n,elicitationId:r}}function M(e,t){if(!e)return false;let o=e.replace(/:\d+$/,"");return t.includes(o)}var S=["localhost","127.0.0.1","::1"];function j(e,t,o){let n=e.filter(l=>l.onCall),r=e.filter(l=>l.onResult).reverse(),s=0,i=async()=>{if(s>=n.length){let a=await o();for(let c of r)a=await c.onResult(a,t);return a}return n[s++].onCall(t,i)};return i}export{h as a,T as b,R as c,x as d,y as e,b as f,$ as g,C as h,M as i,S as j,j as k};
@@ -1 +0,0 @@
1
- function u(){let t=new Map;return {async get(e){let n=t.get(e);if(n){if(n.expiresAt!==void 0&&Date.now()>n.expiresAt){t.delete(e);return}return n.value}},async set(e,n,r){t.set(e,{value:n,expiresAt:r!==void 0?Date.now()+r*1e3:void 0});},async delete(e){t.delete(e);}}}function o(t){let e=t.get("user");if(e){if(typeof e=="string")return e;if(typeof e=="object"&&e!==null){let n=e;if(typeof n.id=="string")return n.id;if(typeof n.id=="number")return String(n.id);if(typeof n.sub=="string")return n.sub}}}function d(t,e){let n=()=>{let r=o(t);if(!r){let s=t.get("user");if(s){let i=typeof s=="object"?JSON.stringify(s):typeof s;throw new Error(`userStore: session has a "user" but could not resolve an ID. Expected: string | { id: string | number } | { sub: string }. Got: ${i}`)}throw new Error("userStore requires a user in session. Call session.set('user', ...) first.")}return r};return {async get(r){return e.get(`user:${n()}:${r}`)},async set(r,s,i){await e.set(`user:${n()}:${r}`,s,i);},async delete(r){await e.delete(`user:${n()}:${r}`);}}}export{u as a,o as b,d as c};