@softure-ai/mcp-access 0.1.14 → 0.1.15

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 CHANGED
@@ -4,6 +4,13 @@ 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.15
8
+
9
+ - The stdio entry resolves its account with `resolveOnlyAccount` from `@softure-ai/auth/server` instead of a private
10
+ copy; codes and messages are unchanged (#411). Needs the auth release that exports it.
11
+ - The `@softure-ai/ui` peer range also accepts 0.2.0.
12
+ - Requires `@softure-ai/auth` `^0.1.13`, the release with `resolveOnlyAccount` (#411).
13
+
7
14
  ## 0.1.14
8
15
 
9
16
  - `issueToken` can replace an app's own token-issue action (#386):
package/README.md CHANGED
@@ -195,7 +195,7 @@ claude mcp add acme -e MCP_USER_ID=<account id> -- npx tsx --import @softure-ai/
195
195
 
196
196
  - The account: `MCP_USER_ID` (or `userIdEnv`) when set, which must name an account; else the only account in the
197
197
  database. With several accounts and no id the entry refuses (`mcp-access.stdio_account_ambiguous`): it never
198
- picks one.
198
+ picks one. The lookup is `resolveOnlyAccount` from `@softure-ai/auth/server`, which an app's own scripts can use too.
199
199
  - `canWrite` needs `allowWrites: true` here and the app's `allowWrites`. The token id is `"stdio"`.
200
200
  - The database opens like a package command's (`database.handle`, else `database.url`) and closes with the
201
201
  connection. Messages go to stderr; stdout is the protocol.
package/dist/index.js CHANGED
@@ -30,7 +30,7 @@ export const MCP_RATE_LIMIT_BUCKETS = {
30
30
  export const mcpAccess = defineModule({
31
31
  manifest: {
32
32
  id: MODULE_ID,
33
- version: "0.1.14",
33
+ version: "0.1.15",
34
34
  dependsOn: { security: "^0.1.0", auth: "^0.1.0" },
35
35
  dbSchema: "mcp",
36
36
  tables: ["access_tokens", "oauth_clients", "oauth_authorization_codes", "oauth_grants"],
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/stdio/serve.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAG9D,OAAO,EAAiB,KAAK,aAAa,EAAE,KAAK,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACjG,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAE3E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAG9D,sFAAsF;AACtF,eAAO,MAAM,uBAAuB,gBAAgB,CAAC;AAErD,6EAA6E;AAC7E,eAAO,MAAM,cAAc,UAAU,CAAC;AAItC,MAAM,MAAM,iBAAiB,GAAG,8BAA8B,GAAG,6BAA6B,GAAG,oCAAoC,GAAG,kCAAkC,CAAC;AAE3K,8GAA8G;AAC9G,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,8BAA8B;IAC9B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,8FAA8F;IAC9F,QAAQ,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,MAAM,MAAM,cAAc,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAA;CAAE,CAAC;AAE3I,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC;IACxC,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,gGAAgG;IAChG,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,8BAA8B;IAC9B,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAC5D,iDAAiD;IACjD,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAC/B,oFAAoF;IACpF,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,qBAAqB,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;CACvF;AAED,uGAAuG;AACvG,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,cAAc,CAAC,CAoD1F"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/stdio/serve.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAG9D,OAAO,EAAiB,KAAK,aAAa,EAAE,KAAK,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACjG,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC3E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAG9D,sFAAsF;AACtF,eAAO,MAAM,uBAAuB,gBAAgB,CAAC;AAErD,6EAA6E;AAC7E,eAAO,MAAM,cAAc,UAAU,CAAC;AAEtC,MAAM,MAAM,iBAAiB,GAAG,8BAA8B,GAAG,6BAA6B,GAAG,oCAAoC,GAAG,kCAAkC,CAAC;AAE3K,8GAA8G;AAC9G,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,8BAA8B;IAC9B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,8FAA8F;IAC9F,QAAQ,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,MAAM,MAAM,cAAc,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAA;CAAE,CAAC;AAE3I,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC;IACxC,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,gGAAgG;IAChG,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,8BAA8B;IAC9B,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAC5D,iDAAiD;IACjD,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAC/B,oFAAoF;IACpF,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,qBAAqB,KAAK,OAAO,CAAC,eAAe,CAAC,CAAC;CACvF;AAED,uGAAuG;AACvG,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,cAAc,CAAC,CAoD1F"}
@@ -1,14 +1,12 @@
1
1
  import { serveStdio, StdioServerTransport } from "@modelcontextprotocol/server/stdio";
2
- import { users } from "@softure-ai/auth";
2
+ import { resolveOnlyAccount } from "@softure-ai/auth/server";
3
3
  import { errorLogLabel } from "@softure-ai/core";
4
4
  import { openCommandDatabase } from "@softure-ai/db";
5
- import { eq } from "drizzle-orm";
6
5
  import { getMcpAccessOptions } from "../server/options.js";
7
6
  /** The variable naming the account to serve, unless the entry is told another one. */
8
7
  export const DEFAULT_MCP_USER_ID_ENV = "MCP_USER_ID";
9
8
  /** The token id the factory receives over stdio, where there is no token. */
10
9
  export const STDIO_TOKEN_ID = "stdio";
11
- const UUID_SHAPE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
12
10
  /** Resolves the account, then serves the app's server for it over stdio until the client goes away. */
13
11
  export async function serveMcpStdio(options) {
14
12
  const { config } = options;
@@ -19,7 +17,7 @@ export async function serveMcpStdio(options) {
19
17
  const database = await (options.openDatabase ?? ((settings) => openCommandDatabase(settings, { max: 1 })))(config.database);
20
18
  let userId;
21
19
  try {
22
- userId = await resolveAccount(database, env[userIdEnv]?.trim() ?? "", userIdEnv);
20
+ userId = await resolveAccount(database, env, userIdEnv);
23
21
  }
24
22
  catch (error) {
25
23
  await database.close();
@@ -64,24 +62,22 @@ export async function serveMcpStdio(options) {
64
62
  },
65
63
  };
66
64
  }
67
- /** The account id, or why there is none. Never the first of several accounts: that could be another person's data. */
68
- async function resolveAccount(database, requested, userIdEnv) {
69
- const { db } = database.handle;
70
- if (requested !== "") {
71
- const unknown = { code: "mcp-access.stdio_account_unknown", message: `@softure-ai/mcp-access: ${userIdEnv} names no account` };
72
- if (!UUID_SHAPE.test(requested))
73
- return unknown;
74
- const found = await db.select({ id: users.id }).from(users).where(eq(users.id, requested)).limit(1);
75
- return found[0]?.id ?? unknown;
65
+ /** The account id (`resolveOnlyAccount` from auth), or why there is none in this entry's own words. */
66
+ async function resolveAccount(database, env, userIdEnv) {
67
+ const resolved = await resolveOnlyAccount({ db: database.handle.db, envName: userIdEnv, env });
68
+ if (resolved.ok)
69
+ return resolved.value;
70
+ switch (resolved.error) {
71
+ case "auth.account_unknown":
72
+ return { code: "mcp-access.stdio_account_unknown", message: `@softure-ai/mcp-access: ${userIdEnv} names no account` };
73
+ case "auth.no_account":
74
+ return { code: "mcp-access.stdio_no_account", message: "@softure-ai/mcp-access: the database has no account to serve" };
75
+ case "auth.account_ambiguous":
76
+ return {
77
+ code: "mcp-access.stdio_account_ambiguous",
78
+ message: `@softure-ai/mcp-access: the database has several accounts; set ${userIdEnv} to the id of the one to serve`,
79
+ };
76
80
  }
77
- const accounts = await db.select({ id: users.id }).from(users).limit(2);
78
- const [only, second] = accounts;
79
- if (only === undefined)
80
- return { code: "mcp-access.stdio_no_account", message: "@softure-ai/mcp-access: the database has no account to serve" };
81
- if (second !== undefined) {
82
- return { code: "mcp-access.stdio_account_ambiguous", message: `@softure-ai/mcp-access: the database has several accounts; set ${userIdEnv} to the id of the one to serve` };
83
- }
84
- return only.id;
85
81
  }
86
82
  function refuse(code, reason) {
87
83
  return { ok: false, error: { code, message: `@softure-ai/mcp-access: ${reason}` } };
@@ -1 +1 @@
1
- {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/stdio/serve.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/stdio/serve.ts"],"names":[],"mappings":"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"}
package/module.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "mcp-access",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "dependsOn": { "security": "^0.1.0", "auth": "^0.1.0" },
5
5
  "dbSchema": "mcp",
6
6
  "tables": ["access_tokens", "oauth_clients", "oauth_authorization_codes", "oauth_grants"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softure-ai/mcp-access",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
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",
@@ -79,7 +79,7 @@
79
79
  "build": "tsc -p tsconfig.build.json"
80
80
  },
81
81
  "dependencies": {
82
- "@softure-ai/auth": "^0.1.0",
82
+ "@softure-ai/auth": "^0.1.13",
83
83
  "@softure-ai/core": "^0.1.9",
84
84
  "@softure-ai/db": "^0.1.0",
85
85
  "@softure-ai/security": "^0.1.8",
@@ -87,12 +87,12 @@
87
87
  },
88
88
  "peerDependencies": {
89
89
  "@modelcontextprotocol/server": "^2.0.0",
90
- "@softure-ai/ui": "^0.1.0",
90
+ "@softure-ai/ui": "^0.1.0 || ^0.2.0",
91
91
  "drizzle-orm": "^0.45.2",
92
92
  "next": "^16.0.0",
93
93
  "react": "^19.0.0"
94
94
  },
95
95
  "devDependencies": {
96
- "@softure-ai/ui": "^0.1.7"
96
+ "@softure-ai/ui": "^0.1.7 || ^0.2.0"
97
97
  }
98
98
  }
package/src/index.ts CHANGED
@@ -34,7 +34,7 @@ export const MCP_RATE_LIMIT_BUCKETS = {
34
34
  export const mcpAccess = defineModule({
35
35
  manifest: {
36
36
  id: MODULE_ID,
37
- version: "0.1.14",
37
+ version: "0.1.15",
38
38
  dependsOn: { security: "^0.1.0", auth: "^0.1.0" },
39
39
  dbSchema: "mcp",
40
40
  tables: ["access_tokens", "oauth_clients", "oauth_authorization_codes", "oauth_grants"],
@@ -7,10 +7,9 @@
7
7
  // if (!started.ok) { console.error(started.error.message); process.exitCode = 1; }
8
8
  import type { Transport } from "@modelcontextprotocol/server";
9
9
  import { serveStdio, StdioServerTransport } from "@modelcontextprotocol/server/stdio";
10
- import { users } from "@softure-ai/auth";
10
+ import { resolveOnlyAccount } from "@softure-ai/auth/server";
11
11
  import { errorLogLabel, type SoftureConfig, type SoftureDatabaseConfig } from "@softure-ai/core";
12
12
  import { openCommandDatabase, type CommandDatabase } from "@softure-ai/db";
13
- import { eq } from "drizzle-orm";
14
13
  import type { McpServerIdentity } from "../contract.js";
15
14
  import type { McpServerFactory } from "../server/endpoint.js";
16
15
  import { getMcpAccessOptions } from "../server/options.js";
@@ -21,8 +20,6 @@ export const DEFAULT_MCP_USER_ID_ENV = "MCP_USER_ID";
21
20
  /** The token id the factory receives over stdio, where there is no token. */
22
21
  export const STDIO_TOKEN_ID = "stdio";
23
22
 
24
- const UUID_SHAPE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
25
-
26
23
  export type McpStdioErrorCode = "mcp-access.stdio_no_database" | "mcp-access.stdio_no_account" | "mcp-access.stdio_account_ambiguous" | "mcp-access.stdio_account_unknown";
27
24
 
28
25
  /** Why the entry did not start, with a message for the person running it (stderr: stdout is the protocol). */
@@ -65,7 +62,7 @@ export async function serveMcpStdio(options: ServeMcpStdioOptions): Promise<McpS
65
62
 
66
63
  let userId: string | McpStdioError;
67
64
  try {
68
- userId = await resolveAccount(database, env[userIdEnv]?.trim() ?? "", userIdEnv);
65
+ userId = await resolveAccount(database, env, userIdEnv);
69
66
  } catch (error) {
70
67
  await database.close();
71
68
  throw new Error(`@softure-ai/mcp-access: looking up the account to serve failed: ${errorLogLabel(error)}`, { cause: error });
@@ -110,22 +107,21 @@ export async function serveMcpStdio(options: ServeMcpStdioOptions): Promise<McpS
110
107
  };
111
108
  }
112
109
 
113
- /** The account id, or why there is none. Never the first of several accounts: that could be another person's data. */
114
- async function resolveAccount(database: CommandDatabase, requested: string, userIdEnv: string): Promise<string | McpStdioError> {
115
- const { db } = database.handle;
116
- if (requested !== "") {
117
- const unknown = { code: "mcp-access.stdio_account_unknown", message: `@softure-ai/mcp-access: ${userIdEnv} names no account` } as const;
118
- if (!UUID_SHAPE.test(requested)) return unknown;
119
- const found = await db.select({ id: users.id }).from(users).where(eq(users.id, requested)).limit(1);
120
- return found[0]?.id ?? unknown;
121
- }
122
- const accounts = await db.select({ id: users.id }).from(users).limit(2);
123
- const [only, second] = accounts;
124
- if (only === undefined) return { code: "mcp-access.stdio_no_account", message: "@softure-ai/mcp-access: the database has no account to serve" };
125
- if (second !== undefined) {
126
- return { code: "mcp-access.stdio_account_ambiguous", message: `@softure-ai/mcp-access: the database has several accounts; set ${userIdEnv} to the id of the one to serve` };
110
+ /** The account id (`resolveOnlyAccount` from auth), or why there is none in this entry's own words. */
111
+ async function resolveAccount(database: CommandDatabase, env: ServeMcpStdioOptions["env"], userIdEnv: string): Promise<string | McpStdioError> {
112
+ const resolved = await resolveOnlyAccount({ db: database.handle.db, envName: userIdEnv, env });
113
+ if (resolved.ok) return resolved.value;
114
+ switch (resolved.error) {
115
+ case "auth.account_unknown":
116
+ return { code: "mcp-access.stdio_account_unknown", message: `@softure-ai/mcp-access: ${userIdEnv} names no account` };
117
+ case "auth.no_account":
118
+ return { code: "mcp-access.stdio_no_account", message: "@softure-ai/mcp-access: the database has no account to serve" };
119
+ case "auth.account_ambiguous":
120
+ return {
121
+ code: "mcp-access.stdio_account_ambiguous",
122
+ message: `@softure-ai/mcp-access: the database has several accounts; set ${userIdEnv} to the id of the one to serve`,
123
+ };
127
124
  }
128
- return only.id;
129
125
  }
130
126
 
131
127
  function refuse(code: McpStdioErrorCode, reason: string): McpStdioResult {