@softure-ai/mcp-access 0.1.11 → 0.1.13

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 (136) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +136 -11
  3. package/dist/cli/bin.d.ts +3 -0
  4. package/dist/cli/bin.d.ts.map +1 -0
  5. package/dist/cli/bin.js +5 -0
  6. package/dist/cli/bin.js.map +1 -0
  7. package/dist/cli/command.d.ts +9 -0
  8. package/dist/cli/command.d.ts.map +1 -0
  9. package/dist/cli/command.js +30 -0
  10. package/dist/cli/command.js.map +1 -0
  11. package/dist/cli/index.d.ts +3 -0
  12. package/dist/cli/index.d.ts.map +1 -0
  13. package/dist/cli/index.js +4 -0
  14. package/dist/cli/index.js.map +1 -0
  15. package/dist/cli/run.d.ts +24 -0
  16. package/dist/cli/run.d.ts.map +1 -0
  17. package/dist/cli/run.js +58 -0
  18. package/dist/cli/run.js.map +1 -0
  19. package/dist/contract.d.ts +4 -2
  20. package/dist/contract.d.ts.map +1 -1
  21. package/dist/index.d.ts +20 -7
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +6 -11
  24. package/dist/index.js.map +1 -1
  25. package/dist/messages/en.d.ts +2 -0
  26. package/dist/messages/en.d.ts.map +1 -1
  27. package/dist/messages/en.js +2 -0
  28. package/dist/messages/en.js.map +1 -1
  29. package/dist/messages/index.d.ts +4 -0
  30. package/dist/messages/index.d.ts.map +1 -1
  31. package/dist/messages/pl.d.ts.map +1 -1
  32. package/dist/messages/pl.js +2 -0
  33. package/dist/messages/pl.js.map +1 -1
  34. package/dist/next/actions.d.ts +2 -2
  35. package/dist/next/actions.d.ts.map +1 -1
  36. package/dist/next/actions.js +8 -47
  37. package/dist/next/actions.js.map +1 -1
  38. package/dist/next/format.d.ts +1 -1
  39. package/dist/next/format.d.ts.map +1 -1
  40. package/dist/next/format.js +3 -1
  41. package/dist/next/format.js.map +1 -1
  42. package/dist/next/index.d.ts +3 -2
  43. package/dist/next/index.d.ts.map +1 -1
  44. package/dist/next/index.js +2 -1
  45. package/dist/next/index.js.map +1 -1
  46. package/dist/next/issue-token.d.ts +15 -0
  47. package/dist/next/issue-token.d.ts.map +1 -0
  48. package/dist/next/issue-token.js +77 -0
  49. package/dist/next/issue-token.js.map +1 -0
  50. package/dist/next/pages.d.ts +9 -1
  51. package/dist/next/pages.d.ts.map +1 -1
  52. package/dist/next/pages.js +2 -2
  53. package/dist/next/pages.js.map +1 -1
  54. package/dist/options.d.ts +16 -0
  55. package/dist/options.d.ts.map +1 -1
  56. package/dist/options.js +7 -3
  57. package/dist/options.js.map +1 -1
  58. package/dist/origins.d.ts +3 -14
  59. package/dist/origins.d.ts.map +1 -1
  60. package/dist/origins.js +4 -20
  61. package/dist/origins.js.map +1 -1
  62. package/dist/routes.d.ts +15 -0
  63. package/dist/routes.d.ts.map +1 -0
  64. package/dist/routes.js +17 -0
  65. package/dist/routes.js.map +1 -0
  66. package/dist/server/action-tool.d.ts +21 -0
  67. package/dist/server/action-tool.d.ts.map +1 -0
  68. package/dist/server/action-tool.js +51 -0
  69. package/dist/server/action-tool.js.map +1 -0
  70. package/dist/server/index.d.ts +3 -0
  71. package/dist/server/index.d.ts.map +1 -1
  72. package/dist/server/index.js +3 -0
  73. package/dist/server/index.js.map +1 -1
  74. package/dist/server/oauth-http.js +1 -1
  75. package/dist/server/oauth-http.js.map +1 -1
  76. package/dist/server/origins.d.ts +1 -1
  77. package/dist/server/origins.d.ts.map +1 -1
  78. package/dist/server/origins.js +7 -2
  79. package/dist/server/origins.js.map +1 -1
  80. package/dist/server/prune.d.ts +8 -0
  81. package/dist/server/prune.d.ts.map +1 -0
  82. package/dist/server/prune.js +10 -0
  83. package/dist/server/prune.js.map +1 -0
  84. package/dist/server/tools.d.ts +25 -0
  85. package/dist/server/tools.d.ts.map +1 -0
  86. package/dist/server/tools.js +61 -0
  87. package/dist/server/tools.js.map +1 -0
  88. package/dist/stdio/index.d.ts +2 -0
  89. package/dist/stdio/index.d.ts.map +1 -0
  90. package/dist/stdio/index.js +4 -0
  91. package/dist/stdio/index.js.map +1 -0
  92. package/dist/stdio/register.d.ts +2 -0
  93. package/dist/stdio/register.d.ts.map +1 -0
  94. package/dist/stdio/register.js +10 -0
  95. package/dist/stdio/register.js.map +1 -0
  96. package/dist/stdio/serve.d.ts +45 -0
  97. package/dist/stdio/serve.d.ts.map +1 -0
  98. package/dist/stdio/serve.js +89 -0
  99. package/dist/stdio/serve.js.map +1 -0
  100. package/dist/testing/catalog.d.ts +15 -0
  101. package/dist/testing/catalog.d.ts.map +1 -0
  102. package/dist/testing/catalog.js +51 -0
  103. package/dist/testing/catalog.js.map +1 -0
  104. package/dist/testing/index.d.ts +2 -0
  105. package/dist/testing/index.d.ts.map +1 -0
  106. package/dist/testing/index.js +3 -0
  107. package/dist/testing/index.js.map +1 -0
  108. package/module.json +1 -1
  109. package/package.json +31 -5
  110. package/src/cli/bin.ts +5 -0
  111. package/src/cli/command.ts +38 -0
  112. package/src/cli/index.ts +3 -0
  113. package/src/cli/run.ts +79 -0
  114. package/src/contract.ts +5 -2
  115. package/src/index.ts +9 -12
  116. package/src/messages/en.ts +2 -0
  117. package/src/messages/pl.ts +2 -0
  118. package/src/next/actions.ts +8 -45
  119. package/src/next/format.ts +2 -2
  120. package/src/next/index.ts +3 -2
  121. package/src/next/issue-token.ts +90 -0
  122. package/src/next/pages.tsx +11 -3
  123. package/src/options.ts +7 -3
  124. package/src/origins.ts +6 -27
  125. package/src/routes.ts +17 -0
  126. package/src/server/action-tool.ts +64 -0
  127. package/src/server/index.ts +3 -0
  128. package/src/server/oauth-http.ts +1 -1
  129. package/src/server/origins.ts +6 -5
  130. package/src/server/prune.ts +14 -0
  131. package/src/server/tools.ts +72 -0
  132. package/src/stdio/index.ts +12 -0
  133. package/src/stdio/register.ts +11 -0
  134. package/src/stdio/serve.ts +133 -0
  135. package/src/testing/catalog.ts +74 -0
  136. package/src/testing/index.ts +2 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,38 @@ Newest first. Each version lists what changed for an app that uses `@softure-ai/
4
4
  production, the version gets a line `verified in: <app>@<commit>` ([docs/05](../../docs/05-adoption-playbook.md),
5
5
  "Definition of done"). Versions before the first one below are described in their GitHub Releases (`mcp-access@x.y.z`).
6
6
 
7
+ ## 0.1.13
8
+
9
+ - Token and grant dates use core's `formatCalendarDay` on the day in the app's zone; same output. Requires
10
+ `@softure-ai/core` `^0.1.9` (#312).
11
+ - What an adopting app wrote around the endpoint ships with the package (#315):
12
+ - `toolResult`, `toolError` and `withToolErrors(work, { hints, label })` (`/server`): a thrown error reaches the
13
+ assistant only as a `PublicError` message or a hint, and the log only by kind, never with its SQL.
14
+ - `actionTool(handler, { message, hints })` (`/server`): a server action as a write tool, its arguments sent as
15
+ the form the action reads.
16
+ - `@softure-ai/mcp-access/stdio`: `serveMcpStdio({ config, createServer, userIdEnv, allowWrites })` serves the
17
+ factory over stdio for the account in `MCP_USER_ID`, or the only account; `/stdio/register` resolves
18
+ `server-only` to an empty module for `node`/`tsx --import`.
19
+ - `@softure-ai/mcp-access/testing`: `expectToolCatalogMatchesServer(config, createServer)` and
20
+ `getToolCatalogDifferences`. Catalog tools take optional localized `title` and `example`.
21
+ - `DEFAULT_MCP_ACCESS_ROUTES` from the root entry; the manifest uses it.
22
+ - `issueToken(previous, form, { beforeIssue })` (`/next`) for a gate before a token is issued, with the codes
23
+ `mcp-access.issue_refused` and `mcp-access.write_refused` (copy in en and pl); `McpAccessPage` takes an
24
+ optional `issueAction`. `issueTokenAction` is unchanged.
25
+ - `pruneMcpAccess(ctx)` (`/server`) and the `softure-mcp prune` command (bin, `/cli`) for a daily job such as a
26
+ deploy `maintain` hook.
27
+
28
+ ## 0.1.12
29
+
30
+ - Without a `resolveAppOrigin` option, the app origin comes from core's `resolveAppOrigin` (#311): a listed origin
31
+ from the config's `origins` block the request was sent to, else `appOrigin`; `origins.trustRequestHost` replaces
32
+ `resolveAppOrigin: readRequestOrigin`. `readRequestHost` and `readRequestOrigin` are core's (`readRequestOrigin`
33
+ answers `null` for a host that is no host). The consent decision still reads `Host` only. Discovery documents vary
34
+ on `x-forwarded-host` too.
35
+ - `getRequestOrigins(config, path)` is exported from `@softure-ai/mcp-access/next`. Needs `@softure-ai/core` 0.1.8.
36
+ - `MCP_RATE_LIMIT_BUCKETS` declares `key: "ip"` on `mcp` and `mcp-oauth` (security 0.1.8 bucket kinds).
37
+ - Requires `@softure-ai/security` `^0.1.8`: earlier versions refuse the `key` field.
38
+
7
39
  ## 0.1.11
8
40
 
9
41
  - The consent decision accepts a form whose `Origin` host is the host the request was sent to
package/README.md CHANGED
@@ -38,6 +38,9 @@ limit check made race-free.
38
38
  count against `maxTokensPerUser` and are not listed with the hand-issued ones.
39
39
  - Tokens an app issued before adopting the module keep working through `legacyTokenPattern`.
40
40
  - Server functions for scripts and other hosts, and a health check for `GET /api/health`.
41
+ - For the app's own server: tool result helpers with a safe error boundary and server actions as write tools
42
+ (`/server`), a stdio entry for local assistants (`/stdio`), a catalog parity check (`/testing`), a gate before a
43
+ token is issued (`issueToken`, `/next`) and the `softure-mcp prune` command for a daily cleanup (§4, §5).
41
44
 
42
45
  ## 2. Installation
43
46
 
@@ -74,7 +77,7 @@ mcpAccess({
74
77
  | Option | Type | Default | Meaning |
75
78
  | --- | --- | --- | --- |
76
79
  | `serverName` | `string` | required | The name clients list the server under (lowercase, digits, inner dashes; no quotes needed in a shell). |
77
- | `tools` | `{ name, access: "read" \| "write", description: { en, pl? } }[]` | `[]` | The catalog the token page shows. Keep it next to the factory; the example app tests that both list the same tools. |
80
+ | `tools` | `{ name, access: "read" \| "write", description: { en, pl? }, title?: { en, pl? }, example?: { en, pl? } }[]` | `[]` | The catalog the token page shows. `title` (a display name) and `example` (a request a person could make) are for agent docs that read the catalog. Keep it next to the factory; `expectToolCatalogMatchesServer` (`/testing`) checks that both list the same tools. |
78
81
  | `allowWrites` | `boolean` | `false` | Lets write tokens be issued and used. Off, the page offers no write tokens and every token reads only. |
79
82
  | `tokenLifetimeDays` | `integer` 1–365 | `90` | How long a hand-issued token works. There is no renewal: the user issues a new one. |
80
83
  | `maxTokensPerUser` | `integer` 1–100 | `20` | Unexpired tokens one account may hold. Counted under a per-user lock, so parallel requests cannot overshoot. |
@@ -86,9 +89,9 @@ mcpAccess({
86
89
  | `oauth.authorizationCodeLifetimeMinutes` | `integer` 1–10 | `10` | Lifetime of an authorization code (single use). |
87
90
  | `oauth.maxBodyBytes` | `integer` 1024–1048576 | `16384` | The largest body the registration, token and consent decision routes read. Larger: `413` (`invalid_request` on registration and token), before it fills memory. Real bodies stay under 4 KiB. |
88
91
  | `oauth.metadata` | `{ authorizationServer?, protectedResource? }`, each an object or `(origins) => object` | — | Extra keys of the discovery documents (`jwks_uri`, `service_documentation`, `resource_documentation`, `agent_auth`…). Generated keys win; a static object that sets one is refused at startup. The protected resource document takes the app's `resource_name` (default: `serverName`). |
89
- | `resolveAppOrigin` | `(request) => string \| null` | — | The app origin of one request, when it is not the fixed `appOrigin` (an image built once and served elsewhere, a proxy). `readRequestOrigin` reads it from `Host`. Null keeps `appOrigin`; anything but a bare http(s) origin throws. |
92
+ | `resolveAppOrigin` | `(request) => string \| null` | — | The app origin of one request, ahead of the config's `origins` block (prefer that block: every module reads it). `readRequestOrigin` reads it from `Host`. Null falls back to the block's rule; anything but a bare http(s) origin throws. |
90
93
  | `resourceOrigins` | `string[]` (≤ 16 http(s) origins) | `[]` | Other public hosts of the app, e.g. the apex next to `app.`: the root protected resource metadata asked on one of them names it as `resource`, and the OAuth endpoints accept it as `resource`. |
91
- | `routes` | `{ page?, endpoint?, oauthConsent?, oauthDecision?, oauthToken?, oauthRegister? }` | `/account/mcp`, `/api/mcp`, `/oauth/authorize`, `/api/oauth/authorize`, `/api/oauth/token`, `/api/oauth/register` | Move the page, the endpoint or the OAuth paths; URLs in the setup and the metadata are the app origin (`appOrigin`, or `resolveAppOrigin`'s) + the path. |
94
+ | `routes` | `{ page?, endpoint?, oauthConsent?, oauthDecision?, oauthToken?, oauthRegister? }` | `DEFAULT_MCP_ACCESS_ROUTES` (root entry): `/account/mcp`, `/api/mcp`, `/oauth/authorize`, `/api/oauth/authorize`, `/api/oauth/token`, `/api/oauth/register` | Move the page, the endpoint or the OAuth paths; URLs in the setup and the metadata are the app origin (`appOrigin`, or `resolveAppOrigin`'s) + the path. |
92
95
  | `messages` | partial `en` / `pl` | — | Copy overrides. |
93
96
 
94
97
  `allowWrites` is a deploy decision, so read it from the environment rather than hard-coding it:
@@ -138,6 +141,112 @@ Answers to 2025-era clients come as one SSE `message` event, to 2026-07-28 clien
138
141
  mid-call notifications (progress, logging) are dropped. Keep `/api/mcp` out of a proxy guard that
139
142
  redirects to the login page: the endpoint authenticates by token, not by cookie.
140
143
 
144
+ ### Writing tools
145
+
146
+ `/server` answers tools safely, so a failure never hands the assistant a query or an error's text:
147
+
148
+ ```ts
149
+ import { actionTool, toolResult, withToolErrors } from "@softure-ai/mcp-access/server";
150
+
151
+ server.registerTool("list_orders", { description: "Lists your orders." }, () =>
152
+ withToolErrors(() => findOrders(db, userId), { label: "list_orders", hints: { "orders.locked": "The shop is closed for stock-taking." } }),
153
+ );
154
+ if (canWrite) {
155
+ server.registerTool("place_order", { inputSchema: z.object({ sku: z.string(), quantity: z.number() }) }, actionTool(
156
+ (form) => placeOrderAction({ status: "idle" }, form),
157
+ { message: "Order placed.", hints: { "orders.out_of_stock": "That item is out of stock." } },
158
+ ));
159
+ }
160
+ ```
161
+
162
+ - `toolResult(value)`: a string as text, anything else as JSON. `toolError(message)`: the same with `isError`.
163
+ - `withToolErrors(work, { hints?, label? })`: a `CallToolResult` passes; a result value is unwrapped (`ok` → its
164
+ value, else the hint for its error code, else the code); any other value becomes `toolResult`. A thrown
165
+ `PublicError` (core) gives its message. Anything else is logged by kind only (`errorLogLabel`, never the SQL or
166
+ its parameters) and answered with the hint for its `safeError` code (`core.database_failed`, `core.unexpected`),
167
+ else a fixed English text.
168
+ - `actionTool(handler, { message?, hints?, label? })`: the server action behind a form as a write tool, so both
169
+ share one validation path. The arguments become `FormData` (numbers as text, `true` as `"on"`, `false` and `null`
170
+ left out, arrays one entry per item, dates as ISO, objects as JSON). A `{ ok: false, error }` or
171
+ `{ status: "error", error }` state is a tool error through the hints; otherwise the answer is `message` (text or
172
+ `(state) => text`), else the state as JSON.
173
+
174
+ ### Stdio
175
+
176
+ A local assistant on the machine that holds the database can run the same factory over stdio, without a token:
177
+
178
+ ```ts
179
+ // scripts/mcp-stdio.ts
180
+ import { serveMcpStdio } from "@softure-ai/mcp-access/stdio";
181
+ import config from "../softure.config";
182
+ import { createServer } from "../lib/mcp-server";
183
+
184
+ const started = await serveMcpStdio({ config, createServer, allowWrites: process.env.MCP_ALLOW_WRITES === "1" });
185
+ if (!started.ok) {
186
+ console.error(started.error.message);
187
+ process.exitCode = 1;
188
+ }
189
+ ```
190
+
191
+ ```bash
192
+ claude mcp add acme -e MCP_USER_ID=<account id> -- npx tsx --import @softure-ai/mcp-access/stdio/register scripts/mcp-stdio.ts
193
+ ```
194
+
195
+ - The account: `MCP_USER_ID` (or `userIdEnv`) when set, which must name an account; else the only account in the
196
+ database. With several accounts and no id the entry refuses (`mcp-access.stdio_account_ambiguous`): it never
197
+ picks one.
198
+ - `canWrite` needs `allowWrites: true` here and the app's `allowWrites`. The token id is `"stdio"`.
199
+ - The database opens like a package command's (`database.handle`, else `database.url`) and closes with the
200
+ connection. Messages go to stderr; stdout is the protocol.
201
+ - `@softure-ai/mcp-access/stdio/register` (`--import`) resolves `server-only` to an empty module, so a factory
202
+ written for Next runs in plain Node. It needs Node 22.15 or later (`module.registerHooks`).
203
+
204
+ ### Catalog parity
205
+
206
+ ```ts
207
+ // lib/mcp-server.test.ts
208
+ import { expectToolCatalogMatchesServer } from "@softure-ai/mcp-access/testing";
209
+
210
+ it("lists the same tools as the server", () => expectToolCatalogMatchesServer(config, createServer));
211
+ ```
212
+
213
+ It lists the server's tools for a read-only and a write identity and throws naming every configured tool the
214
+ server lacks, every registered tool the catalog lacks, and every write tool a read-only token gets.
215
+ `getToolCatalogDifferences` returns the same as lists.
216
+
217
+ ### Gating token issue
218
+
219
+ An app that checks something before a token is issued (a plan that includes write access) calls `issueToken` from
220
+ its own server action instead of copying `issueTokenAction`:
221
+
222
+ ```ts
223
+ // app/account/mcp/actions.ts
224
+ "use server";
225
+ import { err, ok } from "@softure-ai/core";
226
+ import { issueToken } from "@softure-ai/mcp-access/next";
227
+ import type { IssueTokenFormState } from "@softure-ai/mcp-access";
228
+
229
+ export async function issueGatedToken(previous: IssueTokenFormState, form: FormData) {
230
+ return issueToken(previous, form, {
231
+ beforeIssue: async ({ userId, canWrite }) => (canWrite && !(await hasWritePlan(userId)) ? err("mcp-access.write_refused") : ok()),
232
+ });
233
+ }
234
+ ```
235
+
236
+ ```tsx
237
+ // app/account/mcp/page.tsx
238
+ import { McpAccessPage } from "@softure-ai/mcp-access/next";
239
+ import { issueGatedToken } from "./actions";
240
+
241
+ export default function Page() {
242
+ return <McpAccessPage issueAction={issueGatedToken} />;
243
+ }
244
+ ```
245
+
246
+ The gate runs after the session check and before anything is issued. `mcp-access.write_refused` and
247
+ `mcp-access.issue_refused` have copy in `messages.errors["mcp-access"]` (override it per app); a gate that throws
248
+ becomes a `safeError` code. The gate stays in the app's code, out of `softure.config.ts`.
249
+
141
250
  `TokenManager` (`@softure-ai/mcp-access/ui`) takes the actions `issueTokenAction` and
142
251
  `revokeTokenAction` (`/next`) as props for an app that composes its own page, and `grants` with
143
252
  `revokeGrantAction` for the connected apps list.
@@ -174,17 +283,20 @@ TypeScript's `**` skips dot folders: with an `include` list in `tsconfig.json`,
174
283
  `"app/.well-known/**/*.ts"`.
175
284
 
176
285
  **Origins.** Every OAuth URL (issuer, endpoints, `resource`, `resource_metadata`, `iss`) and the
177
- decision's `Origin` check use the app origin of the request: `appOrigin`, unless
178
- `resolveAppOrigin` answers. The decision also accepts a form posted from the host the request was
286
+ decision's `Origin` check use the app origin of the request: core's `resolveAppOrigin` (a listed
287
+ origin from the config's `origins` block the request was sent to, else `appOrigin`; core README,
288
+ "Request origins"), unless the module's `resolveAppOrigin` option answers. The decision also accepts a form posted from the host the request was
179
289
  sent to, so an image with a fixed public origin run under another host (a release pipeline's
180
290
  browser tests on `http://localhost:6510`) completes the consent without a resolver. The discovery
