@softure-ai/mcp-access 0.1.12 → 0.1.14
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/CHANGELOG.md +34 -0
- package/README.md +141 -4
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +5 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/command.d.ts +9 -0
- package/dist/cli/command.d.ts.map +1 -0
- package/dist/cli/command.js +30 -0
- package/dist/cli/command.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +4 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/run.d.ts +24 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +58 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/contract.d.ts +19 -4
- package/dist/contract.d.ts.map +1 -1
- package/dist/index.d.ts +21 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -11
- package/dist/index.js.map +1 -1
- package/dist/messages/en.d.ts +2 -0
- package/dist/messages/en.d.ts.map +1 -1
- package/dist/messages/en.js +2 -0
- package/dist/messages/en.js.map +1 -1
- package/dist/messages/index.d.ts +4 -0
- package/dist/messages/index.d.ts.map +1 -1
- package/dist/messages/pl.d.ts.map +1 -1
- package/dist/messages/pl.js +2 -0
- package/dist/messages/pl.js.map +1 -1
- package/dist/next/actions.d.ts +2 -2
- package/dist/next/actions.d.ts.map +1 -1
- package/dist/next/actions.js +8 -47
- package/dist/next/actions.js.map +1 -1
- package/dist/next/format.d.ts +1 -1
- package/dist/next/format.d.ts.map +1 -1
- package/dist/next/format.js +3 -1
- package/dist/next/format.js.map +1 -1
- package/dist/next/index.d.ts +2 -1
- package/dist/next/index.d.ts.map +1 -1
- package/dist/next/index.js +1 -0
- package/dist/next/index.js.map +1 -1
- package/dist/next/issue-token.d.ts +22 -0
- package/dist/next/issue-token.d.ts.map +1 -0
- package/dist/next/issue-token.js +93 -0
- package/dist/next/issue-token.js.map +1 -0
- package/dist/next/pages.d.ts +9 -1
- package/dist/next/pages.d.ts.map +1 -1
- package/dist/next/pages.js +2 -2
- package/dist/next/pages.js.map +1 -1
- package/dist/options.d.ts +17 -0
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +13 -0
- package/dist/options.js.map +1 -1
- package/dist/routes.d.ts +15 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +17 -0
- package/dist/routes.js.map +1 -0
- package/dist/server/action-tool.d.ts +21 -0
- package/dist/server/action-tool.d.ts.map +1 -0
- package/dist/server/action-tool.js +51 -0
- package/dist/server/action-tool.js.map +1 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +3 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/prune.d.ts +8 -0
- package/dist/server/prune.d.ts.map +1 -0
- package/dist/server/prune.js +10 -0
- package/dist/server/prune.js.map +1 -0
- package/dist/server/tools.d.ts +25 -0
- package/dist/server/tools.d.ts.map +1 -0
- package/dist/server/tools.js +61 -0
- package/dist/server/tools.js.map +1 -0
- package/dist/stdio/index.d.ts +2 -0
- package/dist/stdio/index.d.ts.map +1 -0
- package/dist/stdio/index.js +4 -0
- package/dist/stdio/index.js.map +1 -0
- package/dist/stdio/register.d.ts +2 -0
- package/dist/stdio/register.d.ts.map +1 -0
- package/dist/stdio/register.js +10 -0
- package/dist/stdio/register.js.map +1 -0
- package/dist/stdio/serve.d.ts +45 -0
- package/dist/stdio/serve.d.ts.map +1 -0
- package/dist/stdio/serve.js +89 -0
- package/dist/stdio/serve.js.map +1 -0
- package/dist/testing/catalog.d.ts +15 -0
- package/dist/testing/catalog.d.ts.map +1 -0
- package/dist/testing/catalog.js +51 -0
- package/dist/testing/catalog.js.map +1 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +3 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/ui/token-manager.js +1 -1
- package/dist/ui/token-manager.js.map +1 -1
- package/module.json +1 -1
- package/package.json +31 -5
- package/src/cli/bin.ts +5 -0
- package/src/cli/command.ts +38 -0
- package/src/cli/index.ts +3 -0
- package/src/cli/run.ts +79 -0
- package/src/contract.ts +19 -4
- package/src/index.ts +10 -12
- package/src/messages/en.ts +2 -0
- package/src/messages/pl.ts +2 -0
- package/src/next/actions.ts +8 -45
- package/src/next/format.ts +2 -2
- package/src/next/index.ts +2 -1
- package/src/next/issue-token.ts +113 -0
- package/src/next/pages.tsx +11 -3
- package/src/options.ts +13 -0
- package/src/routes.ts +17 -0
- package/src/server/action-tool.ts +64 -0
- package/src/server/index.ts +3 -0
- package/src/server/prune.ts +14 -0
- package/src/server/tools.ts +72 -0
- package/src/stdio/index.ts +12 -0
- package/src/stdio/register.ts +11 -0
- package/src/stdio/serve.ts +133 -0
- package/src/testing/catalog.ts +74 -0
- package/src/testing/index.ts +2 -0
- package/src/ui/token-manager.tsx +1 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Catalog parity: the tools listed in softure.config.ts (the token page shows them, agent docs read
|
|
2
|
+
// them) against the tools the app's MCP server really registers, for a read-only and a write identity.
|
|
3
|
+
// Framework-free: `expectToolCatalogMatchesServer` throws, so it works in any test runner.
|
|
4
|
+
import { createMcpHandler } from "@modelcontextprotocol/server";
|
|
5
|
+
import { getMcpAccessOptions } from "../server/options.js";
|
|
6
|
+
const READER = { userId: "00000000-0000-4000-8000-000000000001", canWrite: false, tokenId: "catalog-check" };
|
|
7
|
+
const WRITER = { ...READER, canWrite: true };
|
|
8
|
+
/** Every difference between the configured catalog and the server, each list in the catalog's order, then the server's. */
|
|
9
|
+
export async function getToolCatalogDifferences(config, createServer) {
|
|
10
|
+
const catalog = getMcpAccessOptions(config).tools;
|
|
11
|
+
const forReaders = await listServerTools(createServer, READER);
|
|
12
|
+
const forWriters = await listServerTools(createServer, WRITER);
|
|
13
|
+
const listed = new Set(catalog.map((tool) => tool.name));
|
|
14
|
+
return {
|
|
15
|
+
missing: catalog.filter((tool) => !(tool.access === "read" ? forReaders.includes(tool.name) && forWriters.includes(tool.name) : forWriters.includes(tool.name))).map((tool) => tool.name),
|
|
16
|
+
unlisted: [...new Set([...forReaders, ...forWriters])].filter((name) => !listed.has(name)),
|
|
17
|
+
writeForReaders: catalog.filter((tool) => tool.access === "write" && forReaders.includes(tool.name)).map((tool) => tool.name),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** Throws a list of every difference, e.g. in a unit test next to the server. */
|
|
21
|
+
export async function expectToolCatalogMatchesServer(config, createServer) {
|
|
22
|
+
const differences = await getToolCatalogDifferences(config, createServer);
|
|
23
|
+
const problems = [
|
|
24
|
+
differences.missing.length > 0 ? `missing from the server: ${differences.missing.join(", ")}` : null,
|
|
25
|
+
differences.unlisted.length > 0 ? `registered but not in the catalog: ${differences.unlisted.join(", ")}` : null,
|
|
26
|
+
differences.writeForReaders.length > 0 ? `write tools a read-only token gets: ${differences.writeForReaders.join(", ")}` : null,
|
|
27
|
+
].filter((problem) => problem !== null);
|
|
28
|
+
if (problems.length > 0) {
|
|
29
|
+
throw new Error(`@softure-ai/mcp-access: the tool catalog in softure.config.ts does not match the MCP server: ${problems.join("; ")}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** The tool names a fresh server lists for the identity, asked the way the endpoint serves it. */
|
|
33
|
+
async function listServerTools(createServer, identity) {
|
|
34
|
+
const handler = createMcpHandler(() => createServer(identity), { responseMode: "json" });
|
|
35
|
+
const response = await handler.fetch(new Request("http://localhost/api/mcp", {
|
|
36
|
+
method: "POST",
|
|
37
|
+
headers: { "content-type": "application/json", accept: "application/json, text/event-stream" },
|
|
38
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/list", params: {} }),
|
|
39
|
+
}));
|
|
40
|
+
const answer = readAnswer(await response.text());
|
|
41
|
+
if (answer.result?.tools === undefined) {
|
|
42
|
+
throw new Error(`@softure-ai/mcp-access: listing the server's tools failed: ${typeof answer.error?.message === "string" ? answer.error.message : `HTTP ${String(response.status)}`}`);
|
|
43
|
+
}
|
|
44
|
+
return answer.result.tools.map((tool) => (typeof tool.name === "string" ? tool.name : "")).filter((name) => name !== "");
|
|
45
|
+
}
|
|
46
|
+
/** The JSON-RPC answer, sent as JSON or as one SSE `message` event. */
|
|
47
|
+
function readAnswer(body) {
|
|
48
|
+
const data = body.startsWith("event:") ? (body.split("\n").find((line) => line.startsWith("data: "))?.slice("data: ".length) ?? "null") : body;
|
|
49
|
+
return (JSON.parse(data) ?? {});
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog.js","sourceRoot":"","sources":["../../src/testing/catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,8BAA8B,EAAE,yBAAyB,EAAE,KAAK,sBAAsB,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,OAAO,EAAE,8BAA8B,EAAE,yBAAyB,EAA+B,MAAM,cAAc,CAAC"}
|
package/dist/ui/token-manager.js
CHANGED
|
@@ -37,7 +37,7 @@ function IssueForm({ action, allowWrites, maxTokens, messages, locale, slot, uns
|
|
|
37
37
|
// The id of the issued token the owner dismissed; a new one shows again.
|
|
38
38
|
const [dismissedId, setDismissedId] = useState(null);
|
|
39
39
|
const issued = state.status === "ok" && state.issued.id !== dismissedId ? state.issued : null;
|
|
40
|
-
return (_jsxs(_Fragment, { children: [_jsxs("section", { className: slot("section"), children: [_jsx("h2", { className: slot("heading"), children: messages.issue.title }), _jsxs("form", { action: formAction, className: slot("list"), children: [_jsx(TextField, { name: "name", label: messages.issue.name, hint: messages.issue.nameHint, maxLength: MAX_TOKEN_NAME_LENGTH, required: true, autoComplete: "off", locale: locale, unstyled: unstyled }), allowWrites ? _jsx(Checkbox, { name: "canWrite", label: messages.issue.canWrite, description: messages.issue.canWriteHint, unstyled: unstyled }) : null, _jsx("p", { className: slot("text"), children: formatMessage(messages.issue.limit, { max: maxTokens }) }), _jsx(FormError, { message: state.status === "error" ? getTokenErrorMessage(messages, state.error) : undefined, unstyled: unstyled }), _jsx(Button, { type: "submit", variant: "primary", pending: isPending, unstyled: unstyled, children: isPending ? messages.issue.pending : messages.issue.submit })] })] }), issued === null ? null : _jsx(IssuedTokenPanel, { issued: issued, onDone: () => setDismissedId(issued.id), messages: messages, slot: slot, unstyled: unstyled })] }));
|
|
40
|
+
return (_jsxs(_Fragment, { children: [_jsxs("section", { className: slot("section"), children: [_jsx("h2", { className: slot("heading"), children: messages.issue.title }), _jsxs("form", { action: formAction, className: slot("list"), children: [_jsx(TextField, { name: "name", label: messages.issue.name, hint: messages.issue.nameHint, maxLength: MAX_TOKEN_NAME_LENGTH, required: true, autoComplete: "off", locale: locale, unstyled: unstyled }), allowWrites ? _jsx(Checkbox, { name: "canWrite", label: messages.issue.canWrite, description: messages.issue.canWriteHint, unstyled: unstyled }) : null, _jsx("p", { className: slot("text"), children: formatMessage(messages.issue.limit, { max: maxTokens }) }), _jsx(FormError, { message: state.status === "error" ? (state.message ?? getTokenErrorMessage(messages, state.error)) : undefined, unstyled: unstyled }), _jsx(Button, { type: "submit", variant: "primary", pending: isPending, unstyled: unstyled, children: isPending ? messages.issue.pending : messages.issue.submit })] })] }), issued === null ? null : _jsx(IssuedTokenPanel, { issued: issued, onDone: () => setDismissedId(issued.id), messages: messages, slot: slot, unstyled: unstyled })] }));
|
|
41
41
|
}
|
|
42
42
|
function TokenItem({ row, action, messages, slot, unstyled, }) {
|
|
43
43
|
const [state, formAction, isPending] = useActionState(action, { status: "idle" });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-manager.js","sourceRoot":"","sources":["../../src/ui/token-manager.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"token-manager.js","sourceRoot":"","sources":["../../src/ui/token-manager.tsx"],"names":[],"mappings":"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"}
|
package/module.json
CHANGED
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softure-ai/mcp-access",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "SOFTURE AI MCP access module: hashed, scoped, expiring access tokens, a rate-limited Bearer endpoint around the app's MCP server factory, and a token page with client setup instructions for Next.js.",
|
|
3
|
+
"version": "0.1.14",
|
|
4
|
+
"description": "SOFTURE AI MCP access module: hashed, scoped, expiring access tokens, a rate-limited Bearer endpoint around the app's MCP server factory, and a token page with client setup instructions for Next.js; tool result helpers with a safe error boundary, a stdio entry, a catalog parity check and the softure-mcp prune command.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"sideEffects":
|
|
7
|
+
"sideEffects": [
|
|
8
|
+
"./dist/stdio/register.js",
|
|
9
|
+
"./src/stdio/register.ts"
|
|
10
|
+
],
|
|
8
11
|
"engines": {
|
|
9
12
|
"node": ">=22"
|
|
10
13
|
},
|
|
@@ -27,6 +30,9 @@
|
|
|
27
30
|
"access": "public",
|
|
28
31
|
"provenance": true
|
|
29
32
|
},
|
|
33
|
+
"bin": {
|
|
34
|
+
"softure-mcp": "./dist/cli/bin.js"
|
|
35
|
+
},
|
|
30
36
|
"exports": {
|
|
31
37
|
".": {
|
|
32
38
|
"@softure-ai/source": "./src/index.ts",
|
|
@@ -47,6 +53,26 @@
|
|
|
47
53
|
"@softure-ai/source": "./src/ui/index.ts",
|
|
48
54
|
"types": "./dist/ui/index.d.ts",
|
|
49
55
|
"default": "./dist/ui/index.js"
|
|
56
|
+
},
|
|
57
|
+
"./stdio": {
|
|
58
|
+
"@softure-ai/source": "./src/stdio/index.ts",
|
|
59
|
+
"types": "./dist/stdio/index.d.ts",
|
|
60
|
+
"default": "./dist/stdio/index.js"
|
|
61
|
+
},
|
|
62
|
+
"./stdio/register": {
|
|
63
|
+
"@softure-ai/source": "./src/stdio/register.ts",
|
|
64
|
+
"types": "./dist/stdio/register.d.ts",
|
|
65
|
+
"default": "./dist/stdio/register.js"
|
|
66
|
+
},
|
|
67
|
+
"./testing": {
|
|
68
|
+
"@softure-ai/source": "./src/testing/index.ts",
|
|
69
|
+
"types": "./dist/testing/index.d.ts",
|
|
70
|
+
"default": "./dist/testing/index.js"
|
|
71
|
+
},
|
|
72
|
+
"./cli": {
|
|
73
|
+
"@softure-ai/source": "./src/cli/index.ts",
|
|
74
|
+
"types": "./dist/cli/index.d.ts",
|
|
75
|
+
"default": "./dist/cli/index.js"
|
|
50
76
|
}
|
|
51
77
|
},
|
|
52
78
|
"scripts": {
|
|
@@ -54,9 +80,9 @@
|
|
|
54
80
|
},
|
|
55
81
|
"dependencies": {
|
|
56
82
|
"@softure-ai/auth": "^0.1.0",
|
|
57
|
-
"@softure-ai/core": "^0.1.
|
|
83
|
+
"@softure-ai/core": "^0.1.9",
|
|
58
84
|
"@softure-ai/db": "^0.1.0",
|
|
59
|
-
"@softure-ai/security": "^0.1.
|
|
85
|
+
"@softure-ai/security": "^0.1.8",
|
|
60
86
|
"zod": "^4.6.5"
|
|
61
87
|
},
|
|
62
88
|
"peerDependencies": {
|
package/src/cli/bin.ts
ADDED
|
@@ -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
|
+
|
|
5
|
+
process.exitCode = await runMcpAccessCommand({ argv: process.argv.slice(2), cwd: process.cwd() });
|
|
@@ -0,0 +1,38 @@
|
|
|
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, type CliOutput } from "./run.js";
|
|
6
|
+
|
|
7
|
+
export interface RunMcpAccessCommandOptions {
|
|
8
|
+
readonly argv: readonly string[];
|
|
9
|
+
readonly cwd: string;
|
|
10
|
+
readonly output?: CliOutput;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const APP_SCRIPT = { runner: "runMcpAccessCli", packageName: "@softure-ai/mcp-access" };
|
|
14
|
+
|
|
15
|
+
const consoleOutput: CliOutput = {
|
|
16
|
+
log: (line) => console.log(line),
|
|
17
|
+
error: (line) => console.error(line),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** Loads the config (unless only help is asked for) and runs `runMcpAccessCli`. */
|
|
21
|
+
export async function runMcpAccessCommand(options: RunMcpAccessCommandOptions): Promise<number> {
|
|
22
|
+
const output = options.output ?? consoleOutput;
|
|
23
|
+
const taken = takeConfigOption(options.argv);
|
|
24
|
+
if (!taken.ok) {
|
|
25
|
+
output.error(`softure-mcp: ${taken.problem}`);
|
|
26
|
+
output.error(MCP_ACCESS_USAGE);
|
|
27
|
+
return EXIT_USAGE;
|
|
28
|
+
}
|
|
29
|
+
const { configPath, argv } = taken;
|
|
30
|
+
if (argv[0] !== "prune") return runMcpAccessCli({ argv, output });
|
|
31
|
+
|
|
32
|
+
const loaded = await loadAppConfig({ cwd: options.cwd, configPath, appScript: APP_SCRIPT });
|
|
33
|
+
if (!loaded.ok) {
|
|
34
|
+
output.error(`softure-mcp: ${loaded.problem}`);
|
|
35
|
+
return EXIT_FAILED;
|
|
36
|
+
}
|
|
37
|
+
return runMcpAccessCli({ config: loaded.config, argv, output });
|
|
38
|
+
}
|
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// `@softure-ai/mcp-access/cli`: the `softure-mcp` command for app scripts and container bundles.
|
|
2
|
+
export { runMcpAccessCommand, type RunMcpAccessCommandOptions } from "./command.js";
|
|
3
|
+
export { EXIT_FAILED, EXIT_OK, EXIT_USAGE, MCP_ACCESS_USAGE, runMcpAccessCli, type CliOutput, type RunMcpAccessCliOptions } from "./run.js";
|
package/src/cli/run.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
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, type Clock, type SoftureConfig, type SoftureDatabaseConfig } from "@softure-ai/core";
|
|
10
|
+
import { openCommandDatabase, type CommandDatabase } from "@softure-ai/db";
|
|
11
|
+
import { pruneMcpAccess } from "../server/prune.js";
|
|
12
|
+
|
|
13
|
+
export interface CliOutput {
|
|
14
|
+
readonly log: (line: string) => void;
|
|
15
|
+
readonly error: (line: string) => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface RunMcpAccessCliOptions {
|
|
19
|
+
/** The app's config; only help needs none. */
|
|
20
|
+
readonly config?: SoftureConfig;
|
|
21
|
+
/** The arguments after the executable, e.g. `["prune"]`. */
|
|
22
|
+
readonly argv: readonly string[];
|
|
23
|
+
readonly output?: CliOutput;
|
|
24
|
+
/** Default: the wall clock. */
|
|
25
|
+
readonly clock?: Clock;
|
|
26
|
+
/** Default: the config's `database.handle`, else a connection on `database.url`. */
|
|
27
|
+
readonly openDatabase?: (database: SoftureDatabaseConfig) => Promise<CommandDatabase>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const EXIT_OK = 0;
|
|
31
|
+
export const EXIT_FAILED = 1;
|
|
32
|
+
export const EXIT_USAGE = 2;
|
|
33
|
+
|
|
34
|
+
export const MCP_ACCESS_USAGE = `Usage:
|
|
35
|
+
softure-mcp prune deletes expired access tokens, authorization codes, grants and abandoned clients`;
|
|
36
|
+
|
|
37
|
+
const consoleOutput: CliOutput = {
|
|
38
|
+
log: (line) => console.log(line),
|
|
39
|
+
error: (line) => console.error(line),
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/** Runs one command and answers its exit code. */
|
|
43
|
+
export async function runMcpAccessCli(options: RunMcpAccessCliOptions): Promise<number> {
|
|
44
|
+
const output = options.output ?? consoleOutput;
|
|
45
|
+
const [command, ...rest] = options.argv;
|
|
46
|
+
if (command === undefined || command === "--help" || command === "help") {
|
|
47
|
+
output.log(MCP_ACCESS_USAGE);
|
|
48
|
+
return EXIT_OK;
|
|
49
|
+
}
|
|
50
|
+
if (command !== "prune" || rest.length > 0) {
|
|
51
|
+
output.error(command === "prune" ? `softure-mcp prune: unexpected argument "${rest[0] ?? ""}"` : `softure-mcp: unknown command "${command}"`);
|
|
52
|
+
output.error(MCP_ACCESS_USAGE);
|
|
53
|
+
return EXIT_USAGE;
|
|
54
|
+
}
|
|
55
|
+
return prune(options, output);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function prune(options: RunMcpAccessCliOptions, output: CliOutput): Promise<number> {
|
|
59
|
+
const database = options.config?.database ?? null;
|
|
60
|
+
if (options.config === undefined || database === null) {
|
|
61
|
+
output.error("softure-mcp prune: the config has no database; set database.url in softure.config");
|
|
62
|
+
return EXIT_FAILED;
|
|
63
|
+
}
|
|
64
|
+
let opened: CommandDatabase | undefined;
|
|
65
|
+
try {
|
|
66
|
+
opened = await (options.openDatabase ?? ((settings) => openCommandDatabase(settings, { max: 1 })))(database);
|
|
67
|
+
const removed = await pruneMcpAccess({ db: opened.handle.db, clock: options.clock ?? systemClock, config: options.config });
|
|
68
|
+
output.log(
|
|
69
|
+
`softure-mcp prune: removed ${String(removed.accessTokens)} access tokens, ${String(removed.codes)} authorization codes, ${String(removed.grants)} grants, ${String(removed.clients)} clients`,
|
|
70
|
+
);
|
|
71
|
+
return EXIT_OK;
|
|
72
|
+
} catch (error) {
|
|
73
|
+
// The kind only: a driver message can carry the database address or credentials.
|
|
74
|
+
output.error(`softure-mcp prune: failed: ${errorLogLabel(error)}`);
|
|
75
|
+
return EXIT_FAILED;
|
|
76
|
+
} finally {
|
|
77
|
+
await opened?.close();
|
|
78
|
+
}
|
|
79
|
+
}
|
package/src/contract.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Result types and error codes of the mcp-access module. No user-facing copy here: the UI
|
|
2
2
|
// translates codes through `messages` (docs/02-module-standard.md §6).
|
|
3
|
-
import type { CoreErrorCode } from "@softure-ai/core";
|
|
3
|
+
import type { CoreErrorCode, Err } from "@softure-ai/core";
|
|
4
4
|
|
|
5
5
|
export type McpAccessErrorCode =
|
|
6
6
|
| "mcp-access.name_required"
|
|
@@ -9,8 +9,19 @@ export type McpAccessErrorCode =
|
|
|
9
9
|
| "mcp-access.token_not_found"
|
|
10
10
|
| "mcp-access.grant_not_found";
|
|
11
11
|
|
|
12
|
-
/**
|
|
13
|
-
export type
|
|
12
|
+
/** What an app's `beforeIssue` gate may answer instead of letting a token be issued. */
|
|
13
|
+
export type McpIssueRefusalCode = "mcp-access.issue_refused" | "mcp-access.write_refused";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A gate's refusal with the app's own text for the user ("Your trial has ended."), shown instead of the
|
|
17
|
+
* code's copy: an `Err` with a `message`, made by `refuseIssue` (`/next`).
|
|
18
|
+
*/
|
|
19
|
+
export interface McpIssueRefusal extends Err<McpIssueRefusalCode> {
|
|
20
|
+
readonly message: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Every code the token page can show: its own, a gate's refusal, a missing session and the generic ones. */
|
|
24
|
+
export type TokenFormErrorCode = McpAccessErrorCode | McpIssueRefusalCode | "auth.unauthenticated" | CoreErrorCode;
|
|
14
25
|
|
|
15
26
|
/** What the app's MCP server factory receives for one request. */
|
|
16
27
|
export interface McpServerIdentity {
|
|
@@ -42,6 +53,7 @@ export type AccessTokenStatus =
|
|
|
42
53
|
|
|
43
54
|
/** Ready-to-paste setup for MCP clients, built around the plaintext token. */
|
|
44
55
|
export interface McpClientSetup {
|
|
56
|
+
/** The name the client lists the server under: `clientServerName`, else `serverName`. */
|
|
45
57
|
readonly serverName: string;
|
|
46
58
|
readonly endpointUrl: string;
|
|
47
59
|
/** `claude mcp add …` for Claude Code, with the token in it. */
|
|
@@ -65,6 +77,8 @@ export interface IssuedToken {
|
|
|
65
77
|
readonly canWrite: boolean;
|
|
66
78
|
/** The expiry, already formatted in the app's locale and time zone. */
|
|
67
79
|
readonly expiresText: string;
|
|
80
|
+
/** The expiry as ISO 8601, for an app that formats it itself. */
|
|
81
|
+
readonly expiresAt: string;
|
|
68
82
|
readonly setup: McpClientSetup;
|
|
69
83
|
}
|
|
70
84
|
|
|
@@ -72,7 +86,8 @@ export interface IssuedToken {
|
|
|
72
86
|
export type IssueTokenFormState =
|
|
73
87
|
| { readonly status: "idle" }
|
|
74
88
|
| { readonly status: "ok"; readonly issued: IssuedToken }
|
|
75
|
-
|
|
89
|
+
/** `message`: the app's own refusal text from the `beforeIssue` gate, shown instead of the code's copy. */
|
|
90
|
+
| { readonly status: "error"; readonly error: TokenFormErrorCode; readonly message?: string };
|
|
76
91
|
|
|
77
92
|
/** A connected app (an OAuth grant) as its owner sees it. */
|
|
78
93
|
export interface OAuthGrantView {
|
package/src/index.ts
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
// work and the endpoint are in `@softure-ai/mcp-access/server`, the Next.js adapter (route, page,
|
|
4
4
|
// actions) in `/next`, the token manager in `/ui`.
|
|
5
5
|
import { defineModule, resolveMigrationsDir } from "@softure-ai/core";
|
|
6
|
+
import type { RateLimitBucketInput } from "@softure-ai/security";
|
|
6
7
|
import { mcpAccessMessages } from "./messages/index.js";
|
|
7
8
|
import { mcpAccessOptionsSchema } from "./options.js";
|
|
9
|
+
import { DEFAULT_MCP_ACCESS_ROUTES } from "./routes.js";
|
|
8
10
|
import { checkAccessTokensTable } from "./server/health.js";
|
|
9
11
|
import { mcpAccessPrivacyContributor } from "./server/privacy.js";
|
|
10
12
|
|
|
@@ -16,14 +18,14 @@ export const MODULE_ID = "mcp-access";
|
|
|
16
18
|
* the traffic behind one address (several assistants share it), not for one token.
|
|
17
19
|
*/
|
|
18
20
|
export const MCP_RATE_LIMIT_BUCKETS = {
|
|
19
|
-
mcp: { limit: 200, windowMinutes: 15 },
|
|
21
|
+
mcp: { limit: 200, windowMinutes: 15, key: "ip" },
|
|
20
22
|
/**
|
|
21
23
|
* OAuth registration (per address) and token requests (per address and client id), when
|
|
22
24
|
* `oauth.enabled`. Enough for an assistant platform refreshing many users' tokens per client,
|
|
23
25
|
* too little for a script filling the clients table.
|
|
24
26
|
*/
|
|
25
|
-
"mcp-oauth": { limit: 60, windowMinutes: 15 },
|
|
26
|
-
} as const
|
|
27
|
+
"mcp-oauth": { limit: 60, windowMinutes: 15, key: "ip" },
|
|
28
|
+
} as const satisfies Readonly<Record<string, RateLimitBucketInput>>;
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
31
|
* Enables MCP access in `softure.config.ts` (after `security` and `auth`):
|
|
@@ -32,20 +34,13 @@ export const MCP_RATE_LIMIT_BUCKETS = {
|
|
|
32
34
|
export const mcpAccess = defineModule({
|
|
33
35
|
manifest: {
|
|
34
36
|
id: MODULE_ID,
|
|
35
|
-
version: "0.1.
|
|
37
|
+
version: "0.1.14",
|
|
36
38
|
dependsOn: { security: "^0.1.0", auth: "^0.1.0" },
|
|
37
39
|
dbSchema: "mcp",
|
|
38
40
|
tables: ["access_tokens", "oauth_clients", "oauth_authorization_codes", "oauth_grants"],
|
|
39
41
|
env: [],
|
|
40
42
|
switches: [],
|
|
41
|
-
routes: {
|
|
42
|
-
page: "/account/mcp",
|
|
43
|
-
endpoint: "/api/mcp",
|
|
44
|
-
oauthConsent: "/oauth/authorize",
|
|
45
|
-
oauthDecision: "/api/oauth/authorize",
|
|
46
|
-
oauthToken: "/api/oauth/token",
|
|
47
|
-
oauthRegister: "/api/oauth/register",
|
|
48
|
-
},
|
|
43
|
+
routes: { ...DEFAULT_MCP_ACCESS_ROUTES },
|
|
49
44
|
mount: [
|
|
50
45
|
{ kind: "page", path: "app/account/mcp/page.tsx", export: "McpAccessPage" },
|
|
51
46
|
{ kind: "route-handler", path: "app/api/mcp/route.ts", export: "createMcpRoute" },
|
|
@@ -81,6 +76,8 @@ export type {
|
|
|
81
76
|
IssuedToken,
|
|
82
77
|
IssueTokenFormState,
|
|
83
78
|
McpAccessErrorCode,
|
|
79
|
+
McpIssueRefusal,
|
|
80
|
+
McpIssueRefusalCode,
|
|
84
81
|
McpClientSetup,
|
|
85
82
|
McpServerIdentity,
|
|
86
83
|
OAuthGrantView,
|
|
@@ -105,6 +102,7 @@ export {
|
|
|
105
102
|
type McpToolDefinition,
|
|
106
103
|
type McpToolDefinitionInput,
|
|
107
104
|
} from "./options.js";
|
|
105
|
+
export { DEFAULT_MCP_ACCESS_ROUTES } from "./routes.js";
|
|
108
106
|
export { readRequestOrigin, type McpOriginRequest, type McpOrigins } from "./origins.js";
|
|
109
107
|
export { accessTokens, oauthAuthorizationCodes, oauthClients, oauthGrants, type OAuthClientRow, type OAuthTokenEndpointAuthMethod } from "./schema.js";
|
|
110
108
|
export { getAccessTokenStatus, type AccessTokenStatusOptions } from "./token-status.js";
|
package/src/messages/en.ts
CHANGED
|
@@ -92,6 +92,8 @@ export const en = {
|
|
|
92
92
|
token_limit_reached: "You already hold the most active tokens allowed. Revoke one before creating another.",
|
|
93
93
|
token_not_found: "This token no longer exists. It may have been revoked already.",
|
|
94
94
|
grant_not_found: "This app is no longer connected.",
|
|
95
|
+
issue_refused: "Your account cannot create tokens right now.",
|
|
96
|
+
write_refused: "Your account cannot create tokens with write access. Create a read-only token instead.",
|
|
95
97
|
},
|
|
96
98
|
auth: {
|
|
97
99
|
unauthenticated: "Your session has ended. Log in again.",
|
package/src/messages/pl.ts
CHANGED
|
@@ -94,6 +94,8 @@ export const pl: typeof en = {
|
|
|
94
94
|
token_limit_reached: "Masz już najwięcej aktywnych tokenów, ile można. Unieważnij któryś, zanim utworzysz nowy.",
|
|
95
95
|
token_not_found: "Ten token już nie istnieje. Mógł zostać unieważniony.",
|
|
96
96
|
grant_not_found: "Ta aplikacja nie jest już połączona.",
|
|
97
|
+
issue_refused: "Twoje konto nie może teraz tworzyć tokenów.",
|
|
98
|
+
write_refused: "Twoje konto nie może tworzyć tokenów z prawem zapisu. Utwórz token tylko do odczytu.",
|
|
97
99
|
},
|
|
98
100
|
auth: {
|
|
99
101
|
unauthenticated: "Twoja sesja wygasła. Zaloguj się ponownie.",
|
package/src/next/actions.ts
CHANGED
|
@@ -4,18 +4,17 @@
|
|
|
4
4
|
// which the client controls, docs/02 §8) before the form is read, and every query is scoped to
|
|
5
5
|
// that owner. Unexpected failures become `safeError` codes; the token never reaches a log.
|
|
6
6
|
import { getCurrentUser } from "@softure-ai/auth/next";
|
|
7
|
-
import { errorLogLabel,
|
|
7
|
+
import { errorLogLabel, safeError, type SoftureConfig } from "@softure-ai/core";
|
|
8
8
|
import { getSoftureConfig } from "@softure-ai/core/next";
|
|
9
9
|
import { revalidatePath } from "next/cache";
|
|
10
10
|
import { z } from "zod";
|
|
11
|
-
import { getMcpClientSetup } from "../client-setup.js";
|
|
12
11
|
import type { IssueTokenFormState, RevokeGrantFormState, RevokeTokenFormState } from "../contract.js";
|
|
13
12
|
import { MAX_TOKEN_NAME_LENGTH } from "../options.js";
|
|
14
|
-
import {
|
|
13
|
+
import { getMcpAccessRoutes } from "../server/options.js";
|
|
15
14
|
import { revokeOAuthGrant } from "../server/oauth.js";
|
|
16
|
-
import {
|
|
17
|
-
import { getMcpAccessContext
|
|
18
|
-
import {
|
|
15
|
+
import { revokeAccessToken } from "../server/tokens.js";
|
|
16
|
+
import { getMcpAccessContext } from "./context.js";
|
|
17
|
+
import { issueToken } from "./issue-token.js";
|
|
19
18
|
|
|
20
19
|
/** Longer values are cut: the server functions refuse them anyway, and nothing huge is echoed back. */
|
|
21
20
|
const MAX_FIELD_LENGTH = MAX_TOKEN_NAME_LENGTH * 4;
|
|
@@ -24,11 +23,6 @@ const text = z
|
|
|
24
23
|
.catch("")
|
|
25
24
|
.transform((value) => value.slice(0, MAX_FIELD_LENGTH));
|
|
26
25
|
|
|
27
|
-
const issueInput = z.object({
|
|
28
|
-
name: text,
|
|
29
|
-
// A checked HTML checkbox sends "on"; an unchecked one sends nothing, which means read only.
|
|
30
|
-
canWrite: z.string().nullable().catch(null),
|
|
31
|
-
});
|
|
32
26
|
const revokeInput = z.object({ id: text });
|
|
33
27
|
|
|
34
28
|
function reportFailure(operation: string, error: unknown) {
|
|
@@ -40,40 +34,9 @@ function refreshPage(config: SoftureConfig): void {
|
|
|
40
34
|
revalidatePath(getMcpAccessRoutes(config).page);
|
|
41
35
|
}
|
|
42
36
|
|
|
43
|
-
/** Issues a token for the signed-in user and returns its plaintext with the setup snippets, once. */
|
|
44
|
-
export async function issueTokenAction(
|
|
45
|
-
|
|
46
|
-
const user = await getCurrentUser();
|
|
47
|
-
if (user === null) return { status: "error", error: "auth.unauthenticated" };
|
|
48
|
-
|
|
49
|
-
// Every field has a `catch`, so parsing cannot fail.
|
|
50
|
-
const input = issueInput.parse({ name: formData.get("name"), canWrite: formData.get("canWrite") });
|
|
51
|
-
try {
|
|
52
|
-
// Resolved before issuing: a failure afterwards would leave a token nobody received.
|
|
53
|
-
const endpointUrl = getMcpEndpointUrl(config, await getRequestOrigins(config, getMcpAccessRoutes(config).page));
|
|
54
|
-
const result = await issueAccessToken(await getMcpAccessContext(config), { userId: user.id, name: input.name, canWrite: input.canWrite !== null });
|
|
55
|
-
if (!result.ok) return { status: "error", error: result.error };
|
|
56
|
-
refreshPage(config);
|
|
57
|
-
const issued = result.value;
|
|
58
|
-
const messages = getMcpAccessMessages(config);
|
|
59
|
-
return {
|
|
60
|
-
status: "ok",
|
|
61
|
-
issued: {
|
|
62
|
-
id: issued.id,
|
|
63
|
-
name: issued.name,
|
|
64
|
-
canWrite: issued.canWrite,
|
|
65
|
-
expiresText: formatMessage(messages.issued.expires, { date: formatDate(config, issued.expiresAt) }),
|
|
66
|
-
setup: getMcpClientSetup({
|
|
67
|
-
serverName: getMcpAccessOptions(config).serverName,
|
|
68
|
-
endpointUrl,
|
|
69
|
-
token: issued.token,
|
|
70
|
-
promptTemplate: messages.setup.assistantPrompt,
|
|
71
|
-
}),
|
|
72
|
-
},
|
|
73
|
-
};
|
|
74
|
-
} catch (error) {
|
|
75
|
-
return { status: "error", error: reportFailure("issuing a token", error) };
|
|
76
|
-
}
|
|
37
|
+
/** Issues a token for the signed-in user and returns its plaintext with the setup snippets, once. No gate: see `issueToken`. */
|
|
38
|
+
export async function issueTokenAction(previous: IssueTokenFormState, formData: FormData): Promise<IssueTokenFormState> {
|
|
39
|
+
return issueToken(previous, formData);
|
|
77
40
|
}
|
|
78
41
|
|
|
79
42
|
/** Revokes one of the signed-in user's tokens; another account's id matches nothing. */
|
package/src/next/format.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Dates on the token page, in the app's locale and time zone.
|
|
2
|
-
import type
|
|
2
|
+
import { formatCalendarDay, toCalendarDay, type SoftureConfig } from "@softure-ai/core";
|
|
3
3
|
|
|
4
4
|
export function formatDate(config: SoftureConfig, date: Date): string {
|
|
5
|
-
return
|
|
5
|
+
return formatCalendarDay(toCalendarDay(date, config.timezone), config.locale, "long");
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export function formatDateTime(config: SoftureConfig, date: Date): string {
|
package/src/next/index.ts
CHANGED
|
@@ -11,5 +11,6 @@ export {
|
|
|
11
11
|
registerOAuthClientRoute,
|
|
12
12
|
} from "./oauth-routes.js";
|
|
13
13
|
export { getMcpAccessContext, getRequestOrigins } from "./context.js";
|
|
14
|
-
export {
|
|
14
|
+
export { issueToken, refuseIssue, type BeforeIssueGate, type IssueGateInput, type IssueGateResult, type IssueTokenOptions } from "./issue-token.js";
|
|
15
|
+
export { McpAccessPage, type McpAccessPageProps } from "./pages.js";
|
|
15
16
|
export { createMcpRoute } from "./route.js";
|