@subseam/mcp 0.4.2 → 0.4.12

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 (3) hide show
  1. package/README.md +14 -10
  2. package/dist/main.js +47 -11
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -12,9 +12,8 @@ and the rest. It runs on your machine and talks to the subseam service over HTTP
12
12
  ## What you need
13
13
 
14
14
  - Node.js 22 or later, and npm.
15
- - A GitHub account, and a subseam organization that someone has added you to. The org's owner
16
- gives you its id (a UUID); that is your **tenant id**.
17
- - The subseam API URL: `https://subseam.theconstructory.com/api`.
15
+ - A GitHub account, and a subseam organization that someone has added you to.
16
+ - The subseam API URL: `https://subseam.theconstructory.com/subseam`.
18
17
 
19
18
  ## 1. Sign in
20
19
 
@@ -37,12 +36,13 @@ S='<the cookie value>'; C=$(openssl rand -hex 16)
37
36
  mkdir -p ~/.subseam && curl -s -X POST https://subseam.theconstructory.com/tokens \
38
37
  -H "cookie: __Host-subseam_session=$S; subseam_csrf=$C" -H "x-csrf-token: $C" \
39
38
  -H 'content-type: application/json' -d '{"label":"my-machine"}' \
40
- | node -e 'process.stdin.on("data",d=>process.stdout.write(JSON.parse(d).token))' > ~/.subseam/token
39
+ | node -e 'let b="";process.stdin.on("data",d=>b+=d).on("end",()=>{let t;try{t=JSON.parse(b).token}catch{}if(typeof t==="string")process.stdout.write(t);else{console.error("subseam refused the token request: "+b);process.exit(1)}})' > ~/.subseam/token
41
40
  chmod 600 ~/.subseam/token
42
41
  ```
43
42
 
44
- `~/.subseam/token` now holds one line starting `cpt_`. If it is empty, the session cookie was
45
- wrong or expired: sign in again and repeat. The server also accepts the token in the
43
+ `~/.subseam/token` now holds one line starting `cpt_`. If the command prints "subseam refused the
44
+ token request" with the server's answer, the session cookie was wrong or expired: sign in again and
45
+ repeat. The server also accepts the token in the
46
46
  `SUBSEAM_TOKEN` environment variable instead of the file.
47
47
 
48
48
  ## 3. Install
@@ -54,15 +54,19 @@ npm install @subseam/mcp
54
54
 
55
55
  ## 4. Register it with your agent
56
56
 
57
- For Claude Code, with your tenant id in place of `<tenant id>`:
57
+ For Claude Code:
58
58
 
59
59
  ```
