@myspec/mcp-server 1.1.1-next.156 → 1.1.1-next.157

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 +49 -4
  2. package/dist/index.js +266 -58
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -19,13 +19,19 @@ Run with `npx @myspec/mcp-server [command]` (or `myspec-mcp [command]` when inst
19
19
  ```text
20
20
  (default) Start MCP server over stdio
21
21
  serve Start MCP server over stdio
22
- login Sign in via browser loopback OAuth
23
- login --paste Same as login, but never auto-opens a browser
24
- login --org <slug> Sign in and pin that organization
25
- logout Revoke refresh token and clear local credentials
22
+ auth login Sign in via browser loopback OAuth
23
+ auth login --paste Same as auth login, but never auto-opens a browser
24
+ auth login --org <slug> Sign in and pin that organization
25
+ auth logout Revoke refresh token and clear local credentials
26
+ auth status Show the sign-in: account email and active organization
27
+ login, logout Aliases for `auth login` and `auth logout`
26
28
  reverse --root <dir> Connect to ai-agent and expose local_fs tools
27
29
  ```
28
30
 
31
+ `login` and `logout` keep working exactly as before; they run `auth login` and
32
+ `auth logout`. Add `--help` after any auth command to print the usage instead
33
+ of running it.
34
+
29
35
  ### Signing in from another device (`--paste`)
30
36
 
31
37
  `login` opens a browser on this machine and catches the callback on a local
@@ -239,6 +245,45 @@ dev environment:
239
245
  npx -y @myspec/mcp-server login --user-auth-url https://dev-auth.myspec.dev
240
246
  ```
241
247
 
248
+ ### Checking your sign-in (`auth status`)
249
+
250
+ ```bash
251
+ npx -y @myspec/mcp-server auth status
252
+ ```
253
+
254
+ ```text
255
+ Signed in to https://auth.myspec.dev
256
+ Account: jane@example.com
257
+ Organization: Acme Inc (acme)
258
+ Credential: login session (/home/jane/.myspec/oauth_creds.json)
259
+ ```
260
+
261
+ It reports the credential `serve` would use, in the same precedence order
262
+ (`MYSPEC_API_TOKEN` > file `apiToken` > the `login` session), and honours
263
+ `--user-auth-url` the same way. The first line names the auth server, which is
264
+ how you tell a dev sign-in from a production one.
265
+
266
+ - **What is checked online.** The email and organization come from the saved
267
+ access token while it is still valid (they last 15 minutes); once it has
268
+ expired, `auth status` renews it first, like any other command. For a login
269
+ session it then looks the organization name up from user-auth, which also
270
+ confirms the session: one that was ended on the server is reported as signed
271
+ out. `MYSPEC_API_TOKEN` is exchanged on every run. A file `apiToken` whose
272
+ saved access token is still valid is not checked with the server, so a token
273
+ revoked in the webapp can still show as signed in for up to 15 minutes.
274
+ - **API tokens show the organization slug only**, e.g.
275
+ `acme (API tokens do not include the organization name)`, plus the token's
276
+ access mode (`read-only` or `read-write`). user-auth has no endpoint that
277
+ returns the name for an API token.
278
+ - **No active organization** prints `Organization: none` and how to pin one.
279
+ - **If the name lookup fails** (the network, or a server error), the slug is
280
+ shown with the reason and the command still succeeds.
281
+ - **Exit codes:** `0` when signed in; `2` when not signed in, when the
282
+ credential is rejected, or when an expired token could not be renewed — so
283
+ `auth status || auth login` works in a script. A problem with the
284
+ credentials file itself (wrong file mode, bad JSON) fails the same way
285
+ `serve` does. A failure never deletes the saved credentials.
286
+
242
287
  ### `reverse`
243
288
 
244
289
  `reverse` connects out to the ai-agent service over WebSocket and exposes a set
package/dist/index.js CHANGED
@@ -319,6 +319,14 @@ function createFileCredentialsStore(paths = {}) {
319
319
  }
320
320
  };
321
321
  }
322
+ function keepCredentialsOnFailure(store) {
323
+ return {
324
+ load: () => store.load(),
325
+ save: (creds) => store.save(creds),
326
+ clear: () => Promise.resolve(),
327
+ path: () => store.path()
328
+ };
329
+ }
322
330
  async function writeSettingsFile(target, creds) {
323
331
  await writeJsonFile(
324
332
  target,
@@ -809,19 +817,36 @@ var ApiTokenRejectedError = class extends ConfigError {
809
817
 
810
818
  // src/auth/organization.ts
811
819
  import readline2 from "readline";
812
- function readOrgClaim(accessToken) {
820
+
821
+ // src/auth/token-claims.ts
822
+ function readTokenClaims(accessToken) {
813
823
  const payload = accessToken.split(".")[1];
814
824
  if (payload === void 0) {
815
- return void 0;
825
+ return {};
816
826
  }
827
+ let claims;
817
828
  try {
818
- const json = Buffer.from(payload, "base64url").toString("utf-8");
819
- const claims = JSON.parse(json);
820
- const org = claims.org;
821
- return typeof org === "string" && org.length > 0 ? org : void 0;
829
+ claims = JSON.parse(Buffer.from(payload, "base64url").toString("utf-8"));
822
830
  } catch {
823
- return void 0;
831
+ return {};
832
+ }
833
+ if (typeof claims !== "object" || claims === null) {
834
+ return {};
824
835
  }
836
+ const record = claims;
837
+ return {
838
+ email: nonEmptyString(record.email),
839
+ org: nonEmptyString(record.org),
840
+ access: nonEmptyString(record.access)
841
+ };
842
+ }
843
+ function nonEmptyString(value) {
844
+ return typeof value === "string" && value.length > 0 ? value : void 0;
845
+ }
846
+
847
+ // src/auth/organization.ts
848
+ function readOrgClaim(accessToken) {
849
+ return readTokenClaims(accessToken).org;
825
850
  }
826
851
  var NO_ACTIVE_ORG_HINT = "Your CLI session has no active organization, so the access token is missing the `org` claim that the platform API and ai-agent require. Run `npx @myspec/mcp-server login --org <slug>` to pin one.";
827
852
  async function listOrganizations(opts) {
@@ -1199,14 +1224,6 @@ function defaultRefreshTokens(store) {
1199
1224
  const tokenManager = new TokenManager({ store: keepCredentialsOnFailure(store) });
1200
1225
  return tokenManager.forceRefresh();
1201
1226
  }
1202
- function keepCredentialsOnFailure(store) {
1203
- return {
1204
- load: () => store.load(),
1205
- save: (creds) => store.save(creds),
1206
- clear: () => Promise.resolve(),
1207
- path: () => store.path()
1208
- };
1209
- }
1210
1227
  function sameSlug(a, b) {
1211
1228
  return a.toLowerCase() === b.toLowerCase();
1212
1229
  }
@@ -1289,6 +1306,153 @@ async function runLogout(env = process.env) {
1289
1306
  }
1290
1307
  }
1291
1308
 
1309
+ // src/cli/auth-status.ts
1310
+ var NOT_SIGNED_IN = "Not signed in. Run `npx @myspec/mcp-server auth login` to sign in, or set MYSPEC_API_TOKEN to a MySpec API token.";
1311
+ var SESSION_REVOKED = "Your saved sign-in is no longer valid: the session was ended on the server. Run `npx @myspec/mcp-server auth login` to sign in again.";
1312
+ async function resolveAuthStatus(deps) {
1313
+ const stored = await deps.store.load();
1314
+ if (!stored) {
1315
+ throw new NeedsLoginError(NOT_SIGNED_IN);
1316
+ }
1317
+ let accessToken;
1318
+ try {
1319
+ accessToken = await deps.getAccessToken();
1320
+ } catch (err) {
1321
+ throw describeTokenFailure(err, stored.userAuthUrl);
1322
+ }
1323
+ const claims = readTokenClaims(accessToken);
1324
+ const kind = credentialKind(stored, deps.envApiTokenActive);
1325
+ const status = {
1326
+ userAuthUrl: stored.userAuthUrl,
1327
+ credential: { kind, location: deps.store.path() },
1328
+ // The saved user is the fallback only: an API-token record has none.
1329
+ email: claims.email ?? stored.user?.email,
1330
+ access: claims.access
1331
+ };
1332
+ const slug = claims.org;
1333
+ if (slug === void 0) {
1334
+ return status;
1335
+ }
1336
+ if (kind !== "login-session") {
1337
+ return {
1338
+ ...status,
1339
+ organization: { slug },
1340
+ orgNameNote: "API tokens do not include the organization name"
1341
+ };
1342
+ }
1343
+ let organizations;
1344
+ try {
1345
+ organizations = await deps.listOrgs({
1346
+ userAuthUrl: stored.userAuthUrl,
1347
+ accessToken,
1348
+ webappUrl: stored.webappUrl ?? deps.webappUrl
1349
+ });
1350
+ } catch (err) {
1351
+ if (err instanceof HttpStatusError && err.status === 401) {
1352
+ throw new NeedsLoginError(SESSION_REVOKED);
1353
+ }
1354
+ return {
1355
+ ...status,
1356
+ organization: { slug },
1357
+ orgNameNote: `could not look up its name: ${errorMessage2(err)}`
1358
+ };
1359
+ }
1360
+ const match = organizations.find((org) => org.slug.toLowerCase() === slug.toLowerCase());
1361
+ if (!match) {
1362
+ return {
1363
+ ...status,
1364
+ organization: { slug },
1365
+ orgNameNote: "not among your organizations; it may have been deleted, or you were removed"
1366
+ };
1367
+ }
1368
+ return { ...status, organization: { slug, name: match.name } };
1369
+ }
1370
+ function describeTokenFailure(err, userAuthUrl) {
1371
+ if (err instanceof TypeError) {
1372
+ return new ConfigError(
1373
+ `Could not reach ${userAuthUrl} to check the sign-in: ${errorMessage2(err)}`
1374
+ );
1375
+ }
1376
+ if (err instanceof HttpStatusError) {
1377
+ return new ConfigError(
1378
+ `Could not check the sign-in against ${userAuthUrl}: ${errorMessage2(err)}`
1379
+ );
1380
+ }
1381
+ return err;
1382
+ }
1383
+ function credentialKind(stored, envApiTokenActive) {
1384
+ if (stored.apiToken === void 0) {
1385
+ return "login-session";
1386
+ }
1387
+ return envApiTokenActive ? "env-api-token" : "file-api-token";
1388
+ }
1389
+ function formatAuthStatus(status) {
1390
+ const lines = [
1391
+ `Signed in to ${status.userAuthUrl}`,
1392
+ ` Account: ${status.email ?? "unknown"}`,
1393
+ ` Organization: ${formatOrganization(status)}`,
1394
+ ` Credential: ${formatCredential(status)}`
1395
+ ];
1396
+ if (status.organization === void 0) {
1397
+ lines.push("", `Warning: ${NO_ACTIVE_ORG_HINT}`);
1398
+ }
1399
+ return lines;
1400
+ }
1401
+ function formatOrganization(status) {
1402
+ const org = status.organization;
1403
+ if (org === void 0) {
1404
+ return "none";
1405
+ }
1406
+ if (org.name !== void 0) {
1407
+ return `${org.name} (${org.slug})`;
1408
+ }
1409
+ return status.orgNameNote === void 0 ? org.slug : `${org.slug} (${status.orgNameNote})`;
1410
+ }
1411
+ function formatCredential(status) {
1412
+ const access = status.access === void 0 ? "" : ` (${formatAccess(status.access)})`;
1413
+ switch (status.credential.kind) {
1414
+ case "env-api-token":
1415
+ return `API token from MYSPEC_API_TOKEN${access}`;
1416
+ case "file-api-token":
1417
+ return `API token in ${status.credential.location}${access}`;
1418
+ case "login-session":
1419
+ return `login session (${status.credential.location})`;
1420
+ }
1421
+ }
1422
+ function formatAccess(access) {
1423
+ if (access === "read") {
1424
+ return "read-only";
1425
+ }
1426
+ if (access === "write") {
1427
+ return "read-write";
1428
+ }
1429
+ return access;
1430
+ }
1431
+ function errorMessage2(err) {
1432
+ return redactSensitive(err instanceof Error ? err.message : String(err));
1433
+ }
1434
+ async function runAuthStatus(options, deps = {}) {
1435
+ const log = deps.log ?? ((message) => {
1436
+ process.stderr.write(message + "\n");
1437
+ });
1438
+ const tokenManager = new TokenManager({
1439
+ store: keepCredentialsOnFailure(options.store),
1440
+ fetchImpl: deps.fetchImpl
1441
+ });
1442
+ const listOrgs = deps.listOrgs ?? ((opts) => listOrganizations({ ...opts, fetchImpl: deps.fetchImpl }));
1443
+ const status = await resolveAuthStatus({
1444
+ store: options.store,
1445
+ getAccessToken: () => tokenManager.getValidAccessToken(),
1446
+ listOrgs,
1447
+ webappUrl: options.webappUrl,
1448
+ envApiTokenActive: options.envApiTokenActive
1449
+ });
1450
+ for (const line of formatAuthStatus(status)) {
1451
+ log(line);
1452
+ }
1453
+ return status;
1454
+ }
1455
+
1292
1456
  // src/platform/client.ts
1293
1457
  import { createHash } from "crypto";
1294
1458
 
@@ -7328,7 +7492,7 @@ async function handleList(args, opts) {
7328
7492
  try {
7329
7493
  await walk(realPath, "");
7330
7494
  } catch (err) {
7331
- return structuredError("list_dir_failed", errorMessage2(err));
7495
+ return structuredError("list_dir_failed", errorMessage3(err));
7332
7496
  }
7333
7497
  return structuredOk({ entries, truncated });
7334
7498
  }
@@ -7349,7 +7513,7 @@ async function handleRead(args, opts) {
7349
7513
  try {
7350
7514
  stat2 = await fs4.stat(realPath);
7351
7515
  } catch (err) {
7352
- return structuredError("stat_failed", errorMessage2(err));
7516
+ return structuredError("stat_failed", errorMessage3(err));
7353
7517
  }
7354
7518
  if (!stat2.isFile()) {
7355
7519
  return structuredError("not_a_file", `Not a regular file: ${args.path}`);
@@ -7365,7 +7529,7 @@ async function handleRead(args, opts) {
7365
7529
  try {
7366
7530
  raw = await fs4.readFile(realPath);
7367
7531
  } catch (err) {
7368
- return structuredError("read_failed", errorMessage2(err));
7532
+ return structuredError("read_failed", errorMessage3(err));
7369
7533
  }
7370
7534
  const probe = raw.subarray(0, Math.min(READ_FILE_BINARY_PROBE_BYTES, raw.length));
7371
7535
  if (probe.includes(0)) {
@@ -7572,7 +7736,7 @@ async function nodeGrep(searchRoot, realRoot, pattern, opts) {
7572
7736
  try {
7573
7737
  regex = opts.isRegex ? new RegExp(pattern, opts.caseSensitive ? "" : "i") : new RegExp(escapeRegExp(pattern), opts.caseSensitive ? "" : "i");
7574
7738
  } catch (err) {
7575
- throw new InvalidRegexError(errorMessage2(err));
7739
+ throw new InvalidRegexError(errorMessage3(err));
7576
7740
  }
7577
7741
  const matches = [];
7578
7742
  let truncated = false;
@@ -7646,7 +7810,7 @@ async function nodeGrep(searchRoot, realRoot, pattern, opts) {
7646
7810
  function escapeRegExp(s) {
7647
7811
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7648
7812
  }
7649
- function errorMessage2(err) {
7813
+ function errorMessage3(err) {
7650
7814
  return err instanceof Error ? err.message : String(err);
7651
7815
  }
7652
7816
  function mapPathError(err) {
@@ -7659,7 +7823,7 @@ function mapPathError(err) {
7659
7823
  if (err.code === "ENOENT") {
7660
7824
  return structuredError("not_found", "Path does not exist");
7661
7825
  }
7662
- return structuredError("resolve_failed", errorMessage2(err));
7826
+ return structuredError("resolve_failed", errorMessage3(err));
7663
7827
  }
7664
7828
  function mapPackError(err) {
7665
7829
  if (err instanceof PackCacheMissError) {
@@ -7674,7 +7838,7 @@ function mapPackError(err) {
7674
7838
  if (err instanceof AbsolutePathError || err instanceof PathOutsideRootError) {
7675
7839
  return mapPathError(err);
7676
7840
  }
7677
- return structuredError("pack_failed", errorMessage2(err));
7841
+ return structuredError("pack_failed", errorMessage3(err));
7678
7842
  }
7679
7843
  function registerLocalFsTools(server, opts) {
7680
7844
  const packCache = new PackCache();
@@ -7753,7 +7917,7 @@ function registerLocalFsTools(server, opts) {
7753
7917
  `);
