@lunora/mcp 1.0.0-alpha.26 → 1.0.0-alpha.261

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 (50) hide show
  1. package/LICENSE.md +120 -0
  2. package/README.md +250 -14
  3. package/dist/bin.mjs +4 -47
  4. package/dist/docs/index.d.mts +259 -0
  5. package/dist/docs/index.d.ts +259 -0
  6. package/dist/docs/index.mjs +1 -0
  7. package/dist/index.d.mts +83 -82
  8. package/dist/index.d.ts +83 -82
  9. package/dist/index.mjs +1 -5
  10. package/dist/node/index.d.mts +93 -0
  11. package/dist/node/index.d.ts +93 -0
  12. package/dist/node/index.mjs +1 -0
  13. package/dist/packem_shared/AGENT_RUN_INPUT_SCHEMA-BGbKIek4.mjs +1 -0
  14. package/dist/packem_shared/DEFAULT_DOCS_BASE_URL-CiHy_Dbz.mjs +4 -0
  15. package/dist/packem_shared/DEFAULT_MAX_REQUEST_BYTES-CbbpkHRK.mjs +1 -0
  16. package/dist/packem_shared/DEFAULT_SEARCH_LIMIT-BMQ6HQ-x.mjs +3 -0
  17. package/dist/packem_shared/DOCS_SERVER_NAME-ClS4cCGm.mjs +1 -0
  18. package/dist/packem_shared/DOCS_URI_SCHEME-DBlxtzS6.mjs +3 -0
  19. package/dist/packem_shared/LOCAL_SERVER_NAME-BAK3cHeB.mjs +1 -0
  20. package/dist/packem_shared/OBSERVABILITY_TOOL_DEFINITIONS-DQ7AUah0.mjs +1 -0
  21. package/dist/packem_shared/READ_ONLY_TOOL_DEFINITIONS-BimWNHPm.mjs +1 -0
  22. package/dist/packem_shared/compose.d-DiPnHeQv.d.mts +154 -0
  23. package/dist/packem_shared/compose.d-DiPnHeQv.d.ts +154 -0
  24. package/dist/packem_shared/connectFunctionStdio-BGPvMkc7.mjs +1 -0
  25. package/dist/packem_shared/connectStdio-CP2XjVvN.mjs +1 -0
  26. package/dist/packem_shared/createAuthedMcpFetchHandler-enQ5a-ys.mjs +1 -0
  27. package/dist/packem_shared/createFunctionMcpServer-jgYBPzxQ.mjs +1 -0
  28. package/dist/packem_shared/createLunoraMcpServer-BOsMehmQ.mjs +1 -0
  29. package/dist/packem_shared/createMcpFetchHandler-C_pTZ-1E.mjs +1 -0
  30. package/dist/packem_shared/createPaidMcpServer-CdDLw3G4.mjs +1 -0
  31. package/dist/packem_shared/createToolServer-CHAjfuNx.mjs +1 -0
  32. package/dist/packem_shared/function-server.d-ByIkBRXs.d.ts +196 -0
  33. package/dist/packem_shared/function-server.d-D_0-WiS2.d.mts +196 -0
  34. package/dist/packem_shared/package-DsZ0-LJz.mjs +1 -0
  35. package/dist/packem_shared/promise-memo-OqVPWoUX.mjs +1 -0
  36. package/dist/packem_shared/serve-stateless.d-DD5arqFx.d.mts +45 -0
  37. package/dist/packem_shared/serve-stateless.d-DD5arqFx.d.ts +45 -0
  38. package/dist/packem_shared/toDocsSearchHits-CBLmtWXt.mjs +1 -0
  39. package/dist/packem_shared/tool-call-Bs-QeKp6.mjs +1 -0
  40. package/dist/packem_shared/tool-result-CPDsNNuJ.mjs +1 -0
  41. package/dist/packem_shared/tools-Bylr9LPQ.mjs +2 -0
  42. package/dist/worker/index.d.mts +257 -0
  43. package/dist/worker/index.d.ts +257 -0
  44. package/dist/worker/index.mjs +1 -0
  45. package/package.json +27 -5
  46. package/dist/packem_shared/AGENT_RUN_INPUT_SCHEMA-DWobae3P.mjs +0 -168
  47. package/dist/packem_shared/READ_ONLY_TOOL_DEFINITIONS-CWb_wFd5.mjs +0 -204
  48. package/dist/packem_shared/connectStdio-bkJcaXYE.mjs +0 -76
  49. package/dist/packem_shared/createMcpFetchHandler-DfzBSLrm.mjs +0 -14
  50. package/dist/packem_shared/createPaidMcpServer-b-qp_aEF.mjs +0 -93