181
291
  routes read the request, so Next never renders them at build time, and they answer with
182
- `Vary: host, x-forwarded-proto`.
292
+ `Vary: host, x-forwarded-host, x-forwarded-proto`. `getRequestOrigins(config, path)` from
293
+ `@softure-ai/mcp-access/next` gives a page or a server action the same origins from Next's `headers()`.
183
294
 
184
295
  - *An image built once and served under another origin* (a test stack on another port, a
185
- production image against a local stack): `resolveAppOrigin: process.env.APP_ORIGIN ? undefined :
186
- readRequestOrigin`. `readRequestOrigin` trusts `Host` and `X-Forwarded-Proto`, so use it only
187
- behind a proxy that passes `Host` through and refuses hosts it does not serve. The consent page,
296
+ production image against a local stack): `origins: { trustRequestHost: !process.env.APP_ORIGIN }`
297
+ in the config (the older `resolveAppOrigin: readRequestOrigin` option does the same for this
298
+ module only). It trusts `Host` and `X-Forwarded-Proto`, so use it only behind a proxy that passes
299
+ `Host` through and refuses hosts it does not serve. The consent page,
188
300
  its decision and the token endpoint then agree on the origin the browser used.
189
301
  - *A second public host* (product on `https://app.example.com`, site on `https://example.com`):
