@topolo/mcp 0.8.0 → 0.9.0

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/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // src/index.ts
4
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
5
 
6
- // ../topolo-sdk/dist/index.js
6
+ // ../../../topolo-platform/packages/topolo-sdk/dist/index.js
7
7
  function applyAuthHeaders(headers, credential) {
8
8
  if (credential.kind === "api_key") {
9
9
  headers.set("X-Api-Key", credential.apiKey);
@@ -3369,7 +3369,7 @@ function createTopolo(options) {
3369
3369
  };
3370
3370
  }
3371
3371
 
3372
- // ../topolo-sdk/dist/node/credential-store.js
3372
+ // ../../../topolo-platform/packages/topolo-sdk/dist/node/credential-store.js
3373
3373
  import { mkdir, readFile, writeFile, chmod } from "fs/promises";
3374
3374
  import { existsSync } from "fs";
3375
3375
  import { homedir, platform } from "os";
@@ -3699,6 +3699,46 @@ async function refreshCredentialIfNeeded(environment = selectedEnvironment(), op
3699
3699
  return cred;
3700
3700
  }
3701
3701
  }
3702
+ async function refreshContextCredentialIfNeeded(environment, key, options = {}) {
3703
+ const config = await readConfig();
3704
+ const profile = profileForEnvironment(config, environment);
3705
+ const context = profile.contexts?.[key];
3706
+ const credential = context?.credential;
3707
+ if (!credential || credential.kind !== "access_token") return credential ?? null;
3708
+ const oauthClientId = context.oauthClientId ?? profile.oauthClientId;
3709
+ if (!credential.refreshToken || !credential.expiresAt || !oauthClientId) return credential;
3710
+ const now = Math.floor(Date.now() / 1e3);
3711
+ if (!options.force && credential.expiresAt - REFRESH_SKEW_SECONDS > now) return credential;
3712
+ try {
3713
+ const oauth = new TopoloOAuth({
3714
+ serviceUrls: resolveServiceUrls(profile.serviceUrls, environment)
3715
+ });
3716
+ const tokens = await oauth.refreshToken({
3717
+ clientId: oauthClientId,
3718
+ refreshToken: credential.refreshToken
3719
+ });
3720
+ const refreshed = {
3721
+ kind: "access_token",
3722
+ accessToken: tokens.access_token,
3723
+ ...tokens.refresh_token ? { refreshToken: tokens.refresh_token } : { refreshToken: credential.refreshToken },
3724
+ expiresAt: Math.floor(Date.now() / 1e3) + tokens.expires_in
3725
+ };
3726
+ await writeConfig(writeProfileConfig(config, environment, {
3727
+ ...profile.activeContext === key ? { credential: refreshed } : {},
3728
+ contexts: {
3729
+ ...profile.contexts ?? {},
3730
+ [key]: {
3731
+ ...context,
3732
+ credential: refreshed,
3733
+ oauthClientId
3734
+ }
3735
+ }
3736
+ }));
3737
+ return refreshed;
3738
+ } catch {
3739
+ return credential;
3740
+ }
3741
+ }
3702
3742
  function resolveAgentName(configAgentName) {
3703
3743
  return process.env.TOPOLO_AGENT_NAME ?? configAgentName;
3704
3744
  }
@@ -3721,15 +3761,6 @@ function resolveServiceUrls(configUrls, environment = selectedEnvironment()) {
3721
3761
  return Object.keys(merged).length > 0 ? merged : void 0;
3722
3762
  }
3723
3763
 
3724
- // src/auth.ts
3725
- function resolveCredentialFromEnv() {
3726
- const apiKey = process.env.TOPOLO_API_KEY;
3727
- if (apiKey) return { kind: "api_key", apiKey };
3728
- const accessToken = process.env.TOPOLO_ACCESS_TOKEN;
3729
- if (accessToken) return { kind: "access_token", accessToken };
3730
- return null;
3731
- }
3732
-
3733
3764
  // src/server.ts
3734
3765
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3735
3766
  import {
@@ -4444,14 +4475,79 @@ function toolError(message) {
4444
4475
  };
4445
4476
  }
4446
4477
 
