@featureflip/mcp 0.1.3 → 0.1.5

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.
@@ -0,0 +1,12 @@
1
+ import { ToolContext } from './tools/context.js';
2
+ /**
3
+ * Decides what the server serves before it starts.
4
+ *
5
+ * A configured token still preflights and still fails fast on a bad one — the
6
+ * startup error is unchanged. No token at all is a different situation: nobody
7
+ * has wired this up yet, and the caller may only be enumerating the tool surface,
8
+ * so we hand back a context that serves definitions and errors on use. Exiting
9
+ * there made the server indistinguishable from one with no tools.
10
+ */
11
+ export declare function resolveContext(env?: Record<string, string | undefined>, fetchImpl?: typeof fetch): Promise<ToolContext>;
12
+ //# sourceMappingURL=bootstrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAyBtD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,EACrD,SAAS,CAAC,EAAE,OAAO,KAAK,GACvB,OAAO,CAAC,WAAW,CAAC,CAGtB"}
package/dist/cli.mjs CHANGED
@@ -1,20 +1,51 @@
1
- import { a as e, i as t, n, r, t as i } from "./org-CiZ_VQdv.js";
2
- import { serveStdio as a } from "@modelcontextprotocol/server/stdio";
3
- //#region src/cli.ts
4
- async function o() {
5
- let o = n(), s = new e(o);
1
+ import { a as e, i as t, n, o as r, s as i, t as a } from "./org-fuZ3wOph.js";
2
+ import { serveStdio as o } from "@modelcontextprotocol/server/stdio";
3
+ //#region src/unconfigured.ts
4
+ var s = class extends i {
5
+ constructor() {
6
+ super({
7
+ token: "",
8
+ baseUrl: "https://api.featureflip.io"
9
+ });
10
+ }
11
+ request() {
12
+ return Promise.reject(Error(n));
13
+ }
14
+ };
15
+ function c() {
16
+ return {
17
+ api: new s(),
18
+ org: ""
19
+ };
20
+ }
21
+ //#endregion
22
+ //#region src/bootstrap.ts
23
+ async function l(e, t) {
24
+ let n = new i({
25
+ ...e,
26
+ fetchImpl: t
27
+ });
6
28
  try {
7
- await s.request("GET", "/api/v1/me");
29
+ await n.request("GET", "/api/v1/me");
8
30
  } catch (e) {
9
- throw e instanceof t && e.status === 401 ? Error("FEATUREFLIP_TOKEN was rejected (401). Check the token in the Featureflip dashboard (Settings → API Tokens).") : e instanceof t && e.status === 404 ? Error("The public Management API returned 404 for /api/v1/me. This usually means the public API is not enabled for your organization yet — contact Featureflip support.") : e;
31
+ throw e instanceof r && e.status === 401 ? Error("FEATUREFLIP_TOKEN was rejected (401). Check the token in the Featureflip dashboard (Settings → API Tokens).") : e instanceof r && e.status === 404 ? Error("The public Management API returned 404 for /api/v1/me. This usually means the public API is not enabled for your organization yet — contact Featureflip support.") : e;
10
32
  }
11
- let c = await i(s, o.org);
12
- a(() => r({
13
- api: s,
14
- org: c
15
- }));
33
+ return {
34
+ api: n,
35
+ org: await a(n, e.org)
36
+ };
37
+ }
38
+ function u(e = process.env, n) {
39
+ let r = t(e);
40
+ return r ? l(r, n) : Promise.resolve(c());
41
+ }
42
+ //#endregion
43
+ //#region src/cli.ts
44
+ async function d() {
45
+ let t = await u();
46
+ o(() => e(t));
16
47
  }
17
- o().catch((e) => {
48
+ d().catch((e) => {
18
49
  console.error(`[featureflip-mcp] ${e instanceof Error ? e.message : String(e)}`), process.exit(1);
19
50
  });
20
51
  //#endregion
package/dist/config.d.ts CHANGED
@@ -3,5 +3,12 @@ export interface McpConfig {
3
3
  baseUrl: string;
4
4
  org?: string;
5
5
  }
6
+ export declare const MISSING_TOKEN_MESSAGE: string;
7
+ /**
8
+ * Returns null instead of throwing when no token is configured, so the server can
9
+ * still start and advertise its tool definitions to a client that is only
10
+ * inspecting the surface (registries, `tools/list` probes, first-run editors).
11
+ */
12
+ export declare function tryLoadConfig(env?: Record<string, string | undefined>): McpConfig | null;
6
13
  export declare function loadConfig(env?: Record<string, string | undefined>): McpConfig;
7
14
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,SAAS,CAY3F"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,qBAAqB,QAGO,CAAC;AAE1C;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,SAAS,GAAG,IAAI,CAMrG;AAED,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,SAAS,CAI3F"}
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { a as e, i as t, n, o as r, r as i, t as a } from "./org-CiZ_VQdv.js";
2
- export { t as ApiError, e as FeatureflipApi, r as PKG_VERSION, i as createServer, n as loadConfig, a as resolveOrg };
1
+ import { a as e, c as t, o as n, r, s as i, t as a } from "./org-fuZ3wOph.js";
2
+ export { n as ApiError, i as FeatureflipApi, t as PKG_VERSION, e as createServer, r as loadConfig, a as resolveOrg };
@@ -4,7 +4,7 @@ import * as t from "zod/v4";
4
4
  //#region src/version.ts
5
5
  var n = {
6
6
  name: "@featureflip/mcp",
7
- version: "0.1.3",
7
+ version: "0.1.5",
8
8
  mcpName: "io.github.canopy-labs/featureflip",
9
9
  description: "MCP server for managing Featureflip feature flags from AI agents and editors",
10
10
  license: "Apache-2.0",
@@ -31,15 +31,15 @@ var n = {
31
31
  },
32
32
  dependencies: {
33
33
  "@modelcontextprotocol/server": "2.0.0",
34
- zod: "^4.0.0"
34
+ zod: "^4.6.2"
35
35
  },
36
36
  devDependencies: {
37
37
  "@modelcontextprotocol/client": "2.0.0",
38
- "@types/node": "^26.2.0",
38
+ "@types/node": "^26.5.1",
39
39
  typescript: "^7.0.2",
40
- vite: "^8.2.2",
41
- "vite-plugin-dts": "^5.0.3",
42
- vitest: "^4.1.11"
40
+ vite: "^8.3.0",
41
+ "vite-plugin-dts": "^5.1.0",
42
+ vitest: "^5.0.0"
43
43
  },
44
44
  engines: { node: ">=20.19.0" }
45
45
  }.version, r = class extends Error {
@@ -231,7 +231,7 @@ function m(e, n) {
231
231
  })).optional(),
232
232
  idempotency_key: t.string().optional().describe("Idempotency-Key header for safe retries")
233
233
  }),