@@ -0,0 +1,154 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ /**
3
+ * The MCP wire shapes every tool surface in this package speaks — the
4
+ * deployment tools (`./tools`), the agent tools (`./agent-tools`), and the
5
+ * documentation tools (`./docs`).
6
+ *
7
+ * They live in their own module (rather than in `./tools`) so a consumer that
8
+ * only wants the docs surface can import the types without dragging
9
+ * `@lunora/client` into its bundle. `./tools` re-exports them, so the package's
10
+ * public API is unchanged.
11
+ */
12
+ /** A JSON-Schema object describing a tool's arguments, per the MCP spec. */
13
+ interface ToolInputSchema {
14
+ properties: Record<string, unknown>;
15
+ required?: ReadonlyArray<string>;
16
+ type: "object";
17
+ }
18
+ /**
19
+ * A tool's `outputSchema`. Either a success shape, or the root `anyOf` a composed
20
+ * tool gets so an error result also validates. The root stays `type: "object"`,
21
+ * which the MCP SDK requires, so `properties` is absent on that root.
22
+ */
23
+ interface ToolOutputSchema {
24
+ $defs?: Record<string, unknown>;
25
+ additionalProperties?: boolean;
26
+ anyOf?: ReadonlyArray<object>;
27
+ definitions?: Record<string, unknown>;
28
+ properties?: Record<string, unknown>;
29
+ required?: ReadonlyArray<string>;
30
+ type: "object";
31
+ }
32
+ /**
33
+ * Hints every tool carries, whether it reads or writes.
34
+ */
35
+ interface ToolAnnotationsBase {
36
+ /** The tool reaches systems beyond this server (the network, external services). */
37
+ openWorldHint?: boolean;
38
+ /** Human-facing title, shown instead of the raw tool name. */
39
+ title?: string;
40
+ }
41
+ /**
42
+ * A tool that does not modify anything. `destructiveHint` may only be `false`
43
+ * here, so a read-only tool cannot also claim it is destructive.
44
+ */
45
+ interface ReadOnlyToolAnnotations extends ToolAnnotationsBase {
46
+ destructiveHint?: false;
47
+ idempotentHint?: boolean;
48
+ readOnlyHint: true;
49
+ }
50
+ /**
51
+ * A tool that may modify something. Both hints are REQUIRED here: a write that
52
+ * does not say whether it can destroy data, or whether a retry repeats its
53
+ * effect, is exactly the omission this type exists to rule out.
54
+ */
55
+ interface WriteToolAnnotations extends ToolAnnotationsBase {
56
+ /** The tool may perform irreversible changes. */
57
+ destructiveHint: boolean;
58
+ /** Repeating the call with the same arguments has no additional effect. */
59
+ idempotentHint: boolean;
60
+ readOnlyHint: false;
61
+ }
62
+ /**
63
+ * MCP tool annotations — hints a client may use to decide how to present a tool
64
+ * (badge it read-only, confirm before a destructive call).
65
+ *
66
+ * They are hints, not enforcement: this package's actual guarantees are made at
67
+ * dispatch, where a write tool is refused unless `allowWrites` is set. These
68
+ * exist so a client can *show* the user what the server already enforces,
69
+ * instead of every tool looking equally dangerous.
70
+ *
71
+ * `readOnlyHint` is required on every tool, so a new tool cannot ship without
72
+ * declaring its access. See `./tool-annotations.test` for the compile-time proof.
73
+ */
74
+ type ToolAnnotations = ReadOnlyToolAnnotations | WriteToolAnnotations;
75
+ interface ToolDefinition {
76
+ /** Required: every tool declares its access (see {@link ToolAnnotations}). */
77
+ annotations: ToolAnnotations;
78
+ description: string;
79
+ inputSchema: ToolInputSchema;
80
+ name: string;
81
+ /**
82
+ * JSON Schema for the tool's `structuredContent`. Optional: a tool that
83
+ * emits only a text block declares none, and a client that negotiated an
84
+ * MCP revision older than `2025-06-18` ignores it either way.
85
+ */
86
+ outputSchema?: ToolOutputSchema;
87
+ }
88
+ /**
89
+ * The MCP `CallToolResult` shape this package's tools return.
90
+ *
91
+ * A type alias rather than an interface: the SDK's `CallToolResult` has an open
92
+ * index signature, which only an object-literal type satisfies implicitly.
93
+ */
94
+ type ToolResult = {
95
+ content: {
96
+ text: string;
97
+ type: "text";
98
+ }[];
99
+ isError?: boolean;
100
+ /**
101
+ * The machine-readable result, mirroring the text block. Present on success
102
+ * for tools that declare an `outputSchema`, and on EVERY error result, where
103
+ * it is `{ code, message }`. Per the MCP spec it must be a JSON OBJECT (never
104
+ * a bare array) and must survive `JSON.stringify` — the transport serializes
105
+ * it, so an un-mapped `bigint` here fails the whole response rather than one
106
+ * field.
107
+ */
108
+ structuredContent?: Record<string, unknown>;
109
+ };
110
+ /** A tool: what to advertise in `ListTools`, and what to run on `CallTool`. */
111
+ interface McpTool {
112
+ definition: ToolDefinition;
113
+ handle: (input: Record<string, unknown>) => Promise<ToolResult>;
114
+ }
115
+ /** Name/version pair reported in the MCP `initialize` handshake. */
116
+ interface McpServerInfo {
117
+ name: string;
118
+ version: string;
119
+ }
120
+ /** One addressable document a client can list and read directly. */
121
+ interface McpResourceSummary {
122
+ description?: string;
123
+ mimeType?: string;
124
+ name: string;
125
+ uri: string;
126
+ }
127
+ /**
128
+ * A set of resources the server exposes alongside its tools.
129
+ *
130
+ * Tools are calls a model chooses to make; resources are documents a *client*
131
+ * can enumerate and attach on its own — the user picking a page to put in
132
+ * context, without the model having to guess a search query first. The same
133
+ * corpus is worth offering both ways.
134
+ */
135
+ interface McpResourceProvider {
136
+ /** Every resource, for `resources/list`. */
137
+ list: () => Promise<ReadonlyArray<McpResourceSummary>>;
138
+ /** One resource's body, or `undefined` when the uri is unknown. */
139
+ read: (uri: string) => Promise<{
140
+ mimeType?: string;
141
+ text: string;
142
+ } | undefined>;
143
+ }
144
+ /**
145
+ * Build an MCP `Server` that advertises and dispatches `tools`.
146
+ *
147
+ * On duplicate names the FIRST wins, so a caller can layer surfaces in
148
+ * precedence order without the later list silently shadowing the earlier one.
149
+ * Thrown errors become `isError` results rather than rejections, per the MCP
150
+ * convention that a tool failure is output the model can read and react to,
151
+ * not a protocol-level fault.
152
+ */
153
+ declare const createToolServer: (info: McpServerInfo, tools: ReadonlyArray<McpTool>, resources?: McpResourceProvider) => Server;
154
+ export { McpServerInfo as M, ToolDefinition as T, McpTool as a, ToolInputSchema as b, ToolOutputSchema as c, ToolResult as d, createToolServer as e, McpResourceProvider as f, McpResourceSummary as g, ToolAnnotations as h };
@@ -0,0 +1,154 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ /**
3
+ * The MCP wire shapes every tool surface in this package speaks — the
4
+ * deployment tools (`./tools`), the agent tools (`./agent-tools`), and the
5
+ * documentation tools (`./docs`).
6
+ *
7
+ * They live in their own module (rather than in `./tools`) so a consumer that
8
+ * only wants the docs surface can import the types without dragging
9
+ * `@lunora/client` into its bundle. `./tools` re-exports them, so the package's
10
+ * public API is unchanged.
11
+ */
12
+ /** A JSON-Schema object describing a tool's arguments, per the MCP spec. */
13
+ interface ToolInputSchema {
14
+ properties: Record<string, unknown>;
15
+ required?: ReadonlyArray<string>;
16
+ type: "object";
17
+ }
18
+ /**
19
+ * A tool's `outputSchema`. Either a success shape, or the root `anyOf` a composed
20
+ * tool gets so an error result also validates. The root stays `type: "object"`,
21
+ * which the MCP SDK requires, so `properties` is absent on that root.
22
+ */
23
+ interface ToolOutputSchema {
24
+ $defs?: Record<string, unknown>;
25
+ additionalProperties?: boolean;
26
+ anyOf?: ReadonlyArray<object>;
27
+ definitions?: Record<string, unknown>;
28
+ properties?: Record<string, unknown>;
29
+ required?: ReadonlyArray<string>;
30
+ type: "object";
31
+ }
32
+ /**
33
+ * Hints every tool carries, whether it reads or writes.
34
+ */
35
+ interface ToolAnnotationsBase {
36
+ /** The tool reaches systems beyond this server (the network, external services). */
37
+ openWorldHint?: boolean;
38
+ /** Human-facing title, shown instead of the raw tool name. */
39
+ title?: string;
40
+ }
41
+ /**
42
+ * A tool that does not modify anything. `destructiveHint` may only be `false`
43
+ * here, so a read-only tool cannot also claim it is destructive.
44
+ */
45
+ interface ReadOnlyToolAnnotations extends ToolAnnotationsBase {
46
+ destructiveHint?: false;
47
+ idempotentHint?: boolean;
48
+ readOnlyHint: true;
49
+ }
50
+ /**
51
+ * A tool that may modify something. Both hints are REQUIRED here: a write that
52
+ * does not say whether it can destroy data, or whether a retry repeats its
53
+ * effect, is exactly the omission this type exists to rule out.
54
+ */
55
+ interface WriteToolAnnotations extends ToolAnnotationsBase {
56
+ /** The tool may perform irreversible changes. */
57
+ destructiveHint: boolean;
58
+ /** Repeating the call with the same arguments has no additional effect. */
59
+ idempotentHint: boolean;
60
+ readOnlyHint: false;
61
+ }
62
+ /**
63
+ * MCP tool annotations — hints a client may use to decide how to present a tool
64
+ * (badge it read-only, confirm before a destructive call).
65
+ *
66
+ * They are hints, not enforcement: this package's actual guarantees are made at
67
+ * dispatch, where a write tool is refused unless `allowWrites` is set. These
68
+ * exist so a client can *show* the user what the server already enforces,
69
+ * instead of every tool looking equally dangerous.
70
+ *
71
+ * `readOnlyHint` is required on every tool, so a new tool cannot ship without
72
+ * declaring its access. See `./tool-annotations.test` for the compile-time proof.
73
+ */
74
+ type ToolAnnotations = ReadOnlyToolAnnotations | WriteToolAnnotations;
75
+ interface ToolDefinition {
76
+ /** Required: every tool declares its access (see {@link ToolAnnotations}). */
77
+ annotations: ToolAnnotations;
78
+ description: string;
79
+ inputSchema: ToolInputSchema;
80
+ name: string;
81
+ /**
82
+ * JSON Schema for the tool's `structuredContent`. Optional: a tool that
83
+ * emits only a text block declares none, and a client that negotiated an
84
+ * MCP revision older than `2025-06-18` ignores it either way.
85
+ */
86
+ outputSchema?: ToolOutputSchema;
87
+ }
88
+ /**
89
+ * The MCP `CallToolResult` shape this package's tools return.
90
+ *
91
+ * A type alias rather than an interface: the SDK's `CallToolResult` has an open
92
+ * index signature, which only an object-literal type satisfies implicitly.
93
+ */
94
+ type ToolResult = {
95
+ content: {
96
+ text: string;
97
+ type: "text";
98
+ }[];
99
+ isError?: boolean;
100
+ /**
101
+ * The machine-readable result, mirroring the text block. Present on success
102
+ * for tools that declare an `outputSchema`, and on EVERY error result, where
103
+ * it is `{ code, message }`. Per the MCP spec it must be a JSON OBJECT (never
104
+ * a bare array) and must survive `JSON.stringify` — the transport serializes
105
+ * it, so an un-mapped `bigint` here fails the whole response rather than one
106
+ * field.
107
+ */
108
+ structuredContent?: Record<string, unknown>;
109
+ };
110
+ /** A tool: what to advertise in `ListTools`, and what to run on `CallTool`. */
111
+ interface McpTool {
112
+ definition: ToolDefinition;
113
+ handle: (input: Record<string, unknown>) => Promise<ToolResult>;
114
+ }
115
+ /** Name/version pair reported in the MCP `initialize` handshake. */
116
+ interface McpServerInfo {
117
+ name: string;
118
+ version: string;
119
+ }
120
+ /** One addressable document a client can list and read directly. */
121
+ interface McpResourceSummary {
122
+ description?: string;
123
+ mimeType?: string;
124
+ name: string;
125
+ uri: string;
126
+ }
127
+ /**
128
+ * A set of resources the server exposes alongside its tools.
129
+ *
130
+ * Tools are calls a model chooses to make; resources are documents a *client*
131
+ * can enumerate and attach on its own — the user picking a page to put in
132
+ * context, without the model having to guess a search query first. The same
133
+ * corpus is worth offering both ways.
134
+ */
135
+ interface McpResourceProvider {
136
+ /** Every resource, for `resources/list`. */
137
+ list: () => Promise<ReadonlyArray<McpResourceSummary>>;
138
+ /** One resource's body, or `undefined` when the uri is unknown. */
139
+ read: (uri: string) => Promise<{
140
+ mimeType?: string;
141
+ text: string;
142
+ } | undefined>;
143
+ }
144
+ /**
145
+ * Build an MCP `Server` that advertises and dispatches `tools`.
146
+ *
147
+ * On duplicate names the FIRST wins, so a caller can layer surfaces in
148
+ * precedence order without the later list silently shadowing the earlier one.
149
+ * Thrown errors become `isError` results rather than rejections, per the MCP
150
+ * convention that a tool failure is output the model can read and react to,
151
+ * not a protocol-level fault.
152
+ */
153
+ declare const createToolServer: (info: McpServerInfo, tools: ReadonlyArray<McpTool>, resources?: McpResourceProvider) => Server;
154
+ export { McpServerInfo as M, ToolDefinition as T, McpTool as a, ToolInputSchema as b, ToolOutputSchema as c, ToolResult as d, createToolServer as e, McpResourceProvider as f, McpResourceSummary as g, ToolAnnotations as h };
@@ -0,0 +1 @@
1
+ import{StdioServerTransport as e}from"@modelcontextprotocol/sdk/server/stdio.js";import{createFunctionMcpServer as n}from"./createFunctionMcpServer-jgYBPzxQ.mjs";const a=async t=>{const r=n(t);return await r.connect(new e),r};export{a as default};
@@ -0,0 +1 @@
1
+ import{StdioServerTransport as i}from"@modelcontextprotocol/sdk/server/stdio.js";import{readFileSync as a}from"node:fs";import{dirname as n,join as c}from"node:path";import{fileURLToPath as s}from"node:url";import{createLunoraMcpServer as m}from"./createLunoraMcpServer-BOsMehmQ.mjs";const p=()=>{let r=n(s(import.meta.url));for(let e=0;e<8;e+=1){try{const o=JSON.parse(a(c(r,"package.json"),"utf8"));if(o.name==="@lunora/mcp"&&typeof o.version=="string"&&o.version.length>0)return o.version}catch{}const t=n(r);if(t===r)break;r=t}},S=async r=>{const e=p(),t=m(e===void 0?r:{version:e,...r});return await t.connect(new i),t};export{S as default};
@@ -0,0 +1 @@
1
+ import{LunoraError as y}from"@lunora/errors";import{isAgentToolName as S}from"./AGENT_RUN_INPUT_SCHEMA-BGbKIek4.mjs";import{readScreenedBody as g,serveStateless as w}from"./DEFAULT_MAX_REQUEST_BYTES-CbbpkHRK.mjs";import{resolveClient as d,createLunoraMcpServer as T}from"./createLunoraMcpServer-BOsMehmQ.mjs";import{c as M}from"./tool-call-Bs-QeKp6.mjs";import{d as E,s as _,S as k}from"./tools-Bylr9LPQ.mjs";const A=t=>typeof t.scope!="string"?new Set:new Set(t.scope.split(" ").filter(c=>c!=="")),C=(t,c)=>{const o={...t};for(const{flag:s,scope:n}of k)o[s]=t[s]===!0&&c.has(n);return o},I=(t,c,o)=>{const s=new Set,n=c.agents??[];for(const l of t){const e=M(l);if(e===void 0)continue;(E.has(e)||S(e,n))&&s.add(o);const r=_(e,c);r!==void 0&&s.add(r)}return s},N=256,B=t=>{const{maxRequestBytes:c,stepUp:o}=t;if(o!==void 0&&(typeof o.scope!="string"||o.scope===""||typeof o.challenge!="function"))throw new y("MCP_STEP_UP_MISCONFIGURED","createAuthedMcpFetchHandler: `stepUp` needs a non-empty `scope` and a `challenge` function.");const s=typeof t.server=="function"?void 0:d(t.server),n=new Map,l=e=>{if(e.client!==void 0||e.url===void 0||e.token===void 0||e.token.length===0)return d(e);const r=JSON.stringify([e.url,e.token]),i=n.get(r);if(i!==void 0&&i.fetch===e.fetch)return n.delete(r),n.set(r,i),i.client;const f=d(e);return n.delete(r),n.set(r,{client:f,fetch:e.fetch}),n.size>N&&n.delete(n.keys().next().value),f};return t.protect(async(e,r)=>{const i=A(r),f=typeof t.server=="function"?await t.server(r):{...t.server,client:s};let a;if(o!==void 0){const p=await g(e,c);if("response"in p)return p.response;({parsedBody:a}=p);const m=Array.isArray(a)?a:[a],u=[...I(m,f,o.scope)].filter(v=>!i.has(v));if(u.length>0)throw o.challenge(u)}const h=C(typeof t.server=="function"?{...f,client:l(f)}:f,i);return await w(T(h),e,{maxRequestBytes:c,parsedBody:a})})};export{B as createAuthedMcpFetchHandler,A as mcpTokenScopes};
@@ -0,0 +1 @@
1
+ import{LunoraClient as M}from"@lunora/client";import{Server as P}from"@modelcontextprotocol/sdk/server/index.js";import{ListToolsRequestSchema as B,CallToolRequestSchema as H}from"@modelcontextprotocol/sdk/types.js";import{p as W}from"./package-DsZ0-LJz.mjs";import{LunoraError as A}from"@lunora/errors";import{f as q,w as F,e as g,a as C,b as R,o as N,j as K}from"./tool-result-CPDsNNuJ.mjs";import{e as U,r as Y,f as G,g as Q}from"./tools-Bylr9LPQ.mjs";const l=t=>typeof t=="object"&&t!==null&&!Array.isArray(t),L=/^-?\d+$/,$=1024,J=(t,e,r)=>{const{contentEncoding:n,format:o}=t;if(o==="int64"){if(!L.test(e)||e.length>$)throw new A("BAD_REQUEST",`"${r}" must be a decimal integer string of at most ${String($)} digits`);return BigInt(e)}if(n==="base64")try{return q(e).buffer}catch{throw new A("BAD_REQUEST",`"${r}" must be base64`)}return e},V=(t,e)=>{const{type:r}=t;return typeof r!="string"?!0:r==="integer"||r==="number"?typeof e=="number":r==="array"?Array.isArray(e):r==="object"?l(e):r===typeof e},X=/^(?:[\dA-Z+/]{4})*(?:[\dA-Z+/]{2}==|[\dA-Z+/]{3}=)?$/i,Z=(t,e)=>t.format==="int64"&&!L.test(e)?!1:t.contentEncoding!=="base64"||X.test(e),x=(t,e)=>Object.entries(t).every(([r,n])=>!l(n)||!Object.hasOwn(n,"const")||!Object.hasOwn(e,r)?!0:e[r]===n.const),z=(t,e)=>!V(t,e)||Object.hasOwn(t,"const")&&t.const!==e||l(e)&&l(t.properties)&&!x(t.properties,e)?!1:typeof e!="string"||Z(t,e),tt=(t,e)=>{const r=t.filter(n=>l(n)&&z(n,e));return r.length===1?r[0]:void 0},et=(t,e)=>{const{additionalProperties:r,properties:n}=t,o=l(n)&&Object.hasOwn(n,e)?n[e]:r;return l(o)?o:void 0},S=(t,e,r)=>{const{anyOf:n,items:o}=t;if(e===null)return e;if(Array.isArray(n)){const a=tt(n,e);return a===void 0?e:S(a,e,r)}return typeof e=="string"?J(t,e,r):Array.isArray(e)&&l(o)?e.map((a,c)=>S(o,a,`${r}[${c.toString()}]`)):l(e)?Object.fromEntries(Object.entries(e).map(([a,c])=>{const s=et(t,a);return[a,s===void 0?c:S(s,c,`${r}.${a}`)]})):e},O=(t,e)=>S(t,e,"args"),T=t=>t==="action"||t==="mutation",rt=t=>t==="query"||t==="mutation"||t==="action"||t==="stream",k=64,nt=/[^\w-]/g,ot={destructiveHint:!1,idempotentHint:!0,openWorldHint:!0,readOnlyHint:!0},st={destructiveHint:!0,idempotentHint:!1,openWorldHint:!0,readOnlyHint:!1},it={description:"Shard key. Only for a function partitioned with .shardBy(); omit it otherwise.",type:"string"},ct="The first call only proposes the write. Confirm it with confirmed: true and the actionDigest the proposal returns.",at={action:t=>`Runs the action "${t}", which may call external services.`,mutation:t=>`Runs the mutation "${t}", which changes data.`,query:t=>`Runs the query "${t}". Read-only.`,stream:t=>`Runs the stream "${t}", which yields chunks. The result is every chunk, in order. Read-only.`},dt=(t,e)=>{const r=t.description??at[t.kind](t.path);return e?`${r} ${ct}`:r},b=()=>new A("BAD_REQUEST","openRpcSpec must be the OpenRPC document codegen emits (the `openRpcSpec` export of `_generated/openrpc`)"),lt=t=>t.replaceAll(nt,"_"),_=t=>Array.isArray(t)?t.some(e=>_(e)):l(t)&&(t.writeOnly===!0||Object.values(t).some(e=>_(e))),ut=t=>{const{properties:e}=t;return l(e)?Object.entries(e).filter(([,r])=>_(r)).map(([r])=>r):[]},ft=t=>{const e=l(t)&&l(t.schema)?t.schema:void 0,r=e!==void 0&&Object.hasOwn(e,"description")&&Object.keys(e).every(n=>n==="description");return e===void 0||r?{mode:"none"}:e.type==="object"?{mode:"object",schema:{...e,type:"object"}}:{mode:"wrapped",schema:{properties:{result:e},type:"object"}}},mt=t=>{const{params:e}=t,r=Array.isArray(e)?e.find(o=>l(o)&&o.name==="args"):void 0,n=l(r)?r.schema:void 0;return l(n)?n:{properties:{},type:"object"}},ht=t=>{if(!l(t))throw b();const{name:e}=t;if(typeof e!="string")throw b();const r=t["x-lunora-function-kind"];if(!rt(r))return;const n=t["x-lunora-description"];return{argsSchema:mt(t),...typeof n=="string"?{description:n}:{},kind:r,output:r==="stream"?{mode:"none"}:ft(t.result),path:e}},pt=(t,e)=>{const r=lt(t);if(r.length>k)throw new A("BAD_REQUEST",`the MCP tool name for "${t}" is ${r.length.toString()} characters; the limit is ${k.toString()}. Rename the function.`);const n=e.get(r);if(n!==void 0)throw new A("BAD_REQUEST",`"${t}" and "${n}" both map to the MCP tool name "${r}". Rename one of them.`);return e.set(r,t),r},gt=t=>{const{required:e}=t.argsSchema;return Array.isArray(e)&&e.length>0},yt=(t,e)=>({properties:{args:t.argsSchema,shardKey:it,...e?U:{}},required:gt(t)?["args"]:[],type:"object"}),At={additionalProperties:!1,properties:{},type:"object"},St=t=>{if(t.mode==="none")return;const e=F(t.schema);return t.mode==="object"?{...e,anyOf:[...e.anyOf??[],At]}:e},Ot=(t,e)=>{const r=T(e.kind),n=`${e.kind} ${e.path}`,o=r?void 0:St(e.output);return{annotations:r?{...st,title:n}:{...ot,title:n},description:dt(e,r),inputSchema:yt(e,r),name:t,...o===void 0?{}:{outputSchema:o}}},wt=t=>({argsSchema:t.argsSchema,kind:t.kind,outputMode:t.output.mode,path:t.path,sensitiveArgs:ut(t.argsSchema)}),Et=(t,e)=>{const r=l(t)?t.methods:void 0;if(!Array.isArray(r))throw b();const n=[],o=new Map,a=new Map;for(const c of r){const s=ht(c);if(s===void 0)continue;const i=pt(s.path,a);o.set(i,wt(s)),(!T(s.kind)||e.allowWrites)&&n.push(Ot(i,s))}return{definitions:n,routes:o}},Rt="lunora",j=t=>({__lunoraRef:t}),bt=t=>typeof t.shardKey=="string"&&t.shardKey.length>0?t.shardKey:void 0,D=(t,e)=>t.outputMode==="object"?e===void 0?{...R(e),structuredContent:{}}:N(e):t.outputMode==="wrapped"?N({result:e}):R(e),_t=async(t,e,r,n,o,a,c)=>{const s=e.kind==="action"?"action":"mutation";O(e.argsSchema,o);const i=G(n),f=await Q(t,{args:o,functionPath:e.path,idempotencyKey:i.idempotencyKey,kind:s,shardKey:a,tool:r},i,e.sensitiveArgs,["args","shardKey","idempotencyKey"]);if(f!==void 0)return f;const d=O(e.argsSchema,o),u=e.sensitiveArgs.length===0?j(e.path):{__lunoraRef:e.path,sensitive:!0},m=s==="action"?await t.action(u,d,{shardKey:a,signal:c}):await t.mutation(u,d,{onlineOnly:!0,shardKey:a,signal:c});return D(e,m)},E=1e4,v=16*1024*1024,Tt=async(t,e,r,n,o)=>{const a={__lunoraRef:e.path},c=t.stream(a,r,{maxBuffer:E+1,shardKey:n}),{signal:s,progress:i}=o,f=()=>{c.cancel()};s?.addEventListener("abort",f,{once:!0}),s?.aborted===!0&&f();const d=[];let u=0,m=Promise.resolve(),p;const w=(h,y)=>{m=m.then(()=>p===void 0?i?.(h,y):void 0).catch(I=>{p={error:I},c.cancel()})};try{for await(const h of c){if(d.length===E)return c.cancel(),g("PAYLOAD_TOO_LARGE",`the stream "${e.path}" yielded more than ${E.toString()} chunks, so it was cancelled and no chunks are returned.`);const y=h===void 0?"null":JSON.stringify(h,K);if(u+=y.length,u>v)return c.cancel(),g("PAYLOAD_TOO_LARGE",`the stream "${e.path}" yielded more than ${v.toString()} characters of JSON, so it was cancelled and no chunks are returned.`);d.push(h),i!==void 0&&w(d.length-1,y)}}finally{s?.removeEventListener("abort",f)}if(await m,p!==void 0)throw p.error;return s?.aborted===!0?g("CANCELLED",`the stream "${e.path}" was cancelled before it completed, after ${d.length.toString()} chunk(s).`):R(d)},Nt=async(t,e,r,n,o,a={})=>{const c=e.get(r);if(c===void 0)return g("NOT_FOUND",`unknown tool: ${r}`);if(T(c.kind)&&!o)return g("FORBIDDEN",`tool "${r}" is disabled: this MCP server is read-only. Enable writes with allowWrites.`);try{const s=Y(n.args),i=bt(n);return c.kind==="query"?D(c,await t.query(j(c.path),O(c.argsSchema,s),{shardKey:i,signal:a.signal})):c.kind==="stream"?await Tt(t,c,O(c.argsSchema,s),i,a):await _t(t,c,r,n,s,i,a.signal)}catch(s){return a.signal?.aborted===!0?g("CANCELLED",`the call to "${r}" was cancelled before it completed. A write already sent is not undone.`):C(s)}},$t=t=>{let e;const r=new Set,n=i=>{i.calls===0&&r.delete(i)&&i.client.close()},o=i=>{r.add(i),n(i)},a=()=>{e!==void 0&&(o(e),e=void 0)};return{acquire:()=>{const i=typeof t.url=="function"?t.url():t.url;if(i===void 0||i.length===0){a();return}if(e?.url!==i){a();const u=new M({fetch:t.fetch,url:i,WebSocket:t.WebSocket});t.token!==void 0&&(u.setAuthToken(t.token),u.setWsToken(t.token)),e={calls:0,client:u,url:i}}const f=e;f.calls+=1;let d=!0;return{client:f.client,done:()=>{d&&(d=!1,f.calls-=1,n(f))}}},release:()=>{a();for(const i of r)r.delete(i),i.client.close()}}},Mt=t=>{const e=t.allowWrites??!1,r=()=>typeof t.openRpcSpec=="function"?t.openRpcSpec():t.openRpcSpec,n=d=>({spec:d,tools:Et(d,{allowWrites:e})});let o=n(r());const{acquire:a,release:c}=$t(t),s=new P({name:t.name??Rt,version:t.version??W.version},{capabilities:{tools:{listChanged:!0}}}),i=()=>{let d;try{d=r()}catch(u){return t.onReloadError?.(u),o.tools}if(d!==o.spec)try{o=n(d),s.sendToolListChanged().catch(()=>{})}catch(u){t.onReloadError?.(u),o={spec:d,tools:o.tools}}return o.tools},f=s.close.bind(s);return s.close=async()=>{c(),await f()},s.setRequestHandler(B,()=>({tools:[...i().definitions]})),s.setRequestHandler(H,async(d,u)=>{let m;try{m=a()}catch(h){return C(h)}if(m===void 0)return g("SERVICE_UNAVAILABLE","no Lunora deployment URL is available yet. Start `lunora dev` or set the deployment URL, then call the tool again.");const{progressToken:p}=u._meta??{},w={signal:u.signal,...p===void 0?{}:{progress:async(h,y)=>{await u.sendNotification({method:"notifications/progress",params:{message:y,progress:h,progressToken:p}})}}};try{return await Nt(m.client,i().routes,d.params.name,d.params.arguments??{},e,w)}finally{m.done()}}),s};export{E as MAX_STREAM_CHUNKS,Nt as callFunctionTool,Mt as createFunctionMcpServer};
@@ -0,0 +1 @@
1
+ import{LunoraClient as g}from"@lunora/client";import{LunoraError as m}from"@lunora/errors";import{Server as w}from"@modelcontextprotocol/sdk/server/index.js";import{ListToolsRequestSchema as d,CallToolRequestSchema as h}from"@modelcontextprotocol/sdk/types.js";import{p as A}from"./package-DsZ0-LJz.mjs";import{agentToolDefinitions as M,isAgentToolName as T,callAgentTool as R}from"./AGENT_RUN_INPUT_SCHEMA-BGbKIek4.mjs";import{t as k,c as N}from"./tools-Bylr9LPQ.mjs";const E="lunora",L=e=>{if(e.client!==void 0)return e.client;if(e.url===void 0)throw new m("INTERNAL","createLunoraMcpServer requires either a `client` or a `url`");if(e.token===void 0||e.token.length===0)throw new m("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 t=new g({fetch:e.fetch,url:e.url});return t.setAuthToken(e.token),t},C=e=>{const t=L(e),n=e.allowWrites??!1,o=e.allowAgents??!1,r=e.agents??[],s=typeof e.token=="string"&&e.token.length>0,i=e.allowObservability===!0&&s,c=e.allowDataReads===!0&&s,a=new w({name:E,version:e.version??A.version},{capabilities:{tools:{}}});return a.setRequestHandler(d,()=>({tools:[...k(n,i,c),...M(r,o)]})),a.setRequestHandler(h,async v=>{const{arguments:f,name:l}=v.params,u=f??{};return T(l,r)?await R(t,l,u,{allowAgents:o,exposures:r,...e.agentMaxWaitMs===void 0?{}:{maxWaitMs:e.agentMaxWaitMs},...e.agentPollIntervalMs===void 0?{}:{pollIntervalMs:e.agentPollIntervalMs}}):await N(t,l,u,n,i,c)}),a};export{C as createLunoraMcpServer,L as resolveClient};
@@ -0,0 +1 @@
1
+ import{serveStateless as c}from"./DEFAULT_MAX_REQUEST_BYTES-CbbpkHRK.mjs";import{DEFAULT_MAX_REQUEST_BYTES as u}from"./DEFAULT_MAX_REQUEST_BYTES-CbbpkHRK.mjs";import{resolveClient as s,createLunoraMcpServer as a}from"./createLunoraMcpServer-BOsMehmQ.mjs";const l=e=>{const r=s(e);return t=>c(a({...e,client:r}),t,{maxRequestBytes:e.maxRequestBytes})};export{u as DEFAULT_MAX_REQUEST_BYTES,l as createMcpFetchHandler,c as serveStateless};
@@ -0,0 +1 @@
1
+ import{LunoraError as B}from"@lunora/errors";import{Server as E}from"@modelcontextprotocol/sdk/server/index.js";import{CallToolRequestSchema as M,ListToolsRequestSchema as P}from"@modelcontextprotocol/sdk/types.js";import{CfWorkerJsonSchemaValidator as _}from"@modelcontextprotocol/sdk/validation/cfworker";import{m as b}from"./promise-memo-OqVPWoUX.mjs";import{readScreenedBody as x,serveStateless as A}from"./DEFAULT_MAX_REQUEST_BYTES-CbbpkHRK.mjs";import{c as S}from"./tool-call-Bs-QeKp6.mjs";import{e as C,a as F}from"./tool-result-CPDsNNuJ.mjs";let T;const H=()=>(T??=new _,T),N=(t,o,s)=>{const d=t.validate(s);return d.valid?void 0:C("BAD_REQUEST",`invalid arguments for tool "${o}": ${d.errorMessage}`)},V=async()=>{try{return(await import("@lunora/x402/charge")).createChargeMiddleware}catch(t){throw new B("INTERNAL",`paid MCP tools need the optional peer "@lunora/x402" — install it alongside @lunora/mcp to charge for tools (${t instanceof Error?t.message:String(t)})`)}},$={name:"lunora-paid-mcp",version:"0.0.0"},q=()=>Response.json({error:"A JSON-RPC batch may not reference a paid MCP tool; send paid tools/call requests individually."},{status:400}),D=async(t,o,s,d,u)=>{const l={reached:!1},p=new E({name:"lunora-paid-mcp-preflight",version:"0.0.0"},{capabilities:{tools:{}}});p.setRequestHandler(M,v=>{const f=N(d,s,v.params.arguments??{});return l.reached=f===void 0&&v.params.name===s,f??{content:[]}});const h=await A(p,t,{maxRequestBytes:u,parsedBody:o});return l.reached?void 0:h},K=t=>{const o=new Map,s=new Map,d=new Map,u=t.serverInfo??$,l=(e,r,a)=>{if(o.has(e.name))throw new B("BAD_REQUEST",`MCP tool "${e.name}" is already registered.`);const n={annotations:e.annotations,description:e.description,inputSchema:e.inputSchema,name:e.name};o.set(e.name,{definition:n,handler:r,validate:H().getValidator(n.inputSchema)}),a!==void 0&&s.set(e.name,a)},p=()=>{const e=new E(u,{capabilities:{tools:{}}});return e.setRequestHandler(P,()=>({tools:[...o.values()].map(r=>r.definition)})),e.setRequestHandler(M,async r=>{const a=o.get(r.params.name);if(a===void 0)return C("NOT_FOUND",`unknown tool: ${r.params.name}`);const n=r.params.arguments??{},i=N(a,r.params.name,n);if(i!==void 0)return i;try{return await a.handler(n)}catch(m){return F(m)}}),e},h=(e,r)=>b(d,e,async()=>(await V())({...t.charge,price:r},{resource:e}));return{fetchHandler:async(e,r,a)=>{const n=await x(e.clone(),t.maxRequestBytes);if("response"in n)return n.response;const{parsedBody:i}=n,m=()=>A(p(),e,{maxRequestBytes:t.maxRequestBytes,parsedBody:i});if(Array.isArray(i))return i.some(w=>s.has(S(w)??""))?q():m();const c=S(i),y=c===void 0?void 0:s.get(c),R=c===void 0?void 0:o.get(c);if(c===void 0||y===void 0||R===void 0)return m();const g=await D(e,i,c,R,t.maxRequestBytes);if(g!==void 0)return g;const U=await h(c,y),j=typeof a?.waitUntil=="function"?{waitUntil:w=>{a.waitUntil?.(w)}}:void 0;return U.handle(e,m,j)},paidTool:(e,r)=>{l(e,r,e.price)},tool:(e,r)=>{l(e,r)}}};export{K as createPaidMcpServer};
@@ -0,0 +1 @@
1
+ import{Server as l}from"@modelcontextprotocol/sdk/server/index.js";import{ListToolsRequestSchema as p,CallToolRequestSchema as h,ListResourcesRequestSchema as d,ReadResourceRequestSchema as w}from"@modelcontextprotocol/sdk/types.js";import{w as R,e as f,a as S}from"./tool-result-CPDsNNuJ.mjs";const y=(u,c,s)=>{const a=new Map;for(const e of c)a.has(e.definition.name)||a.set(e.definition.name,e);const i=[...a.values()].map(({definition:e})=>e.outputSchema===void 0?e:{...e,outputSchema:R(e.outputSchema)}),r=new l(u,{capabilities:s===void 0?{tools:{}}:{resources:{},tools:{}}});return r.setRequestHandler(p,()=>({tools:i})),r.setRequestHandler(h,async e=>{const{arguments:o,name:t}=e.params,n=a.get(t);if(n===void 0)return f("NOT_FOUND",`unknown tool: ${t}`);try{return await n.handle(o??{})}catch(m){return S(m)}}),s!==void 0&&(r.setRequestHandler(d,async()=>({resources:[...await s.list()]})),r.setRequestHandler(w,async e=>{const{uri:o}=e.params,t=await s.read(o);if(t===void 0)throw new Error(`unknown resource: ${o}`);return{contents:[{mimeType:t.mimeType??"text/plain",text:t.text,uri:o}]}})),r};export{y as createToolServer};
@@ -0,0 +1,196 @@
1
+ import { LunoraClient } from '@lunora/client';
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { b as ToolInputSchema, T as ToolDefinition, d as ToolResult } from "./compose.d-DiPnHeQv.js";
4
+ /**
5
+ * Agent exposure for the MCP server: a durable `@lunora/agent` run fronted as an
6
+ * MCP tool an external agent can call. The capability boundary is the MCP-server
7
+ * process + its token, so WHICH agents are exposed is config on the server (like
8
+ * `allowWrites`), not on `defineAgent` — keeping `@lunora/agent` codegen
9
+ * byte-identical.
10
+ */
11
+ interface McpAgentExposure {
12
+ /** What the agent does — shown to the calling model, which decides from it. */
13
+ description: string;
14
+ /** The agent's export name (its `ctx.agents.<name>` / `AGENT_<NAME>` binding). */
15
+ name: string;
16
+ /** Override the model-facing tool name (default `agent_<name>`). */
17
+ toolName?: string;
18
+ }
19
+ /** The generic status/poll tool advertised alongside the per-agent tools. */
20
+ declare const AGENT_STATUS_TOOL_NAME = "lunora_agent_status";
21
+ /**
22
+ * The uniform input schema every agent tool advertises. Agents share ONE run
23
+ * input (`@lunora/agent` has no per-agent validator), so there is nothing to
24
+ * derive per agent — a single static schema is reused for every agent tool.
25
+ */
26
+ declare const AGENT_RUN_INPUT_SCHEMA: ToolInputSchema;
27
+ /**
28
+ * Parse `LUNORA_MCP_AGENTS` — a `;`-separated list of `name:description` pairs,
29
+ * e.g. `"support:Handles support questions;billing:Billing help"`. The
30
+ * description may itself contain colons (only the FIRST colon splits). Blank
31
+ * entries and entries with an empty name/description are skipped.
32
+ */
33
+ declare const parseAgentsEnv: (raw: string | undefined) => McpAgentExposure[];
34
+ /**
35
+ * The tools this module advertises. Fail-closed: only the boolean `true` opts
36
+ * in (an env-plumbed caller could pass a truthy string), and the tools appear
37
+ * ONLY when at least one agent is exposed — so an agent-free or non-opted-in
38
+ * server never lists them.
39
+ */
40
+ declare const agentToolDefinitions: (exposures: ReadonlyArray<McpAgentExposure>, allowAgents: boolean) => ReadonlyArray<ToolDefinition>;
41
+ /** Options threaded into a single agent tool dispatch. */
42
+ interface CallAgentToolOptions {
43
+ /** Opt-in gate — must be exactly `true` or the call is refused fail-closed. */
44
+ allowAgents: boolean;
45
+ /** The exposures advertised by this server. */
46
+ exposures: ReadonlyArray<McpAgentExposure>;
47
+ /** Wall-clock budget a single call awaits before returning a pending result. */
48
+ maxWaitMs?: number;
49
+ /** Delay between thread-status polls. */
50
+ pollIntervalMs?: number;
51
+ /** Test seam replacing the between-poll wait; production uses a real timer. */
52
+ wait?: (ms: number) => Promise<void>;
53
+ }
54
+ /**
55
+ * Dispatch an agent tool call: start a durable run via `agents:agentRun`, then
56
+ * await-with-timeout — poll `agents:agentThread` until terminal (returning the
57
+ * final answer from `agents:agentMessages`) or, on budget exhaustion, return a
58
+ * NON-error pending payload the caller resumes with `lunora_agent_status`.
59
+ *
60
+ * Fail-closed: refused at dispatch unless `allowAgents === true`, mirroring the
61
+ * `allowWrites` guard — starting a run is a side effect and must not ride the
62
+ * read-only default.
63
+ */
64
+ declare const callAgentTool: (client: LunoraClient, name: string, input: Record<string, unknown>, options: CallAgentToolOptions) => Promise<ToolResult>;
65
+ interface LunoraMcpServerOptions {
66
+ /** Wall-clock budget a single agent tool call awaits before returning a pending result. */
67
+ agentMaxWaitMs?: number;
68
+ /** Delay between agent thread-status polls. */
69
+ agentPollIntervalMs?: number;
70
+ /** The agents this server fronts as MCP tools (see `allowAgents`). */
71
+ agents?: ReadonlyArray<McpAgentExposure>;
72
+ /**
73
+ * Expose the per-agent tools (`agent_<name>` + the generic
74
+ * `lunora_agent_status`). Defaults to `false`, mirroring `allowWrites`:
75
+ * starting a durable agent run is a side effect, so the agent tools are
76
+ * omitted from the advertised list AND refused at dispatch unless explicitly
77
+ * opted in. Only takes effect together with a non-empty `agents` list.
78
+ */
79
+ allowAgents?: boolean;
80
+ /**
81
+ * Expose `lunora_find_related`, which returns raw table rows read through the
82
+ * admin writer, so RLS policies and column masks do not apply. Defaults to
83
+ * `false`, for the same reason as `allowObservability`. Only takes effect
84
+ * when a `token` resolved. On the OAuth-protected path the verified token must
85
+ * also carry the data-read scope (see `DATA_READ_SCOPE` in `./tools`).
86
+ */
87
+ allowDataReads?: boolean;
88
+ /**
89
+ * Expose the observability tools (`lunora_get_logs`, `lunora_get_issues`,
90
+ * `lunora_get_advisories`, `lunora_get_query_insights`,
91
+ * `lunora_get_migration_status`). Defaults to `false`, mirroring
92
+ * `allowWrites`: they are read-only, but every row they return — log lines,
93
+ * request metadata, grouped error messages — is production user data that
94
+ * lands in the model's context and therefore at its provider. Holding the
95
+ * admin bearer is not consent to ship that, so it is a separate opt-in;
96
+ * without it the tools are omitted from the advertised list AND refused at
97
+ * dispatch. Only takes effect when a `token` resolved. On the OAuth-protected
98
+ * path the verified token must also carry the observability scope (see
99
+ * `OBSERVABILITY_SCOPE` in `./tools`); this option stays the outer gate.
100
+ */
101
+ allowObservability?: boolean;
102
+ /**
103
+ * Expose the write tools (`lunora_run_mutation` / `lunora_run_action`).
104
+ * Defaults to `false`: the server is READ-ONLY unless explicitly opted in,
105
+ * so a prompt-injected or misaligned agent can't mutate the deployment with
106
+ * the configured token. When false the write tools are omitted from the
107
+ * advertised tool list AND refused at dispatch.
108
+ */
109
+ allowWrites?: boolean;
110
+ /**
111
+ * Pre-built client (test injection). When omitted a `LunoraClient` is
112
+ * created from `url`/`token`/`fetch`.
113
+ */
114
+ client?: LunoraClient;
115
+ /** `fetch` implementation; defaults to the ambient global. */
116
+ fetch?: typeof fetch;
117
+ /**
118
+ * Bearer token sent on every RPC. **Required** alongside `url`, and it must
119
+ * be the deployment's **admin bearer**: the introspection/allowlist path
120
+ * every tool depends on (`lunora_list_functions`, `lunora_list_tables`, and
121
+ * the `assertRunnable` precheck that runs before every `run` tool) hits
122
+ * admin-gated `/_lunora/admin/*` routes, so no scoped/app token works
123
+ * today — it would 403 (`ADMIN_FORBIDDEN`) on the first tool call. The
124
+ * read-only guarantee is therefore NOT enforced by the token's scope; it is
125
+ * enforced in-process via `allowWrites: false` (the default), which omits
126
+ * the write tools from the advertised list and refuses them at dispatch.
127
+ *
128
+ * Because EVERY tool needs it, omitting it is a misconfiguration rather than
129
+ * a reduced-capability mode, and `createLunoraMcpServer` says so at
130
+ * construction instead of advertising a surface that 403s on first use. The
131
+ * one exception is the `client` injection seam (tests / a pre-authenticated
132
+ * client), where this server cannot know what the client can reach and so
133
+ * reads "unknown" fail-closed: the privileged observability tools stay
134
+ * unadvertised and are refused at dispatch.
135
+ */
136
+ token?: string;
137
+ /** Base URL of the deployed Lunora Worker. Required unless `client` is given. */
138
+ url?: string;
139
+ /** Version reported in the `initialize` handshake. Defaults to the version bundled at build time. */
140
+ version?: string;
141
+ }
142
+ /**
143
+ * Build an MCP `Server` whose tools talk to a Lunora deployment. The server is
144
+ * transport-agnostic — call `.connect(transport)` yourself, or use
145
+ * `connectStdio` for the common stdio case.
146
+ *
147
+ * Tool calls are dispatched through `callTool`, which the deployment reaches
148
+ * over HTTP RPC. No WebSocket is opened (the tools never subscribe), so this is
149
+ * safe to run as a short-lived stdio process.
150
+ */
151
+ declare const createLunoraMcpServer: (options: LunoraMcpServerOptions) => Server;
152
+ interface FunctionMcpServerOptions {
153
+ /**
154
+ * Advertise and allow mutation and action tools. Off by default. A closed write
155
+ * is not listed and is refused at dispatch.
156
+ */
157
+ allowWrites?: boolean;
158
+ fetch?: typeof fetch;
159
+ /** The MCP server name clients show. Defaults to `lunora`. */
160
+ name?: string;
161
+ /**
162
+ * Told when a rewritten document cannot be read or built. The server keeps the tools it
163
+ * last built, so the error is reported here rather than failing the request.
164
+ */
165
+ onReloadError?: (error: unknown) => void;
166
+ /**
167
+ * The `openRpcSpec` export of `_generated/openrpc`, or a function returning it. A
168
+ * function is called on every `tools/list` and `tools/call`, so the tools follow a
169
+ * document that codegen rewrites while the server runs. Return the same object
170
+ * while the document is unchanged: a different object rebuilds the tools.
171
+ */
172
+ openRpcSpec: unknown;
173
+ /** The CALLER's bearer. Writes need one: the confirmation digest is keyed by it. */
174
+ token?: string;
175
+ /**
176
+ * The Lunora deployment URL the app talks to. A function is resolved on every
177
+ * call, so a server started before `lunora dev` picks the URL up once it exists.
178
+ * `undefined` means no deployment is running yet, and the call says so.
179
+ */
180
+ url: string | (() => string | undefined);
181
+ version?: string;
182
+ /**
183
+ * The WebSocket class streams open with. Defaults to the global `WebSocket`, which a
184
+ * runtime without one (older Node) lacks, so such a host passes its own here.
185
+ */
186
+ WebSocket?: typeof WebSocket;
187
+ }
188
+ /**
189
+ * Build an MCP `Server` with one tool per function in `openRpcSpec`. Transport
190
+ * agnostic: call `.connect(transport)` yourself.
191
+ *
192
+ * Throws at construction when the document is malformed or two functions share a
193
+ * tool name, so a bad integration fails at startup rather than on the first call.
194
+ */
195
+ declare const createFunctionMcpServer: (options: FunctionMcpServerOptions) => Server;
196
+ export { AGENT_RUN_INPUT_SCHEMA as A, CallAgentToolOptions as C, FunctionMcpServerOptions as F, LunoraMcpServerOptions as L, McpAgentExposure as M, AGENT_STATUS_TOOL_NAME as a, agentToolDefinitions as b, callAgentTool as c, createFunctionMcpServer as d, createLunoraMcpServer as e, parseAgentsEnv as p };