190
302
  `resourceOrigins: ["https://example.com"]`, and route that host's `/.well-known/oauth-*` to the
@@ -223,7 +335,7 @@ whose last argument they are; `OAuthClientRow` is exported from the package root
223
335
  | `last_used_at` | Written on verification at most once a minute; informational only. |
224
336
 
225
337
  Issuing deletes the issuer's expired tokens; `pruneAccessTokens(ctx)` (`/server`) deletes every
226
- expired token, for a scheduled job. Rollback: in the migration header.
338
+ expired token, for a scheduled job (see "Scheduled pruning" below). Rollback: in the migration header.
227
339
 
228
340
  `migrations/0002_create_oauth_grants.sql` creates `mcp.oauth_clients` (client id, name, redirect
229
341
  URIs, auth method, sha256 of a confidential client's secret), `mcp.oauth_authorization_codes`
@@ -233,6 +345,18 @@ expiry, last use), and adds `access_tokens.grant_id` (null for hand-issued token
233
345
  with the account. `pruneOAuthRecords(ctx)` deletes expired codes, grants whose refresh token has
234
346
  expired (with their access tokens) and clients registered over a day ago that hold no grant.
235
347
 
348
+ ### Scheduled pruning
349
+
350
+ `pruneMcpAccess(ctx)` (`/server`) runs both prunes and counts what each removed. The `softure-mcp prune` command
351
+ runs it with the app's config (loaded like `softure migrate`, or from an app script through `runMcpAccessCli` from
352
+ `/cli`) and prints one line. Run it daily, e.g. as a `maintain` hook of `@softure-ai/deploy`:
353
+
354
+ ```json
355
+ { "hooks": { "maintain": [{ "name": "mcp-prune", "compose": ["exec", "-T", "app", "npx", "softure-mcp", "prune"] }] } }
356
+ ```
357
+
358
+ An app that pruned inline on every token exchange can drop that code.
359
+
236
360
  ### Adopting an app's own tables