234
- annotations: {}
234
+ annotations: { destructiveHint: !1 }
235
235
  }, async ({ project: e, idempotency_key: t, ...i }) => c(async () => o(await n.api.request("POST", r(e), {
236
236
  body: i,
237
237
  idempotencyKey: t
@@ -246,7 +246,7 @@ function m(e, n) {
246
246
  tags: t.array(t.string()).optional(),
247
247
  clientSideVisible: t.boolean().optional()
248
248
  }),
249
- annotations: {}
249
+ annotations: { destructiveHint: !0 }
250
250
  }, async ({ project: e, flag: t, ...a }) => c(async () => (await n.api.request("PUT", `${r(e)}/${i(t)}`, { body: a }), o({
251
251
  project: e,
252
252
  flag: t,
@@ -277,7 +277,7 @@ function m(e, n) {
277
277
  project: t.string(),
278
278
  flag: t.string()
279
279
  }),
280
- annotations: {}
280
+ annotations: { destructiveHint: !1 }
281
281
  }, async ({ project: e, flag: t }) => c(async () => (await n.api.request("POST", `${r(e)}/${i(t)}/restore`), o({
282
282
  flag: t,
283
283
  archived: !1
@@ -460,7 +460,7 @@ function S(e, n) {
460
460
  let t = await n.api.request("GET", `${r}/${i(e.key)}/environments`);
461
461
  if (t.length === 0) continue;
462
462
  let a = t.every((e) => e.isEnabled), o = t.every((e) => !e.isEnabled);
463
- !a && !o || d.push({
463
+ (a || o) && d.push({
464
464
  key: e.key,
465
465
  name: e.name,
466
466
  updatedAt: e.updatedAt,
@@ -664,7 +664,7 @@ function k(e, n) {
664
664
  language: t.enum(C).describe("SDK language of the codebase being edited"),
665
665
  idempotency_key: t.string().optional()
666
666
  }),
667
- annotations: {}
667
+ annotations: { destructiveHint: !1 }
668
668
  }, async ({ project: e, key: t, name: r, description: a, tags: s, language: l, idempotency_key: u }) => c(async () => {
669
669
  let c = await n.api.request("POST", `/api/v1/orgs/${i(n.org)}/projects/${i(e)}/flags`, {
670
670
  body: {
@@ -704,18 +704,23 @@ function A(t) {
704
704
  }
705
705
  //#endregion
706
706
  //#region src/config.ts
707
- function j(e = process.env) {
707
+ var j = "FEATUREFLIP_TOKEN is required. Create one in the Featureflip dashboard — Settings → API Tokens (personal token, ffp_...) or Organization Settings → Service Tokens (ffs_...) — and set it in your MCP client config.";
708
+ function M(e = process.env) {
708
709
  let t = e.FEATUREFLIP_TOKEN?.trim();
709
- if (!t) throw Error("FEATUREFLIP_TOKEN is required. Create one in the Featureflip dashboard — Settings → API Tokens (personal token, ffp_...) or Organization Settings → Service Tokens (ffs_...) — and set it in your MCP client config.");
710
- return {
710
+ return t ? {
711
711
  token: t,
712
712
  baseUrl: (e.FEATUREFLIP_API_URL?.trim() || "https://api.featureflip.io").replace(/\/+$/, ""),
713
713
  org: e.FEATUREFLIP_ORG?.trim() || void 0
714
- };
714
+ } : null;
715
+ }
716
+ function N(e = process.env) {
717
+ let t = M(e);
718
+ if (!t) throw Error(j);
719
+ return t;
715
720
  }
716
721
  //#endregion
717
722
  //#region src/org.ts
718
- async function M(e, t) {
723
+ async function P(e, t) {
719
724
  if (t) return t;
720
725
  let n = (await e.request("GET", "/api/v1/orgs", { query: { limit: 100 } })).items ?? [];
721
726
  if (n.length === 1) {
@@ -726,4 +731,4 @@ async function M(e, t) {
726
731
  throw n.length === 0 ? Error("The token can see no organizations. Check the token in the Featureflip dashboard.") : Error(`The token can see ${n.length} organizations — set FEATUREFLIP_ORG to one of: ` + n.map((e) => e.slug ?? "?").join(", "));
727
732
  }
728
733
  //#endregion
729
- export { a, r as i, j as n, n as o, A as r, M as t };
734
+ export { A as a, n as c, M as i, j as n, r as o, N as r, a as s, P as t };
@@ -0,0 +1,11 @@
1
+ import { ToolContext } from './tools/context.js';
2
+ /**
3
+ * A context that serves tool *definitions* but no data.
4
+ *
5
+ * `tools/list` is how a client discovers what this server can do, and registries
6
+ * (Glama among them) enumerate it before anyone has wired a token. Exiting at
7
+ * startup made the server look like it had no tools at all — so with no token we
8
+ * start anyway and fail at call time instead.
9
+ */
10
+ export declare function unconfiguredContext(): ToolContext;
11
+ //# sourceMappingURL=unconfigured.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unconfigured.d.ts","sourceRoot":"","sources":["../src/unconfigured.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAkBtD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,IAAI,WAAW,CAEjD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@featureflip/mcp",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "mcpName": "io.github.canopy-labs/featureflip",
5
5
  "description": "MCP server for managing Featureflip feature flags from AI agents and editors",
6
6
  "license": "Apache-2.0",
@@ -36,15 +36,15 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@modelcontextprotocol/server": "2.0.0",
39
- "zod": "^4.0.0"
39
+ "zod": "^4.6.2"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@modelcontextprotocol/client": "2.0.0",
43
- "@types/node": "^26.2.0",
43
+ "@types/node": "^26.5.1",
44
44
  "typescript": "^7.0.2",
45
- "vite": "^8.2.2",
46
- "vite-plugin-dts": "^5.0.3",
47
- "vitest": "^4.1.11"
45
+ "vite": "^8.3.0",
46
+ "vite-plugin-dts": "^5.1.0",
47
+ "vitest": "^5.0.0"
48
48
  },
49
49
  "engines": {
50
50
  "node": ">=20.19.0"