@lynq/lynq 0.3.0 → 0.4.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.
Files changed (39) hide show
  1. package/README.md +88 -12
  2. package/dist/adapters/express.d.ts +1 -1
  3. package/dist/adapters/hono.d.ts +1 -1
  4. package/dist/chunk-2KEVF6YL.mjs +1 -0
  5. package/dist/chunk-L7SD7KKW.mjs +1 -0
  6. package/dist/chunk-STWVQGX5.mjs +1 -0
  7. package/dist/index.d.ts +2 -2
  8. package/dist/index.mjs +1 -1
  9. package/dist/middleware/auth.d.ts +6 -8
  10. package/dist/middleware/auth.mjs +1 -1
  11. package/dist/middleware/bearer.d.ts +19 -0
  12. package/dist/middleware/bearer.mjs +1 -0
  13. package/dist/middleware/combine.d.ts +12 -0
  14. package/dist/middleware/combine.mjs +1 -0
  15. package/dist/middleware/credentials.d.ts +19 -0
  16. package/dist/middleware/credentials.mjs +1 -0
  17. package/dist/middleware/github-oauth.d.ts +42 -0
  18. package/dist/middleware/github-oauth.mjs +2 -0
  19. package/dist/middleware/google-oauth.d.ts +43 -0
  20. package/dist/middleware/google-oauth.mjs +2 -0
  21. package/dist/middleware/guard.d.ts +15 -0
  22. package/dist/middleware/guard.mjs +1 -0
  23. package/dist/middleware/jwt.d.ts +27 -0
  24. package/dist/middleware/jwt.mjs +1 -0
  25. package/dist/middleware/logger.d.ts +11 -0
  26. package/dist/middleware/logger.mjs +1 -0
  27. package/dist/middleware/oauth.d.ts +22 -0
  28. package/dist/middleware/oauth.mjs +1 -0
  29. package/dist/middleware/payment.d.ts +22 -0
  30. package/dist/middleware/payment.mjs +1 -0
  31. package/dist/middleware/rate-limit.d.ts +15 -0
  32. package/dist/middleware/rate-limit.mjs +1 -0
  33. package/dist/middleware/truncate.d.ts +13 -0
  34. package/dist/middleware/truncate.mjs +1 -0
  35. package/dist/middleware/url-action.d.ts +24 -0
  36. package/dist/middleware/url-action.mjs +1 -0
  37. package/dist/test.d.ts +1 -1
  38. package/dist/{types-D504PjnN.d.ts → types-CqT2idkd.d.ts} +21 -7
  39. package/package.json +61 -3
package/README.md CHANGED
@@ -3,44 +3,120 @@
3
3
  [![CI](https://github.com/hogekai/lynq/actions/workflows/ci.yml/badge.svg)](https://github.com/hogekai/lynq/actions/workflows/ci.yml)
4
4
  [![npm](https://img.shields.io/npm/v/@lynq/lynq)](https://www.npmjs.com/package/@lynq/lynq)
5
5
 
6
- Lightweight MCP server framework. Tool visibility control through middleware.
6
+ MCP servers are stateless by default. lynq makes them session-aware.
7
+
8
+ ## The Problem
9
+
10
+ With the official SDK, adding session-aware tool visibility requires manual plumbing:
11
+
12
+ ```ts
13
+ // Without lynq — manual session tracking, manual notifications
14
+ const sessions = new Map();
15
+
16
+ server.setRequestHandler(ListToolsRequestSchema, (req, extra) => {
17
+ const session = sessions.get(extra.sessionId);
18
+ const tools = [loginTool];
19
+ if (session?.authorized) tools.push(weatherTool); // manual filtering
20
+ return { tools };
21
+ });
22
+
23
+ server.setRequestHandler(CallToolRequestSchema, async (req, extra) => {
24
+ if (req.params.name === "login") {
25
+ sessions.set(extra.sessionId, { authorized: true });
26
+ server.sendToolListChanged(); // manual notification
27
+ }
28
+ // ...
29
+ });
30
+ ```
31
+
32
+ ## The Solution
33
+
34
+ ```ts
35
+ // With lynq — one line
36
+ server.tool("weather", guard(), config, handler);
37
+ // Client gets notified automatically. No manual wiring.
38
+ ```
39
+
40
+ ## Install
41
+
42
+ ```sh
43
+ npm install @lynq/lynq
44
+ ```
45
+
46
+ ## Quick Start
7
47
 
8
48
  ```ts
9
49
  import { createMCPServer } from "@lynq/lynq";
10
- import { auth } from "@lynq/lynq/auth";
50
+ import { guard } from "@lynq/lynq/guard";
11
51
  import { z } from "zod";
12
52
 
13
53
  const server = createMCPServer({ name: "my-server", version: "1.0.0" });
14
54
 
15
55
  server.tool("login", {
16
56
  input: z.object({ username: z.string(), password: z.string() }),
17
- }, async (args, ctx) => {
57
+ }, async (args, c) => {
18
58
  const user = await authenticate(args.username, args.password);
19
- ctx.session.set("user", user);
20
- ctx.session.authorize("auth");
21
- return ctx.text(`Welcome, ${user.name}`);
59
+ c.session.set("user", user);
60
+ c.session.authorize("guard");
61
+ return c.text(`Welcome, ${user.name}`);
22
62
  });
23
63
 
24
- server.tool("weather", auth(), {
64
+ server.tool("weather", guard(), {
25
65
  description: "Get weather for a city",
26
66
  input: z.object({ city: z.string() }),
27
- }, async (args, ctx) => {
28
- return ctx.text(JSON.stringify(await fetchWeather(args.city)));
67
+ }, async (args, c) => {
68
+ return c.text(JSON.stringify(await fetchWeather(args.city)));
29
69
  });
30
70
 
31
71
  await server.stdio();
32
72
  ```
33
73
 
34
- ## Install
35
-
36
74
  ```sh
37
- npm install @lynq/lynq @modelcontextprotocol/sdk zod
75
+ npx tsx server.ts
38
76
  ```
39
77
 
78
+ ## Features
79
+
80
+ - **Session-Scoped Visibility** — `authorize()` shows tools, `revoke()` hides them. Client notification is automatic.
81
+ - **Hono-Style Middleware** — Global via `server.use()`, per-tool inline. Three hooks: `onRegister`, `onCall`, `onResult`.
82
+ - **Built-in Middleware** — `guard()` `rateLimit()` `logger()` `truncate()` `credentials()` `some()` `every()` `except()`
83
+ - **Response Helpers** — `c.text()` `c.json()` `c.error()` `c.image()` — chainable: `c.text("done").json({ id: 1 })`
84
+ - **Elicitation** — `c.elicit.form(message, zodSchema)` for structured user input. `c.elicit.url()` for external flows.
85
+ - **Framework Adapters** — `server.http()` returns `(Request) => Response`. Mount in Hono, Express, Deno, Workers.
86
+ - **Test Helpers** — `createTestClient()` for in-memory testing. No transport setup.
87
+ - **Tiny Core** — One dependency. ESM only. No config files, no magic.
88
+
89
+ ## Middleware Composition
90
+
91
+ ```ts
92
+ import { guard } from "@lynq/lynq/guard";
93
+ import { rateLimit } from "@lynq/lynq/rate-limit";
94
+ import { logger } from "@lynq/lynq/logger";
95
+
96
+ server.use(logger()); // global
97
+ server.tool("search", guard(), rateLimit({ max: 10 }), config, handler); // per-tool stack
98
+ ```
99
+
100
+ ## Comparison
101
+
102
+ | | lynq | FastMCP | Official SDK |
103
+ |---|---|---|---|
104
+ | Per-tool middleware | Yes | No | No |
105
+ | Session-scoped visibility | Auto-notify | Manual | Manual |
106
+ | onResult hook | Yes | No | No |
107
+ | Response helpers | Chainable | Basic | No |
108
+ | Test helpers | Yes | No | No |
109
+ | HTTP server built-in | No (you choose) | Yes (opinionated) | No |
110
+
40
111
  ## Documentation
41
112
 
42
113
  [https://hogekai.github.io/lynq/](https://hogekai.github.io/lynq/)
43
114
 
115
+ - [Quick Start](https://hogekai.github.io/lynq/getting-started/quick-start)
116
+ - [Middleware Concepts](https://hogekai.github.io/lynq/concepts/middleware)
117
+ - [Why lynq](https://hogekai.github.io/lynq/why-lynq)
118
+ - [API Reference](https://hogekai.github.io/lynq/api-reference/)
119
+
44
120
  ## License
45
121
 
46
122
  MIT
@@ -1,5 +1,5 @@
1
1
  import { Express } from 'express';
2
- import { M as MCPServer } from '../types-D504PjnN.js';
2
+ import { M as MCPServer } from '../types-CqT2idkd.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-D504PjnN.js';
2
+ import { M as MCPServer } from '../types-CqT2idkd.js';
3
3
  import '@modelcontextprotocol/sdk/types.js';
4
4
  import 'zod';
5
5
 
@@ -0,0 +1 @@
1
+ import {a}from'./chunk-STWVQGX5.mjs';function n(e){let t={name:e.name??"oauth",sessionKey:e.sessionKey??"user",message:e.message??"Please sign in to continue.",buildUrl:e.buildUrl,declineMessage:"Authentication cancelled."};return e.timeout!==void 0&&(t.timeout=e.timeout),a(t)}export{n as a};
@@ -0,0 +1 @@
1
+ import {c}from'./chunk-VAAZWX4U.mjs';function g(e){let s=e?.name??"guard",n=e?.sessionKey??"user",t=e?.message??"Authorization required.";return {name:s,onRegister(){return false},async onCall(o,a){return o.session.get(n)?a():c(t)}}}export{g as a};
@@ -0,0 +1 @@
1
+ import {c}from'./chunk-VAAZWX4U.mjs';function m(e){let n=e.name??"url-action",i=e.sessionKey??"user",l=e.timeout??3e5,a=e.declineMessage??"Action cancelled.";return {name:n,onRegister(){return false},async onCall(s,r){if(s.session.get(i))return r();let o=crypto.randomUUID(),c$1=e.buildUrl({sessionId:s.sessionId,elicitationId:o});return (await s.elicit.url(e.message,c$1,{elicitationId:o,waitForCompletion:true,timeout:l})).action!=="accept"?c(a):s.session.get(i)?(s.session.authorize(n),r()):c("Action was not completed.")}}}export{m as a};
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
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';
1
+ import { M as MCPServer } from './types-CqT2idkd.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, S as Sample, h as SampleOptions, i as SampleRawParams, j as SampleRawResult, k as ServerInfo, l as Session, T as TaskConfig, m as TaskContext, n as TaskControl, o as TaskHandler, p as ToolConfig, q as ToolContext, r as ToolHandler, s as ToolInfo, t as ToolMiddleware, u as ToolResponse, v as error, w as image, x as json, y as text } from './types-CqT2idkd.js';
3
3
  import '@modelcontextprotocol/sdk/types.js';
4
4
  import 'zod';
5
5
 
package/dist/index.mjs CHANGED
@@ -1 +1 @@
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
+ 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 j(a){if(a==null)return {type:"object"};let l=normalizeObjectSchema(a);return l?toJsonSchemaCompat(l):a}function $(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 d=l.slice(0,-2);for(let u of d)if(!u||typeof u!="object"||typeof u.name!="string")throw new TypeError(`${a}: each middleware must have a "name" property`);return {middlewares:d,config:i,handler:c}}function A(a,l){let c=[];for(let i of l)i.onRegister?.(a)===false&&c.push(i.name);return c}function Q(a){let c=a.split(/\{[^}]+\}/).map(i=>i.replace(/[.*+?^$|()[\]\\]/g,"\\$&"));return new RegExp(`^${c.join("(.+)")}$`)}function B(a,l,c,i){let d=c.get(l);if(d==="disabled")return false;if(d==="enabled")return true;for(let u of a)if(!i.has(u))return false;return true}function X(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 U(a,l,c){let i=a.filter(m=>m.onCall),d=a.filter(m=>m.onResult).reverse(),u=0,p=async()=>{if(u>=i.length){let S=await c();for(let C of d)S=await C.onResult(S,l);return S}return i[u++].onCall(l,p)};return p}function pe(a,l,c){return {async form(i,d){let u=j(d),p=await a.elicitInput({message:i,requestedSchema:u});return {action:p.action,content:p.content??{}}},async url(i,d,u){let p=u?.elicitationId??crypto.randomUUID(),m;u?.waitForCompletion&&l&&(m=l(p,a));let S=await a.elicitInput({mode:"url",message:i,url:d,elicitationId:p});if(S.action==="accept"&&m){let C=u?.timeout??3e5,x=new Promise((G,I)=>{setTimeout(()=>I(new Error("Elicitation timed out")),C);});await Promise.race([m,x]);}else m&&c&&c(p);return {action:S.action}}}}function F(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 fe(a){async function l(i,d){let u={messages:[{role:"user",content:{type:"text",text:i}}],maxTokens:d?.maxTokens??1024};d?.model!==void 0&&(u.modelPreferences={hints:[{name:d.model}]}),d?.system!==void 0&&(u.systemPrompt=d.system),d?.temperature!==void 0&&(u.temperature=d.temperature),d?.stopSequences!==void 0&&(u.stopSequences=d.stopSequences);let m=(await a.createMessage(u)).content;return m.type==="text"?m.text:""}async function c(i){return a.createMessage(i)}return Object.assign(l,{raw:c})}function N(a$1,l,c$1,i,d$1,u,p){return {toolName:i,session:c$1,signal:d$1,sessionId:l,elicit:pe(a$1,u,p),roots:F(a$1),sample:fe(a$1),text:a,json:b,error:c,image:d}}function ve(a){let l=[],c$1=new Map,i=new Map,d=new Map,u=new Map,p=new Map,m=new Map,S=36e5;function C(){let e=Date.now();for(let[t,n]of m)e-n.createdAt>S&&m.delete(t);}function x(e,t,n){return C(),new Promise(o=>{let r;try{r=n.createElicitationCompletionNotifier(e);}catch{}m.set(e,{resolver:o,completionNotifier:r,createdAt:Date.now()});})}function G(e){C();let t=m.get(e);t&&(m.delete(e),t.completionNotifier&&t.completionNotifier().catch(()=>{}),t.resolver());}function I(e){let t=m.get(e);t&&(m.delete(e),t.resolver());}let q=new Set,ee=new InMemoryTaskStore,W=new Proxy(ee,{get(e,t,n){return t==="updateTaskStatus"?async(o,r,...s)=>(r==="cancelled"&&q.add(o),e.updateTaskStatus.call(e,o,r,...s)):Reflect.get(e,t,n)}}),k=new Server(a,{capabilities:{tools:{listChanged:true},resources:{listChanged:true},tasks:{list:{},cancel:{},requests:{tools:{call:{}}}}},taskStore:W});function E(e){let t=u.get(e);return t||(t={data:new Map,grants:new Set,toolOverrides:new Map,resourceOverrides:new Map},u.set(e,t)),t}function V(e,t){let n=E(t);return B(e.hiddenByMiddlewares,e.name,n.toolOverrides,n.grants)}function H(e,t){let n=E(t);return B(e.hiddenByMiddlewares,e.uri,n.resourceOverrides,n.grants)}function J(e,t){let n=E(t);return B(e.hiddenByMiddlewares,e.name,n.toolOverrides,n.grants)}function O(e){(e&&p.get(e)||k).sendToolListChanged().catch(()=>{});}function _(e){(e&&p.get(e)||k).sendResourceListChanged().catch(()=>{});}function b(e){let t=E(e);return {get(n){return t.data.get(n)},set(n,o){t.data.set(n,o);},authorize(n){t.grants.add(n),O(e),_(e);},revoke(n){t.grants.delete(n),O(e),_(e);},enableTools(...n){for(let o of n)t.toolOverrides.set(o,"enabled");O(e);},disableTools(...n){for(let o of n)t.toolOverrides.set(o,"disabled");O(e);},enableResources(...n){for(let o of n)t.resourceOverrides.set(o,"enabled");_(e);},disableResources(...n){for(let o of n)t.resourceOverrides.set(o,"disabled");_(e);}}}function Z(e){e.setRequestHandler(ListToolsRequestSchema,(t,n)=>{let o=n.sessionId??"default",r=[];for(let s of c$1.values())V(s,o)&&r.push({name:s.name,description:s.description,inputSchema:j(s.input)});for(let s of d.values())J(s,o)&&r.push({name:s.name,description:s.description,inputSchema:j(s.input),execution:{taskSupport:"required"}});return {tools:r}}),e.setRequestHandler(CallToolRequestSchema,async(t,n)=>{let{name:o,arguments:r}=t.params,s=n.sessionId??"default",g=c$1.get(o);if(g){if(!V(g,s))return c(`Tool not available: ${o}`);let w=N(e,s,b(s),o,n.signal,(R,M)=>x(R,s,M),I),f=()=>Promise.resolve(g.handler(r??{},w));return U(g.middlewares,w,f)()}let T=d.get(o);if(T){if(!J(T,s))return c(`Tool not available: ${o}`);let w=n.taskStore;if(!w)return c("Task store not available");let f=await w.createTask({pollInterval:1e3}),h=f.taskId,R={progress(y,P){if(q.has(h))return;let de=P?`${y}% ${P}`:`${y}%`;w.updateTaskStatus(h,"working",de).catch(()=>{});},get cancelled(){return q.has(h)}},M={...N(e,s,b(s),o,n.signal,(y,P)=>x(y,s,P),I),task:R},ce=async()=>((async()=>{try{let y=await T.handler(r??{},M);q.has(h)||await w.storeTaskResult(h,"completed",y);}catch(y){if(!q.has(h)){let P=y instanceof Error?y.message:String(y);await w.storeTaskResult(h,"failed",c(P)).catch(()=>{});}}})(),{task:f});return U(T.middlewares,M,ce)()}return c(`Unknown tool: ${o}`)}),e.setRequestHandler(ListResourcesRequestSchema,(t,n)=>{let o=n.sessionId??"default",r=[];for(let s of i.values())!s.isTemplate&&H(s,o)&&r.push({uri:s.uri,name:s.name,description:s.description,mimeType:s.mimeType});return {resources:r}}),e.setRequestHandler(ListResourceTemplatesRequestSchema,(t,n)=>{let o=n.sessionId??"default",r=[];for(let s of i.values())s.isTemplate&&H(s,o)&&r.push({uriTemplate:s.uri,name:s.name,description:s.description,mimeType:s.mimeType});return {resourceTemplates:r}}),e.setRequestHandler(ReadResourceRequestSchema,async(t,n)=>{let{uri:o}=t.params,r=X(i,o);if(!r)throw new Error(`Unknown resource: ${o}`);let s=n.sessionId??"default";if(!H(r,s))throw new Error(`Resource not available: ${o}`);let g=b(s),T={uri:o,session:g,sessionId:s,roots:F(e)},w=N(e,s,g,r.uri,n.signal,(R,M)=>x(R,s,M),I),f=async()=>{let R=await r.handler(o,T);return {contents:[{uri:o,mimeType:R.mimeType??r.mimeType,...R.text!=null?{text:R.text}:{},...R.blob!=null?{blob:R.blob}:{}}]}};return U(r.middlewares,w,f)()});}Z(k);function te(e){l.push(e);}function ne(...e){let t=e[0],n=$(`tool("${t}")`,e.slice(1));if(typeof n.config.name=="string")throw new TypeError(`tool("${t}"): second-to-last argument must be a config object`);let o=n.config,r=[...l,...n.middlewares],s={name:t,description:o.description,middlewares:r};c$1.set(t,{name:t,description:o.description,input:o.input,handler:n.handler,middlewares:r,hiddenByMiddlewares:A(s,r)});}function oe(...e){let t=e[0],n=$(`resource("${t}")`,e.slice(1));if(typeof n.config.name!="string")throw new TypeError(`resource("${t}"): second-to-last argument must be a config object with a "name" property`);let o=n.config,r={name:o.name,description:o.description,middlewares:n.middlewares},s=t.includes("{");i.set(t,{uri:t,isTemplate:s,uriPattern:s?Q(t):null,name:o.name,description:o.description,mimeType:o.mimeType,handler:n.handler,middlewares:n.middlewares,hiddenByMiddlewares:A(r,n.middlewares)});}function se(...e){let t=e[0],n=$(`task("${t}")`,e.slice(1));if(typeof n.config.name=="string")throw new TypeError(`task("${t}"): second-to-last argument must be a config object`);let o=n.config,r=[...l,...n.middlewares],s={name:t,description:o.description,middlewares:r};d.set(t,{name:t,description:o.description,input:o.input,handler:n.handler,middlewares:r,hiddenByMiddlewares:A(s,r)});}async function re(){let{StdioServerTransport:e}=await import('@modelcontextprotocol/sdk/server/stdio.js'),t=new e;await k.connect(t);}async function ie(e){await k.connect(e);}let ae={tools:{listChanged:true},resources:{listChanged:true},tasks:{list:{},cancel:{},requests:{tools:{call:{}}}}};function K(){let e=new Server(a,{capabilities:ae,taskStore:W});return Z(e),e}function le(e){let t=null;async function n(){return t||(t=(await import('@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js')).WebStandardStreamableHTTPServerTransport),t}if(e?.sessionless)return async r=>{let s=await n(),g=K(),T=new s({sessionIdGenerator:void 0,enableJsonResponse:e?.enableJsonResponse});return await g.connect(T),T.handleRequest(r)};let o=new Map;return async r=>{let s=await n(),g=r.headers.get("mcp-session-id");if(g){let f=o.get(g);return f?(e?.onRequest&&await e.onRequest(r,g,b(g)),f.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 T=K(),w=new s({sessionIdGenerator:e?.sessionIdGenerator??(()=>crypto.randomUUID()),enableJsonResponse:e?.enableJsonResponse,onsessioninitialized:f=>{o.set(f,{server:T,transport:w}),p.set(f,T),e?.onRequest&&e.onRequest(r,f,b(f));},onsessionclosed:f=>{o.delete(f),p.delete(f);}});return await T.connect(w),w.handleRequest(r)}}return {use:te,tool:ne,resource:oe,task:se,stdio:re,http:le,session:b,completeElicitation:G,connect:ie,_server:k,_getSession:E,_isToolVisible(e,t){let n=c$1.get(e);return n?V(n,t):false},_isResourceVisible(e,t){let n=i.get(e);return n?H(n,t):false},_isTaskVisible(e,t){let n=d.get(e);return n?J(n,t):false},_createSessionAPI:b}}export{ve as createMCPServer};
@@ -1,13 +1,11 @@
1
- import { s as ToolMiddleware } from '../types-D504PjnN.js';
1
+ import { t as ToolMiddleware } from '../types-CqT2idkd.js';
2
+ import { GuardOptions } from './guard.js';
2
3
  import '@modelcontextprotocol/sdk/types.js';
3
4
  import 'zod';
4
5
 
5
- interface AuthOptions {
6
- /** Session key to check for authentication. Default: "user" */
7
- sessionKey?: string;
8
- /** Error message when not authenticated. */
9
- message?: string;
10
- }
11
- declare function auth(options?: AuthOptions): ToolMiddleware;
6
+ /** @deprecated Use `GuardOptions` from `@lynq/lynq/guard` instead. */
7
+ type AuthOptions = GuardOptions;
8
+ /** @deprecated Use `guard()` from `@lynq/lynq/guard` instead. */
9
+ declare function auth(options?: GuardOptions): ToolMiddleware;
12
10
 
13
11
  export { type AuthOptions, auth };
@@ -1 +1 @@
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};
1
+ import {a}from'../chunk-L7SD7KKW.mjs';import'../chunk-VAAZWX4U.mjs';function e(o){return a({name:"auth",...o})}export{e as auth};
@@ -0,0 +1,19 @@
1
+ import { t as ToolMiddleware } from '../types-CqT2idkd.js';
2
+ import '@modelcontextprotocol/sdk/types.js';
3
+ import 'zod';
4
+
5
+ interface BearerOptions {
6
+ /** Middleware name. Default: "bearer" */
7
+ name?: string;
8
+ /** Session key where the raw token is stored. Default: "token" */
9
+ tokenKey?: string;
10
+ /** Session key to store verified user. Default: "user" */
11
+ sessionKey?: string;
12
+ /** Verify the token. Return user data or null/throw on failure. */
13
+ verify: (token: string) => Promise<unknown | null>;
14
+ /** Error message. Default: "Invalid or missing token." */
15
+ message?: string;
16
+ }
17
+ declare function bearer(options: BearerOptions): ToolMiddleware;
18
+
19
+ export { type BearerOptions, bearer };
@@ -0,0 +1 @@
1
+ import {c}from'../chunk-VAAZWX4U.mjs';function y(e){let n=e.name??"bearer",m=e.tokenKey??"token",t=e.sessionKey??"user",o=e.message??"Invalid or missing token.";return {name:n,onRegister(){return false},async onCall(s,i){if(s.session.get(t))return i();let a=s.session.get(m);if(!a)return c(o);let g=await e.verify(a);return g?(s.session.set(t,g),s.session.authorize(n),i()):c(o)}}}export{y as bearer};
@@ -0,0 +1,12 @@
1
+ import { t as ToolMiddleware, q as ToolContext } from '../types-CqT2idkd.js';
2
+ import '@modelcontextprotocol/sdk/types.js';
3
+ import 'zod';
4
+
5
+ /** Run the first middleware that doesn't short-circuit. */
6
+ declare function some(...middlewares: ToolMiddleware[]): ToolMiddleware;
7
+ /** Run all middlewares. Stop if any short-circuits. */
8
+ declare function every(...middlewares: ToolMiddleware[]): ToolMiddleware;
9
+ /** Run middleware only when the condition is false. */
10
+ declare function except(condition: (c: ToolContext) => boolean, middleware: ToolMiddleware): ToolMiddleware;
11
+
12
+ export { every, except, some };
@@ -0,0 +1 @@
1
+ function f(...n){return {name:`some(${n.map(e=>e.name).join(",")})`,onRegister(e){for(let o of n)if(o.onRegister?.(e)===false)return false},async onCall(e,o){let t,r;for(let s of n){if(!s.onCall){t=s;break}let u=false,a=async()=>(u=true,o()),i=await s.onCall(e,a);if(u)return t=s,i;r=i;}return t?o():r},onResult(e){return e}}}function c(...n){let l=n.map(o=>o.name),e=n.filter(o=>o.onResult).reverse();return {name:`every(${l.join(",")})`,onRegister(o){for(let t of n)if(t.onRegister?.(o)===false)return false},async onCall(o,t){let r=n.filter(a=>a.onCall),s=0,u=async()=>s>=r.length?t():r[s++].onCall(o,u);return u()},onResult(o,t){let r=o;for(let s of e){let u=s.onResult(r,t);u instanceof Promise||(r=u);}return r}}}function T(n,l){return {name:`except(${l.name})`,onRegister(e){return l.onRegister?.(e)},async onCall(e,o){return n(e)?o():l.onCall?l.onCall(e,o):o()},onResult(e,o){return l.onResult?l.onResult(e,o):e}}}export{c as every,T as except,f as some};
@@ -0,0 +1,19 @@
1
+ import { z } from 'zod';
2
+ import { t as ToolMiddleware } from '../types-CqT2idkd.js';
3
+ import '@modelcontextprotocol/sdk/types.js';
4
+
5
+ interface CredentialsOptions<T extends z.ZodObject<z.ZodRawShape>> {
6
+ /** Middleware name. Default: "credentials" */
7
+ name?: string;
8
+ /** Message shown to the user. */
9
+ message: string;
10
+ /** Zod schema for the form fields. */
11
+ schema: T;
12
+ /** Verify the submitted credentials. Return user data or null. */
13
+ verify: (fields: z.infer<T>) => Promise<unknown | null>;
14
+ /** Session key to store verified user. Default: "user" */
15
+ sessionKey?: string;
16
+ }
17
+ declare function credentials<T extends z.ZodObject<z.ZodRawShape>>(options: CredentialsOptions<T>): ToolMiddleware;
18
+
19
+ export { type CredentialsOptions, credentials };
@@ -0,0 +1 @@
1
+ import {c as c$1}from'../chunk-VAAZWX4U.mjs';function c(e){let r=e.name??"credentials",t=e.sessionKey??"user";return {name:r,onRegister(){return false},async onCall(s,i){if(s.session.get(t))return i();let a=await s.elicit.form(e.message,e.schema);if(a.action!=="accept")return c$1("Authentication cancelled.");let o=await e.verify(a.content);return o?(s.session.set(t,o),s.session.authorize(r),i()):c$1("Invalid credentials.")}}}export{c as credentials};
@@ -0,0 +1,42 @@
1
+ import { t as ToolMiddleware, M as MCPServer } from '../types-CqT2idkd.js';
2
+ import '@modelcontextprotocol/sdk/types.js';
3
+ import 'zod';
4
+
5
+ interface GitHubOAuthOptions {
6
+ /** Middleware name. Default: "github-oauth" */
7
+ name?: string;
8
+ /** GitHub OAuth App client ID. */
9
+ clientId: string;
10
+ /** GitHub OAuth App client secret. */
11
+ clientSecret: string;
12
+ /** OAuth callback URL (your server's callback endpoint). */
13
+ redirectUri: string;
14
+ /** GitHub OAuth scopes. Default: [] */
15
+ scopes?: string[];
16
+ /** Session key for user data. Default: "user" */
17
+ sessionKey?: string;
18
+ /** Message shown to the user. Default: "Please sign in with GitHub to continue." */
19
+ message?: string;
20
+ /** Timeout in ms. Default: 300000 */
21
+ timeout?: number;
22
+ }
23
+ declare function githubOAuth(options: GitHubOAuthOptions): ToolMiddleware;
24
+ interface HandleGitHubCallbackOptions {
25
+ clientId: string;
26
+ clientSecret: string;
27
+ /** Session key for user data. Default: "user" */
28
+ sessionKey?: string;
29
+ }
30
+ /**
31
+ * Handle GitHub OAuth callback. Call from your HTTP callback route.
32
+ * Exchanges code for token, fetches user info, stores in session, and completes elicitation.
33
+ */
34
+ declare function handleGitHubCallback(server: MCPServer, params: {
35
+ code: string;
36
+ state: string;
37
+ }, options: HandleGitHubCallbackOptions): Promise<{
38
+ success: boolean;
39
+ error?: string;
40
+ }>;
41
+
42
+ export { type GitHubOAuthOptions, type HandleGitHubCallbackOptions, githubOAuth, handleGitHubCallback };
@@ -0,0 +1,2 @@
1
+ import {a}from'../chunk-2KEVF6YL.mjs';import'../chunk-STWVQGX5.mjs';import'../chunk-VAAZWX4U.mjs';function h(e){let s=e.scopes??[],t={name:e.name??"github-oauth",sessionKey:e.sessionKey??"user",message:e.message??"Please sign in with GitHub to continue.",buildUrl({sessionId:c,elicitationId:n}){let r=new URLSearchParams({client_id:e.clientId,redirect_uri:e.redirectUri,state:`${c}:${n}`});return s.length>0&&r.set("scope",s.join(" ")),`https://github.com/login/oauth/authorize?${r}`}};return e.timeout!==void 0&&(t.timeout=e.timeout),a(t)}async function m(e,s,t){let c=t.sessionKey??"user",[n,r]=s.state.split(":");if(!n||!r)return {success:false,error:"Invalid state parameter"};try{let i=await(await fetch("https://github.com/login/oauth/access_token",{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({client_id:t.clientId,client_secret:t.clientSecret,code:s.code})})).json();if(!i.access_token)return {success:!1,error:i.error_description??i.error??"Token exchange failed"};let l=await(await fetch("https://api.github.com/user",{headers:{Authorization:`Bearer ${i.access_token}`}})).json(),a=e.session(n);return a.set(c,l),a.set("accessToken",i.access_token),e.completeElicitation(r),{success:!0}}catch(o){return {success:false,error:o instanceof Error?o.message:String(o)}}}
2
+ export{h as githubOAuth,m as handleGitHubCallback};
@@ -0,0 +1,43 @@
1
+ import { t as ToolMiddleware, M as MCPServer } from '../types-CqT2idkd.js';
2
+ import '@modelcontextprotocol/sdk/types.js';
3
+ import 'zod';
4
+
5
+ interface GoogleOAuthOptions {
6
+ /** Middleware name. Default: "google-oauth" */
7
+ name?: string;
8
+ /** Google OAuth client ID. */
9
+ clientId: string;
10
+ /** Google OAuth client secret. */
11
+ clientSecret: string;
12
+ /** OAuth callback URL (your server's callback endpoint). */
13
+ redirectUri: string;
14
+ /** OAuth scopes. Default: ["openid", "profile", "email"] */
15
+ scopes?: string[];
16
+ /** Session key for user data. Default: "user" */
17
+ sessionKey?: string;
18
+ /** Message shown to the user. Default: "Please sign in with Google to continue." */
19
+ message?: string;
20
+ /** Timeout in ms. Default: 300000 */
21
+ timeout?: number;
22
+ }
23
+ declare function googleOAuth(options: GoogleOAuthOptions): ToolMiddleware;
24
+ interface HandleGoogleCallbackOptions {
25
+ clientId: string;
26
+ clientSecret: string;
27
+ redirectUri: string;
28
+ /** Session key for user data. Default: "user" */
29
+ sessionKey?: string;
30
+ }
31
+ /**
32
+ * Handle Google OAuth callback. Call from your HTTP callback route.
33
+ * Exchanges code for tokens, fetches user info, stores in session, and completes elicitation.
34
+ */
35
+ declare function handleGoogleCallback(server: MCPServer, params: {
36
+ code: string;
37
+ state: string;
38
+ }, options: HandleGoogleCallbackOptions): Promise<{
39
+ success: boolean;
40
+ error?: string;
41
+ }>;
42
+
43
+ export { type GoogleOAuthOptions, type HandleGoogleCallbackOptions, googleOAuth, handleGoogleCallback };
@@ -0,0 +1,2 @@
1
+ import {a}from'../chunk-2KEVF6YL.mjs';import'../chunk-STWVQGX5.mjs';import'../chunk-VAAZWX4U.mjs';function m(e){let r=e.scopes??["openid","profile","email"],t={name:e.name??"google-oauth",sessionKey:e.sessionKey??"user",message:e.message??"Please sign in with Google to continue.",buildUrl({sessionId:i,elicitationId:o}){return `https://accounts.google.com/o/oauth2/v2/auth?${new URLSearchParams({client_id:e.clientId,redirect_uri:e.redirectUri,response_type:"code",scope:r.join(" "),state:`${i}:${o}`,access_type:"offline"})}`}};return e.timeout!==void 0&&(t.timeout=e.timeout),a(t)}async function p(e,r,t){let i=t.sessionKey??"user",[o,c]=r.state.split(":");if(!o||!c)return {success:false,error:"Invalid state parameter"};try{let s=await(await fetch("https://oauth2.googleapis.com/token",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({code:r.code,client_id:t.clientId,client_secret:t.clientSecret,redirect_uri:t.redirectUri,grant_type:"authorization_code"})})).json();if(!s.access_token)return {success:!1,error:s.error_description??s.error??"Token exchange failed"};let d=await(await fetch("https://www.googleapis.com/oauth2/v2/userinfo",{headers:{Authorization:`Bearer ${s.access_token}`}})).json(),a=e.session(o);return a.set(i,d),a.set("accessToken",s.access_token),s.id_token&&a.set("idToken",s.id_token),e.completeElicitation(c),{success:!0}}catch(n){return {success:false,error:n instanceof Error?n.message:String(n)}}}
2
+ export{m as googleOAuth,p as handleGoogleCallback};
@@ -0,0 +1,15 @@
1
+ import { t as ToolMiddleware } from '../types-CqT2idkd.js';
2
+ import '@modelcontextprotocol/sdk/types.js';
3
+ import 'zod';
4
+
5
+ interface GuardOptions {
6
+ /** Middleware name. Used for authorize()/revoke(). Default: "guard" */
7
+ name?: string;
8
+ /** Session key to check. Default: "user" */
9
+ sessionKey?: string;
10
+ /** Error message when not authorized. */
11
+ message?: string;
12
+ }
13
+ declare function guard(options?: GuardOptions): ToolMiddleware;
14
+
15
+ export { type GuardOptions, guard };
@@ -0,0 +1 @@
1
+ export{a as guard}from'../chunk-L7SD7KKW.mjs';import'../chunk-VAAZWX4U.mjs';
@@ -0,0 +1,27 @@
1
+ import { t as ToolMiddleware } from '../types-CqT2idkd.js';
2
+ import '@modelcontextprotocol/sdk/types.js';
3
+ import 'zod';
4
+
5
+ interface JwtOptions {
6
+ /** Middleware name. Default: "jwt" */
7
+ name?: string;
8
+ /** Session key where the raw token is stored. Default: "token" */
9
+ tokenKey?: string;
10
+ /** Session key to store decoded payload. Default: "user" */
11
+ sessionKey?: string;
12
+ /** Symmetric secret for HMAC verification. */
13
+ secret?: string;
14
+ /** JWKS URI for remote key fetching. */
15
+ jwksUri?: string;
16
+ /** Expected issuer claim. */
17
+ issuer?: string;
18
+ /** Expected audience claim. */
19
+ audience?: string;
20
+ /** Additional validation on the decoded payload. Return user data or null. */
21
+ validate?: (payload: Record<string, unknown>) => unknown | null | Promise<unknown | null>;
22
+ /** Error message. Default: "Invalid or expired JWT." */
23
+ message?: string;
24
+ }
25
+ declare function jwt(options: JwtOptions): ToolMiddleware;
26
+
27
+ export { type JwtOptions, jwt };
@@ -0,0 +1 @@
1
+ import {c}from'../chunk-VAAZWX4U.mjs';function j(e){let u=e.name??"jwt",y=e.tokenKey??"token",l=e.sessionKey??"user",d=e.message??"Invalid or expired JWT.",s=null;function m(){return s||(s=import('jose').catch(()=>(s=null,null))),s}return {name:u,onRegister(){return false},async onCall(n,c$1){if(n.session.get(l))return c$1();let a=n.session.get(y);if(!a)return c("JWT required.");let t=await m();if(!t)return c("jose library is required for JWT middleware. Install it: pnpm add jose");try{let i;if(e.jwksUri){let o=t.createRemoteJWKSet(new URL(e.jwksUri));i=(await t.jwtVerify(a,o,{issuer:e.issuer,audience:e.audience})).payload;}else if(e.secret){let o=new TextEncoder().encode(e.secret);i=(await t.jwtVerify(a,o,{issuer:e.issuer,audience:e.audience})).payload;}else return c("JWT middleware misconfigured: provide secret or jwksUri.");let w=e.validate?await e.validate(i):i;return w?(n.session.set(l,w),n.session.authorize(u),c$1()):c(d)}catch{return c(d)}}}}export{j as jwt};
@@ -0,0 +1,11 @@
1
+ import { t as ToolMiddleware } from '../types-CqT2idkd.js';
2
+ import '@modelcontextprotocol/sdk/types.js';
3
+ import 'zod';
4
+
5
+ interface LoggerOptions {
6
+ /** Custom log function. Default: console.log */
7
+ log?: (message: string) => void;
8
+ }
9
+ declare function logger(options?: LoggerOptions): ToolMiddleware;
10
+
11
+ export { type LoggerOptions, logger };
@@ -0,0 +1 @@
1
+ function a(s){let e=s?.log??console.log;return {name:"logger",async onCall(o,t){let n=performance.now();e(`[${o.toolName}] called (session: ${o.sessionId})`);let r=await t(),l=(performance.now()-n).toFixed(1);return e(`[${o.toolName}] ${l}ms${r.isError?" ERROR":""}`),r}}}export{a as logger};
@@ -0,0 +1,22 @@
1
+ import { t as ToolMiddleware } from '../types-CqT2idkd.js';
2
+ import '@modelcontextprotocol/sdk/types.js';
3
+ import 'zod';
4
+
5
+ interface OAuthOptions {
6
+ /** Middleware name. Default: "oauth" */
7
+ name?: string;
8
+ /** Session key for storing tokens. Default: "user" */
9
+ sessionKey?: string;
10
+ /** Message shown to the user. Default: "Please sign in to continue." */
11
+ message?: string;
12
+ /** Build the OAuth authorization URL. */
13
+ buildUrl: (params: {
14
+ sessionId: string;
15
+ elicitationId: string;
16
+ }) => string;
17
+ /** Timeout in ms. Default: 300000 */
18
+ timeout?: number;
19
+ }
20
+ declare function oauth(options: OAuthOptions): ToolMiddleware;
21
+
22
+ export { type OAuthOptions, oauth };
@@ -0,0 +1 @@
1
+ export{a as oauth}from'../chunk-2KEVF6YL.mjs';import'../chunk-STWVQGX5.mjs';import'../chunk-VAAZWX4U.mjs';
@@ -0,0 +1,22 @@
1
+ import { t as ToolMiddleware } from '../types-CqT2idkd.js';
2
+ import '@modelcontextprotocol/sdk/types.js';
3
+ import 'zod';
4
+
5
+ interface PaymentOptions {
6
+ /** Middleware name. Default: "payment" */
7
+ name?: string;
8
+ /** Session key for storing payment confirmation. Default: "payment" */
9
+ sessionKey?: string;
10
+ /** Message shown to the user. Default: "Please complete payment to continue." */
11
+ message?: string;
12
+ /** Build the payment page URL. */
13
+ buildUrl: (params: {
14
+ sessionId: string;
15
+ elicitationId: string;
16
+ }) => string;
17
+ /** Timeout in ms. Default: 300000 */
18
+ timeout?: number;
19
+ }
20
+ declare function payment(options: PaymentOptions): ToolMiddleware;
21
+
22
+ export { type PaymentOptions, payment };
@@ -0,0 +1 @@
1
+ import {a}from'../chunk-STWVQGX5.mjs';import'../chunk-VAAZWX4U.mjs';function i(e){let t={name:e.name??"payment",sessionKey:e.sessionKey??"payment",message:e.message??"Please complete payment to continue.",buildUrl:e.buildUrl,declineMessage:"Payment cancelled."};return e.timeout!==void 0&&(t.timeout=e.timeout),a(t)}export{i as payment};
@@ -0,0 +1,15 @@
1
+ import { t as ToolMiddleware } from '../types-CqT2idkd.js';
2
+ import '@modelcontextprotocol/sdk/types.js';
3
+ import 'zod';
4
+
5
+ interface RateLimitOptions {
6
+ /** Maximum calls per window. */
7
+ max: number;
8
+ /** Window duration in milliseconds. Default: 60000 (1 minute) */
9
+ windowMs?: number;
10
+ /** Error message. */
11
+ message?: string;
12
+ }
13
+ declare function rateLimit(options: RateLimitOptions): ToolMiddleware;
14
+
15
+ export { type RateLimitOptions, rateLimit };
@@ -0,0 +1 @@
1
+ import {c}from'../chunk-VAAZWX4U.mjs';function l(n){let{max:o,windowMs:r=6e4}=n,u=n.message??`Rate limit exceeded. Max ${o} calls per ${r/1e3}s.`;return {name:"rateLimit",async onCall(t,i){let s=`rateLimit:${t.toolName}`,e=t.session.get(s),a=Date.now();return !e||a>=e.resetAt?(t.session.set(s,{count:1,resetAt:a+r}),i()):e.count>=o?c(u):(t.session.set(s,{...e,count:e.count+1}),i())}}}export{l as rateLimit};
@@ -0,0 +1,13 @@
1
+ import { t as ToolMiddleware } from '../types-CqT2idkd.js';
2
+ import '@modelcontextprotocol/sdk/types.js';
3
+ import 'zod';
4
+
5
+ interface TruncateOptions {
6
+ /** Maximum characters per text content block. */
7
+ maxChars: number;
8
+ /** Suffix appended when truncated. Default: "..." */
9
+ suffix?: string;
10
+ }
11
+ declare function truncate(options: TruncateOptions): ToolMiddleware;
12
+
13
+ export { type TruncateOptions, truncate };
@@ -0,0 +1 @@
1
+ function a(e){let{maxChars:r}=e,n=e.suffix??"...";return {name:"truncate",onResult(s){return {...s,content:s.content.map(t=>t.type==="text"&&t.text&&t.text.length>r?{...t,text:t.text.slice(0,r-n.length)+n}:t)}}}}export{a as truncate};
@@ -0,0 +1,24 @@
1
+ import { t as ToolMiddleware } from '../types-CqT2idkd.js';
2
+ import '@modelcontextprotocol/sdk/types.js';
3
+ import 'zod';
4
+
5
+ interface UrlActionOptions {
6
+ /** Middleware name. Default: "url-action" */
7
+ name?: string;
8
+ /** Session key to check/store result. Default: "user" */
9
+ sessionKey?: string;
10
+ /** Message shown to the user in the elicitation. */
11
+ message: string;
12
+ /** Build the URL. Receives sessionId and elicitationId for callback routing. */
13
+ buildUrl: (params: {
14
+ sessionId: string;
15
+ elicitationId: string;
16
+ }) => string;
17
+ /** Timeout in ms for waiting for external callback. Default: 300000 (5 min). */
18
+ timeout?: number;
19
+ /** Error message when user declines. Default: "Action cancelled." */
20
+ declineMessage?: string;
21
+ }
22
+ declare function urlAction(options: UrlActionOptions): ToolMiddleware;
23
+
24
+ export { type UrlActionOptions, urlAction };
@@ -0,0 +1 @@
1
+ export{a as urlAction}from'../chunk-STWVQGX5.mjs';import'../chunk-VAAZWX4U.mjs';
package/dist/test.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
- import { k as Session, M as MCPServer } from './types-D504PjnN.js';
2
+ import { l as Session, M as MCPServer } from './types-CqT2idkd.js';
3
3
  import 'zod';
4
4
 
5
5
  interface TestClient {
@@ -45,11 +45,19 @@ interface ElicitFormResult<T = Record<string, string | number | boolean | string
45
45
  interface ElicitUrlResult {
46
46
  action: "accept" | "decline" | "cancel";
47
47
  }
48
+ interface ElicitUrlOptions {
49
+ /** Pre-generated elicitation ID. If omitted, a random UUID is used. */
50
+ elicitationId?: string;
51
+ /** If true, wait for completeElicitation() before resolving. Default: false. */
52
+ waitForCompletion?: boolean;
53
+ /** Timeout in ms for waiting. Default: 300000 (5 minutes). */
54
+ timeout?: number;
55
+ }
48
56
  interface Elicit {
49
57
  /** Request structured data from the user via a form. */
50
58
  form<T extends z.ZodObject<z.ZodRawShape>>(message: string, schema: T): Promise<ElicitFormResult<z.infer<T>>>;
51
59
  /** Direct the user to an external URL. */
52
- url(message: string, url: string): Promise<ElicitUrlResult>;
60
+ url(message: string, url: string, options?: ElicitUrlOptions): Promise<ElicitUrlResult>;
53
61
  }
54
62
  interface SampleOptions {
55
63
  maxTokens?: number;
@@ -108,16 +116,16 @@ interface ToolMiddleware {
108
116
  /** Called when a tool is registered. Return false to hide the tool initially. */
109
117
  onRegister?(tool: ToolInfo): boolean | undefined;
110
118
  /** Called when a tool is invoked. Must call next() to continue the chain. */
111
- onCall?(ctx: ToolContext, next: () => Promise<CallToolResult>): Promise<CallToolResult>;
119
+ onCall?(c: ToolContext, next: () => Promise<CallToolResult>): Promise<CallToolResult>;
112
120
  /** Called after the handler returns. Runs in reverse middleware order. */
113
- onResult?(result: CallToolResult, ctx: ToolContext): CallToolResult | Promise<CallToolResult>;
121
+ onResult?(result: CallToolResult, c: ToolContext): CallToolResult | Promise<CallToolResult>;
114
122
  }
115
123
  type InferInput<T> = T extends z.ZodTypeAny ? z.output<T> : Record<string, unknown>;
116
124
  interface ToolConfig<TInput = unknown> {
117
125
  description?: string;
118
126
  input?: TInput;
119
127
  }
120
- type ToolHandler<TInput = unknown> = (args: InferInput<TInput>, ctx: ToolContext) => CallToolResult | Promise<CallToolResult>;
128
+ type ToolHandler<TInput = unknown> = (args: InferInput<TInput>, c: ToolContext) => CallToolResult | Promise<CallToolResult>;
121
129
  /** @experimental */
122
130
  interface TaskConfig<TInput = unknown> {
123
131
  description?: string;
@@ -135,7 +143,7 @@ interface TaskContext extends ToolContext {
135
143
  task: TaskControl;
136
144
  }
137
145
  /** @experimental */
138
- type TaskHandler<TInput = unknown> = (args: InferInput<TInput>, ctx: TaskContext) => CallToolResult | Promise<CallToolResult>;
146
+ type TaskHandler<TInput = unknown> = (args: InferInput<TInput>, c: TaskContext) => CallToolResult | Promise<CallToolResult>;
139
147
  interface ResourceConfig {
140
148
  name: string;
141
149
  description?: string;
@@ -153,7 +161,7 @@ interface ResourceContext {
153
161
  /** Query client-provided filesystem roots. */
154
162
  roots: () => Promise<RootInfo[]>;
155
163
  }
156
- type ResourceHandler = (uri: string, ctx: ResourceContext) => ResourceContent | Promise<ResourceContent>;
164
+ type ResourceHandler = (uri: string, c: ResourceContext) => ResourceContent | Promise<ResourceContent>;
157
165
  interface HttpAdapterOptions {
158
166
  /** Disable session management. Default: false. */
159
167
  sessionless?: boolean;
@@ -161,6 +169,8 @@ interface HttpAdapterOptions {
161
169
  sessionIdGenerator?: () => string;
162
170
  /** Return JSON instead of SSE streams. Default: false. */
163
171
  enableJsonResponse?: boolean;
172
+ /** Called on each HTTP request after session is resolved. Use to inject auth headers into sessions. */
173
+ onRequest?: (req: Request, sessionId: string, session: Session) => void | Promise<void>;
164
174
  }
165
175
  interface MCPServer {
166
176
  /** Register a global middleware applied to all subsequently registered tools. */
@@ -181,6 +191,10 @@ interface MCPServer {
181
191
  stdio(): Promise<void>;
182
192
  /** Start HTTP transport. Returns a Web Standard request handler. */
183
193
  http(options?: HttpAdapterOptions): (req: Request) => Promise<Response>;
194
+ /** Access a session by ID (for external HTTP callback routes). Stateful mode only. */
195
+ session(sessionId: string): Session;
196
+ /** Complete a pending URL elicitation (called from external HTTP callback). */
197
+ completeElicitation(elicitationId: string): void;
184
198
  }
185
199
 
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 };
200
+ 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 ElicitUrlOptions as b, type ElicitUrlResult as c, type ResourceContent as d, type ResourceContext as e, type ResourceHandler as f, type RootInfo as g, type SampleOptions as h, type SampleRawParams as i, type SampleRawResult as j, type ServerInfo as k, type Session as l, type TaskContext as m, type TaskControl as n, type TaskHandler as o, type ToolConfig as p, type ToolContext as q, type ToolHandler as r, type ToolInfo as s, type ToolMiddleware as t, type ToolResponse as u, error as v, image as w, json as x, text as y };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lynq/lynq",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Lightweight MCP server framework. Tool visibility control through middleware.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -27,6 +27,58 @@
27
27
  "./test": {
28
28
  "types": "./dist/test.d.ts",
29
29
  "import": "./dist/test.mjs"
30
+ },
31
+ "./guard": {
32
+ "types": "./dist/middleware/guard.d.ts",
33
+ "import": "./dist/middleware/guard.mjs"
34
+ },
35
+ "./logger": {
36
+ "types": "./dist/middleware/logger.d.ts",
37
+ "import": "./dist/middleware/logger.mjs"
38
+ },
39
+ "./rate-limit": {
40
+ "types": "./dist/middleware/rate-limit.d.ts",
41
+ "import": "./dist/middleware/rate-limit.mjs"
42
+ },
43
+ "./truncate": {
44
+ "types": "./dist/middleware/truncate.d.ts",
45
+ "import": "./dist/middleware/truncate.mjs"
46
+ },
47
+ "./combine": {
48
+ "types": "./dist/middleware/combine.d.ts",
49
+ "import": "./dist/middleware/combine.mjs"
50
+ },
51
+ "./credentials": {
52
+ "types": "./dist/middleware/credentials.d.ts",
53
+ "import": "./dist/middleware/credentials.mjs"
54
+ },
55
+ "./url-action": {
56
+ "types": "./dist/middleware/url-action.d.ts",
57
+ "import": "./dist/middleware/url-action.mjs"
58
+ },
59
+ "./oauth": {
60
+ "types": "./dist/middleware/oauth.d.ts",
61
+ "import": "./dist/middleware/oauth.mjs"
62
+ },
63
+ "./payment": {
64
+ "types": "./dist/middleware/payment.d.ts",
65
+ "import": "./dist/middleware/payment.mjs"
66
+ },
67
+ "./bearer": {
68
+ "types": "./dist/middleware/bearer.d.ts",
69
+ "import": "./dist/middleware/bearer.mjs"
70
+ },
71
+ "./jwt": {
72
+ "types": "./dist/middleware/jwt.d.ts",
73
+ "import": "./dist/middleware/jwt.mjs"
74
+ },
75
+ "./github-oauth": {
76
+ "types": "./dist/middleware/github-oauth.d.ts",
77
+ "import": "./dist/middleware/github-oauth.mjs"
78
+ },
79
+ "./google-oauth": {
80
+ "types": "./dist/middleware/google-oauth.d.ts",
81
+ "import": "./dist/middleware/google-oauth.mjs"
30
82
  }
31
83
  },
32
84
  "files": [
@@ -46,12 +98,15 @@
46
98
  "docs:preview": "vitepress preview docs",
47
99
  "prepublishOnly": "pnpm build"
48
100
  },
101
+ "dependencies": {
102
+ "@modelcontextprotocol/sdk": "^1.27.0"
103
+ },
49
104
  "devDependencies": {
50
105
  "@biomejs/biome": "^1.9.0",
51
- "@modelcontextprotocol/sdk": "^1.27.0",
52
106
  "@types/express": "^5.0.6",
53
107
  "express": "^5.2.1",
54
108
  "hono": "^4.12.5",
109
+ "jose": "^6.2.1",
55
110
  "tsup": "^8.0.0",
56
111
  "typedoc": "^0.28.17",
57
112
  "typedoc-plugin-markdown": "^4.10.0",
@@ -61,9 +116,9 @@
61
116
  "zod": "^3.24.0"
62
117
  },
63
118
  "peerDependencies": {
64
- "@modelcontextprotocol/sdk": "^1.27.0",
65
119
  "express": "^5.0.0",
66
120
  "hono": "^4.0.0",
121
+ "jose": "^6.0.0",
67
122
  "zod": "^3.0.0"
68
123
  },
69
124
  "peerDependenciesMeta": {
@@ -75,6 +130,9 @@
75
130
  },
76
131
  "express": {
77
132
  "optional": true
133
+ },
134
+ "jose": {
135
+ "optional": true
78
136
  }
79
137
  },
80
138
  "publishConfig": {