@lunora/mcp 1.0.0-alpha.100 → 1.0.0-alpha.102

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
@@ -113,7 +113,7 @@ pnpm add @lunora/mcp
113
113
 
114
114
  ## Usage
115
115
 
116
- MCP clients spawn the `lunora-mcp` binary over stdio. Configuration comes from `LUNORA_URL` (required) and `LUNORA_ADMIN_TOKEN` (optional bearer token):
116
+ MCP clients spawn the `lunora-mcp` binary over stdio. Configuration comes from `LUNORA_URL` and `LUNORA_ADMIN_TOKEN` — both required (see [Tokens](#tokens)):
117
117
 
118
118
  ```jsonc
119
119
  {
@@ -166,7 +166,13 @@ Enable it with two env vars (or the matching `createLunoraMcpServer` options):
166
166
 
167
167
  Each exposed agent gets an `agent_<name>` tool taking `prompt` (required), an optional `threadKey` (reuse to continue a conversation; omit to start a new thread), and an optional `title`. The tool starts a durable run and awaits it up to the timeout budget; if the run outlasts the budget it returns a pending result whose `threadKey` you feed to the generic `lunora_agent_status` tool to poll for the final answer.
168
168
 
169
- Runs are **owner-scoped** to the identity the configured token resolves to. Grant a **least-privilege** token mapped to a bot identity so an agent's threads stay isolated per deployment — never the admin token.
169
+ Runs are **owner-scoped** to the identity the configured token resolves to — which is the deployment's **admin** identity, because `LUNORA_ADMIN_TOKEN` is what every tool needs (see [Tokens](#tokens)). Every agent thread this server starts therefore belongs to that one identity; run a separate MCP server per deployment if you need them kept apart.
170
+
171
+ ### Tokens
172
+
173
+ `LUNORA_ADMIN_TOKEN` must be the deployment's **admin bearer**. It cannot be scoped down: `lunora_list_functions`, `lunora_list_tables`, and the allowlist precheck that runs before _every_ `lunora_run_*` call all hit admin-gated `/_lunora/admin/*` routes, so a least-privilege token returns `ADMIN_FORBIDDEN` on the first tool call. Constructing a server without one fails fast rather than advertising tools that cannot work.
174
+
175
+ The read-only guarantee therefore does **not** come from the token's scope — it comes from `LUNORA_MCP_ALLOW_WRITES` defaulting off, which omits the write tools from `tools/list` _and_ refuses them at dispatch. Treat the MCP server itself as the trust boundary: give it the admin token, and gate who can reach it (the OAuth-protected `createAuthedMcpFetchHandler` is the supported way to expose it beyond a local stdio process).
170
176
 
171
177
  ## Resources and annotations
172
178
 
package/dist/bin.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import{parseAgentsEnv as A}from"./packem_shared/AGENT_RUN_INPUT_SCHEMA-DRSY74ed.mjs";import{connectStdio as L}from"./packem_shared/connectStdio-BCeI0Wqu.mjs";const N=new Set(["1","on","true","yes"]),_=r=>r!==void 0&&N.has(r.trim().toLowerCase());class i extends Error{code;constructor(o,e){super(o),this.name="BinError",this.code=e}}const w=async(r,o={})=>{const e=o.connect??L,a=o.writeError??(t=>{process.stderr.write(t)}),s=r.LUNORA_URL;if(s===void 0||s.length===0)throw a(`lunora-mcp: LUNORA_URL environment variable is required
3
- `),new i("LUNORA_URL environment variable is required",1);const n=Number(r.LUNORA_MCP_AGENT_TIMEOUT_MS),c=Number.isFinite(n)&&n>0?n:void 0;try{await e({agents:A(r.LUNORA_MCP_AGENTS),allowAgents:_(r.LUNORA_MCP_ALLOW_AGENTS),allowWrites:_(r.LUNORA_MCP_ALLOW_WRITES),token:r.LUNORA_ADMIN_TOKEN,url:s,...c===void 0?{}:{agentMaxWaitMs:c}})}catch(t){const E=t instanceof Error?t.message:String(t);throw a(`lunora-mcp: failed to start — ${E}
4
- `),new i(`failed to start — ${E}`,1)}};try{await w(process.env)}catch(r){process.exit(r instanceof i?r.code:1)}
2
+ import{parseAgentsEnv as E}from"./packem_shared/AGENT_RUN_INPUT_SCHEMA-DGpNvk5K.mjs";import{connectStdio as L}from"./packem_shared/connectStdio-CDsCt1IT.mjs";const d=new Set(["1","on","true","yes"]),A=r=>r!==void 0&&d.has(r.trim().toLowerCase());class o extends Error{code;constructor(t,s){super(t),this.name="BinError",this.code=s}}const l=async(r,t={})=>{const s=t.connect??L,n=t.writeError??(e=>{process.stderr.write(e)}),i=r.LUNORA_URL;if(i===void 0||i.length===0)throw n(`lunora-mcp: LUNORA_URL environment variable is required
3
+ `),new o("LUNORA_URL environment variable is required",1);const a=r.LUNORA_ADMIN_TOKEN;if(a===void 0||a.length===0)throw n(`lunora-mcp: LUNORA_ADMIN_TOKEN environment variable is required (every tool reads admin-gated routes)
4
+ `),new o("LUNORA_ADMIN_TOKEN environment variable is required",1);const c=Number(r.LUNORA_MCP_AGENT_TIMEOUT_MS),N=Number.isFinite(c)&&c>0?c:void 0;try{await s({agents:E(r.LUNORA_MCP_AGENTS),allowAgents:A(r.LUNORA_MCP_ALLOW_AGENTS),allowWrites:A(r.LUNORA_MCP_ALLOW_WRITES),token:a,url:i,...N===void 0?{}:{agentMaxWaitMs:N}})}catch(e){const _=e instanceof Error?e.message:String(e);throw n(`lunora-mcp: failed to start — ${_}
5
+ `),new o(`failed to start — ${_}`,1)}};try{await l(process.env)}catch(r){process.exit(r instanceof o?r.code:1)}
package/dist/index.d.mts CHANGED
@@ -131,19 +131,23 @@ interface LunoraMcpServerOptions {
131
131
  /** `fetch` implementation; defaults to the ambient global. */
132
132
  fetch?: typeof fetch;
133
133
  /**
134
- * Bearer token sent on every RPC. This must be the deployment's **admin
135
- * bearer**: the introspection/allowlist path every tool depends on
136
- * (`lunora_list_functions`, `lunora_list_tables`, and the `assertRunnable`
137
- * precheck that runs before every `run` tool) hits admin-gated
138
- * `/_lunora/admin/*` routes, so no scoped/app token works today — it would
139
- * 403 (`ADMIN_FORBIDDEN`) on the first tool call. The read-only guarantee is
140
- * therefore NOT enforced by the token's scope; it is enforced in-process via
141
- * `allowWrites: false` (the default), which omits the write tools from the
142
- * advertised list and refuses them at dispatch.
134
+ * Bearer token sent on every RPC. **Required** alongside `url`, and it must
135
+ * be the deployment's **admin bearer**: the introspection/allowlist path
136
+ * every tool depends on (`lunora_list_functions`, `lunora_list_tables`, and
137
+ * the `assertRunnable` precheck that runs before every `run` tool) hits
138
+ * admin-gated `/_lunora/admin/*` routes, so no scoped/app token works
139
+ * today — it would 403 (`ADMIN_FORBIDDEN`) on the first tool call. The
140
+ * read-only guarantee is therefore NOT enforced by the token's scope; it is
141
+ * enforced in-process via `allowWrites: false` (the default), which omits
142
+ * the write tools from the advertised list and refuses them at dispatch.
143
143
  *
144
- * Its presence is also what gates the observability tools (logs, Issues,
145
- * advisories, query insights, migration status): without a token they are
146
- * omitted from `ListTools` and refused at dispatch.
144
+ * Because EVERY tool needs it, omitting it is a misconfiguration rather than
145
+ * a reduced-capability mode, and `createLunoraMcpServer` says so at
146
+ * construction instead of advertising a surface that 403s on first use. The
147
+ * one exception is the `client` injection seam (tests / a pre-authenticated
148
+ * client), where this server cannot know what the client can reach and so
149
+ * reads "unknown" fail-closed: the privileged observability tools stay
150
+ * unadvertised and are refused at dispatch.
147
151
  */
148
152
  token?: string;
149
153
  /** Base URL of the deployed Lunora Worker. Required unless `client` is given. */
package/dist/index.d.ts CHANGED
@@ -131,19 +131,23 @@ interface LunoraMcpServerOptions {
131
131
  /** `fetch` implementation; defaults to the ambient global. */
132
132
  fetch?: typeof fetch;
133
133
  /**
134
- * Bearer token sent on every RPC. This must be the deployment's **admin
135
- * bearer**: the introspection/allowlist path every tool depends on
136
- * (`lunora_list_functions`, `lunora_list_tables`, and the `assertRunnable`
137
- * precheck that runs before every `run` tool) hits admin-gated
138
- * `/_lunora/admin/*` routes, so no scoped/app token works today — it would
139
- * 403 (`ADMIN_FORBIDDEN`) on the first tool call. The read-only guarantee is
140
- * therefore NOT enforced by the token's scope; it is enforced in-process via
141
- * `allowWrites: false` (the default), which omits the write tools from the
142
- * advertised list and refuses them at dispatch.
134
+ * Bearer token sent on every RPC. **Required** alongside `url`, and it must
135
+ * be the deployment's **admin bearer**: the introspection/allowlist path
136
+ * every tool depends on (`lunora_list_functions`, `lunora_list_tables`, and
137
+ * the `assertRunnable` precheck that runs before every `run` tool) hits
138
+ * admin-gated `/_lunora/admin/*` routes, so no scoped/app token works
139
+ * today — it would 403 (`ADMIN_FORBIDDEN`) on the first tool call. The
140
+ * read-only guarantee is therefore NOT enforced by the token's scope; it is
141
+ * enforced in-process via `allowWrites: false` (the default), which omits
142
+ * the write tools from the advertised list and refuses them at dispatch.
143
143
  *
144
- * Its presence is also what gates the observability tools (logs, Issues,
145
- * advisories, query insights, migration status): without a token they are
146
- * omitted from `ListTools` and refused at dispatch.
144
+ * Because EVERY tool needs it, omitting it is a misconfiguration rather than
145
+ * a reduced-capability mode, and `createLunoraMcpServer` says so at
146
+ * construction instead of advertising a surface that 403s on first use. The
147
+ * one exception is the `client` injection seam (tests / a pre-authenticated
148
+ * client), where this server cannot know what the client can reach and so
149
+ * reads "unknown" fail-closed: the privileged observability tools stay
150
+ * unadvertised and are refused at dispatch.
147
151
  */
148
152
  token?: string;
149
153
  /** Base URL of the deployed Lunora Worker. Required unless `client` is given. */
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{AGENT_RUN_INPUT_SCHEMA as r,AGENT_STATUS_TOOL_NAME as t,agentToolDefinitions as c,callAgentTool as T,parseAgentsEnv as a}from"./packem_shared/AGENT_RUN_INPUT_SCHEMA-DRSY74ed.mjs";import{createAuthedMcpFetchHandler as E,mcpTokenScopes as S}from"./packem_shared/createAuthedMcpFetchHandler-BUoCjrxu.mjs";import{createToolServer as O}from"./packem_shared/createToolServer-BtGuPyMU.mjs";import{createMcpFetchHandler as n}from"./packem_shared/createMcpFetchHandler-TKDnZTGs.mjs";import{LOCAL_SERVER_NAME as A,NO_DEPLOYMENT_MESSAGE as I,connectLocalStdio as f,createLocalMcpServer as L,localTools as m}from"./packem_shared/LOCAL_SERVER_NAME-CisKTqzv.mjs";import{createPaidMcpServer as x}from"./packem_shared/createPaidMcpServer-DyNwdCq2.mjs";import{connectStdio as i,createLunoraMcpServer as D}from"./packem_shared/connectStdio-BCeI0Wqu.mjs";import{READ_ONLY_TOOL_DEFINITIONS as v,WRITE_TOOL_DEFINITIONS as R,callTool as F,toolDefinitions as g}from"./packem_shared/READ_ONLY_TOOL_DEFINITIONS-DGexf3Tn.mjs";import{O as G}from"./packem_shared/observability-tools-Ce0YK58T.mjs";import{serveStateless as P}from"./packem_shared/serveStateless-Db7J3pAO.mjs";export{r as AGENT_RUN_INPUT_SCHEMA,t as AGENT_STATUS_TOOL_NAME,A as LOCAL_SERVER_NAME,I as NO_DEPLOYMENT_MESSAGE,G as OBSERVABILITY_TOOL_DEFINITIONS,v as READ_ONLY_TOOL_DEFINITIONS,R as WRITE_TOOL_DEFINITIONS,c as agentToolDefinitions,T as callAgentTool,F as callTool,f as connectLocalStdio,i as connectStdio,E as createAuthedMcpFetchHandler,L as createLocalMcpServer,D as createLunoraMcpServer,n as createMcpFetchHandler,x as createPaidMcpServer,O as createToolServer,m as localTools,S as mcpTokenScopes,a as parseAgentsEnv,P as serveStateless,g as toolDefinitions};
1
+ import{AGENT_RUN_INPUT_SCHEMA as r,AGENT_STATUS_TOOL_NAME as t,agentToolDefinitions as c,callAgentTool as T,parseAgentsEnv as a}from"./packem_shared/AGENT_RUN_INPUT_SCHEMA-DGpNvk5K.mjs";import{createAuthedMcpFetchHandler as E,mcpTokenScopes as S}from"./packem_shared/createAuthedMcpFetchHandler-LLzMY9SW.mjs";import{createToolServer as O}from"./packem_shared/createToolServer-BtGuPyMU.mjs";import{createMcpFetchHandler as n}from"./packem_shared/createMcpFetchHandler-B9koGwkW.mjs";import{LOCAL_SERVER_NAME as A,NO_DEPLOYMENT_MESSAGE as I,connectLocalStdio as f,createLocalMcpServer as L,localTools as m}from"./packem_shared/LOCAL_SERVER_NAME-BwlBnhX-.mjs";import{createPaidMcpServer as x}from"./packem_shared/createPaidMcpServer-DyNwdCq2.mjs";import{connectStdio as i,createLunoraMcpServer as D}from"./packem_shared/connectStdio-CDsCt1IT.mjs";import{READ_ONLY_TOOL_DEFINITIONS as v,WRITE_TOOL_DEFINITIONS as R,callTool as F,toolDefinitions as g}from"./packem_shared/READ_ONLY_TOOL_DEFINITIONS-aD-b7Hw2.mjs";import{O as G}from"./packem_shared/observability-tools-Ce0YK58T.mjs";import{serveStateless as P}from"./packem_shared/serveStateless-Db7J3pAO.mjs";export{r as AGENT_RUN_INPUT_SCHEMA,t as AGENT_STATUS_TOOL_NAME,A as LOCAL_SERVER_NAME,I as NO_DEPLOYMENT_MESSAGE,G as OBSERVABILITY_TOOL_DEFINITIONS,v as READ_ONLY_TOOL_DEFINITIONS,R as WRITE_TOOL_DEFINITIONS,c as agentToolDefinitions,T as callAgentTool,F as callTool,f as connectLocalStdio,i as connectStdio,E as createAuthedMcpFetchHandler,L as createLocalMcpServer,D as createLunoraMcpServer,n as createMcpFetchHandler,x as createPaidMcpServer,O as createToolServer,m as localTools,S as mcpTokenScopes,a as parseAgentsEnv,P as serveStateless,g as toolDefinitions};
@@ -0,0 +1 @@
1
+ const b="agents:agentRun",T="agents:agentThread",x="agents:agentMessages",d="lunora_agent_status",_=new Set(["awaiting_input","cancelled","error","idle"]),P=6e4,U=600,L={properties:{prompt:{description:"The task or message for the agent.",type:"string"},threadKey:{description:"Reuse to continue a conversation; omit to start a new thread.",type:"string"},title:{description:"Optional thread title (first run only).",type:"string"}},required:["prompt"],type:"object"},k={properties:{threadKey:{description:"The thread key returned by an agent tool call.",type:"string"}},required:["threadKey"],type:"object"},g=t=>t.toolName??`agent_${t.name}`,C=t=>{if(t===void 0)return[];const n=[];for(const e of t.split(";")){const r=e.trim();if(r.length===0)continue;const o=r.indexOf(":");if(o<=0)continue;const s=r.slice(0,o).trim(),a=r.slice(o+1).trim();s.length===0||a.length===0||n.push({description:a,name:s})}return n},G=(t,n)=>n!==!0||t.length===0?[]:[...t.map(r=>({description:`${r.description} Starts a durable agent run and returns its final answer.`,inputSchema:L,name:g(r)})),{description:"Check the status of a durable agent run (and its answer if finished) by its threadKey.",inputSchema:k,name:d}],O=(t,n)=>t===d||n.some(e=>g(e)===t),I=()=>`mcp-${crypto.randomUUID()}`,p=t=>({__lunoraRef:t}),K=async t=>{await new Promise(n=>{setTimeout(n,t)})},c=t=>({content:[{text:JSON.stringify(t,void 0,2),type:"text"}]}),i=t=>({content:[{text:t,type:"text"}],isError:!0}),l=(t,n)=>{const e=t[n];return typeof e=="string"&&e.length>0?e:void 0},A=t=>{for(let n=t.length-1;n>=0;n-=1){const e=t[n],r=e?.toolCalls,o=Array.isArray(r)&&r.length>0;if(e?.role==="assistant"&&!o)return typeof e.content=="string"?e.content:""}return""},w=t=>t!==null&&typeof t=="object"&&typeof t.status=="string"?t.status:"unknown",S=async(t,n,e,r)=>{const o=await t.query(p(x),{key:n});if(e==="error"){const s=r!==null&&typeof r=="object"?r.error:void 0;return c({error:typeof s=="string"?s:"the agent run failed",status:e,threadKey:n})}return c(e==="awaiting_input"?{hint:"This run is paused on a human-in-the-loop tool approval. Approve or reject it in the app that owns the agent; MCP cannot supply the input. Poll lunora_agent_status with this threadKey afterwards.",status:e,text:A(o),threadKey:n}:{status:e,text:A(o),threadKey:n})},R=async(t,n)=>{const e=l(n,"threadKey");if(e===void 0)return i('"threadKey" is required and must be a non-empty string');const r=await t.query(p(T),{key:e}),o=w(r);return _.has(o)?S(t,e,o,r):c({status:o==="unknown"?"running":o,threadKey:e})},q=async(t,n,e,r)=>{try{if(r.allowAgents!==!0)return i(`tool "${n}" is disabled: agent tools are off. Enable them with the LUNORA_MCP_ALLOW_AGENTS env var.`);if(n===d)return await R(t,e);const o=r.exposures.find(u=>g(u)===n);if(o===void 0)return i(`agent tool "${n}" is not exposed by this MCP server`);const s=l(e,"prompt");if(s===void 0)return i('"prompt" is required and must be a non-empty string');const a=l(e,"threadKey")??I(),h=l(e,"title"),{id:E}=await t.mutation(p(b),{agent:o.name,input:s,threadKey:a,...h===void 0?{}:{title:h}}),f=r.pollIntervalMs??U,N=r.maxWaitMs??P,v=r.wait??K,M=Math.max(1,Math.ceil(N/f));for(let u=0;u<M;u+=1){const y=await t.query(p(T),{key:a}),m=w(y);if(_.has(m))return await S(t,a,m,y);await v(f)}return c({hint:"call lunora_agent_status with this threadKey to poll for the answer",runId:E,status:"running",threadKey:a})}catch(o){const s=o instanceof Error?o.message:String(o);return i(s)}};export{L as AGENT_RUN_INPUT_SCHEMA,d as AGENT_STATUS_TOOL_NAME,G as agentToolDefinitions,q as callAgentTool,A as finalAnswer,O as isAgentToolName,C as parseAgentsEnv};
@@ -1 +1 @@
1
- import{LunoraClient as f}from"@lunora/client";import{StdioServerTransport as p}from"@modelcontextprotocol/sdk/server/stdio.js";import{createToolServer as m}from"./createToolServer-BtGuPyMU.mjs";import{createRemoteDocsIndex as v}from"./DEFAULT_DOCS_BASE_URL-CZ3fVsSc.mjs";import{docsResources as h}from"./DOCS_URI_SCHEME-CFANc3vr.mjs";import{docsTools as y}from"./DEFAULT_SEARCH_LIMIT-V7pCZ1wt.mjs";import{toolDefinitions as E,callTool as R}from"./READ_ONLY_TOOL_DEFINITIONS-DGexf3Tn.mjs";const O=(e,n)=>{if(e.size<n)return;const t=e.keys().next().value;t!==void 0&&e.delete(t)},l=e=>e.docs===!1?void 0:v({...e.docs?.baseUrl===void 0?{}:{baseUrl:e.docs.baseUrl},...e.fetch===void 0?{}:{fetch:e.fetch}}),_="lunora",S="no Lunora dev server is running for this project — start one with `lunora dev`, then call this tool again (call lunora_dev_status to check).",T=8,d=e=>{const n=new Map;return t=>{const r=JSON.stringify([t.url,t.token??""]),c=n.get(r);if(c!==void 0)return c;const o=new f({fetch:e,url:t.url});return t.token!==void 0&&t.token.length>0&&o.setAuthToken(t.token),O(n,T),n.set(r,o),o}},i=e=>e?.token!==void 0&&e.token.length>0,C=(e,n,t)=>{const r=typeof e=="function"?e:()=>e;return E(n,i(r())).map(c=>({definition:c,handle:async o=>{const s=r();return s===void 0?{content:[{text:S,type:"text"}],isError:!0}:R(t(s),c.name,o,n,i(s))}}))},P="lunora-spec:openrpc",g="lunora-spec:openapi",a=[{description:"The deployment's generated OpenRPC 1.x document — every RPC function's path, kind, and argument schema in one read, instead of list_functions plus one get_function_schema call per function.",fetch:async e=>e.fetchOpenRpc(),name:"OpenRPC specification",uri:P},{description:"The deployment's generated OpenAPI 3.1 document.",fetch:async e=>e.fetchOpenApi(),name:"OpenAPI specification",uri:g}],k=(e,n)=>{const t=typeof e=="function"?e:()=>e,r=async c=>{const o=t();if(o!==void 0)try{return await c.fetch(n(o))}catch{return}};return{list:async()=>(await Promise.all(a.map(async o=>await r(o)===void 0?void 0:{description:o.description,mimeType:"application/json",name:o.name,uri:o.uri}))).filter(o=>o!==void 0),read:async c=>{const o=a.find(u=>u.uri===c);if(o===void 0)return;const s=await r(o);return s===void 0?void 0:{mimeType:"application/json",text:JSON.stringify(s,void 0,2)}}}},w=e=>({list:async()=>(await Promise.all(e.map(async t=>t.list()))).flat(),read:async n=>{for(const t of e){const r=await t.read(n);if(r!==void 0)return r}}}),x=(e,n)=>{const t=[],r=l(e);return r!==void 0&&t.push(...y(r)),t.push(...e.extraTools??[]),e.deployment!==void 0&&t.push(...C(e.deployment,e.allowWrites??!1,n??d(e.fetch))),t},A=(e={})=>{const n=l(e),t=[];n!==void 0&&t.push(h(n));const r=d(e.fetch);return e.deployment!==void 0&&t.push(k(e.deployment,r)),m({name:_,version:e.version??"0.0.0"},x(e,r),t.length===0?void 0:w(t))},j=async(e={})=>{const n=A(e);return await n.connect(new p),n};export{_ as LOCAL_SERVER_NAME,S as NO_DEPLOYMENT_MESSAGE,g as OPENAPI_RESOURCE_URI,P as OPENRPC_RESOURCE_URI,j as connectLocalStdio,A as createLocalMcpServer,x as localTools};
1
+ import{LunoraClient as f}from"@lunora/client";import{StdioServerTransport as p}from"@modelcontextprotocol/sdk/server/stdio.js";import{createToolServer as m}from"./createToolServer-BtGuPyMU.mjs";import{createRemoteDocsIndex as v}from"./DEFAULT_DOCS_BASE_URL-CZ3fVsSc.mjs";import{docsResources as h}from"./DOCS_URI_SCHEME-CFANc3vr.mjs";import{docsTools as y}from"./DEFAULT_SEARCH_LIMIT-V7pCZ1wt.mjs";import{toolDefinitions as E,callTool as R}from"./READ_ONLY_TOOL_DEFINITIONS-aD-b7Hw2.mjs";const O=(e,n)=>{if(e.size<n)return;const t=e.keys().next().value;t!==void 0&&e.delete(t)},l=e=>e.docs===!1?void 0:v({...e.docs?.baseUrl===void 0?{}:{baseUrl:e.docs.baseUrl},...e.fetch===void 0?{}:{fetch:e.fetch}}),_="lunora",S="no Lunora dev server is running for this project — start one with `lunora dev`, then call this tool again (call lunora_dev_status to check).",T=8,d=e=>{const n=new Map;return t=>{const r=JSON.stringify([t.url,t.token??""]),c=n.get(r);if(c!==void 0)return c;const o=new f({fetch:e,url:t.url});return t.token!==void 0&&t.token.length>0&&o.setAuthToken(t.token),O(n,T),n.set(r,o),o}},i=e=>e?.token!==void 0&&e.token.length>0,C=(e,n,t)=>{const r=typeof e=="function"?e:()=>e;return E(n,i(r())).map(c=>({definition:c,handle:async o=>{const s=r();return s===void 0?{content:[{text:S,type:"text"}],isError:!0}:R(t(s),c.name,o,n,i(s))}}))},P="lunora-spec:openrpc",g="lunora-spec:openapi",a=[{description:"The deployment's generated OpenRPC 1.x document — every RPC function's path, kind, and argument schema in one read, instead of list_functions plus one get_function_schema call per function.",fetch:async e=>e.fetchOpenRpc(),name:"OpenRPC specification",uri:P},{description:"The deployment's generated OpenAPI 3.1 document.",fetch:async e=>e.fetchOpenApi(),name:"OpenAPI specification",uri:g}],k=(e,n)=>{const t=typeof e=="function"?e:()=>e,r=async c=>{const o=t();if(o!==void 0)try{return await c.fetch(n(o))}catch{return}};return{list:async()=>(await Promise.all(a.map(async o=>await r(o)===void 0?void 0:{description:o.description,mimeType:"application/json",name:o.name,uri:o.uri}))).filter(o=>o!==void 0),read:async c=>{const o=a.find(u=>u.uri===c);if(o===void 0)return;const s=await r(o);return s===void 0?void 0:{mimeType:"application/json",text:JSON.stringify(s,void 0,2)}}}},w=e=>({list:async()=>(await Promise.all(e.map(async t=>t.list()))).flat(),read:async n=>{for(const t of e){const r=await t.read(n);if(r!==void 0)return r}}}),x=(e,n)=>{const t=[],r=l(e);return r!==void 0&&t.push(...y(r)),t.push(...e.extraTools??[]),e.deployment!==void 0&&t.push(...C(e.deployment,e.allowWrites??!1,n??d(e.fetch))),t},A=(e={})=>{const n=l(e),t=[];n!==void 0&&t.push(h(n));const r=d(e.fetch);return e.deployment!==void 0&&t.push(k(e.deployment,r)),m({name:_,version:e.version??"0.0.0"},x(e,r),t.length===0?void 0:w(t))},j=async(e={})=>{const n=A(e);return await n.connect(new p),n};export{_ as LOCAL_SERVER_NAME,S as NO_DEPLOYMENT_MESSAGE,g as OPENAPI_RESOURCE_URI,P as OPENRPC_RESOURCE_URI,j as connectLocalStdio,A as createLocalMcpServer,x as localTools};
@@ -0,0 +1 @@
1
+ import{LunoraError as u}from"@lunora/errors";import{O as A,e as d,a as S,c as E,o as c}from"./observability-tools-Ce0YK58T.mjs";const m={properties:{args:{description:"Arguments object passed to the function",type:"object"},functionPath:{description:'Function reference, e.g. "messages:send"',type:"string"},shardKey:{description:"Optional shard key when the function is .shardBy()-partitioned",type:"string"}},required:["functionPath"],type:"object"},g={properties:{},type:"object"},R={properties:{functionPath:{description:'Function reference, e.g. "messages:send"',type:"string"}},required:["functionPath"],type:"object"},l={destructiveHint:!1,idempotentHint:!0,openWorldHint:!0,readOnlyHint:!0},I=[{annotations:{...l,title:"List deployment functions"},description:"List the deployment's public functions (queries, mutations, actions) with their kinds.",inputSchema:g,name:"lunora_list_functions"},{annotations:{...l,title:"List global tables"},description:"List the deployment's .global() tables with their row counts. Names and row counts only — no column shapes.",inputSchema:g,name:"lunora_list_tables"},{annotations:{...l,title:"Describe a function's arguments"},description:"Return a function's argument descriptors (name, validator kind, whether it is optional) and its kind, so a caller can construct a valid arguments object. Call lunora_list_functions first to discover available function paths.",inputSchema:R,name:"lunora_get_function_schema"},{annotations:{...l,title:"Run a query"},description:"Run a query and return its result. Read-only.",inputSchema:m,name:"lunora_run_query"}],w=[{annotations:{destructiveHint:!0,idempotentHint:!1,openWorldHint:!0,readOnlyHint:!1,title:"Run a mutation (writes data)"},description:"Run a mutation and return its result. Writes data — use with care.",inputSchema:m,name:"lunora_run_mutation"},{annotations:{destructiveHint:!0,idempotentHint:!1,openWorldHint:!0,readOnlyHint:!1,title:"Run an action (may call external services)"},description:"Run an action and return its result. May call external services.",inputSchema:m,name:"lunora_run_action"}],L=new Set(w.map(t=>t.name)),k=(t,n=!1)=>[...I,...n===!0?A:[],...t===!0?w:[]],b=t=>{const{functionPath:n}=t;if(typeof n!="string"||n.length===0)throw new u("INTERNAL",'"functionPath" is required and must be a non-empty string');return n},O=t=>typeof t=="object"&&t!==null&&!Array.isArray(t),N=t=>Array.isArray(t)?"an array":`a ${typeof t}`,v=t=>{if(t==null)return{};if(typeof t=="string"){let n;try{n=JSON.parse(t)}catch{throw new u("BAD_REQUEST",'"args" must be a JSON object; received a string that is not valid JSON')}if(!O(n))throw new u("BAD_REQUEST",`"args" must be a JSON object; the provided string decoded to ${N(n)}`);return n}if(!O(t))throw new u("BAD_REQUEST",`"args" must be a JSON object, got ${N(t)}`);return t},f=t=>{const n=b(t),e=v(t.args),o=typeof t.shardKey=="string"&&t.shardKey.length>0?t.shardKey:void 0;return{args:e,functionPath:n,shardKey:o}},p=t=>({__lunoraRef:t}),H=3e4,h=new WeakMap,y=t=>{const n=Date.now(),e=h.get(t);if(e!==void 0&&e.expiresAt>n)return e.promise;const o=t.listFunctions().catch(i=>{throw h.get(t)?.promise===o&&h.delete(t),i});return h.set(t,{expiresAt:n+H,promise:o}),o},_=async(t,n,e)=>{const i=(await y(t)).find(r=>r.path===n);if(i===void 0)throw new u("NOT_FOUND",`function not found or not public: ${n}`);if(i.kind!==e)throw new u("BAD_REQUEST",`function ${n} is a ${i.kind}, not a ${e}`)},U=async(t,n,e,o=!1,i=!1)=>{try{if(o!==!0&&L.has(n))return d(`tool "${n}" is disabled: this MCP server is read-only. Enable writes with the LUNORA_MCP_ALLOW_WRITES env var.`);if(S.has(n))return i!==!0?d(`tool "${n}" is unavailable: it reads the deployment's logs and errors, which needs an admin token. Set LUNORA_ADMIN_TOKEN (or pass --token) and reconnect.`):await E(t,n,e);switch(n){case"lunora_get_function_schema":{const r=b(e),a=(await y(t)).find(T=>T.path===r);return a===void 0?d(`function not found: ${r}`):c({args:a.args??[],kind:a.kind,path:a.path})}case"lunora_list_functions":return c(await y(t));case"lunora_list_tables":return c(await t.listGlobalTables());case"lunora_run_action":{const{args:r,functionPath:s,shardKey:a}=f(e);return await _(t,s,"action"),c(await t.action(p(s),r,{shardKey:a}))}case"lunora_run_mutation":{const{args:r,functionPath:s,shardKey:a}=f(e);return await _(t,s,"mutation"),c(await t.mutation(p(s),r,{shardKey:a}))}case"lunora_run_query":{const{args:r,functionPath:s,shardKey:a}=f(e);return await _(t,s,"query"),c(await t.query(p(s),r,{shardKey:a}))}default:return d(`unknown tool: ${n}`)}}catch(r){const s=r instanceof Error?r.message:String(r);return d(s)}};export{A as OBSERVABILITY_TOOL_DEFINITIONS,I as READ_ONLY_TOOL_DEFINITIONS,w as WRITE_TOOL_DEFINITIONS,U as callTool,k as toolDefinitions};
@@ -0,0 +1 @@
1
+ import{readFileSync as v}from"node:fs";import{dirname as c,join as g}from"node:path";import{fileURLToPath as d}from"node:url";import{LunoraClient as h}from"@lunora/client";import{LunoraError as u}from"@lunora/errors";import{Server as w}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as T}from"@modelcontextprotocol/sdk/server/stdio.js";import{ListToolsRequestSchema as p,CallToolRequestSchema as A}from"@modelcontextprotocol/sdk/types.js";import{agentToolDefinitions as M,isAgentToolName as S,callAgentTool as k}from"./AGENT_RUN_INPUT_SCHEMA-DGpNvk5K.mjs";import{toolDefinitions as y,callTool as N}from"./READ_ONLY_TOOL_DEFINITIONS-aD-b7Hw2.mjs";const L=()=>{try{let e=c(d(import.meta.url));for(let r=0;r<8;r+=1){try{const o=v(g(e,"package.json"),"utf8"),t=JSON.parse(o);if(t.name==="@lunora/mcp"&&typeof t.version=="string"&&t.version.length>0)return t.version}catch{}const n=c(e);if(n===e)break;e=n}}catch{}return"0.0.0"},R={name:"lunora",version:L()},E=e=>{if(e.client!==void 0)return e.client;if(e.url===void 0)throw new u("INTERNAL","createLunoraMcpServer requires either a `client` or a `url`");if(e.token===void 0||e.token.length===0)throw new u("UNAUTHENTICATED","createLunoraMcpServer requires a `token` (LUNORA_ADMIN_TOKEN) alongside `url`: every tool reaches admin-gated /_lunora/admin/* routes, so an unauthenticated server can only 403. Writes stay off unless `allowWrites` is set.");const r=new h({fetch:e.fetch,url:e.url});return r.setAuthToken(e.token),r},I=e=>{const r=E(e),n=e.allowWrites??!1,o=e.allowAgents??!1,t=e.agents??[],s=typeof e.token=="string"&&e.token.length>0,a=new w(R,{capabilities:{tools:{}}});return a.setRequestHandler(p,()=>({tools:[...y(n,s),...M(t,o)]})),a.setRequestHandler(A,async m=>{const{arguments:f,name:l}=m.params,i=f??{};return S(l,t)?await k(r,l,i,{allowAgents:o,exposures:t,...e.agentMaxWaitMs===void 0?{}:{maxWaitMs:e.agentMaxWaitMs},...e.agentPollIntervalMs===void 0?{}:{pollIntervalMs:e.agentPollIntervalMs}}):await N(r,l,i,n,s)}),a},j=async e=>{const r=I(e);return await r.connect(new T),r};export{j as connectStdio,I as createLunoraMcpServer};
@@ -1 +1 @@
1
- import{serveStateless as n}from"./serveStateless-Db7J3pAO.mjs";import{createLunoraMcpServer as o}from"./connectStdio-BCeI0Wqu.mjs";const a=e=>typeof e.scope!="string"?new Set:new Set(e.scope.split(" ").filter(r=>r!=="")),f=e=>e.protect(async(r,t)=>{const c=typeof e.server=="function"?await e.server(t):e.server;return await n(o(c),r)});export{f as createAuthedMcpFetchHandler,a as mcpTokenScopes};
1
+ import{serveStateless as n}from"./serveStateless-Db7J3pAO.mjs";import{createLunoraMcpServer as o}from"./connectStdio-CDsCt1IT.mjs";const a=e=>typeof e.scope!="string"?new Set:new Set(e.scope.split(" ").filter(r=>r!=="")),f=e=>e.protect(async(r,t)=>{const c=typeof e.server=="function"?await e.server(t):e.server;return await n(o(c),r)});export{f as createAuthedMcpFetchHandler,a as mcpTokenScopes};
@@ -1 +1 @@
1
- import{serveStateless as t}from"./serveStateless-Db7J3pAO.mjs";import{createLunoraMcpServer as o}from"./connectStdio-BCeI0Wqu.mjs";const p=e=>r=>t(o(e),r);export{p as createMcpFetchHandler,t as serveStateless};
1
+ import{serveStateless as t}from"./serveStateless-Db7J3pAO.mjs";import{createLunoraMcpServer as o}from"./connectStdio-CDsCt1IT.mjs";const p=e=>r=>t(o(e),r);export{p as createMcpFetchHandler,t as serveStateless};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/mcp",
3
- "version": "1.0.0-alpha.100",
3
+ "version": "1.0.0-alpha.102",
4
4
  "description": "Model Context Protocol server exposing a Lunora deployment to AI agents",
5
5
  "keywords": [
6
6
  "ai-agents",
@@ -53,9 +53,9 @@
53
53
  "access": "public"
54
54
  },
55
55
  "dependencies": {
56
- "@lunora/client": "1.0.0-alpha.66",
57
- "@lunora/errors": "1.0.0-alpha.26",
58
- "@lunora/shard-engine": "1.0.0-alpha.47",
56
+ "@lunora/client": "1.0.0-alpha.68",
57
+ "@lunora/errors": "1.0.0-alpha.27",
58
+ "@lunora/shard-engine": "1.0.0-alpha.49",
59
59
  "@modelcontextprotocol/sdk": "^1.30.0"
60
60
  },
61
61
  "peerDependencies": {
@@ -1 +0,0 @@
1
- const v="agents:agentRun",A="agents:agentThread",b="agents:agentMessages",g="lunora_agent_status",T=new Set(["cancelled","error","idle"]),x=6e4,U=600,L={properties:{prompt:{description:"The task or message for the agent.",type:"string"},threadKey:{description:"Reuse to continue a conversation; omit to start a new thread.",type:"string"},title:{description:"Optional thread title (first run only).",type:"string"}},required:["prompt"],type:"object"},P={properties:{threadKey:{description:"The thread key returned by an agent tool call.",type:"string"}},required:["threadKey"],type:"object"},p=t=>t.toolName??`agent_${t.name}`,G=t=>{if(t===void 0)return[];const n=[];for(const e of t.split(";")){const r=e.trim();if(r.length===0)continue;const o=r.indexOf(":");if(o<=0)continue;const s=r.slice(0,o).trim(),a=r.slice(o+1).trim();s.length===0||a.length===0||n.push({description:a,name:s})}return n},O=(t,n)=>n!==!0||t.length===0?[]:[...t.map(r=>({description:`${r.description} Starts a durable agent run and returns its final answer.`,inputSchema:L,name:p(r)})),{description:"Check the status of a durable agent run (and its answer if finished) by its threadKey.",inputSchema:P,name:g}],q=(t,n)=>t===g||n.some(e=>p(e)===t),k=()=>`mcp-${crypto.randomUUID()}`,l=t=>({__lunoraRef:t}),I=async t=>{await new Promise(n=>{setTimeout(n,t)})},d=t=>({content:[{text:JSON.stringify(t,void 0,2),type:"text"}]}),i=t=>({content:[{text:t,type:"text"}],isError:!0}),u=(t,n)=>{const e=t[n];return typeof e=="string"&&e.length>0?e:void 0},K=t=>{for(let n=t.length-1;n>=0;n-=1){const e=t[n],r=e?.toolCalls,o=Array.isArray(r)&&r.length>0;if(e?.role==="assistant"&&!o)return typeof e.content=="string"?e.content:""}return""},_=t=>t!==null&&typeof t=="object"&&typeof t.status=="string"?t.status:"unknown",S=async(t,n,e,r)=>{const o=await t.query(l(b),{key:n});if(e==="error"){const s=r!==null&&typeof r=="object"?r.error:void 0;return d({error:typeof s=="string"?s:"the agent run failed",status:e,threadKey:n})}return d({status:e,text:K(o),threadKey:n})},R=async(t,n)=>{const e=u(n,"threadKey");if(e===void 0)return i('"threadKey" is required and must be a non-empty string');const r=await t.query(l(A),{key:e}),o=_(r);return T.has(o)?S(t,e,o,r):d({status:o==="unknown"?"running":o,threadKey:e})},C=async(t,n,e,r)=>{try{if(r.allowAgents!==!0)return i(`tool "${n}" is disabled: agent tools are off. Enable them with the LUNORA_MCP_ALLOW_AGENTS env var.`);if(n===g)return await R(t,e);const o=r.exposures.find(c=>p(c)===n);if(o===void 0)return i(`agent tool "${n}" is not exposed by this MCP server`);const s=u(e,"prompt");if(s===void 0)return i('"prompt" is required and must be a non-empty string');const a=u(e,"threadKey")??k(),f=u(e,"title"),{id:w}=await t.mutation(l(v),{agent:o.name,input:s,threadKey:a,...f===void 0?{}:{title:f}}),y=r.pollIntervalMs??U,E=r.maxWaitMs??x,N=r.wait??I,M=Math.max(1,Math.ceil(E/y));for(let c=0;c<M;c+=1){const h=await t.query(l(A),{key:a}),m=_(h);if(T.has(m))return await S(t,a,m,h);await N(y)}return d({hint:"call lunora_agent_status with this threadKey to poll for the answer",runId:w,status:"running",threadKey:a})}catch(o){const s=o instanceof Error?o.message:String(o);return i(s)}};export{L as AGENT_RUN_INPUT_SCHEMA,g as AGENT_STATUS_TOOL_NAME,O as agentToolDefinitions,C as callAgentTool,K as finalAnswer,q as isAgentToolName,G as parseAgentsEnv};
@@ -1 +0,0 @@
1
- import{LunoraError as u}from"@lunora/errors";import{O as A,e as d,a as w,c as E,o as c}from"./observability-tools-Ce0YK58T.mjs";const m={properties:{args:{description:"Arguments object passed to the function",type:"object"},functionPath:{description:'Function reference, e.g. "messages:send"',type:"string"},shardKey:{description:"Optional shard key when the function is .shardBy()-partitioned",type:"string"}},required:["functionPath"],type:"object"},g={properties:{},type:"object"},R={properties:{functionPath:{description:'Function reference, e.g. "messages:send"',type:"string"}},required:["functionPath"],type:"object"},l={destructiveHint:!1,idempotentHint:!0,openWorldHint:!0,readOnlyHint:!0},I=[{annotations:{...l,title:"List deployment functions"},description:"List the deployment's public functions (queries, mutations, actions) with their kinds.",inputSchema:g,name:"lunora_list_functions"},{annotations:{...l,title:"List global tables"},description:"List the deployment's .global() tables and their column shapes.",inputSchema:g,name:"lunora_list_tables"},{annotations:{...l,title:"Describe a function's arguments"},description:"Return a function's argument JSON Schema and kind, so a caller can construct a valid arguments object. Call lunora_list_functions first to discover available function paths.",inputSchema:R,name:"lunora_get_function_schema"},{annotations:{...l,title:"Run a query"},description:"Run a query and return its result. Read-only.",inputSchema:m,name:"lunora_run_query"}],b=[{annotations:{destructiveHint:!0,idempotentHint:!1,openWorldHint:!0,readOnlyHint:!1,title:"Run a mutation (writes data)"},description:"Run a mutation and return its result. Writes data — use with care.",inputSchema:m,name:"lunora_run_mutation"},{annotations:{destructiveHint:!0,idempotentHint:!1,openWorldHint:!0,readOnlyHint:!1,title:"Run an action (may call external services)"},description:"Run an action and return its result. May call external services.",inputSchema:m,name:"lunora_run_action"}],L=new Set(b.map(t=>t.name)),U=(t,n=!1)=>[...I,...n===!0?A:[],...t===!0?b:[]],S=t=>{const{functionPath:n}=t;if(typeof n!="string"||n.length===0)throw new u("INTERNAL",'"functionPath" is required and must be a non-empty string');return n},O=t=>typeof t=="object"&&t!==null&&!Array.isArray(t),N=t=>Array.isArray(t)?"an array":`a ${typeof t}`,v=t=>{if(t==null)return{};if(typeof t=="string"){let n;try{n=JSON.parse(t)}catch{throw new u("BAD_REQUEST",'"args" must be a JSON object; received a string that is not valid JSON')}if(!O(n))throw new u("BAD_REQUEST",`"args" must be a JSON object; the provided string decoded to ${N(n)}`);return n}if(!O(t))throw new u("BAD_REQUEST",`"args" must be a JSON object, got ${N(t)}`);return t},f=t=>{const n=S(t),e=v(t.args),o=typeof t.shardKey=="string"&&t.shardKey.length>0?t.shardKey:void 0;return{args:e,functionPath:n,shardKey:o}},p=t=>({__lunoraRef:t}),H=3e4,h=new WeakMap,y=t=>{const n=Date.now(),e=h.get(t);if(e!==void 0&&e.expiresAt>n)return e.promise;const o=t.listFunctions().catch(i=>{throw h.get(t)?.promise===o&&h.delete(t),i});return h.set(t,{expiresAt:n+H,promise:o}),o},_=async(t,n,e)=>{const i=(await y(t)).find(r=>r.path===n);if(i===void 0)throw new u("NOT_FOUND",`function not found or not public: ${n}`);if(i.kind!==e)throw new u("BAD_REQUEST",`function ${n} is a ${i.kind}, not a ${e}`)},k=async(t,n,e,o=!1,i=!1)=>{try{if(o!==!0&&L.has(n))return d(`tool "${n}" is disabled: this MCP server is read-only. Enable writes with the LUNORA_MCP_ALLOW_WRITES env var.`);if(w.has(n))return i!==!0?d(`tool "${n}" is unavailable: it reads the deployment's logs and errors, which needs an admin token. Set LUNORA_ADMIN_TOKEN (or pass --token) and reconnect.`):await E(t,n,e);switch(n){case"lunora_get_function_schema":{const r=S(e),s=(await y(t)).find(T=>T.path===r);return s===void 0?d(`function not found: ${r}`):c({args:s.args??[],kind:s.kind,path:s.path})}case"lunora_list_functions":return c(await y(t));case"lunora_list_tables":return c(await t.listGlobalTables());case"lunora_run_action":{const{args:r,functionPath:a,shardKey:s}=f(e);return await _(t,a,"action"),c(await t.action(p(a),r,{shardKey:s}))}case"lunora_run_mutation":{const{args:r,functionPath:a,shardKey:s}=f(e);return await _(t,a,"mutation"),c(await t.mutation(p(a),r,{shardKey:s}))}case"lunora_run_query":{const{args:r,functionPath:a,shardKey:s}=f(e);return await _(t,a,"query"),c(await t.query(p(a),r,{shardKey:s}))}default:return d(`unknown tool: ${n}`)}}catch(r){const a=r instanceof Error?r.message:String(r);return d(a)}};export{A as OBSERVABILITY_TOOL_DEFINITIONS,I as READ_ONLY_TOOL_DEFINITIONS,b as WRITE_TOOL_DEFINITIONS,k as callTool,U as toolDefinitions};
@@ -1 +0,0 @@
1
- import{readFileSync as f}from"node:fs";import{dirname as c,join as v}from"node:path";import{fileURLToPath as g}from"node:url";import{LunoraClient as d}from"@lunora/client";import{LunoraError as h}from"@lunora/errors";import{Server as w}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as p}from"@modelcontextprotocol/sdk/server/stdio.js";import{ListToolsRequestSchema as S,CallToolRequestSchema as T}from"@modelcontextprotocol/sdk/types.js";import{agentToolDefinitions as M,isAgentToolName as k,callAgentTool as y}from"./AGENT_RUN_INPUT_SCHEMA-DRSY74ed.mjs";import{toolDefinitions as A,callTool as R}from"./READ_ONLY_TOOL_DEFINITIONS-DGexf3Tn.mjs";const L=()=>{try{let e=c(g(import.meta.url));for(let r=0;r<8;r+=1){try{const o=f(v(e,"package.json"),"utf8"),t=JSON.parse(o);if(t.name==="@lunora/mcp"&&typeof t.version=="string"&&t.version.length>0)return t.version}catch{}const n=c(e);if(n===e)break;e=n}}catch{}return"0.0.0"},q={name:"lunora",version:L()},x=e=>{if(e.client!==void 0)return e.client;if(e.url===void 0)throw new h("INTERNAL","createLunoraMcpServer requires either a `client` or a `url`");const r=new d({fetch:e.fetch,url:e.url});return e.token!==void 0&&r.setAuthToken(e.token),r},I=e=>{const r=x(e),n=e.allowWrites??!1,o=e.allowAgents??!1,t=e.agents??[],s=typeof e.token=="string"&&e.token.length>0,a=new w(q,{capabilities:{tools:{}}});return a.setRequestHandler(S,()=>({tools:[...A(n,s),...M(t,o)]})),a.setRequestHandler(T,async m=>{const{arguments:u,name:l}=m.params,i=u??{};return k(l,t)?await y(r,l,i,{allowAgents:o,exposures:t,...e.agentMaxWaitMs===void 0?{}:{maxWaitMs:e.agentMaxWaitMs},...e.agentPollIntervalMs===void 0?{}:{pollIntervalMs:e.agentPollIntervalMs}}):await R(r,l,i,n,s)}),a},V=async e=>{const r=I(e);return await r.connect(new p),r};export{V as connectStdio,I as createLunoraMcpServer};