@lunora/mcp 1.0.0-alpha.13 → 1.0.0-alpha.131
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/LICENSE.md +126 -0
- package/README.md +157 -10
- package/dist/bin.mjs +4 -35
- package/dist/docs/index.d.mts +258 -0
- package/dist/docs/index.d.ts +258 -0
- package/dist/docs/index.mjs +1 -0
- package/dist/index.d.mts +511 -14
- package/dist/index.d.ts +511 -14
- package/dist/index.mjs +1 -2
- package/dist/packem_shared/AGENT_RUN_INPUT_SCHEMA-hKbpa3Dg.mjs +1 -0
- package/dist/packem_shared/DEFAULT_DOCS_BASE_URL-CZ3fVsSc.mjs +4 -0
- package/dist/packem_shared/DEFAULT_MAX_REQUEST_BYTES-CbbpkHRK.mjs +1 -0
- package/dist/packem_shared/DEFAULT_SEARCH_LIMIT-BqSYN5vr.mjs +3 -0
- package/dist/packem_shared/DOCS_SERVER_NAME-BMeAWi5h.mjs +1 -0
- package/dist/packem_shared/DOCS_URI_SCHEME-Buo752CV.mjs +3 -0
- package/dist/packem_shared/LOCAL_SERVER_NAME-DPay9JzK.mjs +1 -0
- package/dist/packem_shared/OBSERVABILITY_TOOL_DEFINITIONS-Byqgb9wh.mjs +1 -0
- package/dist/packem_shared/READ_ONLY_TOOL_DEFINITIONS-_aG40yWx.mjs +1 -0
- package/dist/packem_shared/connectStdio-BBtfW4UB.mjs +1 -0
- package/dist/packem_shared/createAuthedMcpFetchHandler-DwOzAueC.mjs +1 -0
- package/dist/packem_shared/createMcpFetchHandler-DSK2X9Hd.mjs +1 -0
- package/dist/packem_shared/createPaidMcpServer-BIBKMtxs.mjs +1 -0
- package/dist/packem_shared/createToolServer-BtGuPyMU.mjs +1 -0
- package/dist/packem_shared/observability-tools-B-g9Y9IT.mjs +1 -0
- package/dist/packem_shared/serve-stateless.d-CKsbI0fP.d.mts +154 -0
- package/dist/packem_shared/serve-stateless.d-CKsbI0fP.d.ts +154 -0
- package/dist/packem_shared/toDocsSearchHits-CBLmtWXt.mjs +1 -0
- package/package.json +17 -3
- package/dist/packem_shared/TOOL_DEFINITIONS-Dpiu38ji.mjs +0 -112
- package/dist/packem_shared/connectStdio-C_mvQBs2.mjs +0 -64
package/dist/index.d.ts
CHANGED
|
@@ -1,30 +1,527 @@
|
|
|
1
1
|
import { LunoraClient } from '@lunora/client';
|
|
2
|
+
import { T as ToolDefinition, f as ToolResult, e as ToolInputSchema, a as McpFetchHandler, b as McpTool } from "./packem_shared/serve-stateless.d-CKsbI0fP.js";
|
|
3
|
+
export { D as DEFAULT_MAX_REQUEST_BYTES, type d as McpServerInfo, type S as ServeStatelessOptions, g as createToolServer, s as serveStateless } from "./packem_shared/serve-stateless.d-CKsbI0fP.js";
|
|
2
4
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
5
|
+
import { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
6
|
+
import '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';
|
|
7
|
+
/**
|
|
8
|
+
* The observability tools. Descriptions say WHEN to call the tool, not just
|
|
9
|
+
* what it returns — an agent picking between five similar reads needs the
|
|
10
|
+
* trigger, and it pays for these strings on every turn.
|
|
11
|
+
*/
|
|
12
|
+
declare const OBSERVABILITY_TOOL_DEFINITIONS: ReadonlyArray<ToolDefinition>;
|
|
13
|
+
/** The read-only tool surface: introspection + query. Always exposed. */
|
|
14
|
+
declare const READ_ONLY_TOOL_DEFINITIONS: ReadonlyArray<ToolDefinition>;
|
|
15
|
+
/** The write tool surface (mutations + actions). Exposed ONLY when writes are enabled. */
|
|
16
|
+
declare const WRITE_TOOL_DEFINITIONS: ReadonlyArray<ToolDefinition>;
|
|
17
|
+
/**
|
|
18
|
+
* The tools this server advertises, in three tiers:
|
|
19
|
+
*
|
|
20
|
+
* - the read-only surface, always exposed;
|
|
21
|
+
* - the observability surface, exposed only when `allowObservability` is set —
|
|
22
|
+
* read-only, but every row it returns (log lines, request metadata, grouped
|
|
23
|
+
* error messages) is user data that lands in the model's context and therefore
|
|
24
|
+
* at its provider, so it is opt-in rather than implied by holding a token;
|
|
25
|
+
* - the write surface, exposed only when `allowWrites` is set.
|
|
26
|
+
*
|
|
27
|
+
* Both gates OMIT rather than refuse: an AI agent can't invoke what it can't
|
|
28
|
+
* see. Dispatch re-checks both in {@link callTool}, so the guarantee does not
|
|
29
|
+
* depend on a client honouring the advertised list.
|
|
30
|
+
*/
|
|
31
|
+
declare const toolDefinitions: (allowWrites: boolean, allowObservability?: boolean) => ReadonlyArray<ToolDefinition>;
|
|
32
|
+
/**
|
|
33
|
+
* Dispatch a tool call against `client`. Unknown tools and thrown errors are
|
|
34
|
+
* returned as `isError` results (rather than rejections) so the calling model
|
|
35
|
+
* sees the failure as tool output, per the MCP convention.
|
|
36
|
+
*
|
|
37
|
+
* `allowWrites` gates the mutation/action tools and `allowObservability` gates
|
|
38
|
+
* the observability tools: when either is false a call to the gated tool is
|
|
39
|
+
* refused even if the client somehow names it, so both guarantees hold at
|
|
40
|
+
* dispatch, not just in the advertised tool list.
|
|
41
|
+
*/
|
|
42
|
+
declare const callTool: (client: LunoraClient, name: string, input: Record<string, unknown>, allowWrites?: boolean, allowObservability?: boolean) => Promise<ToolResult>;
|
|
43
|
+
/**
|
|
44
|
+
* Agent exposure for the MCP server: a durable `@lunora/agent` run fronted as an
|
|
45
|
+
* MCP tool an external agent can call. The capability boundary is the MCP-server
|
|
46
|
+
* process + its token, so WHICH agents are exposed is config on the server (like
|
|
47
|
+
* `allowWrites`), not on `defineAgent` — keeping `@lunora/agent` codegen
|
|
48
|
+
* byte-identical.
|
|
49
|
+
*/
|
|
50
|
+
interface McpAgentExposure {
|
|
51
|
+
/** What the agent does — shown to the calling model, which decides from it. */
|
|
52
|
+
description: string;
|
|
53
|
+
/** The agent's export name (its `ctx.agents.<name>` / `AGENT_<NAME>` binding). */
|
|
54
|
+
name: string;
|
|
55
|
+
/** Override the model-facing tool name (default `agent_<name>`). */
|
|
56
|
+
toolName?: string;
|
|
57
|
+
}
|
|
58
|
+
/** The generic status/poll tool advertised alongside the per-agent tools. */
|
|
59
|
+
declare const AGENT_STATUS_TOOL_NAME = "lunora_agent_status";
|
|
60
|
+
/**
|
|
61
|
+
* The uniform input schema every agent tool advertises. Agents share ONE run
|
|
62
|
+
* input (`@lunora/agent` has no per-agent validator), so there is nothing to
|
|
63
|
+
* derive per agent — a single static schema is reused for every agent tool.
|
|
64
|
+
*/
|
|
65
|
+
declare const AGENT_RUN_INPUT_SCHEMA: ToolInputSchema;
|
|
66
|
+
/**
|
|
67
|
+
* Parse `LUNORA_MCP_AGENTS` — a `;`-separated list of `name:description` pairs,
|
|
68
|
+
* e.g. `"support:Handles support questions;billing:Billing help"`. The
|
|
69
|
+
* description may itself contain colons (only the FIRST colon splits). Blank
|
|
70
|
+
* entries and entries with an empty name/description are skipped.
|
|
71
|
+
*/
|
|
72
|
+
declare const parseAgentsEnv: (raw: string | undefined) => McpAgentExposure[];
|
|
73
|
+
/**
|
|
74
|
+
* The tools this module advertises. Fail-closed: only the boolean `true` opts
|
|
75
|
+
* in (an env-plumbed caller could pass a truthy string), and the tools appear
|
|
76
|
+
* ONLY when at least one agent is exposed — so an agent-free or non-opted-in
|
|
77
|
+
* server never lists them.
|
|
78
|
+
*/
|
|
79
|
+
declare const agentToolDefinitions: (exposures: ReadonlyArray<McpAgentExposure>, allowAgents: boolean) => ReadonlyArray<ToolDefinition>;
|
|
80
|
+
/** Options threaded into a single agent tool dispatch. */
|
|
81
|
+
interface CallAgentToolOptions {
|
|
82
|
+
/** Opt-in gate — must be exactly `true` or the call is refused fail-closed. */
|
|
83
|
+
allowAgents: boolean;
|
|
84
|
+
/** The exposures advertised by this server. */
|
|
85
|
+
exposures: ReadonlyArray<McpAgentExposure>;
|
|
86
|
+
/** Wall-clock budget a single call awaits before returning a pending result. */
|
|
87
|
+
maxWaitMs?: number;
|
|
88
|
+
/** Delay between thread-status polls. */
|
|
89
|
+
pollIntervalMs?: number;
|
|
90
|
+
/** Test seam replacing the between-poll wait; production uses a real timer. */
|
|
91
|
+
wait?: (ms: number) => Promise<void>;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Dispatch an agent tool call: start a durable run via `agents:agentRun`, then
|
|
95
|
+
* await-with-timeout — poll `agents:agentThread` until terminal (returning the
|
|
96
|
+
* final answer from `agents:agentMessages`) or, on budget exhaustion, return a
|
|
97
|
+
* NON-error pending payload the caller resumes with `lunora_agent_status`.
|
|
98
|
+
*
|
|
99
|
+
* Fail-closed: refused at dispatch unless `allowAgents === true`, mirroring the
|
|
100
|
+
* `allowWrites` guard — starting a run is a side effect and must not ride the
|
|
101
|
+
* read-only default.
|
|
102
|
+
*/
|
|
103
|
+
declare const callAgentTool: (client: LunoraClient, name: string, input: Record<string, unknown>, options: CallAgentToolOptions) => Promise<ToolResult>;
|
|
3
104
|
interface LunoraMcpServerOptions {
|
|
105
|
+
/** Wall-clock budget a single agent tool call awaits before returning a pending result. */
|
|
106
|
+
agentMaxWaitMs?: number;
|
|
107
|
+
/** Delay between agent thread-status polls. */
|
|
108
|
+
agentPollIntervalMs?: number;
|
|
109
|
+
/** The agents this server fronts as MCP tools (see `allowAgents`). */
|
|
110
|
+
agents?: ReadonlyArray<McpAgentExposure>;
|
|
111
|
+
/**
|
|
112
|
+
* Expose the per-agent tools (`agent_<name>` + the generic
|
|
113
|
+
* `lunora_agent_status`). Defaults to `false`, mirroring `allowWrites`:
|
|
114
|
+
* starting a durable agent run is a side effect, so the agent tools are
|
|
115
|
+
* omitted from the advertised list AND refused at dispatch unless explicitly
|
|
116
|
+
* opted in. Only takes effect together with a non-empty `agents` list.
|
|
117
|
+
*/
|
|
118
|
+
allowAgents?: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Expose the observability tools (`lunora_get_logs`, `lunora_get_issues`,
|
|
121
|
+
* `lunora_get_advisories`, `lunora_get_query_insights`,
|
|
122
|
+
* `lunora_get_migration_status`). Defaults to `false`, mirroring
|
|
123
|
+
* `allowWrites`: they are read-only, but every row they return — log lines,
|
|
124
|
+
* request metadata, grouped error messages — is production user data that
|
|
125
|
+
* lands in the model's context and therefore at its provider. Holding the
|
|
126
|
+
* admin bearer is not consent to ship that, so it is a separate opt-in;
|
|
127
|
+
* without it the tools are omitted from the advertised list AND refused at
|
|
128
|
+
* dispatch. Only takes effect when a `token` resolved.
|
|
129
|
+
*/
|
|
130
|
+
allowObservability?: boolean;
|
|
131
|
+
/**
|
|
132
|
+
* Expose the write tools (`lunora_run_mutation` / `lunora_run_action`).
|
|
133
|
+
* Defaults to `false`: the server is READ-ONLY unless explicitly opted in,
|
|
134
|
+
* so a prompt-injected or misaligned agent can't mutate the deployment with
|
|
135
|
+
* the configured token. When false the write tools are omitted from the
|
|
136
|
+
* advertised tool list AND refused at dispatch.
|
|
137
|
+
*/
|
|
138
|
+
allowWrites?: boolean;
|
|
139
|
+
/**
|
|
140
|
+
* Pre-built client (test injection). When omitted a `LunoraClient` is
|
|
141
|
+
* created from `url`/`token`/`fetch`.
|
|
142
|
+
*/
|
|
4
143
|
client?: LunoraClient;
|
|
144
|
+
/** `fetch` implementation; defaults to the ambient global. */
|
|
5
145
|
fetch?: typeof fetch;
|
|
146
|
+
/**
|
|
147
|
+
* Bearer token sent on every RPC. **Required** alongside `url`, and it must
|
|
148
|
+
* be the deployment's **admin bearer**: the introspection/allowlist path
|
|
149
|
+
* every tool depends on (`lunora_list_functions`, `lunora_list_tables`, and
|
|
150
|
+
* the `assertRunnable` precheck that runs before every `run` tool) hits
|
|
151
|
+
* admin-gated `/_lunora/admin/*` routes, so no scoped/app token works
|
|
152
|
+
* today — it would 403 (`ADMIN_FORBIDDEN`) on the first tool call. The
|
|
153
|
+
* read-only guarantee is therefore NOT enforced by the token's scope; it is
|
|
154
|
+
* enforced in-process via `allowWrites: false` (the default), which omits
|
|
155
|
+
* the write tools from the advertised list and refuses them at dispatch.
|
|
156
|
+
*
|
|
157
|
+
* Because EVERY tool needs it, omitting it is a misconfiguration rather than
|
|
158
|
+
* a reduced-capability mode, and `createLunoraMcpServer` says so at
|
|
159
|
+
* construction instead of advertising a surface that 403s on first use. The
|
|
160
|
+
* one exception is the `client` injection seam (tests / a pre-authenticated
|
|
161
|
+
* client), where this server cannot know what the client can reach and so
|
|
162
|
+
* reads "unknown" fail-closed: the privileged observability tools stay
|
|
163
|
+
* unadvertised and are refused at dispatch.
|
|
164
|
+
*/
|
|
6
165
|
token?: string;
|
|
166
|
+
/** Base URL of the deployed Lunora Worker. Required unless `client` is given. */
|
|
7
167
|
url?: string;
|
|
8
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Build an MCP `Server` whose tools talk to a Lunora deployment. The server is
|
|
171
|
+
* transport-agnostic — call `.connect(transport)` yourself, or use
|
|
172
|
+
* `connectStdio` for the common stdio case.
|
|
173
|
+
*
|
|
174
|
+
* Tool calls are dispatched through `callTool`, which the deployment reaches
|
|
175
|
+
* over HTTP RPC. No WebSocket is opened (the tools never subscribe), so this is
|
|
176
|
+
* safe to run as a short-lived stdio process.
|
|
177
|
+
*/
|
|
9
178
|
declare const createLunoraMcpServer: (options: LunoraMcpServerOptions) => Server;
|
|
179
|
+
/**
|
|
180
|
+
* Build the server and connect it over stdio — the transport MCP clients use
|
|
181
|
+
* when they spawn the `lunora-mcp` binary. Resolves once the transport is
|
|
182
|
+
* connected; the process then stays alive serving requests.
|
|
183
|
+
*/
|
|
10
184
|
declare const connectStdio: (options: LunoraMcpServerOptions) => Promise<Server>;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
185
|
+
/**
|
|
186
|
+
* The verified access-token payload better-auth hands a protected handler.
|
|
187
|
+
*
|
|
188
|
+
* A JWT payload is an open bag of claims, so this is deliberately an index
|
|
189
|
+
* signature with the two entries this module reads named. It is structurally
|
|
190
|
+
* satisfied by `jose`'s `JWTPayload`, which is what better-auth passes.
|
|
191
|
+
*/
|
|
192
|
+
interface McpAccessTokenClaims {
|
|
193
|
+
readonly [claim: string]: unknown;
|
|
194
|
+
/** Space-delimited granted scopes (RFC 6749 §3.3). */
|
|
195
|
+
readonly scope?: unknown;
|
|
196
|
+
/** Subject — the user the token was issued for. */
|
|
197
|
+
readonly sub?: string;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* The MCP auth gate: wraps a claims-aware handler into a plain fetch handler.
|
|
201
|
+
*
|
|
202
|
+
* Declared structurally here rather than imported from `@better-auth/mcp`,
|
|
203
|
+
* following the same rule `./paid` follows for `@lunora/x402`: a type import
|
|
204
|
+
* from a package this one does not depend on puts that package's `.d.ts` into
|
|
205
|
+
* the build graph, and a consumer that never installs it never builds it
|
|
206
|
+
* either — so the dts bundler looks for a `dist/` that does not exist and fails
|
|
207
|
+
* the build. Structural typing costs nothing here because this module never
|
|
208
|
+
* inspects the gate; it only applies it.
|
|
209
|
+
*
|
|
210
|
+
* Both better-auth entry points partially apply to this shape:
|
|
211
|
+
* `(handler) => requireMcpAuth(auth, handler, opts)` and
|
|
212
|
+
* `(handler) => createMcpProtectedRequestHandler(options, handler)`.
|
|
213
|
+
*/
|
|
214
|
+
type McpAuthProtect = (handler: (request: Request, claims: McpAccessTokenClaims) => Promise<Response>) => McpFetchHandler;
|
|
215
|
+
/** Server options, or a function deriving them from the request's verified claims. */
|
|
216
|
+
type AuthedMcpServerOptions = ((claims: McpAccessTokenClaims) => LunoraMcpServerOptions | Promise<LunoraMcpServerOptions>) | LunoraMcpServerOptions;
|
|
217
|
+
interface AuthedMcpFetchHandlerOptions {
|
|
218
|
+
/**
|
|
219
|
+
* Largest accepted request body, in bytes — enforced while the body streams
|
|
220
|
+
* in, not after it is buffered. Defaults to `DEFAULT_MAX_REQUEST_BYTES`
|
|
221
|
+
* (128 KiB), which a value that is not a non-negative safe integer also
|
|
222
|
+
* falls back to.
|
|
223
|
+
*/
|
|
224
|
+
maxRequestBytes?: number;
|
|
225
|
+
/**
|
|
226
|
+
* The OAuth gate to mount the MCP server behind. Pass
|
|
227
|
+
* `(handler) => requireMcpAuth(auth, handler, opts)` from
|
|
228
|
+
* `@lunora/auth/plugins`.
|
|
229
|
+
*/
|
|
230
|
+
protect: McpAuthProtect;
|
|
231
|
+
/**
|
|
232
|
+
* The Lunora MCP server to serve once a request is authorized — either a
|
|
233
|
+
* fixed options object, or a function of the verified token claims so tool
|
|
234
|
+
* exposure can follow the scopes the token actually carries.
|
|
235
|
+
*/
|
|
236
|
+
server: AuthedMcpServerOptions;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Parse an access token's `scope` claim into a set.
|
|
240
|
+
*
|
|
241
|
+
* RFC 6749 §3.3 makes `scope` a space-delimited string, and better-auth issues
|
|
242
|
+
* it that way; anything else (absent, or a non-string an extension wrote)
|
|
243
|
+
* yields an empty set rather than throwing, so a scope check on a malformed
|
|
244
|
+
* token denies instead of crashing the tool call.
|
|
245
|
+
*/
|
|
246
|
+
declare const mcpTokenScopes: (claims: McpAccessTokenClaims) => ReadonlySet<string>;
|
|
247
|
+
/**
|
|
248
|
+
* Build an OAuth-protected stateless Streamable-HTTP fetch handler for a Lunora
|
|
249
|
+
* MCP server.
|
|
250
|
+
*
|
|
251
|
+
* Unauthenticated requests never reach the MCP server at all: `protect` answers
|
|
252
|
+
* them with the RFC 9728 `WWW-Authenticate` challenge that starts the client's
|
|
253
|
+
* authorization flow. An authorized request builds a fresh proxy server from
|
|
254
|
+
* `server` (resolved against the verified claims) and serves it through
|
|
255
|
+
* {@link serveStateless}, exactly as the unprotected `createMcpFetchHandler`
|
|
256
|
+
* does — the transport behaviour is identical, only the gate is new.
|
|
257
|
+
*
|
|
258
|
+
* A fixed `server` object names one deployment, so its `LunoraClient` is
|
|
259
|
+
* resolved once and shared: the public-function registry memo in `./tools` is
|
|
260
|
+
* keyed by client identity and never hits when each request builds its own. The
|
|
261
|
+
* `(claims) => …` form is per-request by construction — the claims decide which
|
|
262
|
+
* deployment and token to use — so it keeps a client per request.
|
|
263
|
+
*/
|
|
264
|
+
declare const createAuthedMcpFetchHandler: (options: AuthedMcpFetchHandlerOptions) => McpFetchHandler;
|
|
265
|
+
/** {@link createMcpFetchHandler} options: the server's, plus this transport's body limit. */
|
|
266
|
+
interface McpFetchHandlerOptions extends LunoraMcpServerOptions {
|
|
267
|
+
/**
|
|
268
|
+
* Largest accepted request body, in bytes — enforced while the body streams
|
|
269
|
+
* in, not after it is buffered. Defaults to `DEFAULT_MAX_REQUEST_BYTES`
|
|
270
|
+
* (128 KiB), which a value that is not a non-negative safe integer also
|
|
271
|
+
* falls back to.
|
|
272
|
+
*/
|
|
273
|
+
maxRequestBytes?: number;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Build a stateless Streamable-HTTP fetch handler for a Lunora MCP server. Each
|
|
277
|
+
* invocation constructs a fresh proxy server and serves the request through
|
|
278
|
+
* {@link serveStateless}.
|
|
279
|
+
*
|
|
280
|
+
* The `LunoraClient` is resolved ONCE, here, and shared by every request: it is
|
|
281
|
+
* the same deployment on each one, and the public-function registry memo in
|
|
282
|
+
* `./tools` is keyed by client identity, so a per-request client would re-fetch
|
|
283
|
+
* that registry on every tool call. A misconfiguration (`url` without `token`)
|
|
284
|
+
* therefore throws when the handler is built rather than on first request,
|
|
285
|
+
* which is where `createLunoraMcpServer` already documents reporting it.
|
|
286
|
+
*/
|
|
287
|
+
declare const createMcpFetchHandler: (options: McpFetchHandlerOptions) => McpFetchHandler;
|
|
288
|
+
/** A Lunora deployment the tools dispatch against. */
|
|
289
|
+
interface LocalDeployment {
|
|
290
|
+
token?: string;
|
|
291
|
+
url: string;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Where the deployment comes from: a fixed value, or a function consulted on
|
|
295
|
+
* every tool call.
|
|
296
|
+
*
|
|
297
|
+
* The resolver form exists because an editor spawns this server when the
|
|
298
|
+
* project opens — routinely *before* `lunora dev` is running, and it keeps the
|
|
299
|
+
* process alive across every restart afterwards. A URL captured once at startup
|
|
300
|
+
* would therefore be absent for the entire first session and stale after the
|
|
301
|
+
* first restart.
|
|
302
|
+
*/
|
|
303
|
+
type LocalDeploymentSource = (() => LocalDeployment | undefined) | LocalDeployment;
|
|
304
|
+
interface LocalMcpServerOptions {
|
|
305
|
+
/**
|
|
306
|
+
* Expose the deployment write tools (`lunora_run_mutation` /
|
|
307
|
+
* `lunora_run_action`). Defaults to `false` — the same fail-closed default
|
|
308
|
+
* as the remote server. Locally the blast radius is dev data rather than
|
|
309
|
+
* production, but a mutation is still a side effect an agent should be
|
|
310
|
+
* granted deliberately.
|
|
311
|
+
*/
|
|
312
|
+
allowWrites?: boolean;
|
|
313
|
+
/**
|
|
314
|
+
* The Lunora deployment (usually the running dev server) to expose. Omit to
|
|
315
|
+
* leave the deployment tools out entirely.
|
|
316
|
+
*/
|
|
317
|
+
deployment?: LocalDeploymentSource;
|
|
318
|
+
/** Docs site origin backing the documentation tools; `false` omits them. */
|
|
319
|
+
docs?: false | {
|
|
320
|
+
baseUrl?: string;
|
|
321
|
+
};
|
|
322
|
+
/** Extra tools to compose in, e.g. the CLI's local dev-server tools. */
|
|
323
|
+
extraTools?: ReadonlyArray<McpTool>;
|
|
324
|
+
/** `fetch` implementation; defaults to the ambient global. */
|
|
325
|
+
fetch?: typeof fetch;
|
|
326
|
+
/** Version reported in the MCP handshake — the host CLI's, not this package's. */
|
|
327
|
+
version?: string;
|
|
328
|
+
}
|
|
329
|
+
/** Server identity advertised in the MCP `initialize` handshake. */
|
|
330
|
+
declare const LOCAL_SERVER_NAME = "lunora";
|
|
331
|
+
/** Shown when a deployment tool is called and no dev server can be found. */
|
|
332
|
+
declare const NO_DEPLOYMENT_MESSAGE = "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).";
|
|
333
|
+
/**
|
|
334
|
+
* Assemble the tool list, in the order it is advertised: docs first (the
|
|
335
|
+
* surface that always works), then the caller's extras, then the deployment
|
|
336
|
+
* tools. Order also decides precedence — `createToolServer` keeps the first
|
|
337
|
+
* registration of a duplicated name.
|
|
338
|
+
*
|
|
339
|
+
* `clientFor` is the shared client cache built once by
|
|
340
|
+
* {@link createLocalMcpServer} and threaded into both the tool and resource
|
|
341
|
+
* surfaces. Exported (and called directly by tests) without going through
|
|
342
|
+
* `createLocalMcpServer`, so a caller that omits it gets a private,
|
|
343
|
+
* call-scoped cache — same shape as before this surface was shared, just
|
|
344
|
+
* without the cross-surface sharing that only matters once a resource
|
|
345
|
+
* surface exists alongside it.
|
|
346
|
+
*/
|
|
347
|
+
declare const localTools: (options: LocalMcpServerOptions, clientFor?: (deployment: LocalDeployment) => LunoraClient) => ReadonlyArray<McpTool>;
|
|
348
|
+
/** Build the composed local server without connecting a transport. */
|
|
349
|
+
declare const createLocalMcpServer: (options?: LocalMcpServerOptions) => Server;
|
|
350
|
+
/**
|
|
351
|
+
* Build the composed local server and connect it over stdio — the transport an
|
|
352
|
+
* MCP client uses when it spawns `lunora mcp serve`. Resolves once connected;
|
|
353
|
+
* the process then stays alive serving requests.
|
|
354
|
+
*/
|
|
355
|
+
declare const connectLocalStdio: (options?: LocalMcpServerOptions) => Promise<Server>;
|
|
356
|
+
/** A tool handler: receives the call's `arguments` bag, returns an MCP tool result. */
|
|
357
|
+
/**
|
|
358
|
+
* The x402 vocabulary this module needs, declared here rather than imported.
|
|
359
|
+
*
|
|
360
|
+
* The x402 package is an optional peer, and a type import from its `charge`
|
|
361
|
+
* entry puts its `.d.ts` back into this package's build graph:
|
|
362
|
+
* a consumer that never installs x402 never builds it either, so the dts bundler
|
|
363
|
+
* looks for a `dist/` that does not exist and fails. That is not hypothetical —
|
|
364
|
+
* it broke the docs site build, which runs a filtered build over the docs app and its dependency closure
|
|
365
|
+
* and therefore never builds x402.
|
|
366
|
+
*
|
|
367
|
+
* Declaring them locally is safe because this module never *inspects* a charge
|
|
368
|
+
* config; it forwards it whole to `createChargeMiddleware`. The index signature
|
|
369
|
+
* keeps a real charge config assignable as x402 grows fields.
|
|
370
|
+
*/
|
|
371
|
+
/** Mirrors x402's `X402Price` — a decimal string like `"$0.05"`, or a number. */
|
|
372
|
+
type X402Price = number | string;
|
|
373
|
+
/** Mirrors x402's charge config with the per-tool price omitted. */
|
|
374
|
+
interface X402ChargeSettings {
|
|
375
|
+
/** Network this resource settles on. */
|
|
376
|
+
readonly network: string;
|
|
377
|
+
/** Everything else x402 accepts, forwarded untouched. */
|
|
378
|
+
readonly [key: string]: unknown;
|
|
379
|
+
/** Payout wallet(s), per network family. */
|
|
380
|
+
readonly recipient: {
|
|
381
|
+
readonly evm?: string;
|
|
382
|
+
readonly svm?: string;
|
|
383
|
+
};
|
|
15
384
|
}
|
|
16
|
-
|
|
385
|
+
type ToolHandler = (arguments_: Record<string, unknown>) => Promise<ToolResult> | ToolResult;
|
|
386
|
+
/** Registration shape for a free tool. */
|
|
387
|
+
interface RegisterToolOptions {
|
|
388
|
+
/** Optional MCP tool annotations (`readOnlyHint`, `title`, …). */
|
|
389
|
+
annotations?: Tool["annotations"];
|
|
390
|
+
/** Human/model-facing description of what the tool does. */
|
|
17
391
|
description: string;
|
|
392
|
+
/** JSON-Schema object describing the tool's arguments. */
|
|
18
393
|
inputSchema: ToolInputSchema;
|
|
394
|
+
/** Unique tool name (the MCP `tools/call` `name`). */
|
|
19
395
|
name: string;
|
|
20
396
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
397
|
+
/** Registration shape for a paid tool: a {@link RegisterToolOptions} plus its USD price. */
|
|
398
|
+
interface RegisterPaidToolOptions extends RegisterToolOptions {
|
|
399
|
+
/** USD price per call (e.g. `"$0.05"`), charged via x402 before dispatch. */
|
|
400
|
+
price: X402Price;
|
|
401
|
+
}
|
|
402
|
+
/** x402 settlement vocabulary shared by every paid tool (network, recipient, facilitator); price is per-tool. */
|
|
403
|
+
type PaidMcpChargeConfig = X402ChargeSettings;
|
|
404
|
+
/** Config for `createPaidMcpServer`. */
|
|
405
|
+
interface PaidMcpServerConfig {
|
|
406
|
+
/** The worker-level x402 charge config; each paid tool supplies only its own `price`. */
|
|
407
|
+
charge: PaidMcpChargeConfig;
|
|
408
|
+
/**
|
|
409
|
+
* Largest accepted request body, in bytes — enforced while the body streams
|
|
410
|
+
* in, not after it is buffered. Defaults to `DEFAULT_MAX_REQUEST_BYTES`
|
|
411
|
+
* (128 KiB), which a value that is not a non-negative safe integer also
|
|
412
|
+
* falls back to.
|
|
413
|
+
*/
|
|
414
|
+
maxRequestBytes?: number;
|
|
415
|
+
/** Name/version advertised in the MCP `initialize` handshake. Defaults to `lunora-paid-mcp`. */
|
|
416
|
+
serverInfo?: {
|
|
417
|
+
name: string;
|
|
418
|
+
version: string;
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* The Worker execution context, as this module reads it: only `waitUntil`, and
|
|
423
|
+
* structurally, so the package takes no `@cloudflare/workers-types` dependency.
|
|
424
|
+
*/
|
|
425
|
+
interface PaidMcpExecutionContext {
|
|
426
|
+
waitUntil?: (promise: Promise<unknown>) => void;
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* The paid server's fetch handler.
|
|
430
|
+
*
|
|
431
|
+
* Unlike the free `McpFetchHandler` it takes the Worker's full
|
|
432
|
+
* `(request, env, ctx)` triple — the shape `export default { fetch }` is called
|
|
433
|
+
* with — because the x402 receipt sink NEEDS `ctx.waitUntil`: work that is
|
|
434
|
+
* neither awaited into the response nor registered with it is cancelled when the
|
|
435
|
+
* request ends, so an async `onReceipt` (inserting the settled payment into a
|
|
436
|
+
* durable table) frequently never runs while the money has already moved
|
|
437
|
+
* on-chain. `env` is accepted and ignored so the handler drops straight into the
|
|
438
|
+
* default export; both are optional, so a non-Workers caller may still invoke it
|
|
439
|
+
* with a bare `Request` (the middleware then simply sees no `waitUntil`).
|
|
440
|
+
*/
|
|
441
|
+
type PaidMcpFetchHandler = (request: Request, env?: unknown, context?: PaidMcpExecutionContext) => Promise<Response>;
|
|
442
|
+
/** A paid MCP server: register free/paid tools, then serve over Streamable HTTP. */
|
|
443
|
+
interface PaidMcpServer {
|
|
444
|
+
/** The Streamable-HTTP fetch handler; gates each paid `tools/call` behind x402. */
|
|
445
|
+
readonly fetchHandler: PaidMcpFetchHandler;
|
|
446
|
+
/** Register a **paid** tool: its dispatch runs the x402 charge middleware first. */
|
|
447
|
+
paidTool: (options: RegisterPaidToolOptions, handler: ToolHandler) => void;
|
|
448
|
+
/** Register a **free** tool (coexists with paid tools on the same server). */
|
|
449
|
+
tool: (options: RegisterToolOptions, handler: ToolHandler) => void;
|
|
27
450
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
451
|
+
/**
|
|
452
|
+
* Create a paid MCP server. Register free tools with `tool()` and priced tools
|
|
453
|
+
* with `paidTool()` (they coexist), then serve `fetchHandler` over HTTP.
|
|
454
|
+
*
|
|
455
|
+
* The server is **stateless**: `fetchHandler` builds a fresh `Server` per
|
|
456
|
+
* request (reading the live tool registry), so tools registered before the
|
|
457
|
+
* first request are all visible. Each priced tool memoises one initialised
|
|
458
|
+
* `ChargeMiddleware` (keyed by tool name, baking that tool's price and naming
|
|
459
|
+
* the tool as the challenge `resource`); a failed init is not cached, so a
|
|
460
|
+
* transient facilitator outage retries on the next call.
|
|
461
|
+
*/
|
|
462
|
+
declare const createPaidMcpServer: (config: PaidMcpServerConfig) => PaidMcpServer;
|
|
463
|
+
export { AGENT_RUN_INPUT_SCHEMA, AGENT_STATUS_TOOL_NAME, type AuthedMcpFetchHandlerOptions, type AuthedMcpServerOptions,
|
|
464
|
+
/**
|
|
465
|
+
* `@lunora/mcp` — Model Context Protocol servers for Lunora. This entry exposes
|
|
466
|
+
* a *deployment* to AI agents; the `@lunora/mcp/docs` subpath exposes the
|
|
467
|
+
* framework's *documentation* (credential-free, and safe to host publicly).
|
|
468
|
+
*
|
|
469
|
+
* The deployment server: It registers tools for introspecting a deployment
|
|
470
|
+
* (`lunora_list_functions`, `lunora_list_tables`) and invoking its functions
|
|
471
|
+
* (`lunora_run_query`, plus `lunora_run_mutation` and `lunora_run_action` when
|
|
472
|
+
* writes are enabled), each backed by `LunoraClient` over HTTP RPC. It also
|
|
473
|
+
* exposes the deployment's observability reads (`lunora_get_logs`,
|
|
474
|
+
* `lunora_get_issues`, `lunora_get_advisories`, `lunora_get_query_insights`,
|
|
475
|
+
* `lunora_get_migration_status`) when `allowObservability` (or the
|
|
476
|
+
* LUNORA_MCP_ALLOW_OBSERVABILITY env) is set — read-only, but they return
|
|
477
|
+
* production user data, so they are omitted entirely without it. The server is
|
|
478
|
+
* read-only by default — the write tools are exposed only when `allowWrites`
|
|
479
|
+
* (or the `LUNORA_MCP_ALLOW_WRITES` env) is set, and every run tool is
|
|
480
|
+
* allowlisted against the deployment's discovered public functions. It can also
|
|
481
|
+
* front durable `@lunora/agent` runs as `agent_<name>` tools when `allowAgents`
|
|
482
|
+
* (or `LUNORA_MCP_ALLOW_AGENTS` + `LUNORA_MCP_AGENTS`) is set. Run the
|
|
483
|
+
* `lunora-mcp` binary (configured via the `LUNORA_URL`, `LUNORA_ADMIN_TOKEN`,
|
|
484
|
+
* and `LUNORA_MCP_ALLOW_WRITES` env vars) for the stdio transport, serve the
|
|
485
|
+
* server remotely over Streamable HTTP with `createMcpFetchHandler` (a
|
|
486
|
+
* Workers-ready `Request` → `Response` handler), or build a server
|
|
487
|
+
* programmatically with `createLunoraMcpServer` and connect any transport.
|
|
488
|
+
*
|
|
489
|
+
* A remote endpoint is reachable by anyone who knows its URL, and the tools
|
|
490
|
+
* carry the deployment's admin bearer — so for a public deployment use
|
|
491
|
+
* `createAuthedMcpFetchHandler` instead, which mounts the same server behind
|
|
492
|
+
* better-auth's MCP OAuth gate (`requireMcpAuth` from `@lunora/auth/plugins`)
|
|
493
|
+
* and can scope tool exposure to the access token's own scopes.
|
|
494
|
+
*/
|
|
495
|
+
type CallAgentToolOptions, LOCAL_SERVER_NAME, type LocalDeployment, type LocalDeploymentSource, type LocalMcpServerOptions, type LunoraMcpServerOptions, type McpAccessTokenClaims,
|
|
496
|
+
/**
|
|
497
|
+
* `@lunora/mcp` — Model Context Protocol servers for Lunora. This entry exposes
|
|
498
|
+
* a *deployment* to AI agents; the `@lunora/mcp/docs` subpath exposes the
|
|
499
|
+
* framework's *documentation* (credential-free, and safe to host publicly).
|
|
500
|
+
*
|
|
501
|
+
* The deployment server: It registers tools for introspecting a deployment
|
|
502
|
+
* (`lunora_list_functions`, `lunora_list_tables`) and invoking its functions
|
|
503
|
+
* (`lunora_run_query`, plus `lunora_run_mutation` and `lunora_run_action` when
|
|
504
|
+
* writes are enabled), each backed by `LunoraClient` over HTTP RPC. It also
|
|
505
|
+
* exposes the deployment's observability reads (`lunora_get_logs`,
|
|
506
|
+
* `lunora_get_issues`, `lunora_get_advisories`, `lunora_get_query_insights`,
|
|
507
|
+
* `lunora_get_migration_status`) when `allowObservability` (or the
|
|
508
|
+
* LUNORA_MCP_ALLOW_OBSERVABILITY env) is set — read-only, but they return
|
|
509
|
+
* production user data, so they are omitted entirely without it. The server is
|
|
510
|
+
* read-only by default — the write tools are exposed only when `allowWrites`
|
|
511
|
+
* (or the `LUNORA_MCP_ALLOW_WRITES` env) is set, and every run tool is
|
|
512
|
+
* allowlisted against the deployment's discovered public functions. It can also
|
|
513
|
+
* front durable `@lunora/agent` runs as `agent_<name>` tools when `allowAgents`
|
|
514
|
+
* (or `LUNORA_MCP_ALLOW_AGENTS` + `LUNORA_MCP_AGENTS`) is set. Run the
|
|
515
|
+
* `lunora-mcp` binary (configured via the `LUNORA_URL`, `LUNORA_ADMIN_TOKEN`,
|
|
516
|
+
* and `LUNORA_MCP_ALLOW_WRITES` env vars) for the stdio transport, serve the
|
|
517
|
+
* server remotely over Streamable HTTP with `createMcpFetchHandler` (a
|
|
518
|
+
* Workers-ready `Request` → `Response` handler), or build a server
|
|
519
|
+
* programmatically with `createLunoraMcpServer` and connect any transport.
|
|
520
|
+
*
|
|
521
|
+
* A remote endpoint is reachable by anyone who knows its URL, and the tools
|
|
522
|
+
* carry the deployment's admin bearer — so for a public deployment use
|
|
523
|
+
* `createAuthedMcpFetchHandler` instead, which mounts the same server behind
|
|
524
|
+
* better-auth's MCP OAuth gate (`requireMcpAuth` from `@lunora/auth/plugins`)
|
|
525
|
+
* and can scope tool exposure to the access token's own scopes.
|
|
526
|
+
*/
|
|
527
|
+
type McpAgentExposure, type McpAuthProtect, type McpFetchHandler, type McpFetchHandlerOptions, type McpTool, NO_DEPLOYMENT_MESSAGE, OBSERVABILITY_TOOL_DEFINITIONS, type PaidMcpChargeConfig, type PaidMcpExecutionContext, type PaidMcpFetchHandler, type PaidMcpServer, type PaidMcpServerConfig, READ_ONLY_TOOL_DEFINITIONS, type RegisterPaidToolOptions, type RegisterToolOptions, type ToolDefinition, type ToolHandler, type ToolInputSchema, type ToolResult, WRITE_TOOL_DEFINITIONS, agentToolDefinitions, callAgentTool, callTool, connectLocalStdio, connectStdio, createAuthedMcpFetchHandler, createLocalMcpServer, createLunoraMcpServer, createMcpFetchHandler, createPaidMcpServer, localTools, mcpTokenScopes, parseAgentsEnv, toolDefinitions };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { TOOL_DEFINITIONS, callTool } from './packem_shared/TOOL_DEFINITIONS-Dpiu38ji.mjs';
|
|
1
|
+
import{AGENT_RUN_INPUT_SCHEMA as r,AGENT_STATUS_TOOL_NAME as t,agentToolDefinitions as c,callAgentTool as T,parseAgentsEnv as E}from"./packem_shared/AGENT_RUN_INPUT_SCHEMA-hKbpa3Dg.mjs";import{createAuthedMcpFetchHandler as _,mcpTokenScopes as a}from"./packem_shared/createAuthedMcpFetchHandler-DwOzAueC.mjs";import{createToolServer as l}from"./packem_shared/createToolServer-BtGuPyMU.mjs";import{createMcpFetchHandler as n}from"./packem_shared/createMcpFetchHandler-DSK2X9Hd.mjs";import{LOCAL_SERVER_NAME as N,NO_DEPLOYMENT_MESSAGE as I,connectLocalStdio as L,createLocalMcpServer as f,localTools as m}from"./packem_shared/LOCAL_SERVER_NAME-DPay9JzK.mjs";import{createPaidMcpServer as s}from"./packem_shared/createPaidMcpServer-BIBKMtxs.mjs";import{connectStdio as i,createLunoraMcpServer as D}from"./packem_shared/connectStdio-BBtfW4UB.mjs";import{READ_ONLY_TOOL_DEFINITIONS as d,WRITE_TOOL_DEFINITIONS as v,callTool as F,toolDefinitions as U}from"./packem_shared/READ_ONLY_TOOL_DEFINITIONS-_aG40yWx.mjs";import{DEFAULT_MAX_REQUEST_BYTES as g,serveStateless as h}from"./packem_shared/DEFAULT_MAX_REQUEST_BYTES-CbbpkHRK.mjs";import{O as G}from"./packem_shared/observability-tools-B-g9Y9IT.mjs";export{r as AGENT_RUN_INPUT_SCHEMA,t as AGENT_STATUS_TOOL_NAME,g as DEFAULT_MAX_REQUEST_BYTES,N as LOCAL_SERVER_NAME,I as NO_DEPLOYMENT_MESSAGE,G as OBSERVABILITY_TOOL_DEFINITIONS,d as READ_ONLY_TOOL_DEFINITIONS,v as WRITE_TOOL_DEFINITIONS,c as agentToolDefinitions,T as callAgentTool,F as callTool,L as connectLocalStdio,i as connectStdio,_ as createAuthedMcpFetchHandler,f as createLocalMcpServer,D as createLunoraMcpServer,n as createMcpFetchHandler,s as createPaidMcpServer,l as createToolServer,m as localTools,a as mcpTokenScopes,E as parseAgentsEnv,h as serveStateless,U as toolDefinitions};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const b="agents:agentRun",T="agents:agentThread",H="agents:agentMessages",p="lunora_agent_status",_=new Set(["awaiting_input","cancelled","error","idle"]),O=6e4,x=600,P={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"},U={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 a=r.slice(0,o).trim(),s=r.slice(o+1).trim();a.length===0||s.length===0||n.push({description:s,name:a})}return n},L={destructiveHint:!1,idempotentHint:!0,openWorldHint:!0,readOnlyHint:!0},K=(t,n)=>n!==!0||t.length===0?[]:[...t.map(r=>({annotations:{destructiveHint:!0,idempotentHint:!1,openWorldHint:!0,readOnlyHint:!1,title:`Run the ${r.name} agent (starts a durable run)`},description:`${r.description} Starts a durable agent run and returns its final answer.`,inputSchema:P,name:g(r)})),{annotations:{...L,title:"Check a durable agent run"},description:"Check the status of a durable agent run (and its answer if finished) by its threadKey.",inputSchema:U,name:p}],G=(t,n)=>t===p||n.some(e=>g(e)===t),R=()=>`mcp-${crypto.randomUUID()}`,d=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(d(H),{key:n});if(e==="error"){const a=r!==null&&typeof r=="object"?r.error:void 0;return c({error:typeof a=="string"?a:"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})},I=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(d(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===p)return await I(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 a=l(e,"prompt");if(a===void 0)return i('"prompt" is required and must be a non-empty string');const s=l(e,"threadKey")??R(),h=l(e,"title"),{id:E}=await t.mutation(d(b),{agent:o.name,input:a,threadKey:s,...h===void 0?{}:{title:h}}),f=r.pollIntervalMs??x,N=r.maxWaitMs??O,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(d(T),{key:s}),m=w(y);if(_.has(m))return await S(t,s,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:s})}catch(o){const a=o instanceof Error?o.message:String(o);return i(a)}};export{P as AGENT_RUN_INPUT_SCHEMA,p as AGENT_STATUS_TOOL_NAME,K as agentToolDefinitions,q as callAgentTool,A as finalAnswer,G as isAgentToolName,C as parseAgentsEnv};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import{toDocsSearchHits as h}from"./toDocsSearchHits-CBLmtWXt.mjs";const f="https://lunora.sh",u=1e4,m=t=>t.endsWith("/")?t.slice(0,-1):t,p="\\",g=(t,n,i)=>{for(let s=i;s<t.length;s+=1)if(t[s]===n&&t[s-1]!==p)return s;return-1},d=t=>t.replaceAll(String.raw`\[`,"[").replaceAll(String.raw`\]`,"]").replaceAll(String.raw`\(`,"(").replaceAll(String.raw`\)`,")"),S=t=>{const n=t.trim();if(!n.startsWith("- ["))return;const i=n.lastIndexOf("](");if(i===-1)return;const s=g(n,")",i+2);if(s===-1)return;const o=d(n.slice(3,i).trim()),c=d(n.slice(i+2,s).trim());if(o.length===0||c.length===0)return;const e=n.slice(s+1).trim(),r=e.startsWith(":")?e.slice(1).trim():"";return r.length>0?{description:r,title:o,url:c}:{title:o,url:c}},x=t=>{if(!t.endsWith(")"))return t;const n=t.lastIndexOf(" (");if(n===-1)return t;const i=t.slice(n+2,-1);return i.startsWith("/")||i.startsWith("http")?t.slice(0,n).trim():t},y=(t,n)=>{for(const i of t.split(`
|
|
2
|
+
`)){if(i.startsWith("# "))return x(i.slice(2).trim());if(i.trim().length>0)break}return n},b=t=>{if(!t.startsWith("# "))return t;const n=t.indexOf(`
|
|
3
|
+
`);return n===-1?"":t.slice(n+1).trimStart()},A=(t={})=>{const n=m(t.baseUrl??f),i=t.fetch??globalThis.fetch,s=t.timeoutMs??u,o=async e=>{try{const r=await i(`${n}${e}`,{headers:{accept:"text/plain, application/json"},signal:AbortSignal.timeout(s)});return r.ok?{body:await r.text()}:{missing:!0}}catch(r){return{unreachable:r instanceof Error?r.message:String(r)}}},c=(e,r)=>{if("unreachable"in e)throw new Error(`could not reach the documentation site at ${n}${r}: ${e.unreachable}`);return"missing"in e?void 0:e.body};return{getPage:async e=>{const r=c(await o(`/llms.mdx${e}`),`/llms.mdx${e}`);if(!(r===void 0||r.trim().length===0))return{content:b(r),title:y(r,e),url:e}},listPages:async()=>{const e=c(await o("/llms.txt"),"/llms.txt");if(e===void 0)return[];const r=[];for(const l of e.split(`
|
|
4
|
+
`)){const a=S(l);a!==void 0&&r.push(a)}return r},search:async e=>{const r=`/api/search?query=${encodeURIComponent(e)}`,l=c(await o(r),r);if(l===void 0)return[];let a;try{a=JSON.parse(l)}catch{return[]}return Array.isArray(a)?h(a):[]}}};export{f as DEFAULT_DOCS_BASE_URL,u as DEFAULT_REQUEST_TIMEOUT_MS,A as createRemoteDocsIndex,S as parseIndexLine};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{WebStandardStreamableHTTPServerTransport as y}from"@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";const u=128*1024,p=e=>e!==void 0&&Number.isSafeInteger(e)&&e>=0?e:u,l=async(e,t)=>{if(!e.body)return"";const r=e.body.getReader(),s=new TextDecoder;let n=0,o="";for(;;){const{done:i,value:a}=await r.read();if(i)break;if(a){if(n+=a.byteLength,n>t){r.cancel().catch(()=>{});return}o+=s.decode(a,{stream:!0})}}return o+s.decode()},d=(e,t,r)=>Response.json({error:{code:t,message:r},id:null,jsonrpc:"2.0"},{status:e}),c=()=>d(400,-32600,"batched requests are not supported: send one JSON-RPC message per request"),f=async(e,t)=>{if(e.method!=="POST")return{parsedBody:void 0};const r=p(t),s={response:d(413,-32600,`request body exceeds ${String(r)} bytes`)},n=Number(e.headers.get("content-length"));if(Number.isFinite(n)&&n>r)return s;const o=await l(e,r);if(o===void 0)return s;try{return{parsedBody:JSON.parse(o)}}catch{return{response:d(400,-32700,"parse error: body is not valid JSON")}}},b=async(e,t)=>{if(e.method!=="POST")return{parsedBody:t?.parsedBody};if(t?.parsedBody!==void 0)return Array.isArray(t.parsedBody)?{response:c()}:{parsedBody:t.parsedBody};const r=await f(e,t?.maxRequestBytes);return"response"in r?r:Array.isArray(r.parsedBody)?{response:c()}:r},B=async(e,t,r)=>{const s=await b(t,r);if("response"in s)return s.response;const n=new y({enableJsonResponse:!0,sessionIdGenerator:void 0});try{return await e.connect(n),await n.handleRequest(t,{authInfo:r?.authInfo,parsedBody:s.parsedBody})}finally{n.close().catch(()=>{}),e.close().catch(()=>{})}};export{u as DEFAULT_MAX_REQUEST_BYTES,f as readScreenedBody,B as serveStateless};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
const T=10,A=50,h="docs",L=500,_={properties:{limit:{description:`Maximum hits to return (default ${String(10)}, max ${String(50)})`,type:"number"},query:{description:'Search terms, e.g. "shardBy" or "optimistic updates"',type:"string"}},required:["query"],type:"object"},m={properties:{url:{description:'Site-relative page URL from a search hit, e.g. "/docs/sharding"',type:"string"}},required:["url"],type:"object"},g={properties:{},type:"object"},s={destructiveHint:!1,idempotentHint:!0,openWorldHint:!0,readOnlyHint:!0},u={annotations:{...s,title:"Search the Lunora documentation"},description:"Search the Lunora documentation and return matching pages and sections. Use this before writing Lunora code (schema, queries, mutations, actions, sharding, .global(), client hooks) so the answer reflects the framework's current API rather than a guess. Follow a hit with lunora_get_doc to read the full page.",inputSchema:_,name:"lunora_search_docs"},l={annotations:{...s,title:"Read a Lunora documentation page"},description:"Return one Lunora documentation page in full, as Markdown. Takes the `url` of a lunora_search_docs hit or a lunora_list_docs entry.",inputSchema:m,name:"lunora_get_doc"},d={annotations:{...s,title:"List the Lunora documentation pages"},description:"List every Lunora documentation page with its title and description. Prefer lunora_search_docs when you know what you're looking for.",inputSchema:g,name:"lunora_list_docs"},y=[u,l,d],r=n=>({content:[{text:JSON.stringify(n,void 0,2),type:"text"}]}),p=n=>({content:[{text:n,type:"text"}],isError:!0}),a=512,c=(n,t)=>{const e=n[t];if(typeof e!="string"||e.trim().length===0)throw new TypeError(`"${t}" is required and must be a non-empty string`);if(e.length>a)throw new RangeError(`"${t}" must be at most ${String(a)} characters`);return e.trim()},f=n=>{const t=typeof n=="string"?Number(n):n;return typeof t!="number"||!Number.isFinite(t)?10:Math.min(50,Math.max(1,Math.floor(t)))},S=(n,t)=>{let e;try{e=decodeURIComponent(n)}catch{throw new RangeError(`"url" contains a malformed percent-escape: ${t}`)}if(e.includes("%")||e.includes("\\"))throw new RangeError(`"url" must not contain ".." or encoded segments: ${t}`);for(const o of e.split("/"))if(o===".."||o===".")throw new RangeError(`"url" must not contain ".." or encoded segments: ${t}`)},E=n=>{let t=n.trim().replaceAll("\\","/");if(t.startsWith("http://")||t.startsWith("https://")){const e=t.slice(t.indexOf("//")+2),o=e.indexOf("/");t=o===-1?"/":e.slice(o)}for(const e of["?","#"]){const o=t.indexOf(e);o!==-1&&(t=t.slice(0,o))}for(;t.endsWith("/")&&t.length>1;)t=t.slice(0,-1);return t.startsWith("/")||(t=t.startsWith("docs/")?`/${t}`:`/${h}/${t}`),S(t,n),t},O=n=>[{definition:u,handle:async t=>{const e=c(t,"query"),o=f(t.limit),i=(await n.search(e)).slice(0,o);return i.length===0?r({hits:[],note:`no documentation matched "${e}" — try fewer or more general terms, or lunora_list_docs to browse`}):r({hits:i})}},{definition:l,handle:async t=>{const e=E(c(t,"url")),o=await n.getPage(e);return o===void 0?p(`documentation page not found: ${e}. Use lunora_search_docs or lunora_list_docs to find a valid url.`):{content:[{text:`# ${o.title} (${o.url})
|
|
2
|
+
|
|
3
|
+
${o.content}`,type:"text"}]}}},{definition:d,handle:async()=>{const t=await n.listPages();return t.length>500?r({note:`showing the first ${String(500)} of ${String(t.length)} pages — use lunora_search_docs to find the rest`,pages:t.slice(0,500)}):r(t)}}];export{T as DEFAULT_SEARCH_LIMIT,y as DOCS_TOOL_DEFINITIONS,a as MAX_ARGUMENT_LENGTH,L as MAX_LISTED_PAGES,A as MAX_SEARCH_LIMIT,O as docsTools,E as normalizeDocUrl};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createToolServer as o}from"./createToolServer-BtGuPyMU.mjs";import{serveStateless as s}from"./DEFAULT_MAX_REQUEST_BYTES-CbbpkHRK.mjs";import{DEFAULT_MAX_REQUEST_BYTES as i}from"./DEFAULT_MAX_REQUEST_BYTES-CbbpkHRK.mjs";import{docsResources as t}from"./DOCS_URI_SCHEME-Buo752CV.mjs";import{docsTools as c}from"./DEFAULT_SEARCH_LIMIT-BqSYN5vr.mjs";const m="lunora-docs",n=e=>({name:m,version:e??"0.0.0"}),a=e=>o(n(e.version),c(e.index),t(e.index)),v=e=>r=>s(a(e),r,{maxRequestBytes:e.maxRequestBytes});export{i as DEFAULT_MAX_REQUEST_BYTES,m as DOCS_SERVER_NAME,v as createDocsMcpFetchHandler,a as createDocsMcpServer};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{normalizeDocUrl as s}from"./DEFAULT_SEARCH_LIMIT-BqSYN5vr.mjs";const e="lunora-docs:",n=t=>`${e}${t}`,c=t=>t.startsWith(e)?t.slice(e.length):void 0,d=t=>({list:async()=>(await t.listPages()).map(o=>({...o.description===void 0?{}:{description:o.description},mimeType:"text/markdown",name:o.title,uri:n(o.url)})),read:async i=>{const o=c(i);if(o===void 0)return;const r=await t.getPage(s(o));return r===void 0?void 0:{mimeType:"text/markdown",text:`# ${r.title} (${r.url})
|
|
2
|
+
|
|
3
|
+
${r.content}`}}});export{e as DOCS_URI_SCHEME,d as docsResources,c as fromDocsUri,n as toDocsUri};
|
|
@@ -0,0 +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-Buo752CV.mjs";import{docsTools as y}from"./DEFAULT_SEARCH_LIMIT-BqSYN5vr.mjs";import{toolDefinitions as E,callTool as R}from"./READ_ONLY_TOOL_DEFINITIONS-_aG40yWx.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{D as T,M as a,O as L,a as s,c as _}from"./observability-tools-B-g9Y9IT.mjs";export{T as DEFAULT_LIMIT,a as MAX_LIMIT,L as OBSERVABILITY_TOOL_DEFINITIONS,s as OBSERVABILITY_TOOL_NAMES,_ as callObservabilityTool};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as c}from"@lunora/errors";import{O as T,e as d,a as N,c as E,o as u}from"./observability-tools-B-g9Y9IT.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"}],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?T:[],...t===!0?b:[]],A=t=>{const{functionPath:n}=t;if(typeof n!="string"||n.length===0)throw new c("BAD_REQUEST",'"functionPath" is required and must be a non-empty string');return n},O=t=>typeof t=="object"&&t!==null&&!Array.isArray(t),w=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 c("BAD_REQUEST",'"args" must be a JSON object; received a string that is not valid JSON')}if(!O(n))throw new c("BAD_REQUEST",`"args" must be a JSON object; the provided string decoded to ${w(n)}`);return n}if(!O(t))throw new c("BAD_REQUEST",`"args" must be a JSON object, got ${w(t)}`);return t},f=t=>{const n=A(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}),P=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+P,promise:o}),o},_=async(t,n,e)=>{const i=(await y(t)).find(r=>r.path===n);if(i===void 0)throw new c("NOT_FOUND",`function not found or not public: ${n}`);if(i.kind!==e)throw new c("BAD_REQUEST",`function ${n} is a ${i.kind}, not a ${e}`)},B=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(N.has(n))return i!==!0?d(`tool "${n}" is disabled: it reads the deployment's logs, request metadata and grouped errors — user data that would land at the model provider. Enable it with the LUNORA_MCP_ALLOW_OBSERVABILITY env var.`):await E(t,n,e);switch(n){case"lunora_get_function_schema":{const r=A(e),s=(await y(t)).find(S=>S.path===r);return s===void 0?d(`function not found: ${r}`):u({args:s.args??[],kind:s.kind,path:s.path})}case"lunora_list_functions":return u(await y(t));case"lunora_list_tables":return u(await t.listGlobalTables());case"lunora_run_action":{const{args:r,functionPath:a,shardKey:s}=f(e);return await _(t,a,"action"),u(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"),u(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"),u(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{T as OBSERVABILITY_TOOL_DEFINITIONS,I as READ_ONLY_TOOL_DEFINITIONS,b as WRITE_TOOL_DEFINITIONS,B as callTool,U as toolDefinitions};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readFileSync as g}from"node:fs";import{dirname as c,join as d}from"node:path";import{fileURLToPath as w}from"node:url";import{LunoraClient as h}from"@lunora/client";import{LunoraError as u}from"@lunora/errors";import{Server as T}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as y}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-hKbpa3Dg.mjs";import{toolDefinitions as N,callTool as L}from"./READ_ONLY_TOOL_DEFINITIONS-_aG40yWx.mjs";const R=()=>{try{let e=c(w(import.meta.url));for(let r=0;r<8;r+=1){try{const a=g(d(e,"package.json"),"utf8"),t=JSON.parse(a);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"},E={name:"lunora",version:R()},I=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},W=e=>{const r=I(e),n=e.allowWrites??!1,a=e.allowAgents??!1,t=e.agents??[],m=typeof e.token=="string"&&e.token.length>0,s=e.allowObservability===!0&&m,o=new T(E,{capabilities:{tools:{}}});return o.setRequestHandler(p,()=>({tools:[...N(n,s),...M(t,a)]})),o.setRequestHandler(A,async f=>{const{arguments:v,name:l}=f.params,i=v??{};return S(l,t)?await k(r,l,i,{allowAgents:a,exposures:t,...e.agentMaxWaitMs===void 0?{}:{maxWaitMs:e.agentMaxWaitMs},...e.agentPollIntervalMs===void 0?{}:{pollIntervalMs:e.agentPollIntervalMs}}):await L(r,l,i,n,s)}),o},F=async e=>{const r=W(e);return await r.connect(new y),r};export{F as connectStdio,W as createLunoraMcpServer,I as resolveClient};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{serveStateless as n}from"./DEFAULT_MAX_REQUEST_BYTES-CbbpkHRK.mjs";import{resolveClient as a,createLunoraMcpServer as o}from"./connectStdio-BBtfW4UB.mjs";const f=e=>typeof e.scope!="string"?new Set:new Set(e.scope.split(" ").filter(r=>r!=="")),l=e=>{const r=typeof e.server=="function"?void 0:a(e.server);return e.protect(async(t,s)=>{const c=typeof e.server=="function"?await e.server(s):{...e.server,client:r};return await n(o(c),t,{maxRequestBytes:e.maxRequestBytes})})};export{l as createAuthedMcpFetchHandler,f as mcpTokenScopes};
|