237
361
 
238
362
  An app that ran its own `public.access_tokens` (with `grant_id`) and `oauth_clients`,
@@ -277,7 +401,8 @@ the `tools` option, per locale.
277
401
 
278
402
  ## 10. Hooks
279
403
 
280
- The MCP server factory passed to `createMcpRoute` is the module's only hook. `/server` also exports
404
+ The MCP server factory passed to `createMcpRoute` (and to `serveMcpStdio`) and the `beforeIssue` gate of
405
+ `issueToken` are the module's hooks. `/server` also exports
281
406
  `createMcpEndpoint({ createServer })`, a `(ctx, request) => Response` for hosts other than Next, and
282
407
  `issueAccessToken`, `listAccessTokens`, `revokeAccessToken`, `verifyAccessToken` and
283
408
  `pruneAccessTokens` for scripts. For OAuth: `listOAuthGrants`, `revokeOAuthGrant`,
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../../src/cli/bin.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ // The `softure-mcp` executable. All logic lives in command.ts and run.ts, which the tests call directly.
3
+ import { runMcpAccessCommand } from "./command.js";
4
+ process.exitCode = await runMcpAccessCommand({ argv: process.argv.slice(2), cwd: process.cwd() });
5
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../../src/cli/bin.ts"],"names":[],"mappings":";AACA,yGAAyG;AACzG,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,CAAC,QAAQ,GAAG,MAAM,mBAAmB,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { type CliOutput } from "./run.js";
2
+ export interface RunMcpAccessCommandOptions {
3
+ readonly argv: readonly string[];
4
+ readonly cwd: string;
5
+ readonly output?: CliOutput;
6
+ }
7
+ /** Loads the config (unless only help is asked for) and runs `runMcpAccessCli`. */
8
+ export declare function runMcpAccessCommand(options: RunMcpAccessCommandOptions): Promise<number>;
9
+ //# sourceMappingURL=command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/cli/command.ts"],"names":[],"mappings":"AAIA,OAAO,EAA8D,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AAEtG,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;CAC7B;AASD,mFAAmF;AACnF,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB9F"}
@@ -0,0 +1,30 @@
1
+ // The `softure-mcp` bin. It loads the app's config through `@softure-ai/core/cli`, like `softure-mail`
2
+ // (@softure-ai/mailing): the file must be loadable by Node itself (`.js`/`.mjs`, or `.ts` where Node
3
+ // strips types). Anything else uses an app script with `runMcpAccessCli` instead (see run.ts).
4
+ import { loadAppConfig, takeConfigOption } from "@softure-ai/core/cli";
5
+ import { EXIT_FAILED, EXIT_USAGE, MCP_ACCESS_USAGE, runMcpAccessCli } from "./run.js";
6
+ const APP_SCRIPT = { runner: "runMcpAccessCli", packageName: "@softure-ai/mcp-access" };
7
+ const consoleOutput = {
8
+ log: (line) => console.log(line),
9
+ error: (line) => console.error(line),
10
+ };
11
+ /** Loads the config (unless only help is asked for) and runs `runMcpAccessCli`. */
12
+ export async function runMcpAccessCommand(options) {
13
+ const output = options.output ?? consoleOutput;
14
+ const taken = takeConfigOption(options.argv);
15
+ if (!taken.ok) {
16
+ output.error(`softure-mcp: ${taken.problem}`);
17
+ output.error(MCP_ACCESS_USAGE);
18
+ return EXIT_USAGE;
19
+ }
20
+ const { configPath, argv } = taken;
21
+ if (argv[0] !== "prune")
22
+ return runMcpAccessCli({ argv, output });
23
+ const loaded = await loadAppConfig({ cwd: options.cwd, configPath, appScript: APP_SCRIPT });
24
+ if (!loaded.ok) {
25
+ output.error(`softure-mcp: ${loaded.problem}`);
26
+ return EXIT_FAILED;
27
+ }
28
+ return runMcpAccessCli({ config: loaded.config, argv, output });
29
+ }
30
+ //# sourceMappingURL=command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.js","sourceRoot":"","sources":["../../src/cli/command.ts"],"names":[],"mappings":"AAAA,uGAAuG;AACvG,qGAAqG;AACrG,+FAA+F;AAC/F,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAkB,MAAM,UAAU,CAAC;AAQtG,MAAM,UAAU,GAAG,EAAE,MAAM,EAAE,iBAAiB,EAAE,WAAW,EAAE,wBAAwB,EAAE,CAAC;AAExF,MAAM,aAAa,GAAc;IAC/B,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;IAChC,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;CACrC,CAAC;AAEF,mFAAmF;AACnF,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,OAAmC;IAC3E,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,aAAa,CAAC;IAC/C,MAAM,KAAK,GAAG,gBAAgB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;QACd,MAAM,CAAC,KAAK,CAAC,gBAAgB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC9C,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;QAC/B,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;IACnC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO;QAAE,OAAO,eAAe,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAElE,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC;IAC5F,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,gBAAgB,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;QAC/C,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,OAAO,eAAe,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;AAClE,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { runMcpAccessCommand, type RunMcpAccessCommandOptions } from "./command.js";
2
+ export { EXIT_FAILED, EXIT_OK, EXIT_USAGE, MCP_ACCESS_USAGE, runMcpAccessCli, type CliOutput, type RunMcpAccessCliOptions } from "./run.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,KAAK,0BAA0B,EAAE,MAAM,cAAc,CAAC;AACpF,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE,KAAK,SAAS,EAAE,KAAK,sBAAsB,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,4 @@
1
+ // `@softure-ai/mcp-access/cli`: the `softure-mcp` command for app scripts and container bundles.
2
+ export { runMcpAccessCommand } from "./command.js";
3
+ export { EXIT_FAILED, EXIT_OK, EXIT_USAGE, MCP_ACCESS_USAGE, runMcpAccessCli } from "./run.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,OAAO,EAAE,mBAAmB,EAAmC,MAAM,cAAc,CAAC;AACpF,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAA+C,MAAM,UAAU,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { type Clock, type SoftureConfig, type SoftureDatabaseConfig } from "@softure-ai/core";
2
+ import { type CommandDatabase } from "@softure-ai/db";
3
+ export interface CliOutput {
4
+ readonly log: (line: string) => void;
5
+ readonly error: (line: string) => void;
6
+ }
7
+ export interface RunMcpAccessCliOptions {
8
+ /** The app's config; only help needs none. */
9
+ readonly config?: SoftureConfig;
10
+ /** The arguments after the executable, e.g. `["prune"]`. */
11
+ readonly argv: readonly string[];
12
+ readonly output?: CliOutput;
13
+ /** Default: the wall clock. */
14
+ readonly clock?: Clock;
15
+ /** Default: the config's `database.handle`, else a connection on `database.url`. */
16
+ readonly openDatabase?: (database: SoftureDatabaseConfig) => Promise<CommandDatabase>;
17
+ }
18
+ export declare const EXIT_OK = 0;
19
+ export declare const EXIT_FAILED = 1;
20
+ export declare const EXIT_USAGE = 2;
21
+ export declare const MCP_ACCESS_USAGE = "Usage:\n softure-mcp prune deletes expired access tokens, authorization codes, grants and abandoned clients";
22
+ /** Runs one command and answers its exit code. */
23
+ export declare function runMcpAccessCli(options: RunMcpAccessCliOptions): Promise<number>;
24
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"AAQA,OAAO,EAA8B,KAAK,KAAK,EAAE,KAAK,aAAa,EAAE,KAAK,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC1H,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAG3E,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC;AAED,MAAM,WAAW,sBAAsB;IACrC,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC;IAChC,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;IAC5B,+BAA+B;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;IACvB,oFAAoF;IACpF,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,qBAAqB,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;CACvF;AAED,eAAO,MAAM,OAAO,IAAI,CAAC;AACzB,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,UAAU,IAAI,CAAC;AAE5B,eAAO,MAAM,gBAAgB,oHAC2E,CAAC;AAOzG,kDAAkD;AAClD,wBAAsB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,MAAM,CAAC,CAatF"}
@@ -0,0 +1,58 @@
1
+ // `softure-mcp`: the module's maintenance command. `prune` deletes expired access tokens and dead OAuth
2
+ // records (`pruneMcpAccess`), for a daily job such as a deploy `maintain` hook. The app passes its
3
+ // validated config, so the same function serves the bin and an app script a bundler packs:
4
+ //
5
+ // // scripts/mcp.ts
6
+ // import { runMcpAccessCli } from "@softure-ai/mcp-access/cli";
7
+ // import config from "../softure.config";
8
+ // process.exitCode = await runMcpAccessCli({ config, argv: process.argv.slice(2) });
9
+ import { errorLogLabel, systemClock } from "@softure-ai/core";
10
+ import { openCommandDatabase } from "@softure-ai/db";
11
+ import { pruneMcpAccess } from "../server/prune.js";
12
+ export const EXIT_OK = 0;
13
+ export const EXIT_FAILED = 1;
14
+ export const EXIT_USAGE = 2;
15
+ export const MCP_ACCESS_USAGE = `Usage:
16
+ softure-mcp prune deletes expired access tokens, authorization codes, grants and abandoned clients`;
17
+ const consoleOutput = {
18
+ log: (line) => console.log(line),
19
+ error: (line) => console.error(line),
20
+ };
21
+ /** Runs one command and answers its exit code. */
22
+ export async function runMcpAccessCli(options) {
23
+ const output = options.output ?? consoleOutput;
24
+ const [command, ...rest] = options.argv;
25
+ if (command === undefined || command === "--help" || command === "help") {
26
+ output.log(MCP_ACCESS_USAGE);
27
+ return EXIT_OK;
28
+ }
29
+ if (command !== "prune" || rest.length > 0) {
30
+ output.error(command === "prune" ? `softure-mcp prune: unexpected argument "${rest[0] ?? ""}"` : `softure-mcp: unknown command "${command}"`);
31
+ output.error(MCP_ACCESS_USAGE);
32
+ return EXIT_USAGE;
33
+ }
34
+ return prune(options, output);
35
+ }
36
+ async function prune(options, output) {
37
+ const database = options.config?.database ?? null;
38
+ if (options.config === undefined || database === null) {
39
+ output.error("softure-mcp prune: the config has no database; set database.url in softure.config");
40
+ return EXIT_FAILED;
41
+ }
42
+ let opened;
43
+ try {
44
+ opened = await (options.openDatabase ?? ((settings) => openCommandDatabase(settings, { max: 1 })))(database);
45
+ const removed = await pruneMcpAccess({ db: opened.handle.db, clock: options.clock ?? systemClock, config: options.config });
46
+ output.log(`softure-mcp prune: removed ${String(removed.accessTokens)} access tokens, ${String(removed.codes)} authorization codes, ${String(removed.grants)} grants, ${String(removed.clients)} clients`);
47
+ return EXIT_OK;
48
+ }
49
+ catch (error) {
50
+ // The kind only: a driver message can carry the database address or credentials.
51
+ output.error(`softure-mcp prune: failed: ${errorLogLabel(error)}`);
52
+ return EXIT_FAILED;
53
+ }
54
+ finally {
55
+ await opened?.close();
56
+ }
57
+ }
58
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/cli/run.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,9 @@
1
1
  import type { CoreErrorCode } from "@softure-ai/core";
2
2
  export type McpAccessErrorCode = "mcp-access.name_required" | "mcp-access.name_too_long" | "mcp-access.token_limit_reached" | "mcp-access.token_not_found" | "mcp-access.grant_not_found";
3
- /** Every code the token page can show: its own, a missing session and the generic ones. */
4
- export type TokenFormErrorCode = McpAccessErrorCode | "auth.unauthenticated" | CoreErrorCode;
3
+ /** What an app's `beforeIssue` gate may answer instead of letting a token be issued. */
4
+ export type McpIssueRefusalCode = "mcp-access.issue_refused" | "mcp-access.write_refused";
5
+ /** Every code the token page can show: its own, a gate's refusal, a missing session and the generic ones. */
6
+ export type TokenFormErrorCode = McpAccessErrorCode | McpIssueRefusalCode | "auth.unauthenticated" | CoreErrorCode;
5
7
  /** What the app's MCP server factory receives for one request. */
6
8
  export interface McpServerIdentity {
7
9
  /** The auth user id of the token's owner: every query of the server is scoped to it. */
@@ -1 +1 @@
1
- {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ export declare const MCP_RATE_LIMIT_BUCKETS: {
8
8
  readonly mcp: {
9
9
  readonly limit: 200;
10
10
  readonly windowMinutes: 15;
11
+ readonly key: "ip";
11
12
  };
12
13
  /**
13
14
  * OAuth registration (per address) and token requests (per address and client id), when
@@ -17,6 +18,7 @@ export declare const MCP_RATE_LIMIT_BUCKETS: {
17
18
  readonly "mcp-oauth": {
18
19
  readonly limit: 60;
19
20
  readonly windowMinutes: 15;
21
+ readonly key: "ip";
20
22
  };
21
23
  };
22
24
  /**
@@ -24,12 +26,12 @@ export declare const MCP_RATE_LIMIT_BUCKETS: {
24
26
  * `mcpAccess({ serverName: "acme", tools: [{ name: "list_orders", access: "read", description: { en: "Lists your orders." } }] })`.
25
27
  */
26
28
  export declare const mcpAccess: import("@softure-ai/core").ModuleFactory<{
27
- page: string;
28
- endpoint: string;
29
- oauthConsent: string;
30
- oauthDecision: string;
31
- oauthToken: string;
32
- oauthRegister: string;
29
+ page: "/account/mcp";
30
+ endpoint: "/api/mcp";
31
+ oauthConsent: "/oauth/authorize";
32
+ oauthDecision: "/api/oauth/authorize";
33
+ oauthToken: "/api/oauth/token";
34
+ oauthRegister: "/api/oauth/register";
33
35
  }, {
34
36
  page: {
35
37
  title: string;
@@ -124,6 +126,8 @@ export declare const mcpAccess: import("@softure-ai/core").ModuleFactory<{
124
126
  token_limit_reached: string;
125
127
  token_not_found: string;
126
128
  grant_not_found: string;
129
+ issue_refused: string;
130
+ write_refused: string;
127
131
  };
128
132
  auth: {
129
133
  unauthenticated: string;
@@ -145,6 +149,14 @@ export declare const mcpAccess: import("@softure-ai/core").ModuleFactory<{
145
149
  en: "en";
146
150
  pl: "pl";
147
151
  }> & import("zod/v4/core").$partial, import("zod").ZodString>;
152
+ title: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodEnum<{
153
+ en: "en";
154
+ pl: "pl";
155
+ }> & import("zod/v4/core").$partial, import("zod").ZodString>>;
156
+ example: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodEnum<{
157
+ en: "en";
158
+ pl: "pl";
159
+ }> & import("zod/v4/core").$partial, import("zod").ZodString>>;
148
160
  }, import("zod/v4/core").$strict>>>;
149
161
  allowWrites: import("zod").ZodDefault<import("zod").ZodBoolean>;
150
162
  tokenLifetimeDays: import("zod").ZodDefault<import("zod").ZodInt>;
@@ -166,9 +178,10 @@ export declare const mcpAccess: import("@softure-ai/core").ModuleFactory<{
166
178
  }, import("zod/v4/core").$strict>>;
167
179
  }, import("zod/v4/core").$strict>>;
168
180
  export { getAuthorizationHeader, getClaudeCodeCommand, getClaudeCodeLink, getDesktopConfig, getJsonConfig, getMcpClientSetup, type McpClientSetupInput, } from "./client-setup.js";
169
- export type { AccessTokenStatus, AccessTokenView, IssuedToken, IssueTokenFormState, McpAccessErrorCode, McpClientSetup, McpServerIdentity, OAuthGrantView, RevokeGrantFormState, RevokeTokenFormState, TokenFormErrorCode, } from "./contract.js";
181
+ export type { AccessTokenStatus, AccessTokenView, IssuedToken, IssueTokenFormState, McpAccessErrorCode, McpIssueRefusalCode, McpClientSetup, McpServerIdentity, OAuthGrantView, RevokeGrantFormState, RevokeTokenFormState, TokenFormErrorCode, } from "./contract.js";
170
182
  export { getTokenErrorMessage, mcpAccessMessages, type McpAccessMessages } from "./messages/index.js";
171
183
  export { MAX_PRESENTED_TOKEN_LENGTH, MAX_TOKEN_NAME_LENGTH, SERVER_NAME_PATTERN, TOOL_NAME_PATTERN, GENERATED_AUTHORIZATION_SERVER_KEYS, GENERATED_PROTECTED_RESOURCE_KEYS, type McpAccessOptions, type McpAccessOptionsInput, type McpAppOriginResolver, type McpMetadataExtension, type McpOAuthOptions, type McpToolAccess, type McpToolDefinition, type McpToolDefinitionInput, } from "./options.js";
184
+ export { DEFAULT_MCP_ACCESS_ROUTES } from "./routes.js";
172
185
  export { readRequestOrigin, type McpOriginRequest, type McpOrigins } from "./origins.js";
173
186
  export { accessTokens, oauthAuthorizationCodes, oauthClients, oauthGrants, type OAuthClientRow, type OAuthTokenEndpointAuthMethod } from "./schema.js";
174
187
  export { getAccessTokenStatus, type AccessTokenStatusOptions } from "./token-status.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,SAAS,eAAe,CAAC;AAEtC;;;;GAIG;AACH,eAAO,MAAM,sBAAsB;;;;;IAEjC;;;;OAIG;;;;;CAEK,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kCAmCpB,CAAC;AAEH,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,KAAK,mBAAmB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,iBAAiB,EACjB,eAAe,EACf,WAAW,EACX,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACtG,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,mBAAmB,EACnB,iBAAiB,EACjB,mCAAmC,EACnC,iCAAiC,EACjC,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,GAC5B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AACzF,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,cAAc,EAAE,KAAK,4BAA4B,EAAE,MAAM,aAAa,CAAC;AACvJ,OAAO,EAAE,oBAAoB,EAAE,KAAK,wBAAwB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,SAAS,eAAe,CAAC;AAEtC;;;;GAIG;AACH,eAAO,MAAM,sBAAsB;;;;;;IAEjC;;;;OAIG;;;;;;CAE8D,CAAC;AAEpE;;;GAGG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kCA4BpB,CAAC;AAEH,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,EACjB,KAAK,mBAAmB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,iBAAiB,EACjB,eAAe,EACf,WAAW,EACX,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,KAAK,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACtG,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,mBAAmB,EACnB,iBAAiB,EACjB,mCAAmC,EACnC,iCAAiC,EACjC,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,GAC5B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AACzF,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,cAAc,EAAE,KAAK,4BAA4B,EAAE,MAAM,aAAa,CAAC;AACvJ,OAAO,EAAE,oBAAoB,EAAE,KAAK,wBAAwB,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@
5
5
  import { defineModule, resolveMigrationsDir } from "@softure-ai/core";
6
6
  import { mcpAccessMessages } from "./messages/index.js";
7
7
  import { mcpAccessOptionsSchema } from "./options.js";
8
+ import { DEFAULT_MCP_ACCESS_ROUTES } from "./routes.js";
8
9
  import { checkAccessTokensTable } from "./server/health.js";
9
10
  import { mcpAccessPrivacyContributor } from "./server/privacy.js";
10
11
  export const MODULE_ID = "mcp-access";
@@ -14,13 +15,13 @@ export const MODULE_ID = "mcp-access";
14
15
  * the traffic behind one address (several assistants share it), not for one token.
15
16
  */
16
17
  export const MCP_RATE_LIMIT_BUCKETS = {
17
- mcp: { limit: 200, windowMinutes: 15 },
18
+ mcp: { limit: 200, windowMinutes: 15, key: "ip" },
18
19
  /**
19
20
  * OAuth registration (per address) and token requests (per address and client id), when
20
21
  * `oauth.enabled`. Enough for an assistant platform refreshing many users' tokens per client,
21
22
  * too little for a script filling the clients table.
22
23
  */
23
- "mcp-oauth": { limit: 60, windowMinutes: 15 },
24
+ "mcp-oauth": { limit: 60, windowMinutes: 15, key: "ip" },
24
25
  };
25
26
  /**
26
27
  * Enables MCP access in `softure.config.ts` (after `security` and `auth`):
@@ -29,20 +30,13 @@ export const MCP_RATE_LIMIT_BUCKETS = {
29
30
  export const mcpAccess = defineModule({
30
31
  manifest: {
31
32
  id: MODULE_ID,
32
- version: "0.1.11",
33
+ version: "0.1.13",
33
34
  dependsOn: { security: "^0.1.0", auth: "^0.1.0" },
34
35
  dbSchema: "mcp",
35
36
  tables: ["access_tokens", "oauth_clients", "oauth_authorization_codes", "oauth_grants"],
36
37
  env: [],
37
38
  switches: [],
38
- routes: {
39
- page: "/account/mcp",
40
- endpoint: "/api/mcp",
41
- oauthConsent: "/oauth/authorize",
42
- oauthDecision: "/api/oauth/authorize",
43
- oauthToken: "/api/oauth/token",
44
- oauthRegister: "/api/oauth/register",
45
- },
39
+ routes: { ...DEFAULT_MCP_ACCESS_ROUTES },
46
40
  mount: [
47
41
  { kind: "page", path: "app/account/mcp/page.tsx", export: "McpAccessPage" },
48
42
  { kind: "route-handler", path: "app/api/mcp/route.ts", export: "createMcpRoute" },
@@ -65,6 +59,7 @@ export const mcpAccess = defineModule({
65
59
  export { getAuthorizationHeader, getClaudeCodeCommand, getClaudeCodeLink, getDesktopConfig, getJsonConfig, getMcpClientSetup, } from "./client-setup.js";
66
60
  export { getTokenErrorMessage, mcpAccessMessages } from "./messages/index.js";
67
61
  export { MAX_PRESENTED_TOKEN_LENGTH, MAX_TOKEN_NAME_LENGTH, SERVER_NAME_PATTERN, TOOL_NAME_PATTERN, GENERATED_AUTHORIZATION_SERVER_KEYS, GENERATED_PROTECTED_RESOURCE_KEYS, } from "./options.js";
62
+ export { DEFAULT_MCP_ACCESS_ROUTES } from "./routes.js";
68
63
  export { readRequestOrigin } from "./origins.js";
69
64
  export { accessTokens, oauthAuthorizationCodes, oauthClients, oauthGrants } from "./schema.js";
70
65
  export { getAccessTokenStatus } from "./token-status.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}