60
- claude mcp add-json --scope user subseam '{"command":"node","args":["'"$HOME"'/.subseam/mcp/node_modules/@subseam/mcp/dist/main.js"],"env":{"SUBSEAM_URL":"https://subseam.theconstructory.com/api","SUBSEAM_TENANT":"<tenant id>"}}'
60
+ claude mcp add-json --scope user subseam '{"command":"node","args":["'"$HOME"'/.subseam/mcp/node_modules/@subseam/mcp/dist/main.js"],"env":{"SUBSEAM_URL":"https://subseam.theconstructory.com/subseam"}}'
61
61
  ```
62
62
 
63
63
  `claude mcp list` should show `subseam ... Connected`. Any other MCP host: run
64
- `node ~/.subseam/mcp/node_modules/@subseam/mcp/dist/main.js` over stdio with the same two
65
- environment variables.
64
+ `node ~/.subseam/mcp/node_modules/@subseam/mcp/dist/main.js` over stdio with the same
65
+ environment.
66
+
67
+ The server acts for your organization. If you belong to several, it mounts only
68
+ `subseam_unavailable`, which lists each one's name and id: add `"SUBSEAM_TENANT":"<id>"` to the
69
+ `env` above for the one this agent works in.
66
70
 
67
71
  ## 5. Check it works
68
72
 
package/dist/main.js CHANGED
@@ -24036,10 +24036,11 @@ function requireEnv(env, name, purpose) {
24036
24036
  function loadConfig2(env = process.env, sources = realTokenSources()) {
24037
24037
  return Object.freeze({
24038
24038
  get url() {
24039
- return requireEnv(env, "SUBSEAM_URL", "to reach the hosted service, e.g. https://app.subseam.dev");
24039
+ return requireEnv(env, "SUBSEAM_URL", "to reach the hosted service, e.g. https://subseam.theconstructory.com/subseam");
24040
24040
  },
24041
24041
  get tenantId() {
24042
- return requireEnv(env, "SUBSEAM_TENANT", "as the organization id the channel acts for (a UUID)");
24042
+ const value = env["SUBSEAM_TENANT"];
24043
+ return value === void 0 || value === "" ? void 0 : value;
24043
24044
  },
24044
24045
  get token() {
24045
24046
  return resolveTenantToken(sources);
@@ -28203,14 +28204,35 @@ async function requireRepo(env, readOrigin2) {
28203
28204
  throw new Error(`SUBSEAM_REPO unset; git remote origin = ${seen} did not parse as owner/name`);
28204
28205
  }
28205
28206
 
28207
+ // src/account.ts
28208
+ init_esm_shims();
28209
+ var AccountChoiceError = class extends Error {
28210
+ };
28211
+ async function resolveAccount(config3) {
28212
+ if (config3.tenantId !== void 0) return config3.tenantId;
28213
+ const url2 = `${config3.url.replace(/\/$/, "")}/me`;
28214
+ const response = await fetch(url2, { headers: { authorization: `Bearer ${config3.token}` } });
28215
+ if (!response.ok) {
28216
+ throw new Error(
28217
+ `account lookup (GET ${url2}) failed: ${response.status} ${response.statusText}. ` + (response.status === 401 ? "Check the stored token (SUBSEAM_TOKEN or ~/.subseam/token) is valid." : await response.text().catch(() => ""))
28218
+ );
28219
+ }
28220
+ const body2 = await response.json();
28221
+ const orgs = (body2.accounts ?? []).filter((a) => a.kind === "org");
28222
+ if (orgs.length <= 1) return orgs[0]?.accountId ?? null;
28223
+ const listed = orgs.map((a) => `${a.handle ?? "(no handle)"} ${a.accountId}`).join("; ");
28224
+ throw new AccountChoiceError(`this token's account belongs to ${orgs.length} organizations (${listed}). Set SUBSEAM_TENANT to the id of the one this session acts for.`);
28225
+ }
28226
+
28206
28227
  // src/discovery-failure.ts
28207
28228
  init_esm_shims();
28208
28229
  var DISCOVERY_FAILURE_TOOL = "subseam_unavailable";
