@purposebot/mcp 0.1.1 → 0.1.2

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 (3) hide show
  1. package/README.md +7 -5
  2. package/dist/index.js +16 -15
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -29,7 +29,7 @@ Requires Node >= 18. Get an API key from your PurposeBot account.
29
29
  "mcpServers": {
30
30
  "purposebot": {
31
31
  "command": "npx",
32
- "args": ["-y", "@purposebot/mcp@0.1.1"],
32
+ "args": ["-y", "@purposebot/mcp@0.1.2"],
33
33
  "env": {
34
34
  "PURPOSEBOT_API_KEY": "pb_xxx"
35
35
  }
@@ -41,20 +41,22 @@ Requires Node >= 18. Get an API key from your PurposeBot account.
41
41
  ### Claude Code
42
42
 
43
43
  ```bash
44
- claude mcp add purposebot --env PURPOSEBOT_API_KEY=pb_xxx -- npx -y @purposebot/mcp@0.1.1
44
+ claude mcp add purposebot --env PURPOSEBOT_API_KEY=pb_xxx -- npx -y @purposebot/mcp@0.1.2
45
45
  ```
46
46
 
47
47
  ## Environment
48
48
 
49
49
  | Variable | Required | Default | Notes |
50
50
  |---|---|---|---|
51
- | `PURPOSEBOT_API_KEY` | yes | - | sent as `X-API-Key` |
51
+ | `PURPOSEBOT_API_KEY` | no | - | sent as `X-API-Key`; keyless mode supports first-key registration |
52
52
  | `PURPOSEBOT_API_URL` | no | `https://purposebot.ai` | origin only; use `https://api.purposebot.ai` or sandbox/self-host origin |
53
53
  | `PURPOSEBOT_ALLOW_CUSTOM_HOST` | no | unset | set to `1` for non-PurposeBot self-hosts |
54
54
 
55
- The wrapper validates the target URL before sending the API key. It only sends
55
+ The wrapper validates the target URL before sending an API key. It only sends
56
56
  keys to PurposeBot hosts by default, allows `http://localhost` for development,
57
- rejects embedded credentials, and refuses HTTP redirects.
57
+ rejects embedded credentials, and refuses HTTP redirects. Without
58
+ `PURPOSEBOT_API_KEY`, the wrapper can still call keyless MCP tools such as
59
+ `begin_registration`; protected tools return the hosted PurposeBot auth error.
58
60
 
59
61
  ## Develop
60
62
 
package/dist/index.js CHANGED
@@ -9,14 +9,14 @@
9
9
  * checks, so new tools appear automatically.
10
10
  *
11
11
  * Env:
12
- * PURPOSEBOT_API_KEY (required) your PurposeBot API key (sent as X-API-Key)
12
+ * PURPOSEBOT_API_KEY (optional) - your PurposeBot API key (sent as X-API-Key)
13
13
  * PURPOSEBOT_API_URL (optional) — defaults to https://purposebot.ai
14
14
  * PURPOSEBOT_ALLOW_CUSTOM_HOST (optional) — set to 1 for self-hosts
15
15
  */
16
16
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
17
17
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
18
18
  import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
19
- const API_KEY = process.env.PURPOSEBOT_API_KEY;
19
+ const API_KEY = process.env.PURPOSEBOT_API_KEY?.trim() || undefined;
20
20
  const REQUEST_TIMEOUT_MS = 60_000;
21
21
  const MAX_DIAGNOSTIC_BYTES = 2048;
22
22
  const PURPOSEBOT_TOKEN_RE = /\bpb_[A-Za-z0-9_=-]{12,}\b/g;
@@ -36,10 +36,10 @@ function die(msg) {
36
36
  console.error(`[purposebot-mcp] ${msg}`);
37
37
  process.exit(1);
38
38
  }
39
- // The wrapper's trust boundary: we send X-API-Key to this host on every call, so
40
- // validate it before any request. Default + allowlist PurposeBot hosts; require an
41
- // explicit opt-in for anything else; never send the key over plain http (except
42
- // localhost dev); reject embedded credentials.
39
+ // The wrapper's trust boundary: if an API key is configured, it is sent to this
40
+ // host. Validate before any request. Default + allowlist PurposeBot hosts;
41
+ // require an explicit opt-in for anything else; never send the key over plain
42
+ // http (except localhost dev); reject embedded credentials.
43
43
  const ALLOWED_HOSTS = new Set([
44
44
  "api.purposebot.ai",
45
45
  "api-sandbox.purposebot.ai",
@@ -71,9 +71,6 @@ function resolveApiBase(raw) {
71
71
  }
72
72
  return `${u.protocol}//${u.host}`;
73
73
  }
74
- if (!API_KEY) {
75
- die("PURPOSEBOT_API_KEY is required. Set it in your MCP client config (env).");
76
- }
77
74
  const API_URL = resolveApiBase(process.env.PURPOSEBOT_API_URL ?? "https://purposebot.ai");
78
75
  const MCP_ENDPOINT = `${API_URL}/mcp`;
79
76
  let rpcId = 0;
@@ -111,13 +108,16 @@ async function rpc(method, params) {
111
108
  const controller = new AbortController();
112
109
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
113
110
  let res;
111
+ const headers = {
112
+ "content-type": "application/json",
113
+ };
114
+ if (API_KEY) {
115
+ headers["x-api-key"] = API_KEY;
116
+ }
114
117
  try {
115
118
  res = await fetch(MCP_ENDPOINT, {
116
119
  method: "POST",
117
- headers: {
118
- "content-type": "application/json",
119
- "x-api-key": API_KEY,
120
- },
120
+ headers,
121
121
  body: JSON.stringify({ jsonrpc: "2.0", id: ++rpcId, method, params }),
122
122
  // Never follow redirects: undici keeps custom headers (X-API-Key) on
123
123
  // cross-origin redirects, so a 30x from an allowlisted host could leak the
@@ -142,7 +142,7 @@ async function rpc(method, params) {
142
142
  }
143
143
  return body.result;
144
144
  }
145
- const server = new Server({ name: "purposebot", version: "0.1.1" }, { capabilities: { tools: {} } });
145
+ const server = new Server({ name: "purposebot", version: "0.1.2" }, { capabilities: { tools: {} } });
146
146
  server.setRequestHandler(ListToolsRequestSchema, async (request) => {
147
147
  const result = await rpc("tools/list", request.params);
148
148
  return { ...(result ?? {}), tools: Array.isArray(result?.tools) ? result.tools : [] };
@@ -161,7 +161,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
161
161
  });
162
162
  async function main() {
163
163
  await server.connect(new StdioServerTransport());
164
- console.error(`[purposebot-mcp] connected - proxying ${MCP_ENDPOINT}`);
164
+ const authMode = API_KEY ? "authenticated" : "keyless";
165
+ console.error(`[purposebot-mcp] connected - proxying ${MCP_ENDPOINT} (${authMode})`);
165
166
  }
166
167
  main().catch((err) => {
167
168
  console.error("[purposebot-mcp] fatal:", err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@purposebot/mcp",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "PurposeBot agent trust layer for MCP: agent identity, provenance, API access decisions, commerce reputation, and trust-ranked discovery.",
5
5
  "type": "module",
6
6
  "bin": {