pombus 0.1.7 → 0.1.8

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
@@ -50,6 +50,10 @@ Set `POMBUS_API_KEY` in the environment. `init` writes it into the MCP server
50
50
  block for you; the key never lands in your terminal scrollback. Get a key at
51
51
  [pombus.com](https://pombus.com).
52
52
 
53
+ Without a key, `pombus mcp` still starts, for introspection only: it lists the
54
+ tool catalog, and every tool call returns a "POMBUS_API_KEY is not set" error.
55
+ It makes no network request until a key is set.
56
+
53
57
  ```json
54
58
  {
55
59
  "mcpServers": {
package/dist/i18n.js CHANGED
@@ -12,7 +12,9 @@ const LOCALES = ["en-US", "pt-BR", "es-419"];
12
12
  // Codes that only ever reach the MODEL (inside a tool call) are NOT human i18n — they stay en-US
13
13
  // protocol text in server.ts (`error: <code>`), never in this dict.
14
14
  // Boot-reachable codes (path 1):
15
- // - no_api_key: the key env var is unset (server.ts, before any request).
15
+ // - no_api_key: the key env var is unset (server.ts, before any request). Also the text of every
16
+ // tool call on a keyless server — the one human message that reaches the model, because only the
17
+ // human can fix it.
16
18
  // - auth_failed: /v0/auth/me rejected the key at boot (invalid/expired).
17
19
  // - network: the core was unreachable at boot.
18
20
  // - unknown: the last-resort fallback for a boot error with no mapped code (bin.ts catch).
package/dist/scope.js CHANGED
@@ -76,6 +76,12 @@ export class ScopeBinding {
76
76
  // domain / pod / organization: no single implied inbox.
77
77
  return new ScopeBinding(me.scope_type, null, permissions, podId);
78
78
  }
79
+ /** The binding of a server started WITHOUT a key (stdio introspection): shaped like an org master key
80
+ * so the catalog lists every tool with its generic schema. It never reaches a request — the keyless
81
+ * server refuses every call before dispatch (server.ts keylessServer). */
82
+ static unbound() {
83
+ return new ScopeBinding("organization", null, null, null);
84
+ }
79
85
  /** Record inbox ids returned by list_inboxes so later calls can validate against the enumeration. */
80
86
  remember(ids) {
81
87
  for (const id of ids)
package/dist/server.js CHANGED
@@ -13,17 +13,18 @@ import { CoreClient, CoreError, readApiKey } from "./client.js";
13
13
  import { ScopeBinding, ScopeError } from "./scope.js";
14
14
  import { specByName, toolDescriptors, callTool, callOrgTool, isOrgTool, ToolArgError, } from "./tools.js";
15
15
  const SERVER_NAME = "pombus";
16
- const SERVER_VERSION = "0.1.7";
17
- /** Build and connect the MCP server over stdio. Fails fast (before connecting) when the key is absent,
18
- * so a misconfigured client sees a clear stderr line instead of a server that answers every tool with
19
- * an auth error. */
16
+ const SERVER_VERSION = "0.1.8";
17
+ /** Build and connect the MCP server over stdio. Without a key the server still comes up for
18
+ * introspection (registries such as Glama boot it with no credential to read the catalog): it lists
19
+ * every tool and answers every call with the localized "set POMBUS_API_KEY" error, making no network
20
+ * call. A key that IS set but rejected still fails fast at boot — that is a config error to surface. */
20
21
  export async function runMcpServer() {
21
22
  const { humanMessage, humanCodeForBoot } = await import("./i18n.js");
22
23
  const apiKey = readApiKey();
23
24
  if (!apiKey) {
24
25
  // Human-facing, localized, to stderr — never stdout (would corrupt the MCP stream).
25
26
  process.stderr.write(humanMessage("no_api_key") + "\n");
26
- process.exitCode = 1;
27
+ await (await buildServer(null)).connect(new StdioServerTransport());
27
28
  return;
28
29
  }
29
30
  const client = new CoreClient({ apiKey });
@@ -46,16 +47,37 @@ export async function runMcpServer() {
46
47
  }
47
48
  /** Build the Server bound to a given core client. Exposed so tests drive it with a fake core and
48
49
  * without a real stdio transport. Resolves the key's scope up front (one /v0/auth/me call) so the
49
- * tool schemas can omit inbox_id for a single-inbox key. */
50
+ * tool schemas can omit inbox_id for a single-inbox key. `null` = no key (stdio introspection only):
51
+ * the full catalog is listed and every call is refused before any request could exist. */
50
52
  export async function buildServer(client, opts = {}) {
51
- const scope = await ScopeBinding.resolve(client);
53
+ if (client === null)
54
+ return keylessServer();
55
+ return boundServer(client, await ScopeBinding.resolve(client), opts);
56
+ }
57
+ /** No key: list the whole catalog (an org master binding — every tool, generic schemas) and refuse
58
+ * every call with the localized no_api_key line, so the model can tell the human what to configure.
59
+ * Localized on purpose (unlike the en-US `error: <code>` protocol text): the fix is the human's. */
60
+ function keylessServer() {
61
+ const scope = ScopeBinding.unbound();
62
+ const server = newServer();
63
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: toolDescriptors(scope) }));
64
+ server.setRequestHandler(CallToolRequestSchema, async () => {
65
+ const { humanMessage } = await import("./i18n.js");
66
+ return toolError(humanMessage("no_api_key"));
67
+ });
68
+ return server;
69
+ }
70
+ function newServer() {
71
+ return new Server({ name: SERVER_NAME, version: SERVER_VERSION }, { capabilities: { tools: {} } });
72
+ }
73
+ function boundServer(client, scope, opts) {
52
74
  const orgTools = opts.orgSession !== undefined;
53
75
  const ctx = {
54
76
  client,
55
77
  scope,
56
78
  ...(opts.orgSession !== undefined ? { orgSession: opts.orgSession } : {}),
57
79
  };
58
- const server = new Server({ name: SERVER_NAME, version: SERVER_VERSION }, { capabilities: { tools: {} } });
80
+ const server = newServer();
59
81
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
60
82
  tools: toolDescriptors(scope, { orgTools }),
61
83
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pombus",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Pombus MCP server — email boxes for AI agents. A thin stdio translator between MCP tool calls and the Pombus REST core.",
5
5
  "license": "MIT",
6
6
  "author": "Runaki",