28209
- function discoveryFailureText(url2, reason) {
28210
- return `subseam is unavailable this session: capability discovery failed, so no subseam tools are mounted. Tried GET ${url2.replace(/\/$/, "")}/capabilities \u2014 ${reason}. Check SUBSEAM_URL is your subseam API URL and that it is reachable, then restart the MCP server.`;
28230
+ function discoveryFailureText(url2, reason, path7 = "/capabilities", advice = REACHABILITY_ADVICE) {
28231
+ return `subseam is unavailable this session: capability discovery failed, so no subseam tools are mounted. Tried GET ${url2.replace(/\/$/, "")}${path7} \u2014 ${reason}. ${advice}`;
28211
28232
  }
28212
- function mountDiscoveryFailure(server, url2, reason) {
28213
- const text = discoveryFailureText(url2, reason);
28233
+ var REACHABILITY_ADVICE = "Check SUBSEAM_URL is your subseam API URL and that it is reachable, then restart the MCP server.";
28234
+ function mountDiscoveryFailure(server, url2, reason, path7, advice) {
28235
+ const text = discoveryFailureText(url2, reason, path7, advice);
28214
28236
  server.registerTool(DISCOVERY_FAILURE_TOOL, { description: text, inputSchema: {} }, () => ({
28215
28237
  content: [{ type: "text", text }],
28216
28238
  isError: true
@@ -29683,10 +29705,13 @@ init_esm_shims();
29683
29705
  init_esm_shims();
29684
29706
 
29685
29707
  // src/gateway.ts
29708
+ function accountHeader(config3) {
29709
+ return config3.tenantId === void 0 ? {} : { [TENANT_HEADER]: config3.tenantId };
29710
+ }
29686
29711
  async function fetchEnabledCapabilities(config3) {
29687
29712
  const base = config3.url.replace(/\/$/, "");
29688
29713
  const response = await fetch(`${base}/capabilities`, {
29689
- headers: { authorization: `Bearer ${config3.token}`, [TENANT_HEADER]: config3.tenantId }
29714
+ headers: { authorization: `Bearer ${config3.token}`, ...accountHeader(config3) }
29690
29715
  });
29691
29716
  if (!response.ok) {
29692
29717
  const detail = await response.text().catch(() => "");
@@ -29706,7 +29731,7 @@ function createGatewayClient(config3) {
29706
29731
  headers: {
29707
29732
  "content-type": "application/json",
29708
29733
  authorization: `Bearer ${config3.token}`,
29709
- [TENANT_HEADER]: config3.tenantId
29734
+ ...accountHeader(config3)
29710
29735
  },
29711
29736
  body: JSON.stringify(input12)
29712
29737
  });
@@ -48311,7 +48336,7 @@ var workReadTools = createWorkReadToolProviders({ capability: "work" });
48311
48336
  var bundledProviders = [src_default2, src_default, tension_default, ...defineTools, ...decisionTools, ...captureTools, ...buildTools, secureTool, ...workTools, ...workReadTools];
48312
48337
 
48313
48338
  // package.json
48314
- var version2 = "0.4.2";
48339
+ var version2 = "0.4.12";
48315
48340
 
48316
48341
  // src/server.ts
48317
48342
  var SERVER_VERSION = version2;
@@ -48320,9 +48345,20 @@ function selectEntitled(providers, enabled) {
48320
48345
  }
48321
48346
  async function createServer(config3, cwd, initialWorkspaceId) {
48322
48347
  const server = new McpServer({ name: "subseam", version: SERVER_VERSION });
48348
+ let session;
48349
+ try {
48350
+ const account = await resolveAccount(config3);
48351
+ session = { url: config3.url, token: config3.token, tenantId: account ?? void 0 };
48352
+ } catch (err2) {
48353
+ const reason = err2 instanceof Error ? err2.message : String(err2);
48354
+ process.stderr.write(`subseam-mcp: account lookup failed; mounting only ${DISCOVERY_FAILURE_TOOL}. ${reason}
48355
+ `);
48356
+ mountDiscoveryFailure(server, config3.url, reason, "/me", err2 instanceof AccountChoiceError ? "Then restart the MCP server." : void 0);
48357
+ return server;
48358
+ }
48323
48359
  let enabled = [];
48324
48360
  try {
48325
- enabled = await fetchEnabledCapabilities(config3);
48361
+ enabled = await fetchEnabledCapabilities(session);
48326
48362
  } catch (err2) {
48327
48363
  const reason = err2 instanceof Error ? err2.message : String(err2);
48328
48364
  process.stderr.write(
@@ -48333,7 +48369,7 @@ async function createServer(config3, cwd, initialWorkspaceId) {
48333
48369
  return server;
48334
48370
  }
48335
48371
  const providers = bundledProviders.map((p, i) => assertToolProvider(p, `bundled provider [${i}]`));
48336
- const ctx = { cwd, tenantId: config3.tenantId, gateway: createGatewayClient(config3), enabledCapabilities: enabled, workspace: createWorkspaceBinding(initialWorkspaceId) };
48372
+ const ctx = { cwd, tenantId: session.tenantId ?? null, gateway: createGatewayClient(session), enabledCapabilities: enabled, workspace: createWorkspaceBinding(initialWorkspaceId) };
48337
48373
  mountProviders(server, selectEntitled(providers, enabled), ctx);
48338
48374
  return server;
48339
48375
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@subseam/mcp",
3
- "version": "0.4.2",
3
+ "version": "0.4.12",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {