@markmnl/fmsg-mcp 0.1.0 → 0.1.1

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/README.md CHANGED
@@ -120,6 +120,8 @@ attach resources; prompts `chat` and `reply` script the wait → reply loop and
120
120
 
121
121
  The API key is exchanged for a short-lived access token that the server renews automatically.
122
122
 
123
+ Over stdio the server also starts with no credentials at all, so hosts and directories can list its tools; every tool call then returns a message naming the missing variables.
124
+
123
125
  ## Safety
124
126
 
125
127
  - Sent messages cannot be edited or recalled; send tools say so in their descriptions and are
package/dist/config.d.ts CHANGED
@@ -28,4 +28,12 @@ export type ConfigOverrides = {
28
28
  host?: string;
29
29
  port?: number;
30
30
  };
31
- export declare function loadConfig(env: NodeJS.ProcessEnv, transport: Transport, overrides?: ConfigOverrides): Config;
31
+ export type LoadConfigOptions = {
32
+ /**
33
+ * stdio only: when false, a missing FMSG_API_URL / FMSG_API_KEY does not throw and
34
+ * `apiUrl` is left empty, so the server can still start and answer introspection
35
+ * (tools/list etc.). Tools then fail with a configuration hint when called.
36
+ */
37
+ requireCredentials?: boolean;
38
+ };
39
+ export declare function loadConfig(env: NodeJS.ProcessEnv, transport: Transport, overrides?: ConfigOverrides, options?: LoadConfigOptions): Config;
package/dist/config.js CHANGED
@@ -37,14 +37,16 @@ function loadDirectory(path) {
37
37
  }
38
38
  return out;
39
39
  }
40
- export function loadConfig(env, transport, overrides = {}) {
41
- const apiUrl = env.FMSG_API_URL?.trim();
42
- if (!apiUrl)
40
+ export function loadConfig(env, transport, overrides = {}, options = {}) {
41
+ const requireCredentials = options.requireCredentials ?? true;
42
+ const apiUrl = env.FMSG_API_URL?.trim() ?? "";
43
+ if (!apiUrl && (transport === "http" || requireCredentials)) {
43
44
  throw new Error("FMSG_API_URL is required (base URL of the fmsg Web API, e.g. https://api.example.com)");
44
- if (!/^https?:\/\//u.test(apiUrl))
45
+ }
46
+ if (apiUrl && !/^https?:\/\//u.test(apiUrl))
45
47
  throw new Error("FMSG_API_URL must start with http:// or https://");
46
48
  const apiKey = env.FMSG_API_KEY?.trim();
47
- if (transport === "stdio" && !apiKey) {
49
+ if (transport === "stdio" && !apiKey && requireCredentials) {
48
50
  throw new Error("FMSG_API_KEY is required in stdio mode (an fmsgk_... key for the address this server sends as)");
49
51
  }
50
52
  if (transport === "http" && apiKey) {
package/dist/context.d.ts CHANGED
@@ -17,4 +17,10 @@ export declare class StaticCallerProvider implements CallerProvider {
17
17
  constructor(client: FmsgClient);
18
18
  forRequest(): Promise<Caller>;
19
19
  }
20
+ /** stdio without credentials: the server starts (so hosts can list tools) but every tool explains what is missing. */
21
+ export declare class UnconfiguredCallerProvider implements CallerProvider {
22
+ private readonly reason;
23
+ constructor(reason: string);
24
+ forRequest(): Promise<Caller>;
25
+ }
20
26
  export declare function callerFor(provider: CallerProvider, ctx: ServerContext): Promise<Caller>;
package/dist/context.js CHANGED
@@ -12,6 +12,16 @@ export class StaticCallerProvider {
12
12
  return this.caller;
13
13
  }
14
14
  }
15
+ /** stdio without credentials: the server starts (so hosts can list tools) but every tool explains what is missing. */
16
+ export class UnconfiguredCallerProvider {
17
+ reason;
18
+ constructor(reason) {
19
+ this.reason = reason;
20
+ }
21
+ forRequest() {
22
+ return Promise.reject(new Error(this.reason));
23
+ }
24
+ }
15
25
  export async function callerFor(provider, ctx) {
16
26
  return provider.forRequest(ctx.http?.authInfo);
17
27
  }
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
4
4
  import { serveStdio } from "@modelcontextprotocol/server/stdio";
5
5
  import { FmsgClient } from "./client/client.js";
6
6
  import { loadConfig, DEFAULT_HTTP_PORT } from "./config.js";
7
- import { StaticCallerProvider } from "./context.js";
7
+ import { StaticCallerProvider, UnconfiguredCallerProvider } from "./context.js";
8
8
  import { createHttpServer, MCP_PATH } from "./http.js";
9
9
  import { createFmsgMcpServer } from "./server.js";
10
10
  import { PACKAGE_NAME, VERSION } from "./version.js";
@@ -89,18 +89,26 @@ async function main() {
89
89
  const transport = args.mode;
90
90
  let config;
91
91
  try {
92
- config = loadConfig(process.env, transport, args.overrides);
92
+ config = loadConfig(process.env, transport, args.overrides, { requireCredentials: false });
93
93
  }
94
94
  catch (error) {
95
95
  console.error(`fmsg-mcp: ${error instanceof Error ? error.message : String(error)}`);
96
96
  process.exit(2);
97
97
  }
98
98
  if (transport === "stdio") {
99
- const client = new FmsgClient(config.apiUrl, config.apiKey);
100
- const provider = new StaticCallerProvider(client);
101
99
  const cfg = config;
100
+ let provider;
101
+ if (cfg.apiUrl && cfg.apiKey) {
102
+ provider = new StaticCallerProvider(new FmsgClient(cfg.apiUrl, cfg.apiKey));
103
+ console.error(`fmsg-mcp ${VERSION} serving stdio for ${cfg.apiUrl}`);
104
+ }
105
+ else {
106
+ const missing = [!cfg.apiUrl && "FMSG_API_URL", !cfg.apiKey && "FMSG_API_KEY"].filter(Boolean).join(" and ");
107
+ const reason = `fmsg-mcp is not configured: set ${missing} (the fmsg Web API base URL and an fmsgk_... API key for the address this server sends as)`;
108
+ provider = new UnconfiguredCallerProvider(reason);
109
+ console.error(`fmsg-mcp ${VERSION} serving stdio WITHOUT credentials (${missing} not set): tools are listed but every call will fail until configured`);
110
+ }
102
111
  const handle = serveStdio(() => createFmsgMcpServer(provider, cfg));
103
- console.error(`fmsg-mcp ${VERSION} serving stdio for ${config.apiUrl}`);
104
112
  const stop = () => void handle.close().finally(() => process.exit(0));
105
113
  process.on("SIGINT", stop);
106
114
  process.on("SIGTERM", stop);
package/dist/public.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { createFmsgMcpServer, SERVER_NAME } from "./server.js";
2
2
  export { createHttpServer, type HttpServerHandle } from "./http.js";
3
- export { loadConfig, type Config, type Transport } from "./config.js";
4
- export { StaticCallerProvider, type Caller, type CallerProvider } from "./context.js";
3
+ export { loadConfig, type Config, type LoadConfigOptions, type Transport } from "./config.js";
4
+ export { StaticCallerProvider, UnconfiguredCallerProvider, type Caller, type CallerProvider } from "./context.js";
5
5
  export { ApiKeyCallerProvider } from "./auth.js";
6
6
  export { waitForMessage, type WaitOptions, type WaitResult } from "./wait.js";
7
7
  export { assembleThread, renderThread, type AssembledThread } from "./thread.js";
package/dist/public.js CHANGED
@@ -1,7 +1,7 @@
1
1
  export { createFmsgMcpServer, SERVER_NAME } from "./server.js";
2
2
  export { createHttpServer } from "./http.js";
3
3
  export { loadConfig } from "./config.js";
4
- export { StaticCallerProvider } from "./context.js";
4
+ export { StaticCallerProvider, UnconfiguredCallerProvider } from "./context.js";
5
5
  export { ApiKeyCallerProvider } from "./auth.js";
6
6
  export { waitForMessage } from "./wait.js";
7
7
  export { assembleThread, renderThread } from "./thread.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@markmnl/fmsg-mcp",
3
3
  "mcpName": "io.github.markmnl/fmsg-mcp",
4
- "version": "0.1.0",
4
+ "version": "0.1.1",
5
5
  "description": "MCP server for fmsg: send and receive federated messages from any AI agent via a deployed fmsg Web API",
6
6
  "type": "module",
7
7
  "license": "MIT",
package/server.json CHANGED
@@ -6,18 +6,33 @@
6
6
  "url": "https://github.com/markmnl/fmsg-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.1.0",
9
+ "version": "0.1.1",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "registryBaseUrl": "https://registry.npmjs.org",
14
14
  "identifier": "@markmnl/fmsg-mcp",
15
- "version": "0.1.0",
16
- "transport": { "type": "stdio" },
15
+ "version": "0.1.1",
16
+ "transport": {
17
+ "type": "stdio"
18
+ },
17
19
  "environmentVariables": [
18
- { "name": "FMSG_API_URL", "description": "Base URL of the fmsg Web API", "isRequired": true },
19
- { "name": "FMSG_API_KEY", "description": "fmsg API key (fmsgk_...) for the address to send as", "isRequired": true, "isSecret": true },
20
- { "name": "FMSG_DEFAULT_DOMAIN", "description": "Optional: lets short names resolve to @name@<domain>", "isRequired": false }
20
+ {
21
+ "name": "FMSG_API_URL",
22
+ "description": "Base URL of the fmsg Web API",
23
+ "isRequired": true
24
+ },
25
+ {
26
+ "name": "FMSG_API_KEY",
27
+ "description": "fmsg API key (fmsgk_...) for the address to send as",
28
+ "isRequired": true,
29
+ "isSecret": true
30
+ },
31
+ {
32
+ "name": "FMSG_DEFAULT_DOMAIN",
33
+ "description": "Optional: lets short names resolve to @name@<domain>",
34
+ "isRequired": false
35
+ }
21
36
  ]
22
37
  }
23
38
  ]