@lynq/lynq 0.2.0 → 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/README.md CHANGED
@@ -1,34 +1,31 @@
1
1
  # lynq
2
2
 
3
3
  [![CI](https://github.com/hogekai/lynq/actions/workflows/ci.yml/badge.svg)](https://github.com/hogekai/lynq/actions/workflows/ci.yml)
4
- [![npm](https://img.shields.io/npm/v/lynq)](https://www.npmjs.com/package/lynq)
4
+ [![npm](https://img.shields.io/npm/v/@lynq/lynq)](https://www.npmjs.com/package/@lynq/lynq)
5
5
 
6
6
  Lightweight MCP server framework. Tool visibility control through middleware.
7
7
 
8
8
  ```ts
9
- import { createMCPServer } from "lynq";
10
- import { auth } from "lynq/auth";
9
+ import { createMCPServer } from "@lynq/lynq";
10
+ import { auth } from "@lynq/lynq/auth";
11
11
  import { z } from "zod";
12
12
 
13
13
  const server = createMCPServer({ name: "my-server", version: "1.0.0" });
14
14
 
15
- // Login tool — always visible
16
15
  server.tool("login", {
17
16
  input: z.object({ username: z.string(), password: z.string() }),
18
17
  }, async (args, ctx) => {
19
18
  const user = await authenticate(args.username, args.password);
20
19
  ctx.session.set("user", user);
21
20
  ctx.session.authorize("auth");
22
- return { content: [{ type: "text", text: `Welcome, ${user.name}` }] };
21
+ return ctx.text(`Welcome, ${user.name}`);
23
22
  });
24
23
 
25
- // Weather tool — hidden until authenticated
26
24
  server.tool("weather", auth(), {
27
25
  description: "Get weather for a city",
28
26
  input: z.object({ city: z.string() }),
29
- }, async (args) => {
30
- const data = await fetchWeather(args.city);
31
- return { content: [{ type: "text", text: JSON.stringify(data) }] };
27
+ }, async (args, ctx) => {
28
+ return ctx.text(JSON.stringify(await fetchWeather(args.city)));
32
29
  });
33
30
 
34
31
  await server.stdio();
@@ -37,145 +34,12 @@ await server.stdio();
37
34
  ## Install
38
35
 
39
36
  ```sh
40
- npm install lynq @modelcontextprotocol/sdk zod
41
- ```
42
-
43
- ## Why lynq
44
-
45
- When you build an MCP server, you often need to show different tools depending on session state — hide admin tools from unauthenticated users, reveal features after onboarding, etc. The MCP protocol supports this via bidirectional tool list notifications, but wiring it by hand means managing visibility sets, diffing tool lists, and calling `sendToolListChanged` at the right time. lynq lets you declare visibility as middleware and handles the rest.
46
-
47
- ## API
48
-
49
- ### `createMCPServer(info)`
50
-
51
- Creates a server instance.
52
-
53
- ```ts
54
- const server = createMCPServer({ name: "my-server", version: "1.0.0" });
55
- ```
56
-
57
- ### `server.tool(name, ...middlewares?, config, handler)`
58
-
59
- Register a tool. Middlewares are optional, config holds `description` and `input` schema.
60
-
61
- ```ts
62
- server.tool("greet", {
63
- description: "Greet someone",
64
- input: z.object({ name: z.string() }),
65
- }, async (args) => ({
66
- content: [{ type: "text", text: `Hello ${args.name}` }],
67
- }));
68
-
69
- server.tool("secret", auth(), {
70
- input: z.object({ query: z.string() }),
71
- }, async (args) => ({
72
- content: [{ type: "text", text: args.query }],
73
- }));
74
- ```
75
-
76
- ### `server.resource(uri, ...middlewares?, config, handler)`
77
-
78
- Register a resource. Same middleware pattern as `tool()`. Global middleware (`server.use()`) does not apply to resources.
79
-
80
- ```ts
81
- server.resource("config://settings", {
82
- name: "App Settings",
83
- mimeType: "application/json",
84
- }, async (uri) => ({
85
- text: JSON.stringify(config),
86
- }));
87
-
88
- server.resource("data://users", auth(), {
89
- name: "User Database",
90
- mimeType: "application/json",
91
- }, async (uri, ctx) => ({
92
- text: JSON.stringify(await db.getUsers()),
93
- }));
37
+ npm install @lynq/lynq @modelcontextprotocol/sdk zod
94
38
  ```
95
39
 
96
- ### `server.use(middleware)`
97
-
98
- Apply middleware to all subsequently registered tools.
40
+ ## Documentation
99
41
 
100
- ```ts
101
- server.use(auth());
102
- ```
103
-
104
- ### Session
105
-
106
- Available in handlers and middleware via `ctx.session`:
107
-
108
- ```ts
109
- ctx.session.set("key", value);
110
- ctx.session.get("key");
111
- ctx.session.authorize("auth"); // Enable tools guarded by "auth" middleware
112
- ```
113
-
114
- ### `auth(options?)`
115
-
116
- Middleware that hides tools until authenticated.
117
-
118
- ```ts
119
- import { auth } from "lynq/auth";
120
-
121
- auth(); // checks ctx.session.get("user")
122
- auth({ sessionKey: "token" }); // checks ctx.session.get("token")
123
- auth({ message: "Login first" }); // custom error message
124
- ```
125
-
126
- ## Testing
127
-
128
- lynq ships a test helper that eliminates MCP boilerplate. No manual `Client`/`InMemoryTransport` setup.
129
-
130
- ```ts
131
- import { createTestClient } from "lynq/test";
132
- import { createMCPServer } from "lynq";
133
- import { auth } from "lynq/auth";
134
- import { z } from "zod";
135
-
136
- const server = createMCPServer({ name: "my-server", version: "1.0.0" });
137
- server.tool("weather", auth(), {
138
- input: z.object({ city: z.string() }),
139
- }, async (args) => ({
140
- content: [{ type: "text", text: `Sunny in ${args.city}` }],
141
- }));
142
-
143
- const t = await createTestClient(server);
144
-
145
- // Tool visibility
146
- const tools = await t.listTools(); // string[]
147
- expect(tools).not.toContain("weather");
148
-
149
- // Authorize and call
150
- t.authorize("auth");
151
- const text = await t.callToolText("weather", { city: "Tokyo" });
152
- expect(text).toContain("Sunny");
153
-
154
- // Full result access
155
- const result = await t.callTool("weather", { city: "Tokyo" });
156
-
157
- // Resources
158
- const uris = await t.listResources();
159
- const content = await t.readResource("config://settings");
160
-
161
- // Session access
162
- t.session.set("user", { name: "alice" });
163
-
164
- await t.close();
165
- ```
166
-
167
- ### Custom matchers
168
-
169
- Optional vitest/jest matchers for more expressive assertions:
170
-
171
- ```ts
172
- import { matchers } from "lynq/test";
173
- expect.extend(matchers);
174
-
175
- const result = await t.callTool("weather", { city: "Tokyo" });
176
- expect(result).toHaveTextContent("Sunny");
177
- expect(result).not.toBeError();
178
- ```
42
+ [https://hogekai.github.io/lynq/](https://hogekai.github.io/lynq/)
179
43
 
180
44
  ## License
181
45
 
@@ -1,5 +1,5 @@
1
1
  import { Express } from 'express';
2
- import { M as MCPServer } from '../types-BqH9Me9B.js';
2
+ import { M as MCPServer } from '../types-D504PjnN.js';
3
3
  import '@modelcontextprotocol/sdk/types.js';
4
4
  import 'zod';
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { Hono } from 'hono';
2
- import { M as MCPServer } from '../types-BqH9Me9B.js';
2
+ import { M as MCPServer } from '../types-D504PjnN.js';
3
3
  import '@modelcontextprotocol/sdk/types.js';
4
4
  import 'zod';
5
5
 
@@ -0,0 +1 @@
1
+ function t(e,n){return {content:e,...n?{isError:true}:{},text(o){return t([...e,{type:"text",text:o}],n)},json(o){return t([...e,{type:"text",text:JSON.stringify(o,null,2)}],n)},error(o){return t([...e,{type:"text",text:o}],true)},image(o,r){return t([...e,{type:"image",data:o,mimeType:r}],n)}}}function s(e){return t([{type:"text",text:e}])}function l(e){return t([{type:"text",text:JSON.stringify(e,null,2)}])}function p(e){return t([{type:"text",text:e}],true)}function i(e,n){return t([{type:"image",data:e,mimeType:n}])}export{s as a,l as b,p as c,i as d};
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as MCPServer } from './types-BqH9Me9B.js';
2
- export { E as Elicit, a as ElicitFormParams, b as ElicitFormResult, c as ElicitUrlParams, d as ElicitUrlResult, H as HttpAdapterOptions, R as ResourceConfig, e as ResourceContent, f as ResourceContext, g as ResourceHandler, h as RootInfo, S as Sample, i as SampleOptions, j as SampleRawParams, k as SampleRawResult, l as ServerInfo, m as Session, T as TaskConfig, n as TaskContext, o as TaskControl, p as TaskHandler, q as ToolConfig, r as ToolContext, s as ToolHandler, t as ToolInfo, u as ToolMiddleware } from './types-BqH9Me9B.js';
1
+ import { M as MCPServer } from './types-D504PjnN.js';
2
+ export { E as Elicit, a as ElicitFormResult, b as ElicitUrlResult, H as HttpAdapterOptions, R as ResourceConfig, c as ResourceContent, d as ResourceContext, e as ResourceHandler, f as RootInfo, S as Sample, g as SampleOptions, h as SampleRawParams, i as SampleRawResult, j as ServerInfo, k as Session, T as TaskConfig, l as TaskContext, m as TaskControl, n as TaskHandler, o as ToolConfig, p as ToolContext, q as ToolHandler, r as ToolInfo, s as ToolMiddleware, t as ToolResponse, u as error, v as image, w as json, x as text } from './types-D504PjnN.js';
3
3
  import '@modelcontextprotocol/sdk/types.js';
4
4
  import 'zod';
5
5
 
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import {InMemoryTaskStore}from'@modelcontextprotocol/sdk/experimental/tasks';import {Server}from'@modelcontextprotocol/sdk/server/index.js';import {ListToolsRequestSchema,CallToolRequestSchema,ListResourcesRequestSchema,ListResourceTemplatesRequestSchema,ReadResourceRequestSchema}from'@modelcontextprotocol/sdk/types.js';import {normalizeObjectSchema}from'@modelcontextprotocol/sdk/server/zod-compat.js';import {toJsonSchemaCompat}from'@modelcontextprotocol/sdk/server/zod-json-schema-compat.js';function ne(a){return {async form({message:l,schema:c}){let i=await a.elicitInput({message:l,requestedSchema:{type:"object",properties:c}});return {action:i.action,content:i.content??{}}},async url({message:l,url:c}){return {action:(await a.elicitInput({mode:"url",message:l,url:c,elicitationId:crypto.randomUUID()})).action}}}}function A(a){return async()=>{try{return (await a.listRoots()).roots.map(c=>{let i={uri:c.uri};return c.name!==void 0&&(i.name=c.name),i})}catch{return []}}}function se(a){async function l(i,u){let p={messages:[{role:"user",content:{type:"text",text:i}}],maxTokens:u?.maxTokens??1024};u?.model!==void 0&&(p.modelPreferences={hints:[{name:u.model}]}),u?.system!==void 0&&(p.systemPrompt=u.system),u?.temperature!==void 0&&(p.temperature=u.temperature),u?.stopSequences!==void 0&&(p.stopSequences=u.stopSequences);let f=(await a.createMessage(p)).content;return f.type==="text"?f.text:""}async function c(i){return a.createMessage(i)}return Object.assign(l,{raw:c})}function q(a,l,c,i,u){return {toolName:i,session:c,signal:u,sessionId:l,elicit:ne(a),roots:A(a),sample:se(a)}}function B(a){if(a==null)return {type:"object"};let l=normalizeObjectSchema(a);return l?toJsonSchemaCompat(l):a}function H(a,l){let c=l[l.length-1];if(typeof c!="function")throw new TypeError(`${a}: last argument must be a handler function`);let i=l[l.length-2];if(i==null||typeof i!="object"||Array.isArray(i))throw new TypeError(`${a}: second-to-last argument must be a config object`);let u=l.slice(0,-2);for(let p of u)if(!p||typeof p!="object"||typeof p.name!="string")throw new TypeError(`${a}: each middleware must have a "name" property`);return {middlewares:u,config:i,handler:c}}function j(a,l){let c=[];for(let i of l)i.onRegister?.(a)===false&&c.push(i.name);return c}function L(a){let c=a.split(/\{[^}]+\}/).map(i=>i.replace(/[.*+?^$|()[\]\\]/g,"\\$&"));return new RegExp(`^${c.join("(.+)")}$`)}function E(a,l,c,i){let u=c.get(l);if(u==="disabled")return false;if(u==="enabled")return true;for(let p of a)if(!i.has(p))return false;return true}function z(a,l){let c=a.get(l);if(c)return c;for(let i of a.values())if(i.isTemplate&&i.uriPattern?.test(l))return i}function $(a,l,c){let i=a.filter(f=>f.onCall),u=a.filter(f=>f.onResult).reverse(),p=0,y=async()=>{if(p>=i.length){let b=await c();for(let C of u)b=await C.onResult(b,l);return b}return i[p++].onCall(l,y)};return y}function pe(a){let l=[],c=new Map,i=new Map,u=new Map,p=new Map,y=new Map,f=new Set,b=new InMemoryTaskStore,C=new Proxy(b,{get(e,n,t){return n==="updateTaskStatus"?async(s,r,...o)=>(r==="cancelled"&&f.add(s),e.updateTaskStatus.call(e,s,r,...o)):Reflect.get(e,n,t)}}),h=new Server(a,{capabilities:{tools:{listChanged:true},resources:{listChanged:true},tasks:{list:{},cancel:{},requests:{tools:{call:{}}}}},taskStore:C});function S(e){let n=p.get(e);return n||(n={data:new Map,grants:new Set,toolOverrides:new Map,resourceOverrides:new Map},p.set(e,n)),n}function O(e,n){let t=S(n);return E(e.hiddenByMiddlewares,e.name,t.toolOverrides,t.grants)}function k(e,n){let t=S(n);return E(e.hiddenByMiddlewares,e.uri,t.resourceOverrides,t.grants)}function _(e,n){let t=S(n);return E(e.hiddenByMiddlewares,e.name,t.toolOverrides,t.grants)}function M(e){(e&&y.get(e)||h).sendToolListChanged().catch(()=>{});}function x(e){(e&&y.get(e)||h).sendResourceListChanged().catch(()=>{});}function I(e){let n=S(e);return {get(t){return n.data.get(t)},set(t,s){n.data.set(t,s);},authorize(t){n.grants.add(t),M(e),x(e);},revoke(t){n.grants.delete(t),M(e),x(e);},enableTools(...t){for(let s of t)n.toolOverrides.set(s,"enabled");M(e);},disableTools(...t){for(let s of t)n.toolOverrides.set(s,"disabled");M(e);},enableResources(...t){for(let s of t)n.resourceOverrides.set(s,"enabled");x(e);},disableResources(...t){for(let s of t)n.resourceOverrides.set(s,"disabled");x(e);}}}function V(e){e.setRequestHandler(ListToolsRequestSchema,(n,t)=>{let s=t.sessionId??"default",r=[];for(let o of c.values())O(o,s)&&r.push({name:o.name,description:o.description,inputSchema:B(o.input)});for(let o of u.values())_(o,s)&&r.push({name:o.name,description:o.description,inputSchema:B(o.input),execution:{taskSupport:"required"}});return {tools:r}}),e.setRequestHandler(CallToolRequestSchema,async(n,t)=>{let{name:s,arguments:r}=n.params,o=t.sessionId??"default",w=d=>({content:[{type:"text",text:d}],isError:true}),g=c.get(s);if(g){if(!O(g,o))return w(`Tool not available: ${s}`);let d=q(e,o,I(o),s,t.signal),v=()=>Promise.resolve(g.handler(r??{},d));return $(g.middlewares,d,v)()}let T=u.get(s);if(T){if(!_(T,o))return w(`Tool not available: ${s}`);let d=t.taskStore;if(!d)return w("Task store not available");let v=await d.createTask({pollInterval:1e3}),m=v.taskId,Y={progress(R,P){if(f.has(m))return;let te=P?`${R}% ${P}`:`${R}%`;d.updateTaskStatus(m,"working",te).catch(()=>{});},get cancelled(){return f.has(m)}},J={...q(e,o,I(o),s,t.signal),task:Y},ee=async()=>((async()=>{try{let R=await T.handler(r??{},J);f.has(m)||await d.storeTaskResult(m,"completed",R);}catch(R){if(!f.has(m)){let P=R instanceof Error?R.message:String(R);await d.storeTaskResult(m,"failed",{content:[{type:"text",text:P}],isError:true}).catch(()=>{});}}})(),{task:v});return $(T.middlewares,J,ee)()}return w(`Unknown tool: ${s}`)}),e.setRequestHandler(ListResourcesRequestSchema,(n,t)=>{let s=t.sessionId??"default",r=[];for(let o of i.values())!o.isTemplate&&k(o,s)&&r.push({uri:o.uri,name:o.name,description:o.description,mimeType:o.mimeType});return {resources:r}}),e.setRequestHandler(ListResourceTemplatesRequestSchema,(n,t)=>{let s=t.sessionId??"default",r=[];for(let o of i.values())o.isTemplate&&k(o,s)&&r.push({uriTemplate:o.uri,name:o.name,description:o.description,mimeType:o.mimeType});return {resourceTemplates:r}}),e.setRequestHandler(ReadResourceRequestSchema,async(n,t)=>{let{uri:s}=n.params,r=z(i,s);if(!r)throw new Error(`Unknown resource: ${s}`);let o=t.sessionId??"default";if(!k(r,o))throw new Error(`Resource not available: ${s}`);let w=I(o),g={uri:s,session:w,sessionId:o,roots:A(e)},T=q(e,o,w,r.uri,t.signal),d=async()=>{let m=await r.handler(s,g);return {contents:[{uri:s,mimeType:m.mimeType??r.mimeType,...m.text!=null?{text:m.text}:{},...m.blob!=null?{blob:m.blob}:{}}]}};return $(r.middlewares,T,d)()});}V(h);function F(e){l.push(e);}function G(...e){let n=e[0],t=H(`tool("${n}")`,e.slice(1));if(typeof t.config.name=="string")throw new TypeError(`tool("${n}"): second-to-last argument must be a config object`);let s=t.config,r=[...l,...t.middlewares],o={name:n,description:s.description,middlewares:r};c.set(n,{name:n,description:s.description,input:s.input,handler:t.handler,middlewares:r,hiddenByMiddlewares:j(o,r)});}function D(...e){let n=e[0],t=H(`resource("${n}")`,e.slice(1));if(typeof t.config.name!="string")throw new TypeError(`resource("${n}"): second-to-last argument must be a config object with a "name" property`);let s=t.config,r={name:s.name,description:s.description,middlewares:t.middlewares},o=n.includes("{");i.set(n,{uri:n,isTemplate:o,uriPattern:o?L(n):null,name:s.name,description:s.description,mimeType:s.mimeType,handler:t.handler,middlewares:t.middlewares,hiddenByMiddlewares:j(r,t.middlewares)});}function W(...e){let n=e[0],t=H(`task("${n}")`,e.slice(1));if(typeof t.config.name=="string")throw new TypeError(`task("${n}"): second-to-last argument must be a config object`);let s=t.config,r=[...l,...t.middlewares],o={name:n,description:s.description,middlewares:r};u.set(n,{name:n,description:s.description,input:s.input,handler:t.handler,middlewares:r,hiddenByMiddlewares:j(o,r)});}async function Z(){let{StdioServerTransport:e}=await import('@modelcontextprotocol/sdk/server/stdio.js'),n=new e;await h.connect(n);}async function K(e){await h.connect(e);}let Q={tools:{listChanged:true},resources:{listChanged:true},tasks:{list:{},cancel:{},requests:{tools:{call:{}}}}};function U(){let e=new Server(a,{capabilities:Q,taskStore:C});return V(e),e}function X(e){let n=null;async function t(){return n||(n=(await import('@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js')).WebStandardStreamableHTTPServerTransport),n}if(e?.sessionless)return async r=>{let o=await t(),w=U(),g=new o({sessionIdGenerator:void 0,enableJsonResponse:e?.enableJsonResponse});return await w.connect(g),g.handleRequest(r)};let s=new Map;return async r=>{let o=await t(),w=r.headers.get("mcp-session-id");if(w){let d=s.get(w);return d?d.transport.handleRequest(r):new Response(JSON.stringify({jsonrpc:"2.0",error:{code:-32e3,message:"Session not found"}}),{status:404,headers:{"Content-Type":"application/json"}})}let g=U(),T=new o({sessionIdGenerator:e?.sessionIdGenerator??(()=>crypto.randomUUID()),enableJsonResponse:e?.enableJsonResponse,onsessioninitialized:d=>{s.set(d,{server:g,transport:T}),y.set(d,g);},onsessionclosed:d=>{s.delete(d),y.delete(d);}});return await g.connect(T),T.handleRequest(r)}}return {use:F,tool:G,resource:D,task:W,stdio:Z,http:X,connect:K,_server:h,_getSession:S,_isToolVisible(e,n){let t=c.get(e);return t?O(t,n):false},_isResourceVisible(e,n){let t=i.get(e);return t?k(t,n):false},_isTaskVisible(e,n){let t=u.get(e);return t?_(t,n):false},_createSessionAPI:I}}export{pe as createMCPServer};
1
+ import {c,d,b,a}from'./chunk-VAAZWX4U.mjs';export{c as error,d as image,b as json,a as text}from'./chunk-VAAZWX4U.mjs';import {InMemoryTaskStore}from'@modelcontextprotocol/sdk/experimental/tasks';import {Server}from'@modelcontextprotocol/sdk/server/index.js';import {ListToolsRequestSchema,CallToolRequestSchema,ListResourcesRequestSchema,ListResourceTemplatesRequestSchema,ReadResourceRequestSchema}from'@modelcontextprotocol/sdk/types.js';import {normalizeObjectSchema}from'@modelcontextprotocol/sdk/server/zod-compat.js';import {toJsonSchemaCompat}from'@modelcontextprotocol/sdk/server/zod-json-schema-compat.js';function k(i){if(i==null)return {type:"object"};let l=normalizeObjectSchema(i);return l?toJsonSchemaCompat(l):i}function H(i,l){let c=l[l.length-1];if(typeof c!="function")throw new TypeError(`${i}: last argument must be a handler function`);let a=l[l.length-2];if(a==null||typeof a!="object"||Array.isArray(a))throw new TypeError(`${i}: second-to-last argument must be a config object`);let u=l.slice(0,-2);for(let d of u)if(!d||typeof d!="object"||typeof d.name!="string")throw new TypeError(`${i}: each middleware must have a "name" property`);return {middlewares:u,config:a,handler:c}}function $(i,l){let c=[];for(let a of l)a.onRegister?.(i)===false&&c.push(a.name);return c}function G(i){let c=i.split(/\{[^}]+\}/).map(a=>a.replace(/[.*+?^$|()[\]\\]/g,"\\$&"));return new RegExp(`^${c.join("(.+)")}$`)}function O(i,l,c,a){let u=c.get(l);if(u==="disabled")return false;if(u==="enabled")return true;for(let d of i)if(!a.has(d))return false;return true}function D(i,l){let c=i.get(l);if(c)return c;for(let a of i.values())if(a.isTemplate&&a.uriPattern?.test(l))return a}function E(i,l,c){let a=i.filter(f=>f.onCall),u=i.filter(f=>f.onResult).reverse(),d=0,y=async()=>{if(d>=a.length){let v=await c();for(let M of u)v=await M.onResult(v,l);return v}return a[d++].onCall(l,y)};return y}function ie(i){return {async form(l,c){let a=k(c),u=await i.elicitInput({message:l,requestedSchema:a});return {action:u.action,content:u.content??{}}},async url(l,c){return {action:(await i.elicitInput({mode:"url",message:l,url:c,elicitationId:crypto.randomUUID()})).action}}}}function L(i){return async()=>{try{return (await i.listRoots()).roots.map(c=>{let a={uri:c.uri};return c.name!==void 0&&(a.name=c.name),a})}catch{return []}}}function le(i){async function l(a,u){let d={messages:[{role:"user",content:{type:"text",text:a}}],maxTokens:u?.maxTokens??1024};u?.model!==void 0&&(d.modelPreferences={hints:[{name:u.model}]}),u?.system!==void 0&&(d.systemPrompt=u.system),u?.temperature!==void 0&&(d.temperature=u.temperature),u?.stopSequences!==void 0&&(d.stopSequences=u.stopSequences);let f=(await i.createMessage(d)).content;return f.type==="text"?f.text:""}async function c(a){return i.createMessage(a)}return Object.assign(l,{raw:c})}function _(i,l,c$1,a$1,u){return {toolName:a$1,session:c$1,signal:u,sessionId:l,elicit:ie(i),roots:L(i),sample:le(i),text:a,json:b,error:c,image:d}}function ge(i){let l=[],c$1=new Map,a=new Map,u=new Map,d=new Map,y=new Map,f=new Set,v=new InMemoryTaskStore,M=new Proxy(v,{get(e,n,t){return n==="updateTaskStatus"?async(s,r,...o)=>(r==="cancelled"&&f.add(s),e.updateTaskStatus.call(e,s,r,...o)):Reflect.get(e,n,t)}}),b=new Server(i,{capabilities:{tools:{listChanged:true},resources:{listChanged:true},tasks:{list:{},cancel:{},requests:{tools:{call:{}}}}},taskStore:M});function C(e){let n=d.get(e);return n||(n={data:new Map,grants:new Set,toolOverrides:new Map,resourceOverrides:new Map},d.set(e,n)),n}function A(e,n){let t=C(n);return O(e.hiddenByMiddlewares,e.name,t.toolOverrides,t.grants)}function x(e,n){let t=C(n);return O(e.hiddenByMiddlewares,e.uri,t.resourceOverrides,t.grants)}function B(e,n){let t=C(n);return O(e.hiddenByMiddlewares,e.name,t.toolOverrides,t.grants)}function I(e){(e&&y.get(e)||b).sendToolListChanged().catch(()=>{});}function P(e){(e&&y.get(e)||b).sendResourceListChanged().catch(()=>{});}function q(e){let n=C(e);return {get(t){return n.data.get(t)},set(t,s){n.data.set(t,s);},authorize(t){n.grants.add(t),I(e),P(e);},revoke(t){n.grants.delete(t),I(e),P(e);},enableTools(...t){for(let s of t)n.toolOverrides.set(s,"enabled");I(e);},disableTools(...t){for(let s of t)n.toolOverrides.set(s,"disabled");I(e);},enableResources(...t){for(let s of t)n.resourceOverrides.set(s,"enabled");P(e);},disableResources(...t){for(let s of t)n.resourceOverrides.set(s,"disabled");P(e);}}}function z(e){e.setRequestHandler(ListToolsRequestSchema,(n,t)=>{let s=t.sessionId??"default",r=[];for(let o of c$1.values())A(o,s)&&r.push({name:o.name,description:o.description,inputSchema:k(o.input)});for(let o of u.values())B(o,s)&&r.push({name:o.name,description:o.description,inputSchema:k(o.input),execution:{taskSupport:"required"}});return {tools:r}}),e.setRequestHandler(CallToolRequestSchema,async(n,t)=>{let{name:s,arguments:r}=n.params,o=t.sessionId??"default",w=c$1.get(s);if(w){if(!A(w,o))return c(`Tool not available: ${s}`);let m=_(e,o,q(o),s,t.signal),p=()=>Promise.resolve(w.handler(r??{},m));return E(w.middlewares,m,p)()}let g=u.get(s);if(g){if(!B(g,o))return c(`Tool not available: ${s}`);let m=t.taskStore;if(!m)return c("Task store not available");let p=await m.createTask({pollInterval:1e3}),T=p.taskId,S={progress(h,j){if(f.has(T))return;let oe=j?`${h}% ${j}`:`${h}%`;m.updateTaskStatus(T,"working",oe).catch(()=>{});},get cancelled(){return f.has(T)}},F={..._(e,o,q(o),s,t.signal),task:S},se=async()=>((async()=>{try{let h=await g.handler(r??{},F);f.has(T)||await m.storeTaskResult(T,"completed",h);}catch(h){if(!f.has(T)){let j=h instanceof Error?h.message:String(h);await m.storeTaskResult(T,"failed",c(j)).catch(()=>{});}}})(),{task:p});return E(g.middlewares,F,se)()}return c(`Unknown tool: ${s}`)}),e.setRequestHandler(ListResourcesRequestSchema,(n,t)=>{let s=t.sessionId??"default",r=[];for(let o of a.values())!o.isTemplate&&x(o,s)&&r.push({uri:o.uri,name:o.name,description:o.description,mimeType:o.mimeType});return {resources:r}}),e.setRequestHandler(ListResourceTemplatesRequestSchema,(n,t)=>{let s=t.sessionId??"default",r=[];for(let o of a.values())o.isTemplate&&x(o,s)&&r.push({uriTemplate:o.uri,name:o.name,description:o.description,mimeType:o.mimeType});return {resourceTemplates:r}}),e.setRequestHandler(ReadResourceRequestSchema,async(n,t)=>{let{uri:s}=n.params,r=D(a,s);if(!r)throw new Error(`Unknown resource: ${s}`);let o=t.sessionId??"default";if(!x(r,o))throw new Error(`Resource not available: ${s}`);let w=q(o),g={uri:s,session:w,sessionId:o,roots:L(e)},m=_(e,o,w,r.uri,t.signal),p=async()=>{let S=await r.handler(s,g);return {contents:[{uri:s,mimeType:S.mimeType??r.mimeType,...S.text!=null?{text:S.text}:{},...S.blob!=null?{blob:S.blob}:{}}]}};return E(r.middlewares,m,p)()});}z(b);function Z(e){l.push(e);}function K(...e){let n=e[0],t=H(`tool("${n}")`,e.slice(1));if(typeof t.config.name=="string")throw new TypeError(`tool("${n}"): second-to-last argument must be a config object`);let s=t.config,r=[...l,...t.middlewares],o={name:n,description:s.description,middlewares:r};c$1.set(n,{name:n,description:s.description,input:s.input,handler:t.handler,middlewares:r,hiddenByMiddlewares:$(o,r)});}function Q(...e){let n=e[0],t=H(`resource("${n}")`,e.slice(1));if(typeof t.config.name!="string")throw new TypeError(`resource("${n}"): second-to-last argument must be a config object with a "name" property`);let s=t.config,r={name:s.name,description:s.description,middlewares:t.middlewares},o=n.includes("{");a.set(n,{uri:n,isTemplate:o,uriPattern:o?G(n):null,name:s.name,description:s.description,mimeType:s.mimeType,handler:t.handler,middlewares:t.middlewares,hiddenByMiddlewares:$(r,t.middlewares)});}function X(...e){let n=e[0],t=H(`task("${n}")`,e.slice(1));if(typeof t.config.name=="string")throw new TypeError(`task("${n}"): second-to-last argument must be a config object`);let s=t.config,r=[...l,...t.middlewares],o={name:n,description:s.description,middlewares:r};u.set(n,{name:n,description:s.description,input:s.input,handler:t.handler,middlewares:r,hiddenByMiddlewares:$(o,r)});}async function Y(){let{StdioServerTransport:e}=await import('@modelcontextprotocol/sdk/server/stdio.js'),n=new e;await b.connect(n);}async function ee(e){await b.connect(e);}let te={tools:{listChanged:true},resources:{listChanged:true},tasks:{list:{},cancel:{},requests:{tools:{call:{}}}}};function N(){let e=new Server(i,{capabilities:te,taskStore:M});return z(e),e}function ne(e){let n=null;async function t(){return n||(n=(await import('@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js')).WebStandardStreamableHTTPServerTransport),n}if(e?.sessionless)return async r=>{let o=await t(),w=N(),g=new o({sessionIdGenerator:void 0,enableJsonResponse:e?.enableJsonResponse});return await w.connect(g),g.handleRequest(r)};let s=new Map;return async r=>{let o=await t(),w=r.headers.get("mcp-session-id");if(w){let p=s.get(w);return p?p.transport.handleRequest(r):new Response(JSON.stringify({jsonrpc:"2.0",error:{code:-32e3,message:"Session not found"}}),{status:404,headers:{"Content-Type":"application/json"}})}let g=N(),m=new o({sessionIdGenerator:e?.sessionIdGenerator??(()=>crypto.randomUUID()),enableJsonResponse:e?.enableJsonResponse,onsessioninitialized:p=>{s.set(p,{server:g,transport:m}),y.set(p,g);},onsessionclosed:p=>{s.delete(p),y.delete(p);}});return await g.connect(m),m.handleRequest(r)}}return {use:Z,tool:K,resource:Q,task:X,stdio:Y,http:ne,connect:ee,_server:b,_getSession:C,_isToolVisible(e,n){let t=c$1.get(e);return t?A(t,n):false},_isResourceVisible(e,n){let t=a.get(e);return t?x(t,n):false},_isTaskVisible(e,n){let t=u.get(e);return t?B(t,n):false},_createSessionAPI:q}}export{ge as createMCPServer};
@@ -1,4 +1,4 @@
1
- import { u as ToolMiddleware } from '../types-BqH9Me9B.js';
1
+ import { s as ToolMiddleware } from '../types-D504PjnN.js';
2
2
  import '@modelcontextprotocol/sdk/types.js';
3
3
  import 'zod';
4
4
 
@@ -1 +1 @@
1
- function i(e){let t=e?.sessionKey??"user",s=e?.message??"Authentication required. Please login first.";return {name:"auth",onRegister(){return false},async onCall(n,r){return n.session.get(t)?r():{content:[{type:"text",text:s}],isError:true}}}}export{i as auth};
1
+ import {c}from'../chunk-VAAZWX4U.mjs';function u(e){let t=e?.sessionKey??"user",r=e?.message??"Authentication required. Please login first.";return {name:"auth",onRegister(){return false},async onCall(n,o){return n.session.get(t)?o():c(r)}}}export{u as auth};
package/dist/test.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
- import { m as Session, M as MCPServer } from './types-BqH9Me9B.js';
2
+ import { k as Session, M as MCPServer } from './types-D504PjnN.js';
3
3
  import 'zod';
4
4
 
5
5
  interface TestClient {
@@ -1,6 +1,21 @@
1
- import { CreateMessageRequestParamsBase, CreateMessageResult, CallToolResult } from '@modelcontextprotocol/sdk/types.js';
1
+ import { CallToolResult, CreateMessageRequestParamsBase, CreateMessageResult } from '@modelcontextprotocol/sdk/types.js';
2
2
  import { z } from 'zod';
3
3
 
4
+ interface ToolResponse extends CallToolResult {
5
+ text(value: string): ToolResponse;
6
+ json(value: unknown): ToolResponse;
7
+ error(message: string): ToolResponse;
8
+ image(data: string, mimeType: string): ToolResponse;
9
+ }
10
+ /** Create a text response. Chainable. */
11
+ declare function text(value: string): ToolResponse;
12
+ /** Create a JSON response (serialized to text). Chainable. */
13
+ declare function json(value: unknown): ToolResponse;
14
+ /** Create an error response. Chainable. */
15
+ declare function error(message: string): ToolResponse;
16
+ /** Create an image response. Chainable. */
17
+ declare function image(data: string, mimeType: string): ToolResponse;
18
+
4
19
  interface ServerInfo {
5
20
  name: string;
6
21
  version: string;
@@ -23,31 +38,18 @@ interface Session {
23
38
  /** Disable specific resources by URI. */
24
39
  disableResources(...uris: string[]): void;
25
40
  }
26
- interface ElicitFormParams {
27
- message: string;
28
- schema: Record<string, {
29
- type: "string" | "number" | "boolean";
30
- description?: string;
31
- enum?: string[];
32
- default?: unknown;
33
- }>;
34
- }
35
- interface ElicitFormResult {
41
+ interface ElicitFormResult<T = Record<string, string | number | boolean | string[]>> {
36
42
  action: "accept" | "decline" | "cancel";
37
- content: Record<string, string | number | boolean | string[]>;
38
- }
39
- interface ElicitUrlParams {
40
- message: string;
41
- url: string;
43
+ content: T;
42
44
  }
43
45
  interface ElicitUrlResult {
44
46
  action: "accept" | "decline" | "cancel";
45
47
  }
46
48
  interface Elicit {
47
49
  /** Request structured data from the user via a form. */
48
- form(params: ElicitFormParams): Promise<ElicitFormResult>;
50
+ form<T extends z.ZodObject<z.ZodRawShape>>(message: string, schema: T): Promise<ElicitFormResult<z.infer<T>>>;
49
51
  /** Direct the user to an external URL. */
50
- url(params: ElicitUrlParams): Promise<ElicitUrlResult>;
52
+ url(message: string, url: string): Promise<ElicitUrlResult>;
51
53
  }
52
54
  interface SampleOptions {
53
55
  maxTokens?: number;
@@ -86,6 +88,14 @@ interface ToolContext {
86
88
  roots: () => Promise<RootInfo[]>;
87
89
  /** Request LLM inference from the client. */
88
90
  sample: Sample;
91
+ /** Create a text response. Chainable. */
92
+ text(value: string): ToolResponse;
93
+ /** Create a JSON response. Chainable. */
94
+ json(value: unknown): ToolResponse;
95
+ /** Create an error response. Chainable. */
96
+ error(message: string): ToolResponse;
97
+ /** Create an image response. Chainable. */
98
+ image(data: string, mimeType: string): ToolResponse;
89
99
  }
90
100
  interface ToolInfo {
91
101
  name: string;
@@ -173,4 +183,4 @@ interface MCPServer {
173
183
  http(options?: HttpAdapterOptions): (req: Request) => Promise<Response>;
174
184
  }
175
185
 
176
- export type { Elicit as E, HttpAdapterOptions as H, MCPServer as M, ResourceConfig as R, Sample as S, TaskConfig as T, ElicitFormParams as a, ElicitFormResult as b, ElicitUrlParams as c, ElicitUrlResult as d, ResourceContent as e, ResourceContext as f, ResourceHandler as g, RootInfo as h, SampleOptions as i, SampleRawParams as j, SampleRawResult as k, ServerInfo as l, Session as m, TaskContext as n, TaskControl as o, TaskHandler as p, ToolConfig as q, ToolContext as r, ToolHandler as s, ToolInfo as t, ToolMiddleware as u };
186
+ export { type Elicit as E, type HttpAdapterOptions as H, type MCPServer as M, type ResourceConfig as R, type Sample as S, type TaskConfig as T, type ElicitFormResult as a, type ElicitUrlResult as b, type ResourceContent as c, type ResourceContext as d, type ResourceHandler as e, type RootInfo as f, type SampleOptions as g, type SampleRawParams as h, type SampleRawResult as i, type ServerInfo as j, type Session as k, type TaskContext as l, type TaskControl as m, type TaskHandler as n, type ToolConfig as o, type ToolContext as p, type ToolHandler as q, type ToolInfo as r, type ToolMiddleware as s, type ToolResponse as t, error as u, image as v, json as w, text as x };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lynq/lynq",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Lightweight MCP server framework. Tool visibility control through middleware.",
5
5
  "type": "module",
6
6
  "exports": {