@flomenco/claude-plugin-mcp 0.1.2 → 0.1.3

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +67 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flomenco/claude-plugin-mcp",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Flo Claude Co-Work plugin bridge for Claude Desktop/Code",
5
5
  "private": false,
6
6
  "license": "UNLICENSED",
package/src/index.js CHANGED
@@ -17,6 +17,7 @@ import { spawn } from "node:child_process";
17
17
  const DEFAULT_TIMEOUT_MS = 60_000;
18
18
  const OAUTH_WAIT_TIMEOUT_MS = 180_000;
19
19
  const TOKEN_REFRESH_SKEW_SECONDS = 60;
20
+ const ASSET_ID_REGEX = /^[A-Za-z0-9._-]+$/;
20
21
  const DEFAULT_TOKEN_CACHE_PATH = path.join(
21
22
  os.homedir(),
22
23
  ".flo",
@@ -78,6 +79,17 @@ function defaultSettingsUrlForEnv() {
78
79
  return "https://dev.floapp.co/settings/api";
79
80
  }
80
81
 
82
+ function defaultWebAppUrlForEnv() {
83
+ const env = normalizedPluginEnv();
84
+ if (env === "prod") {
85
+ return "https://floapp.co";
86
+ }
87
+ if (env === "stg") {
88
+ return "https://stg.floapp.co";
89
+ }
90
+ return "https://dev.floapp.co";
91
+ }
92
+
81
93
  function getInvocationUrl() {
82
94
  const raw =
83
95
  process.env.FLO_INTERFACE_AGENT_INVOCATION_URL ||
@@ -119,6 +131,8 @@ function getOAuthConfig(strict = false) {
119
131
  process.env.FLO_OAUTH_CLIENT_ID_HELP_URL ||
120
132
  defaultSettingsUrlForEnv()
121
133
  ).trim();
134
+ const rawAppLoginUrl = (process.env.FLO_OAUTH_APP_LOGIN_URL || "").trim();
135
+ const appLoginUrl = rawAppLoginUrl || `${defaultWebAppUrlForEnv()}/login`;
122
136
 
123
137
  const ready = Boolean(authorizeUrl && tokenUrl && clientId);
124
138
  if (strict && !ready) {
@@ -141,6 +155,7 @@ function getOAuthConfig(strict = false) {
141
155
  userPoolName,
142
156
  expectedClientName,
143
157
  settingsUrl,
158
+ appLoginUrl,
144
159
  };
145
160
  }
146
161
 
@@ -643,6 +658,17 @@ function requireString(value, fieldName) {
643
658
  return value.trim();
644
659
  }
645
660
 
661
+ function requireAssetId(value, fieldName) {
662
+ const assetId = requireString(value, fieldName);
663
+ if (assetId.length > 128 || !ASSET_ID_REGEX.test(assetId)) {
664
+ throw new McpError(
665
+ ErrorCode.InvalidParams,
666
+ `\`${fieldName}\` must match ^[A-Za-z0-9._-]+$ and be <= 128 chars.`
667
+ );
668
+ }
669
+ return assetId;
670
+ }
671
+
646
672
  function assertSearchPayload(payload) {
647
673
  if (!payload || payload.status !== "ok" || !Array.isArray(payload.menuItems)) {
648
674
  throw new McpError(
@@ -742,7 +768,7 @@ const tools = [
742
768
  {
743
769
  name: "flo_auth_login",
744
770
  description:
745
- "Start OAuth login (browser PKCE flow), cache token locally, and return auth status.",
771
+ "Start OAuth login (browser PKCE flow), cache token locally, and return auth status. If prompted by hosted login UI, first sign in via appLoginUrl.",
746
772
  inputSchema: {
747
773
  type: "object",
748
774
  properties: {},
@@ -777,6 +803,28 @@ const tools = [
777
803
  additionalProperties: false,
778
804
  },
779
805
  },
806
+ {
807
+ name: "flo_analyze",
808
+ description:
809
+ "Analyze an asset directly (friendly alias for /flo:analyze-image <assetId>).",
810
+ inputSchema: {
811
+ type: "object",
812
+ properties: {
813
+ assetId: {
814
+ type: "string",
815
+ minLength: 1,
816
+ maxLength: 128,
817
+ pattern: "^[A-Za-z0-9._-]+$",
818
+ },
819
+ authToken: {
820
+ type: "string",
821
+ description: "Optional bearer token override for this call only.",
822
+ },
823
+ },
824
+ required: ["assetId"],
825
+ additionalProperties: false,
826
+ },
827
+ },
780
828
  {
781
829
  name: "flo_search",
782
830
  description:
@@ -814,11 +862,16 @@ const tools = [
814
862
  {
815
863
  name: "flo_skill_routing",
816
864
  description:
817
- "Run /flo:skill-routing for an asset and return available plugin skills.",
865
+ "List available actions for an asset (what can I do with this file?).",
818
866
  inputSchema: {
819
867
  type: "object",
820
868
  properties: {
821
- assetId: { type: "string", minLength: 1 },
869
+ assetId: {
870
+ type: "string",
871
+ minLength: 1,
872
+ maxLength: 128,
873
+ pattern: "^[A-Za-z0-9._-]+$",
874
+ },
822
875
  authToken: {
823
876
  type: "string",
824
877
  description: "Optional bearer token override for this call only.",
@@ -960,6 +1013,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
960
1013
  oauthUserPoolName: oauth.userPoolName,
961
1014
  oauthUserPoolId: oauth.userPoolId || null,
962
1015
  oauthSettingsUrl: oauth.settingsUrl,
1016
+ appLoginUrl: oauth.appLoginUrl,
963
1017
  envTokenConfigured: envToken,
964
1018
  cachePath: getTokenCachePath(),
965
1019
  cachedTokenPresent: Boolean(cachedToken?.access_token),
@@ -982,11 +1036,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
982
1036
  oauthUserPoolName: oauth.userPoolName,
983
1037
  oauthUserPoolId: oauth.userPoolId || null,
984
1038
  oauthSettingsUrl: oauth.settingsUrl,
1039
+ appLoginUrl: oauth.appLoginUrl,
985
1040
  message:
986
- "Open oauthSettingsUrl, then copy the app client id for oauthExpectedClientName into FLO_OAUTH_CLIENT_ID.",
1041
+ "If hosted login UI appears, sign in at appLoginUrl first, then run flo_auth_login again. Open oauthSettingsUrl to copy client id for oauthExpectedClientName into FLO_OAUTH_CLIENT_ID.",
987
1042
  });
988
1043
  }
989
1044
 
1045
+ if (name === "flo_analyze") {
1046
+ const assetId = requireAssetId(args.assetId, "assetId");
1047
+ const prompt = `/flo:analyze-image ${assetId}`;
1048
+ const bodyText = await invokeInterfaceAgent(prompt, args.authToken);
1049
+ return asTextResult(parseMaybeJson(bodyText) || bodyText);
1050
+ }
1051
+
990
1052
  if (name === "flo_auth_logout") {
991
1053
  await clearCachedToken();
992
1054
  return asTextResult({
@@ -1011,7 +1073,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1011
1073
  }
1012
1074
 
1013
1075
  if (name === "flo_skill_routing") {
1014
- const assetId = requireString(args.assetId, "assetId");
1076
+ const assetId = requireAssetId(args.assetId, "assetId");
1015
1077
  const prompt = `/flo:skill-routing ${assetId}`;
1016
1078
  const bodyText = await invokeInterfaceAgent(prompt, args.authToken);
1017
1079
  return asTextResult(parseMaybeJson(bodyText) || bodyText);