4478
+ // src/auth.ts
4479
+ function resolveCredentialFromEnv() {
4480
+ const apiKey = process.env.TOPOLO_API_KEY;
4481
+ if (apiKey) return { kind: "api_key", apiKey };
4482
+ const accessToken = process.env.TOPOLO_ACCESS_TOKEN;
4483
+ if (accessToken) return { kind: "access_token", accessToken };
4484
+ return null;
4485
+ }
4486
+
4487
+ // src/context-credential.ts
4488
+ var DEFAULT_DEPENDENCIES = {
4489
+ readConfig,
4490
+ refreshContextCredentialIfNeeded,
4491
+ refreshCredentialIfNeeded,
4492
+ resolveCredentialFromEnv
4493
+ };
4494
+ function resolvePinnedContextKey(profile, selector) {
4495
+ const normalized = selector.trim().toLowerCase();
4496
+ if (!normalized) {
4497
+ throw new Error("TOPOLO_CONTEXT must not be empty");
4498
+ }
4499
+ const contexts = Object.entries(profile.contexts ?? {});
4500
+ const exact = contexts.find(([key]) => key.toLowerCase() === normalized);
4501
+ if (exact) return exact[0];
4502
+ const matches = contexts.filter(([, context]) => [
4503
+ context.organizationId,
4504
+ context.organizationSlug,
4505
+ context.organizationName
4506
+ ].some((value) => String(value || "").trim().toLowerCase() === normalized));
4507
+ if (matches.length === 1) return matches[0][0];
4508
+ if (matches.length > 1) {
4509
+ throw new Error(`TOPOLO_CONTEXT "${selector}" is ambiguous; use an exact context key`);
4510
+ }
4511
+ throw new Error(`TOPOLO_CONTEXT "${selector}" is not signed in for this environment`);
4512
+ }
4513
+ async function resolveMcpCredential(environment, selector = process.env.TOPOLO_CONTEXT, dependencies = DEFAULT_DEPENDENCIES) {
4514
+ const environmentCredential = dependencies.resolveCredentialFromEnv();
4515
+ const requestedSelector = selector?.trim();
4516
+ if (environmentCredential) {
4517
+ if (requestedSelector) {
4518
+ throw new Error("TOPOLO_CONTEXT cannot be combined with an environment credential");
4519
+ }
4520
+ return { credential: environmentCredential, contextKey: null };
4521
+ }
4522
+ if (requestedSelector) {
4523
+ const config = await dependencies.readConfig();
4524
+ const contextKey = resolvePinnedContextKey(
4525
+ profileForEnvironment(config, environment),
4526
+ requestedSelector
4527
+ );
4528
+ const credential = await dependencies.refreshContextCredentialIfNeeded(environment, contextKey);
4529
+ return {
4530
+ credential,
4531
+ contextKey,
4532
+ refreshCredential: () => dependencies.refreshContextCredentialIfNeeded(
4533
+ environment,
4534
+ contextKey,
4535
+ { force: true }
4536
+ )
4537
+ };
4538
+ }
4539
+ return {
4540
+ credential: await dependencies.refreshCredentialIfNeeded(environment),
4541
+ contextKey: null,
4542
+ refreshCredential: () => dependencies.refreshCredentialIfNeeded(environment, { force: true })
4543
+ };
4544
+ }
4545
+
4447
4546
  // src/index.ts
4448
4547
  async function main() {
4449
4548
  const environment = normalizeEnvironment(process.env.TOPOLO_ENV);
4450
- const environmentCredential = resolveCredentialFromEnv();
4451
- let credential = environmentCredential;
4452
- if (!credential) {
4453
- credential = await refreshCredentialIfNeeded(environment);
4454
- }
4549
+ const resolvedCredential = await resolveMcpCredential(environment);
4550
+ const credential = resolvedCredential.credential;
4455
4551
  if (!credential) {
4456
4552
  process.stderr.write(
4457
4553
  "TopoloMCP: no Topolo credential. Run `topolo auth login`, or set TOPOLO_API_KEY / TOPOLO_ACCESS_TOKEN.\n"
@@ -4471,7 +4567,7 @@ async function main() {
4471
4567
  },
4472
4568
  serviceUrls,
4473
4569
  requireConfirmForWrites: true,
4474
- ...!environmentCredential ? { refreshCredential: () => refreshCredentialIfNeeded(environment, { force: true }) } : {}
4570
+ ...resolvedCredential.refreshCredential ? { refreshCredential: resolvedCredential.refreshCredential } : {}
4475
4571
  });
4476
4572
  const scopes = await loadScopes(topolo);
4477
4573
  const server = buildServer(topolo, scopes);
@@ -4479,7 +4575,7 @@ async function main() {
4479
4575
  await server.connect(transport);
4480
4576
  const toolNames = await advertisedToolNames(topolo, scopes);
4481
4577
  process.stderr.write(
4482
- `TopoloMCP v${MCP_VERSION} connected (${environment}). Tools available: ${toolNames.join(", ") || "(none)"}
4578
+ `TopoloMCP v${MCP_VERSION} connected (${environment}${resolvedCredential.contextKey ? `, ${resolvedCredential.contextKey}` : ""}). Tools available: ${toolNames.join(", ") || "(none)"}
4483
4579
  `
4484
4580
  );
4485
4581
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topolo/mcp",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Model Context Protocol server for the Topolo platform. Exposes scope-gated tools that third-party agents (Claude, Codex, etc.) can call natively.",
5
5
  "type": "module",
6
6
  "bin": {