7754
7918
  return result;
7755
7919
  } catch (err) {
7756
- const msg = errorMessage2(err);
7920
+ const msg = errorMessage3(err);
7757
7921
  const elapsedMs = Math.round(performance.now() - started);
7758
7922
  process.stdout.write(`[tool] ${name} -> threw ${String(elapsedMs)}ms ${msg}
7759
7923
  `);
@@ -8114,20 +8278,49 @@ function createReverseMcpPresenceProbe(deps) {
8114
8278
  }
8115
8279
 
8116
8280
  // src/index.ts
8281
+ var AUTH_COMMANDS = ["login", "logout", "status"];
8282
+ function isAuthCommand(value) {
8283
+ return AUTH_COMMANDS.includes(value);
8284
+ }
8117
8285
  function parseArgs(argv) {
8118
8286
  const [first, ...rest] = argv;
8119
- let command = "serve";
8120
- let flagArgs = argv;
8121
- if (first === "login" || first === "logout" || first === "serve" || first === "reverse" || first === "help" || first === "version") {
8122
- command = first;
8123
- flagArgs = rest;
8124
- } else if (first === "--help" || first === "-h") {
8125
- command = "help";
8126
- flagArgs = rest;
8127
- } else if (first === "--version" || first === "-v") {
8128
- command = "version";
8129
- flagArgs = rest;
8287
+ if (first === "auth") {
8288
+ const [sub, ...subRest] = rest;
8289
+ if (sub === void 0 || sub === "help" || sub === "--help" || sub === "-h") {
8290
+ return { command: "help", flags: /* @__PURE__ */ new Map() };
8291
+ }
8292
+ if (!isAuthCommand(sub)) {
8293
+ throw new ConfigError(
8294
+ `Unknown auth command: ${sub}. Expected one of: ${AUTH_COMMANDS.join(", ")}.`
8295
+ );
8296
+ }
8297
+ return authArgs(sub, subRest);
8298
+ }
8299
+ if (first === "login" || first === "logout") {
8300
+ return authArgs(first, rest);
8301
+ }
8302
+ if (first === "serve" || first === "reverse" || first === "help" || first === "version") {
8303
+ return { command: first, flags: parseFlags(rest) };
8304
+ }
8305
+ if (first === "--help" || first === "-h") {
8306
+ return { command: "help", flags: parseFlags(rest) };
8307
+ }
8308
+ if (first === "--version" || first === "-v") {
8309
+ return { command: "version", flags: parseFlags(rest) };
8310
+ }
8311
+ return { command: "serve", flags: parseFlags(argv) };
8312
+ }
8313
+ function authArgs(authCommand, args) {
8314
+ if (args.includes("-h")) {
8315
+ return { command: "help", flags: /* @__PURE__ */ new Map() };
8130
8316
  }
8317
+ const flags = parseFlags(args);
8318
+ if (flags.has("help")) {
8319
+ return { command: "help", flags };
8320
+ }
8321
+ return { command: "auth", authCommand, flags };
8322
+ }
8323
+ function parseFlags(flagArgs) {
8131
8324
  const flags = /* @__PURE__ */ new Map();
8132
8325
  const unknownPositional = [];
8133
8326
  for (let i = 0; i < flagArgs.length; i += 1) {
@@ -8157,7 +8350,7 @@ function parseArgs(argv) {
8157
8350
  `Unknown positional arguments: ${unknownPositional.join(", ")}. Run \`npx @myspec/mcp-server --help\` for usage.`
8158
8351
  );
8159
8352
  }
8160
- return { command, flags };
8353
+ return flags;
8161
8354
  }
8162
8355
  function flagString(flags, key) {
8163
8356
  const value = flags.get(key);
@@ -8176,16 +8369,21 @@ function printHelp() {
8176
8369
  "Commands:",
8177
8370
  " (default) Start MCP server over stdio",
8178
8371
  " serve Start MCP server over stdio",
8179
- " login Sign in via browser (chooser page on the webapp)",
8180
- " login --paste Same as login, but never auto-opens a browser. Open",
8181
- " the printed URL yourself; if the page cannot reach this",
8182
- " CLI it shows a `<code>.<state>` token to paste back.",
8183
- " login --org <slug> Sign in and pin that organization. Tokens only work",
8372
+ " auth login Sign in via browser (chooser page on the webapp)",
8373
+ " auth login --paste Same as auth login, but never auto-opens a browser.",
8374
+ " Open the printed URL yourself; if the page cannot reach",
8375
+ " this CLI it shows a `<code>.<state>` token to paste back.",
8376
+ " auth login --org <slug> Sign in and pin that organization. Tokens only work",
8184
8377
  " when an organization is active; the CLI asks when you",
8185
8378
  " belong to several and this flag is not given.",
8186
- " logout Sign out and clear the session credentials. A",
8379
+ " auth logout Sign out and clear the session credentials. A",
8187
8380
  " configured apiToken is KEPT, so an unattended",
8188
8381
  " install keeps working.",
8382
+ " auth status Show whether you are signed in, the account email and",
8383
+ " the active organization. Renews an expired access",
8384
+ " token first, as any command does. Exits 2 when not",
8385
+ " signed in.",
8386
+ " login, logout Aliases for `auth login` and `auth logout`.",
8189
8387
  " reverse --root <dir> Connect to ai-agent and expose local_fs tools.",
8190
8388
  " The agent WebSocket URL is discovered from the webapp",
8191
8389
  " (GET /api/v1/config) using your saved credentials;",
@@ -8238,14 +8436,28 @@ function assertOrgFlagHasValue(flags) {
8238
8436
  }
8239
8437
  }
8240
8438
  async function main(argv = process.argv.slice(2)) {
8241
- const { command, flags } = parseArgs(argv);
8242
- switch (command) {
8439
+ const parsed = parseArgs(argv);
8440
+ const { flags } = parsed;
8441
+ switch (parsed.command) {
8243
8442
  case "help":
8244
8443
  printHelp();
8245
8444
  return;
8246
8445
  case "version":
8247
8446
  process.stderr.write(readVersion() + "\n");
8248
8447
  return;
8448
+ case "auth":
8449
+ await runAuthCommand(parsed.authCommand, flags);
8450
+ return;
8451
+ case "reverse":
8452
+ await runReverseCommand(flags);
8453
+ return;
8454
+ case "serve":
8455
+ await runServe(flags);
8456
+ return;
8457
+ }
8458
+ }
8459
+ async function runAuthCommand(authCommand, flags) {
8460
+ switch (authCommand) {
8249
8461
  case "login":
8250
8462
  assertOrgFlagHasValue(flags);
8251
8463
  await runLogin({
@@ -8257,13 +8469,14 @@ async function main(argv = process.argv.slice(2)) {
8257
8469
  case "logout":
8258
8470
  await runLogout();
8259
8471
  return;
8260
- case "reverse":
8261
- await runReverseCommand(flags);
8262
- return;
8263
- case "serve":
8264
- default:
8265
- await runServe(flags);
8472
+ case "status": {
8473
+ assertNoEnvAccessToken();
8474
+ const { store, config, envApiTokenActive } = await buildCredentialsStore({
8475
+ cliUserAuthUrl: flagString(flags, "user-auth-url")
8476
+ });
8477
+ await runAuthStatus({ store, webappUrl: config.webappUrl, envApiTokenActive });
8266
8478
  return;
8479
+ }
8267
8480
  }
8268
8481
  }
8269
8482
  var REVERSE_DISCOVERY_HINT = "Pass --agent-url <url>, set MYSPEC_AI_AGENT_WS_URL, or run `npx @myspec/mcp-server login` and retry.";
@@ -8353,17 +8566,12 @@ async function buildCredentialsStore(opts = {}) {
8353
8566
  // ConfigError exit — and one that never names the file it came from.
8354
8567
  storedUserAuthUrl: stored?.userAuthUrl ?? usableAuthUrl(fromSettings, settingsPath, warn)
8355
8568
  });
8569
+ const envConfig = readEnvApiToken(config.userAuthUrl, env, warn);
8356
8570
  return {
8357
- store: createEnvApiTokenStore({
8358
- fileStore,
8359
- // `warn` rather than the default sink, so the "cannot be sent as a
8360
- // header" warning lands wherever the caller's other warnings do.
8361
- envConfig: readEnvApiToken(config.userAuthUrl, env, warn),
8362
- settingsPath,
8363
- warn
8364
- }),
8571
+ store: createEnvApiTokenStore({ fileStore, envConfig, settingsPath, warn }),
8365
8572
  config,
8366
- usingEnvApiToken
8573
+ usingEnvApiToken,
8574
+ envApiTokenActive: envConfig !== null
8367
8575
  };
8368
8576
  }
8369
8577
  function warnIfStaticTokenShadowsApiToken(inlineToken, env = process.env, warn = (message) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myspec/mcp-server",
3
- "version": "1.1.1-next.156",
3
+ "version": "1.1.1-next.157",
4
4
  "description": "MySpec MCP server — exposes MySpec platform projects, files and attachments to MCP-aware clients via OAuth-authenticated access tokens.",
5
5
  "type": "module",
6
6
  "repository": {