@lunora/mcp 1.0.0-alpha.12 → 1.0.0-alpha.120

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 (30) hide show
  1. package/LICENSE.md +126 -0
  2. package/README.md +157 -10
  3. package/dist/bin.mjs +4 -35
  4. package/dist/docs/index.d.mts +258 -0
  5. package/dist/docs/index.d.ts +258 -0
  6. package/dist/docs/index.mjs +1 -0
  7. package/dist/index.d.mts +511 -14
  8. package/dist/index.d.ts +511 -14
  9. package/dist/index.mjs +1 -2
  10. package/dist/packem_shared/AGENT_RUN_INPUT_SCHEMA-hKbpa3Dg.mjs +1 -0
  11. package/dist/packem_shared/DEFAULT_DOCS_BASE_URL-CZ3fVsSc.mjs +4 -0
  12. package/dist/packem_shared/DEFAULT_MAX_REQUEST_BYTES-CbbpkHRK.mjs +1 -0
  13. package/dist/packem_shared/DEFAULT_SEARCH_LIMIT-BqSYN5vr.mjs +3 -0
  14. package/dist/packem_shared/DOCS_SERVER_NAME-BMeAWi5h.mjs +1 -0
  15. package/dist/packem_shared/DOCS_URI_SCHEME-Buo752CV.mjs +3 -0
  16. package/dist/packem_shared/LOCAL_SERVER_NAME-DPay9JzK.mjs +1 -0
  17. package/dist/packem_shared/OBSERVABILITY_TOOL_DEFINITIONS-Byqgb9wh.mjs +1 -0
  18. package/dist/packem_shared/READ_ONLY_TOOL_DEFINITIONS-_aG40yWx.mjs +1 -0
  19. package/dist/packem_shared/connectStdio-BBtfW4UB.mjs +1 -0
  20. package/dist/packem_shared/createAuthedMcpFetchHandler-DwOzAueC.mjs +1 -0
  21. package/dist/packem_shared/createMcpFetchHandler-DSK2X9Hd.mjs +1 -0
  22. package/dist/packem_shared/createPaidMcpServer-BIBKMtxs.mjs +1 -0
  23. package/dist/packem_shared/createToolServer-BtGuPyMU.mjs +1 -0
  24. package/dist/packem_shared/observability-tools-B-g9Y9IT.mjs +1 -0
  25. package/dist/packem_shared/serve-stateless.d-CKsbI0fP.d.mts +154 -0
  26. package/dist/packem_shared/serve-stateless.d-CKsbI0fP.d.ts +154 -0
  27. package/dist/packem_shared/toDocsSearchHits-CBLmtWXt.mjs +1 -0
  28. package/package.json +17 -3
  29. package/dist/packem_shared/TOOL_DEFINITIONS-Dpiu38ji.mjs +0 -112
  30. package/dist/packem_shared/connectStdio-C_mvQBs2.mjs +0 -64
package/LICENSE.md CHANGED
@@ -103,3 +103,129 @@ Unless required by applicable law or agreed to in writing, software distributed
103
103
  under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
104
104
  CONDITIONS OF ANY KIND, either express or implied. See the License for the
105
105
  specific language governing permissions and limitations under the License.
106
+
107
+ <!-- DEPENDENCIES -->
108
+ <!-- /DEPENDENCIES -->
109
+
110
+ <!-- TYPE_DEPENDENCIES -->
111
+
112
+ # Licenses of bundled types
113
+ The published @lunora/mcp artifact additionally contains code with the following licenses:
114
+ FSL-1.1-Apache-2.0
115
+
116
+ # Bundled types:
117
+ ## @lunora/x402
118
+ License: FSL-1.1-Apache-2.0
119
+ By: Daniel Bannert
120
+ Repository: git+https://github.com/anolilab/lunora.git
121
+
122
+ > # Functional Source License, Version 1.1, Apache 2.0 Future License
123
+ >
124
+ > ## Abbreviation
125
+ >
126
+ > FSL-1.1-Apache-2.0
127
+ >
128
+ > ## Notice
129
+ >
130
+ > Copyright 2026 anolilab and contributors
131
+ >
132
+ > ## Terms and Conditions
133
+ >
134
+ > ### Licensor ("We")
135
+ >
136
+ > The party offering the Software under these Terms and Conditions.
137
+ >
138
+ > ### The Software
139
+ >
140
+ > The "Software" is each version of the software that we make available under
141
+ > these Terms and Conditions, as indicated by our inclusion of these Terms and
142
+ > Conditions with the Software.
143
+ >
144
+ > ### License Grant
145
+ >
146
+ > Subject to your compliance with this License Grant and the Patents,
147
+ > Redistribution and Trademark clauses below, we hereby grant you the right to
148
+ > use, copy, modify, create derivative works, publicly perform, publicly display
149
+ > and redistribute the Software for any Permitted Purpose identified below.
150
+ >
151
+ > ### Permitted Purpose
152
+ >
153
+ > A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
154
+ > means making the Software available to others in a commercial product or service
155
+ > that:
156
+ >
157
+ > 1. substitutes for the Software;
158
+ >
159
+ > 2. substitutes for any other product or service we offer using the Software that
160
+ > exists as of the date we make the Software available; or
161
+ >
162
+ > 3. offers the same or substantially similar functionality as the Software.
163
+ >
164
+ > Permitted Purposes specifically include using the Software:
165
+ >
166
+ > 1. for your internal use and access;
167
+ >
168
+ > 2. for non-commercial education;
169
+ >
170
+ > 3. for non-commercial research; and
171
+ >
172
+ > 4. in connection with professional services that you provide to a licensee using
173
+ > the Software in accordance with these Terms and Conditions.
174
+ >
175
+ > ### Patents
176
+ >
177
+ > To the extent your use for a Permitted Purpose would necessarily infringe our
178
+ > patents, the license grant above includes a license under our patents. If you
179
+ > make a claim against any party that the Software infringes or contributes to the
180
+ > infringement of any patent, then your patent license to the Software ends
181
+ > immediately.
182
+ >
183
+ > ### Redistribution
184
+ >
185
+ > The Terms and Conditions apply to all copies, modifications and derivatives of
186
+ > the Software.
187
+ >
188
+ > If you redistribute any copies, modifications or derivatives of the Software,
189
+ > you must include a copy of or a link to these Terms and Conditions and not
190
+ > remove any copyright notices provided in or with the Software.
191
+ >
192
+ > ### Disclaimer
193
+ >
194
+ > THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
195
+ > IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
196
+ > PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
197
+ >
198
+ > IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
199
+ > SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, EVEN
200
+ > IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
201
+ >
202
+ > ### Trademarks
203
+ >
204
+ > Except for displaying the License Details and identifying us as the origin of
205
+ > the Software, you have no right under these Terms and Conditions to use our
206
+ > trademarks, trade names, service marks or product names.
207
+ >
208
+ > ## Grant of Future License
209
+ >
210
+ > We hereby irrevocably grant you an additional license to use the Software under
211
+ > the Apache License, Version 2.0 that is effective on the second anniversary of
212
+ > the date we make the Software available. On or after that date, you may use the
213
+ > Software under the Apache License, Version 2.0, in which case the following will
214
+ > apply:
215
+ >
216
+ > Licensed under the Apache License, Version 2.0 (the "License"); you may not use
217
+ > this file except in compliance with the License.
218
+ >
219
+ > You may obtain a copy of the License at
220
+ >
221
+ > http://www.apache.org/licenses/LICENSE-2.0
222
+ >
223
+ > Unless required by applicable law or agreed to in writing, software distributed
224
+ > under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
225
+ > CONDITIONS OF ANY KIND, either express or implied. See the License for the
226
+ > specific language governing permissions and limitations under the License.
227
+ >
228
+ > <!-- DEPENDENCIES -->
229
+ > <!-- /DEPENDENCIES -->
230
+
231
+ <!-- /TYPE_DEPENDENCIES -->
package/README.md CHANGED
@@ -34,20 +34,32 @@
34
34
 
35
35
  ---
36
36
 
37
- A [Model Context Protocol](https://modelcontextprotocol.io) server that exposes a deployed Lunora app to AI agents. It registers tools for introspecting a deployment (`lunora_list_functions`, `lunora_list_tables`, `lunora_get_function_schema`) and invoking its functions (`lunora_run_query`, `lunora_run_mutation`, `lunora_run_action`), each backed by `@lunora/client` over HTTP RPC.
37
+ [Model Context Protocol](https://modelcontextprotocol.io) servers for Lunora, in two flavours:
38
+
39
+ - **Deployment** (the main entry) — exposes a deployed Lunora app to AI agents: introspection tools (`lunora_list_functions`, `lunora_list_tables`, `lunora_get_function_schema`) and invocation tools (`lunora_run_query`, `lunora_run_mutation`, `lunora_run_action`), each backed by `@lunora/client` over HTTP RPC. Needs an admin token.
40
+ - **Documentation** ([`@lunora/mcp/docs`](#documentation-server)) — exposes the framework's _docs_ so an agent writing Lunora code can look up the real API instead of guessing. Credential-free, and safe to host publicly; Lunora runs it at `https://lunora.sh/mcp`.
41
+
42
+ Most users never install this package directly — `lunora mcp install` wires both servers into their editor. See [`@lunora/cli`](https://www.npmjs.com/package/@lunora/cli).
38
43
 
39
44
  Part of the [Lunora](https://github.com/anolilab/lunora) framework — a type-safe, real-time backend on Cloudflare Workers + Durable Objects with a Vite-first DX.
40
45
 
41
46
  ## Tools
42
47
 
43
- | Tool | Description |
44
- | ---------------------------- | -------------------------------------------------------------------------------------------------------------- |
45
- | `lunora_list_functions` | List the deployment's public functions (queries, mutations, actions) with their kinds. |
46
- | `lunora_list_tables` | List the deployment's `.global()` tables with their row counts. |
47
- | `lunora_get_function_schema` | Return a function's argument descriptors and kind by path, so a caller can construct a valid arguments object. |
48
- | `lunora_run_query` | Run a query and return its result. Read-only. |
49
- | `lunora_run_mutation` | Run a mutation and return its result. Writes data — use with care. |
50
- | `lunora_run_action` | Run an action and return its result. May call external services. |
48
+ | Tool | Description |
49
+ | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
50
+ | `lunora_list_functions` | List the deployment's public functions (queries, mutations, actions) with their kinds. |
51
+ | `lunora_list_tables` | List the deployment's `.global()` tables with their row counts. |
52
+ | `lunora_get_function_schema` | Return a function's argument descriptors and kind by path, so a caller can construct a valid arguments object. |
53
+ | `lunora_run_query` | Run a query and return its result. Read-only. |
54
+ | `lunora_run_mutation` | Run a mutation and return its result. Writes data — use with care. |
55
+ | `lunora_run_action` | Run an action and return its result. May call external services. |
56
+ | `lunora_get_logs` | Read the deployment's recent log entries (newest first). Requires an admin token. |
57
+ | `lunora_get_issues` | List errors grouped into Issues by fingerprint, with counts and triage status. Requires an admin token. |
58
+ | `lunora_get_advisories` | List the deployment's schema/query advisories. Requires an admin token. |
59
+ | `lunora_get_query_insights` | Per-statement execution counts and latency over a recent window. Requires an admin token. |
60
+ | `lunora_get_migration_status` | Which migrations are applied and which are pending. Requires an admin token. |
61
+ | `agent_<name>` | Start a durable [`@lunora/agent`](https://www.npmjs.com/package/@lunora/agent) run and await its answer. One tool per exposed agent. Requires agents enabled. |
62
+ | `lunora_agent_status` | Poll a running agent by `threadKey` and return its answer once finished. Requires agents enabled. |
51
63
 
52
64
  ### Recommended agent flow
53
65
 
@@ -58,6 +70,27 @@ Part of the [Lunora](https://github.com/anolilab/lunora) framework — a type-sa
58
70
  → call the function with a well-formed arguments object
59
71
  ```
60
72
 
73
+ ### Observability tools (privileged)
74
+
75
+ The five `lunora_get_*` observability tools are read-only, but they surface the
76
+ deployment's **operational data** — log lines, request metadata, and grouped
77
+ error messages, all of which may contain user data, and all of which land in the
78
+ model's context (and therefore at its provider). They are therefore **off by
79
+ default**: set `LUNORA_MCP_ALLOW_OBSERVABILITY=1` (or pass
80
+ `allowObservability: true`) to expose them. Without it they are omitted from
81
+ `ListTools` entirely and refused at dispatch, the same omit-don't-refuse rule the
82
+ write tools use. They are independent of `--allow-writes`, which is about
83
+ changing data, not reading operational data — and independent of the admin
84
+ bearer, which every tool already needs, so holding it is not the opt-in.
85
+
86
+ They return `structuredContent` alongside the usual text block, described by each
87
+ tool's `outputSchema` (MCP revision `2025-06-18` and later; older clients keep
88
+ reading the text block). All but `lunora_get_migration_status` take a `limit`
89
+ clamped server-side; migration status takes only `shardKey` and returns every
90
+ migration, because truncating that list would hide the pending one. Each also
91
+ takes an optional `shardKey` — on a `.shardBy()`-partitioned deployment these
92
+ reads are **per-shard**, not deployment-wide.
93
+
61
94
  `lunora_get_function_schema` returns a JSON object with three fields:
62
95
 
63
96
  - `path` — the function path (e.g. `"messages:send"`)
@@ -70,6 +103,8 @@ Part of the [Lunora](https://github.com/anolilab/lunora) framework — a type-sa
70
103
  npm install @lunora/mcp
71
104
  ```
72
105
 
106
+ Paid MCP tools (`createPaidMcpServer`) additionally need the optional peer [`@lunora/x402`](https://www.npmjs.com/package/@lunora/x402); it is loaded lazily, so installs that never charge for a tool don't pay for its dependency tree.
107
+
73
108
  ```sh
74
109
  yarn add @lunora/mcp
75
110
  ```
@@ -80,7 +115,7 @@ pnpm add @lunora/mcp
80
115
 
81
116
  ## Usage
82
117
 
83
- MCP clients spawn the `lunora-mcp` binary over stdio. Configuration comes from `LUNORA_URL` (required) and `LUNORA_ADMIN_TOKEN` (optional bearer token):
118
+ MCP clients spawn the `lunora-mcp` binary over stdio. Configuration comes from `LUNORA_URL` and `LUNORA_ADMIN_TOKEN` — both required (see [Tokens](#tokens)):
84
119
 
85
120
  ```jsonc
86
121
  {
@@ -105,6 +140,117 @@ const server = createLunoraMcpServer({ url: "https://app.example.workers.dev", t
105
140
  await server.connect(myTransport);
106
141
  ```
107
142
 
143
+ ## Expose an agent
144
+
145
+ A deployment's durable [`@lunora/agent`](https://www.npmjs.com/package/@lunora/agent) agents can be fronted as MCP tools. This is **opt-in and fail-closed**, mirroring `allowWrites`: starting an agent run is a side effect, so the agent tools are omitted from the advertised list _and_ refused at dispatch unless you explicitly enable them. `@lunora/mcp` takes no dependency on `@lunora/agent` — it reaches the agent's public `agents:agentRun` mutation over RPC like any other function.
146
+
147
+ Two opt-ins are needed, on **both** sides:
148
+
149
+ 1. **On the agent**, `defineAgent({ publicRun: true })`. `agents:agentRun` refuses
150
+ any agent without it (`FORBIDDEN`), because starting a durable run from
151
+ outside the deployment is a side effect the agent's author has to allow. The
152
+ env vars below do not grant it — an agent exposed here but not marked
153
+ `publicRun` is advertised and fails on its first call.
154
+ 2. **On this server**, the env vars (or the matching `createLunoraMcpServer` options):
155
+
156
+ - `LUNORA_MCP_ALLOW_AGENTS` — set to `1`/`true`/`yes`/`on` to expose the agent tools. Default: agents disabled.
157
+ - `LUNORA_MCP_AGENTS` — a `;`-separated list of `name:description` pairs selecting which agents to expose, e.g. `"support:Support questions;billing:Billing help"`.
158
+ - `LUNORA_MCP_AGENT_TIMEOUT_MS` (optional) — wall-clock budget a single `agent_<name>` call awaits before returning a pending result to poll.
159
+
160
+ ```jsonc
161
+ {
162
+ "mcpServers": {
163
+ "lunora": {
164
+ "command": "lunora-mcp",
165
+ "env": {
166
+ "LUNORA_URL": "https://app.example.workers.dev",
167
+ "LUNORA_ADMIN_TOKEN": "...",
168
+ "LUNORA_MCP_ALLOW_AGENTS": "1",
169
+ "LUNORA_MCP_AGENTS": "support:Support questions;billing:Billing help",
170
+ },
171
+ },
172
+ },
173
+ }
174
+ ```
175
+
176
+ 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.
177
+
178
+ 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.
179
+
180
+ ### Tokens
181
+
182
+ `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.
183
+
184
+ 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).
185
+
186
+ ## Resources and annotations
187
+
188
+ Both servers implement MCP **tools**; the documentation server additionally exposes every page as an MCP **resource** (`lunora-docs:/docs/…`, `text/markdown`), so a client can enumerate and attach a page directly instead of the model having to guess a search query first.
189
+
190
+ Every tool carries **annotations** — `readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint` and a human-facing `title` — so a client can badge the read-only surface and confirm before a write. They are hints; the actual guarantee is still made at dispatch, where a write tool is refused unless writes are enabled.
191
+
192
+ ## Documentation server
193
+
194
+ `@lunora/mcp/docs` is a second, independent surface: it serves **published documentation**, not a deployment. No credentials, no writes, no `@lunora/client` — and no Node built-ins, so it runs unchanged on Workers, Netlify/Vercel functions, Deno, and Bun.
195
+
196
+ | Tool | Description |
197
+ | -------------------- | --------------------------------------------------------------------- |
198
+ | `lunora_search_docs` | Search the docs; returns matching pages and sections with their URLs. |
199
+ | `lunora_get_doc` | Return one page in full, as Markdown. |
200
+ | `lunora_list_docs` | List every page with its title and description. |
201
+
202
+ The tools read a `DocsIndex`, which has two implementations. A docs site wires up its own in-process index and mounts the server as an HTTP route:
203
+
204
+ ```ts
205
+ import { createDocsMcpFetchHandler } from "@lunora/mcp/docs";
206
+
207
+ const handle = createDocsMcpFetchHandler({ index: myDocsIndex });
208
+
209
+ // e.g. in a Worker: export default { fetch: handle }
210
+ ```
211
+
212
+ Anything else reads a published site over HTTP:
213
+
214
+ ```ts
215
+ import { createDocsMcpServer, createRemoteDocsIndex } from "@lunora/mcp/docs";
216
+
217
+ const server = createDocsMcpServer({ index: createRemoteDocsIndex({ baseUrl: "https://lunora.sh" }) });
218
+ ```
219
+
220
+ Point a client at the hosted endpoint with no install at all:
221
+
222
+ ```sh
223
+ claude mcp add --transport http lunora-docs https://lunora.sh/mcp
224
+ ```
225
+
226
+ ### Hosting it safely
227
+
228
+ `createDocsMcpFetchHandler` screens each request before the transport sees it, because this surface is meant to be public and unauthenticated:
229
+
230
+ - **Bodies are capped** (128 KiB by default; override with `maxRequestBytes`).
231
+ - **JSON-RPC batches are refused.** The stateless transport buffers a whole batch's replies into one response, so a single small request carrying thousands of `tools/call` messages would amplify into hundreds of megabytes out, with no session to rate-limit against. A docs client gains nothing from batching.
232
+ - `lunora_search_docs` bounds its `query`, and `lunora_list_docs` caps how many pages it serialises.
233
+
234
+ ## Local development server
235
+
236
+ `createLocalMcpServer` / `connectLocalStdio` compose the docs tools, the deployment tools, and any extra tools a host supplies into one stdio server — this is what `lunora mcp serve` runs.
237
+
238
+ ```ts
239
+ import { connectLocalStdio } from "@lunora/mcp";
240
+
241
+ await connectLocalStdio({
242
+ // Consulted per tool call, so a dev server started later is picked up
243
+ // without reconnecting.
244
+ deployment: () => readMyDevServer(),
245
+ docs: { baseUrl: "https://lunora.sh" },
246
+ extraTools: myLocalTools,
247
+ });
248
+ ```
249
+
250
+ The deployment tools are advertised even when the resolver currently returns nothing — MCP clients cache the tool list, so a surface that appeared only when the dev server happened to be up would stay invisible for the rest of the session. Calling one with nothing running returns an actionable error instead.
251
+
252
+ The observability tools are the exception: their gate is snapshotted when the tool list is built, so a session started before `lunora dev` never advertises them (and the cached list keeps them absent afterwards). Restart the MCP server once the dev server is up.
253
+
108
254
  > This README covers the basics. For the full API, options, and guides, see the **[documentation](https://lunora.sh/docs)**.
109
255
 
110
256
  ## Related
@@ -112,6 +258,7 @@ await server.connect(myTransport);
112
258
  - [`@lunora/client`](https://www.npmjs.com/package/@lunora/client) — the HTTP RPC client backing every tool.
113
259
  - [`@lunora/cli`](https://www.npmjs.com/package/@lunora/cli) — deploy the app the server introspects and invokes.
114
260
  - [`@lunora/server`](https://www.npmjs.com/package/@lunora/server) — defines the queries, mutations, and actions the tools call.
261
+ - [`@lunora/agent`](https://www.npmjs.com/package/@lunora/agent) — the durable agents the `agent_<name>` tools front over RPC.
115
262
 
116
263
  ## Supported Node.js Versions
117
264
 
package/dist/bin.mjs CHANGED
@@ -1,36 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { connectStdio } from './packem_shared/connectStdio-C_mvQBs2.mjs';
3
-
4
- class BinError extends Error {
5
- code;
6
- constructor(message, code) {
7
- super(message);
8
- this.name = "BinError";
9
- this.code = code;
10
- }
11
- }
12
- const runBin = async (environment, dependencies = {}) => {
13
- const connect = dependencies.connect ?? connectStdio;
14
- const writeError = dependencies.writeError ?? ((message) => {
15
- process.stderr.write(message);
16
- });
17
- const url = environment.LUNORA_URL;
18
- if (url === void 0 || url.length === 0) {
19
- writeError("lunora-mcp: LUNORA_URL environment variable is required\n");
20
- throw new BinError("LUNORA_URL environment variable is required", 1);
21
- }
22
- try {
23
- await connect({ token: environment.LUNORA_ADMIN_TOKEN, url });
24
- } catch (error) {
25
- const message = error instanceof Error ? error.message : String(error);
26
- writeError(`lunora-mcp: failed to start — ${message}
27
- `);
28
- throw new BinError(`failed to start — ${message}`, 1);
29
- }
30
- };
31
-
32
- try {
33
- await runBin(process.env);
34
- } catch (error) {
35
- process.exit(error instanceof BinError ? error.code : 1);
36
- }
2
+ import{parseAgentsEnv as N}from"./packem_shared/AGENT_RUN_INPUT_SCHEMA-hKbpa3Dg.mjs";import{connectStdio as E}from"./packem_shared/connectStdio-BBtfW4UB.mjs";const l=new Set(["1","on","true","yes"]),_=r=>r!==void 0&&l.has(r.trim().toLowerCase());class o extends Error{code;constructor(t,s){super(t),this.name="BinError",this.code=s}}const O=async(r,t={})=>{const s=t.connect??E,i=t.writeError??(e=>{process.stderr.write(e)}),n=r.LUNORA_URL;if(n===void 0||n.length===0)throw i(`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 i(`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),A=Number.isFinite(c)&&c>0?c:void 0;try{await s({agents:N(r.LUNORA_MCP_AGENTS),allowAgents:_(r.LUNORA_MCP_ALLOW_AGENTS),allowObservability:_(r.LUNORA_MCP_ALLOW_OBSERVABILITY),allowWrites:_(r.LUNORA_MCP_ALLOW_WRITES),token:a,url:n,...A===void 0?{}:{agentMaxWaitMs:A}})}catch(e){const L=e instanceof Error?e.message:String(e);throw i(`lunora-mcp: failed to start — ${L}
5
+ `),new o(`failed to start — ${L}`,1)}};try{await O(process.env)}catch(r){process.exit(r instanceof o?r.code:1)}
@@ -0,0 +1,258 @@
1
+ import { M as McpResourceProvider, a as McpFetchHandler, T as ToolDefinition, b as McpTool } from "../packem_shared/serve-stateless.d-CKsbI0fP.mjs";
2
+ export type {
3
+ /**
4
+ * `@lunora/mcp/docs` — the documentation tool surface: `lunora_search_docs`,
5
+ * `lunora_get_doc`, and `lunora_list_docs`.
6
+ *
7
+ * Where the package's main entry exposes a deployment to an agent (its
8
+ * functions and data, behind an admin token), this entry exposes the
9
+ * framework's documentation, so an agent writing Lunora code can look up the
10
+ * real API instead of inventing one. It reads published docs only — no
11
+ * credentials, no writes — so a server built from it is safe to host
12
+ * unauthenticated.
13
+ *
14
+ * Two backends implement the same `DocsIndex` contract: a docs site wires up
15
+ * its own in-process search index, while anything else (the CLI's
16
+ * `lunora mcp serve`, a script) uses `createRemoteDocsIndex` to read a
17
+ * published site over HTTP.
18
+ *
19
+ * This entry is free of Node built-ins and of `@lunora/client`, so it runs on
20
+ * Workers, Netlify/Vercel functions, Deno, and Bun unchanged —
21
+ * `scripts/check-dist-production.js` asserts that against the emitted chunks.
22
+ *
23
+ * Only the documentation surface lives here. The generic composition primitive
24
+ * (`createToolServer`) is exported from the package root, so a consumer using
25
+ * both entries gets one implementation rather than two copies.
26
+ */
27
+ c as McpResourceSummary,
28
+ /**
29
+ * `@lunora/mcp/docs` — the documentation tool surface: `lunora_search_docs`,
30
+ * `lunora_get_doc`, and `lunora_list_docs`.
31
+ *
32
+ * Where the package's main entry exposes a deployment to an agent (its
33
+ * functions and data, behind an admin token), this entry exposes the
34
+ * framework's documentation, so an agent writing Lunora code can look up the
35
+ * real API instead of inventing one. It reads published docs only — no
36
+ * credentials, no writes — so a server built from it is safe to host
37
+ * unauthenticated.
38
+ *
39
+ * Two backends implement the same `DocsIndex` contract: a docs site wires up
40
+ * its own in-process search index, while anything else (the CLI's
41
+ * `lunora mcp serve`, a script) uses `createRemoteDocsIndex` to read a
42
+ * published site over HTTP.
43
+ *
44
+ * This entry is free of Node built-ins and of `@lunora/client`, so it runs on
45
+ * Workers, Netlify/Vercel functions, Deno, and Bun unchanged —
46
+ * `scripts/check-dist-production.js` asserts that against the emitted chunks.
47
+ *
48
+ * Only the documentation surface lives here. The generic composition primitive
49
+ * (`createToolServer`) is exported from the package root, so a consumer using
50
+ * both entries gets one implementation rather than two copies.
51
+ */
52
+ d as McpServerInfo, e as ToolInputSchema, f as ToolResult } from "../packem_shared/serve-stateless.d-CKsbI0fP.mjs";
53
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
54
+ import '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';
55
+ /**
56
+ * The documentation source the docs tools read.
57
+ *
58
+ * The tools are written against this interface rather than against a concrete
59
+ * search engine so the same tool surface can be backed two ways: in-process by
60
+ * the docs site itself (fumadocs `source` + its Orama index), or over HTTP by a
61
+ * remote reader that only has the published site (see `./remote-index`). Both
62
+ * expose the identical tool names and result shapes to the model.
63
+ */
64
+ /** One hit from {@link DocsIndex.search}. */
65
+ interface DocsSearchHit {
66
+ /** The matched text, when the backend returns one (headings/paragraph hits). */
67
+ excerpt?: string;
68
+ /** Breadcrumb trail to the matched section, e.g. `"Guides › Sharding"`. */
69
+ section?: string;
70
+ /** Page title. */
71
+ title: string;
72
+ /** Site-relative page URL, e.g. `"/docs/sharding"` — feed this to `lunora_get_doc`. */
73
+ url: string;
74
+ }
75
+ /** A page listed by {@link DocsIndex.listPages}. */
76
+ interface DocsPageSummary {
77
+ description?: string;
78
+ title: string;
79
+ /** Site-relative page URL, e.g. `"/docs/sharding"`. */
80
+ url: string;
81
+ }
82
+ /** A page's full text, as returned by {@link DocsIndex.getPage}. */
83
+ interface DocsPage extends DocsPageSummary {
84
+ /** The page body as Markdown. */
85
+ content: string;
86
+ }
87
+ interface DocsIndex {
88
+ /**
89
+ * The page at a site-relative URL, or `undefined` when there is no such
90
+ * page. Implementations should accept the URL exactly as it appears in a
91
+ * search hit.
92
+ */
93
+ getPage: (url: string) => Promise<DocsPage | undefined>;
94
+ /** Every indexed page, for a model that wants to browse rather than search. */
95
+ listPages: () => Promise<ReadonlyArray<DocsPageSummary>>;
96
+ /**
97
+ * Full-text search, returning whatever the backend found.
98
+ *
99
+ * Deliberately unbounded: the tool layer decides how many hits reach the
100
+ * model's context, so there is one truncation site rather than one per
101
+ * backend plus one in the tool.
102
+ */
103
+ search: (query: string) => Promise<ReadonlyArray<DocsSearchHit>>;
104
+ }
105
+ /** The subset of a fumadocs `SortedResult` these tools consume. */
106
+ interface FumadocsSearchResult {
107
+ breadcrumbs?: string[];
108
+ content?: string;
109
+ type?: string;
110
+ url?: string;
111
+ }
112
+ /** Map every result, skipping any entry {@link toDocsSearchHit} rejects. */
113
+ declare const toDocsSearchHits: (results: ReadonlyArray<unknown>) => DocsSearchHit[];
114
+ /** The public docs site the remote index reads when no base URL is configured. */
115
+ declare const DEFAULT_DOCS_BASE_URL = "https://lunora.sh";
116
+ interface RemoteDocsIndexOptions {
117
+ /** Origin of the docs site, e.g. `"https://lunora.sh"`. Defaults to {@link DEFAULT_DOCS_BASE_URL}. */
118
+ baseUrl?: string;
119
+ /** `fetch` implementation; defaults to the ambient global. */
120
+ fetch?: typeof fetch;
121
+ /** Per-request deadline in ms. Defaults to {@link DEFAULT_REQUEST_TIMEOUT_MS}. */
122
+ timeoutMs?: number;
123
+ }
124
+ declare const createRemoteDocsIndex: (options?: RemoteDocsIndexOptions) => DocsIndex;
125
+ /** URI scheme identifying a Lunora documentation page. */
126
+ declare const DOCS_URI_SCHEME = "lunora-docs:";
127
+ /** `"/docs/sharding"` → `"lunora-docs:/docs/sharding"`. */
128
+ declare const toDocsUri: (url: string) => string;
129
+ /** The inverse, or `undefined` for a uri this provider does not own. */
130
+ declare const fromDocsUri: (uri: string) => string | undefined;
131
+ /** Expose `index`'s pages as listable, readable resources. */
132
+ declare const docsResources: (index: DocsIndex) => McpResourceProvider;
133
+ /** Server identity advertised in the MCP `initialize` handshake. */
134
+ declare const DOCS_SERVER_NAME = "lunora-docs";
135
+ interface DocsMcpServerOptions {
136
+ /** The documentation source the tools read. */
137
+ index: DocsIndex;
138
+ /**
139
+ * Largest accepted request body, in bytes — enforced while the body streams
140
+ * in, not after it is buffered. Defaults to `DEFAULT_MAX_REQUEST_BYTES`
141
+ * (re-exported at the foot of this module), which a value that is not a
142
+ * non-negative safe integer also falls back to.
143
+ */
144
+ maxRequestBytes?: number;
145
+ /**
146
+ * Version reported in the handshake. Defaults to `"0.0.0"` — a docs site
147
+ * bundles this code rather than installing it, so it, not the package, is
148
+ * the thing whose version a client would want to see.
149
+ */
150
+ version?: string;
151
+ }
152
+ /**
153
+ * Build a transport-agnostic MCP server exposing the documentation tools.
154
+ * Connect it yourself, or use {@link createDocsMcpFetchHandler} for the remote
155
+ * HTTP case.
156
+ */
157
+ declare const createDocsMcpServer: (options: DocsMcpServerOptions) => Server;
158
+ /**
159
+ * Build a stateless Streamable-HTTP fetch handler serving the documentation
160
+ * tools — the `Request` → `Response` function a docs site mounts at `/mcp`.
161
+ *
162
+ * A fresh server per request keeps the handler safe on platforms that fan
163
+ * requests across isolates, where nothing may be assumed to persist between
164
+ * them.
165
+ */
166
+ declare const createDocsMcpFetchHandler: (options: DocsMcpServerOptions) => McpFetchHandler;
167
+ /** Hits returned when the caller doesn't ask for a specific number. */
168
+ declare const DEFAULT_SEARCH_LIMIT = 10;
169
+ /**
170
+ * Ceiling on hits per call. Search results are pasted into the model's context
171
+ * verbatim, so an unbounded `limit` is a context-exhaustion foot-gun rather
172
+ * than a useful option.
173
+ */
174
+ declare const MAX_SEARCH_LIMIT = 50;
175
+ /** The advertised surface, in the order a caller should reach for it. */
176
+ declare const DOCS_TOOL_DEFINITIONS: ReadonlyArray<ToolDefinition>;
177
+ /**
178
+ * Normalize whatever a model passes as a page URL into the site-relative form
179
+ * an index stores.
180
+ *
181
+ * Models copy URLs from anywhere — a search hit (`/docs/sharding`), the browser
182
+ * (`https://lunora.sh/docs/sharding`), or their own memory of the slug
183
+ * (`sharding`, `docs/sharding`). All four are the same page, and failing three
184
+ * of them would push the model into a guess-and-retry loop, so resolve them to
185
+ * one form. A trailing slash is dropped for the same reason.
186
+ *
187
+ * A `..` segment is REJECTED rather than resolved. The remote backend appends
188
+ * this path to `/llms.mdx`, so `../../api/search` would walk back out of the
189
+ * documentation tree and pull an unrelated path on the docs origin into the
190
+ * model's context — harmless against a public site, less so against the
191
+ * internal host a self-hosted `--docs-url` may point at. Percent-encoded
192
+ * forms (`%2e%2e`, doubled `%252e`) are rejected the same way: WHATWG URL
193
+ * parsing at fetch time decodes and collapses them into the very traversal
194
+ * the literal check would have caught.
195
+ */
196
+ declare const normalizeDocUrl: (raw: string) => string;
197
+ /**
198
+ * The documentation surface, bound to `index`.
199
+ *
200
+ * Each tool carries its own handler rather than routing through a shared
201
+ * `switch`: `createToolServer` already dispatches by name, so a second switch
202
+ * here would be a duplicate table with an unreachable `default`. It also owns
203
+ * the throw-to-`isError` conversion, so these handlers signal argument problems
204
+ * by throwing and return `isError` only for the expected misses a model should
205
+ * read and act on.
206
+ */
207
+ declare const docsTools: (index: DocsIndex) => ReadonlyArray<McpTool>;
208
+ export { DEFAULT_DOCS_BASE_URL, DEFAULT_SEARCH_LIMIT, DOCS_SERVER_NAME, DOCS_TOOL_DEFINITIONS, DOCS_URI_SCHEME, type DocsIndex, type DocsMcpServerOptions, type DocsPage, type DocsPageSummary, type DocsSearchHit, type FumadocsSearchResult, MAX_SEARCH_LIMIT, type McpFetchHandler,
209
+ /**
210
+ * `@lunora/mcp/docs` — the documentation tool surface: `lunora_search_docs`,
211
+ * `lunora_get_doc`, and `lunora_list_docs`.
212
+ *
213
+ * Where the package's main entry exposes a deployment to an agent (its
214
+ * functions and data, behind an admin token), this entry exposes the
215
+ * framework's documentation, so an agent writing Lunora code can look up the
216
+ * real API instead of inventing one. It reads published docs only — no
217
+ * credentials, no writes — so a server built from it is safe to host
218
+ * unauthenticated.
219
+ *
220
+ * Two backends implement the same `DocsIndex` contract: a docs site wires up
221
+ * its own in-process search index, while anything else (the CLI's
222
+ * `lunora mcp serve`, a script) uses `createRemoteDocsIndex` to read a
223
+ * published site over HTTP.
224
+ *
225
+ * This entry is free of Node built-ins and of `@lunora/client`, so it runs on
226
+ * Workers, Netlify/Vercel functions, Deno, and Bun unchanged —
227
+ * `scripts/check-dist-production.js` asserts that against the emitted chunks.
228
+ *
229
+ * Only the documentation surface lives here. The generic composition primitive
230
+ * (`createToolServer`) is exported from the package root, so a consumer using
231
+ * both entries gets one implementation rather than two copies.
232
+ */
233
+ type McpResourceProvider,
234
+ /**
235
+ * `@lunora/mcp/docs` — the documentation tool surface: `lunora_search_docs`,
236
+ * `lunora_get_doc`, and `lunora_list_docs`.
237
+ *
238
+ * Where the package's main entry exposes a deployment to an agent (its
239
+ * functions and data, behind an admin token), this entry exposes the
240
+ * framework's documentation, so an agent writing Lunora code can look up the
241
+ * real API instead of inventing one. It reads published docs only — no
242
+ * credentials, no writes — so a server built from it is safe to host
243
+ * unauthenticated.
244
+ *
245
+ * Two backends implement the same `DocsIndex` contract: a docs site wires up
246
+ * its own in-process search index, while anything else (the CLI's
247
+ * `lunora mcp serve`, a script) uses `createRemoteDocsIndex` to read a
248
+ * published site over HTTP.
249
+ *
250
+ * This entry is free of Node built-ins and of `@lunora/client`, so it runs on
251
+ * Workers, Netlify/Vercel functions, Deno, and Bun unchanged —
252
+ * `scripts/check-dist-production.js` asserts that against the emitted chunks.
253
+ *
254
+ * Only the documentation surface lives here. The generic composition primitive
255
+ * (`createToolServer`) is exported from the package root, so a consumer using
256
+ * both entries gets one implementation rather than two copies.
257
+ */
258
+ type McpTool, type RemoteDocsIndexOptions, type ToolDefinition, createDocsMcpFetchHandler, createDocsMcpServer, createRemoteDocsIndex, docsResources, docsTools, fromDocsUri, normalizeDocUrl, toDocsSearchHits, toDocsUri };