Skip to content

Commit 6ab47e4

Browse files
chore(release): v0.5.0
1 parent b6e687e commit 6ab47e4

11 files changed

Lines changed: 153 additions & 125 deletions

.gitleaks.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ useDefault = true
77
description = "Obvious SDK documentation and test placeholders"
88
regexTarget = "match"
99
regexes = [
10-
'''bsk_live_(\.\.\.|[a-z0-9_]*(example|secret|do_not_log|123456)[a-z0-9_]*)''',
11-
'''bsp_live_(\.\.\.|[a-z0-9_]*(example|secret|123456)[a-z0-9_]*)''',
10+
'''bsb_key_live_(\.\.\.|[a-z0-9_]*(example|secret|do_not_log|123456)[a-z0-9_]*)''',
11+
'''bsb_pat_live_(\.\.\.|[a-z0-9_]*(example|secret|123456)[a-z0-9_]*)''',
1212
'''mig_(\.\.\.|secret)''',
1313
'''Bearer reflected-secret''',
1414
'''US/Texas/Austin''',

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## Unreleased
44

5+
## 0.5.0 - 2026-07-29
6+
7+
- Breaking: require public ID v3 prefixes and `bsb_key_*` or `bsb_pat_live_` credentials.
8+
Public ID v2 values and legacy `bsk_*` or `bsp_*` credentials are no longer accepted.
9+
510
## 0.4.0 - 2026-07-29
611

712
- Breaking: use the same 84 unprefixed `snake_case` tool names as `/api/mcp`; clients must remove

README.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ The SDK is consumed from the npm registry:
3737
## Environment
3838

3939
```sh
40-
export BISIBILITY_API_KEY="bsp_live_..."
40+
export BISIBILITY_API_KEY="bsb_pat_live_..."
4141
export BISIBILITY_BASE_URL="https://bisibility.com/api/v1"
4242
export BISIBILITY_PROJECT_ID="prj_..."
4343
export BISIBILITY_MCP_READ_ONLY="1"
@@ -46,18 +46,18 @@ export BISIBILITY_MCP_TOOLSETS="projects,keywords,checks,rank-history"
4646

4747
`BISIBILITY_BASE_URL` is optional and defaults to `https://bisibility.com/api/v1`. For self-hosted
4848
installs, set it to your API v1 root, for example `https://rank.example/api/v1`.
49-
`BISIBILITY_API_KEY` accepts a project key (`bsk_live_...`) or personal access
50-
token (`bsp_live_...`). Set optional `BISIBILITY_PROJECT_ID` as the default
49+
`BISIBILITY_API_KEY` accepts a project key (`bsb_key_live_...`) or personal access
50+
token (`bsb_pat_live_...`). Set optional `BISIBILITY_PROJECT_ID` as the default
5151
`X-Bisibility-Project` selector for project-implicit PAT tools; a tool's
5252
optional `project_id` argument overrides it for that call. Every resource ID
5353
accepted by the MCP server, including `BISIBILITY_PROJECT_ID`, must use public
54-
ID v2: a canonical lowercase prefix plus `_` and a 24-character lowercase
54+
ID v3: a canonical lowercase prefix plus `_` and a 24-character lowercase
5555
CUID2 suffix, for example `prj_a1b2c3d4e5f6g7h8j9k0m2n3`. Raw database IDs,
5656
legacy IDs, mixed-case IDs, and wrong resource prefixes are rejected. Location
5757
selection uses the returned `location_key`, never a location ID.
5858

59-
The SDK dependency and lockfile use the published registry package. Do not use
60-
a local SDK link.
59+
The server consumes the public ID v3 contract from the published
60+
`@bisibility/sdk` package. Do not replace it with a local SDK link.
6161

6262
`BISIBILITY_MCP_READ_ONLY` accepts `1`, `true`, `yes`, or `on`, ignoring case. When enabled,
6363
write tools are not registered and do not appear in `tools/list`.
@@ -73,7 +73,7 @@ control, not a way to improve tool selection.
7373

7474
```sh
7575
npm run build
76-
BISIBILITY_API_KEY="bsk_live_..." node dist/stdio.js
76+
BISIBILITY_API_KEY="bsb_key_live_..." node dist/stdio.js
7777
```
7878

7979
When installed as a package, the bin is:
@@ -93,7 +93,7 @@ Example MCP client configuration using the built local package:
9393
"command": "node",
9494
"args": ["/path/to/bisibility-mcp/dist/stdio.js"],
9595
"env": {
96-
"BISIBILITY_API_KEY": "bsk_live_...",
96+
"BISIBILITY_API_KEY": "bsb_key_live_...",
9797
"BISIBILITY_BASE_URL": "https://bisibility.com/api/v1"
9898
}
9999
}
@@ -109,7 +109,7 @@ Example using the package bin:
109109
"bisibility": {
110110
"command": "bisibility-mcp",
111111
"env": {
112-
"BISIBILITY_API_KEY": "bsk_live_...",
112+
"BISIBILITY_API_KEY": "bsb_key_live_...",
113113
"BISIBILITY_BASE_URL": "https://bisibility.com/api/v1"
114114
}
115115
}
@@ -230,9 +230,9 @@ registered tool surface.
230230
## Security
231231

232232
The credential's scope is the primary authorization control, and the server accepts two kinds.
233-
A project key (`bsk_live_...`) belongs to exactly one project, which bounds the damage from a
233+
A project key (`bsb_key_live_...`) belongs to exactly one project, which bounds the damage from a
234234
leak and makes it a good fit for a single-project or machine setup. A personal access token
235-
(`bsp_live_...`) covers the projects you are a member of, so one token serves them all; its
235+
(`bsb_pat_live_...`) covers the projects you are a member of, so one token serves them all; its
236236
effective access in each project is the lower of the token's scope and your role there, meaning
237237
a token never grants more than the person behind it. Use `BISIBILITY_PROJECT_ID` to set the
238238
default project for a token that spans several, as described under Environment above.

package-lock.json

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@bisibility/mcp",
3-
"version": "0.4.0",
3+
"version": "0.5.0",
44
"description": "Model Context Protocol server for the Bisibility REST API.",
55
"type": "module",
66
"license": "Apache-2.0",
@@ -65,7 +65,7 @@
6565
"typecheck": "tsc --noEmit"
6666
},
6767
"dependencies": {
68-
"@bisibility/sdk": "^0.5.0",
68+
"@bisibility/sdk": "^0.6.0",
6969
"@modelcontextprotocol/sdk": "1.29.0",
7070
"zod": "^4.4.3"
7171
},

src/config.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,21 @@ export interface BisibilityMcpConfig {
4747
projectId?: PublicIdForPrefix<"prj">;
4848
}
4949

50+
const API_CREDENTIAL_PREFIXES = ["bsb_key_live_", "bsb_key_test_", "bsb_pat_live_"] as const;
51+
52+
function readApiCredential(value: string | undefined) {
53+
const credential = cleanEnvValue(value);
54+
if (!credential) {
55+
throw new Error("BISIBILITY_API_KEY is required to run the Bisibility MCP server.");
56+
}
57+
if (!API_CREDENTIAL_PREFIXES.some((prefix) => credential.startsWith(prefix))) {
58+
throw new Error(
59+
"BISIBILITY_API_KEY must use bsb_key_live_, bsb_key_test_, or bsb_pat_live_. Legacy bsp_ and bsk_ credentials are not accepted.",
60+
);
61+
}
62+
return credential;
63+
}
64+
5065
function cleanEnvValue(value: string | undefined) {
5166
const cleaned = value?.trim();
5267
if (!cleaned) {
@@ -93,10 +108,7 @@ export function readBisibilityMcpToolConfig(
93108
}
94109

95110
export function readBisibilityMcpConfig(env: BisibilityMcpEnv = process.env): BisibilityMcpConfig {
96-
const apiKey = cleanEnvValue(env.BISIBILITY_API_KEY);
97-
if (!apiKey) {
98-
throw new Error("BISIBILITY_API_KEY is required to run the Bisibility MCP server.");
99-
}
111+
const apiKey = readApiCredential(env.BISIBILITY_API_KEY);
100112

101113
const baseUrl = cleanEnvValue(env.BISIBILITY_BASE_URL ?? DEFAULT_BISIBILITY_BASE_URL);
102114
if (!baseUrl) {
@@ -107,7 +119,7 @@ export function readBisibilityMcpConfig(env: BisibilityMcpEnv = process.env): Bi
107119
if (projectId) {
108120
const parsedProjectId = projectIdInput.safeParse(projectId);
109121
if (!parsedProjectId.success) {
110-
throw new Error("BISIBILITY_PROJECT_ID must be a prj_ public ID v2.");
122+
throw new Error("BISIBILITY_PROJECT_ID must be a prj_ public ID v3.");
111123
}
112124
return { apiKey, baseUrl, projectId: parsedProjectId.data };
113125
}

src/schemas.ts

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
11
import * as z from "zod/v4";
22

33
export const publicIdPrefixes = [
4-
"alert",
4+
"al",
5+
"alr",
56
"audit",
67
"check",
7-
"comp",
8+
"cmp",
89
"conn",
9-
"hook",
10-
"invite",
11-
"job",
10+
"dwh",
11+
"ferry",
12+
"imp",
13+
"inv",
1214
"key",
1315
"kw",
14-
"member",
15-
"mtok",
16-
"notif",
16+
"mbr",
17+
"ntf",
1718
"pat",
1819
"prj",
19-
"rule",
20-
"ses",
20+
"sid",
2121
"sig",
22-
"skw",
22+
"svkw",
2323
"tag",
2424
"usr",
25-
"view",
26-
"webhook",
25+
"viw",
26+
"we",
2727
] as const;
2828

2929
export type PublicIdPrefix = (typeof publicIdPrefixes)[number];
@@ -54,7 +54,7 @@ export function assertPublicId<Prefix extends PublicIdPrefix>(
5454
prefix: Prefix,
5555
): asserts value is PublicIdForPrefix<Prefix> {
5656
if (!matchesPublicId(value, prefix)) {
57-
throw new Error(`Expected a ${prefix}_ public ID v2.`);
57+
throw new Error(`Expected a ${prefix}_ public ID v3.`);
5858
}
5959
}
6060

@@ -71,31 +71,31 @@ export const publicIdInput = <Prefix extends PublicIdPrefix>(prefix: Prefix) =>
7171
.string()
7272
.trim()
7373
.regex(publicIdPattern(prefix), {
74-
message: `Expected a ${prefix}_ public ID v2.`,
74+
message: `Expected a ${prefix}_ public ID v3.`,
7575
})
7676
.transform((value) => parsePublicId(value, prefix));
7777

78-
const alertIdInput = publicIdInput("alert").describe(
78+
const alertIdInput = publicIdInput("al").describe(
7979
"Identifier of the triggered alert to operate on.",
8080
);
8181
const checkIdInput = publicIdInput("check").describe("Identifier of the rank check to retrieve.");
82-
const competitorIdInput = publicIdInput("comp").describe(
82+
const competitorIdInput = publicIdInput("cmp").describe(
8383
"Identifier of the competitor to operate on.",
8484
);
8585
const connectionIdInput = publicIdInput("conn").describe(
8686
"Identifier of the provider connection to operate on.",
8787
);
88-
const inviteIdInput = publicIdInput("invite").describe(
88+
const inviteIdInput = publicIdInput("inv").describe(
8989
"Identifier of the team invitation to operate on.",
9090
);
9191
const keyIdInput = publicIdInput("key").describe("Identifier of the API key to operate on.");
9292
const keywordIdInput = publicIdInput("kw").describe(
9393
"Identifier of the tracked keyword to operate on.",
9494
);
95-
const memberIdInput = publicIdInput("member").describe(
95+
const memberIdInput = publicIdInput("mbr").describe(
9696
"Identifier of the project member to operate on.",
9797
);
98-
const migrationTokenIdInput = publicIdInput("mtok").describe(
98+
const migrationTokenIdInput = publicIdInput("ferry").describe(
9999
"Identifier of the migration token to operate on.",
100100
);
101101
const personalAccessTokenIdInput = publicIdInput("pat").describe(
@@ -104,9 +104,9 @@ const personalAccessTokenIdInput = publicIdInput("pat").describe(
104104
export const projectIdInput = publicIdInput("prj").describe(
105105
"Identifier of the Bisibility project to operate on; list_projects returns valid project ids.",
106106
);
107-
const ruleIdInput = publicIdInput("rule").describe("Identifier of the alert rule to operate on.");
108-
const viewIdInput = publicIdInput("view").describe("Identifier of the saved view to operate on.");
109-
const webhookIdInput = publicIdInput("webhook").describe(
107+
const ruleIdInput = publicIdInput("alr").describe("Identifier of the alert rule to operate on.");
108+
const viewIdInput = publicIdInput("viw").describe("Identifier of the saved view to operate on.");
109+
const webhookIdInput = publicIdInput("we").describe(
110110
"Identifier of the webhook endpoint to operate on.",
111111
);
112112
export const deviceInput = z
@@ -979,7 +979,7 @@ function validateAlertTargets(
979979
if (!matchesPublicId(targetId, expectedPrefix)) {
980980
ctx.addIssue({
981981
code: "custom",
982-
message: `Expected a ${expectedPrefix}_ public ID v2.`,
982+
message: `Expected a ${expectedPrefix}_ public ID v3.`,
983983
path: ["target_ids", index],
984984
});
985985
}

0 commit comments

Comments
 (0)