@linkbunny/mcp 0.1.4 → 0.1.6
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 +6 -6
- package/dist/index.js +11 -7
- package/dist/skill.md +38 -26
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,10 +4,10 @@ A local stdio MCP server that lets your coding agent run LinkBunny nofollow link
|
|
|
4
4
|
|
|
5
5
|
Its official MCP Registry name is `com.getlinkbunny/exchange`.
|
|
6
6
|
|
|
7
|
-
These examples pin the `0.1.
|
|
7
|
+
These examples pin the `0.1.6` release.
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
|
-
npx -y @linkbunny/mcp@0.1.
|
|
10
|
+
npx -y @linkbunny/mcp@0.1.6
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
Your MCP client starts this process and communicates over stdin/stdout. Running the command alone waits for protocol input. npm hosts package files; no LinkBunny MCP process is hosted. API requests go only to `https://api.getlinkbunny.com/v1` over HTTPS. There is no endpoint override, daemon, polling, automatic retry, publication, or tool chaining.
|
|
@@ -25,7 +25,7 @@ For [Codex](https://developers.openai.com/codex/mcp), after authorization merge
|
|
|
25
25
|
```toml
|
|
26
26
|
[mcp_servers.linkbunny]
|
|
27
27
|
command = "npx"
|
|
28
|
-
args = ["-y", "@linkbunny/mcp@0.1.
|
|
28
|
+
args = ["-y", "@linkbunny/mcp@0.1.6", "--token-file", "/absolute/project/.env.linkbunny"]
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
Run `codex mcp list` from the intended project to check registration. Have the owner complete native trust, restart the client, and begin a fresh backing session in Codex or Agent Control before discovery. Same-process hot reload is not proven. Use `/mcp` to check the active server.
|
|
@@ -40,7 +40,7 @@ For [Claude Code](https://code.claude.com/docs/en/mcp), after authorization merg
|
|
|
40
40
|
"command": "npx",
|
|
41
41
|
"args": [
|
|
42
42
|
"-y",
|
|
43
|
-
"@linkbunny/mcp@0.1.
|
|
43
|
+
"@linkbunny/mcp@0.1.6",
|
|
44
44
|
"--token-file",
|
|
45
45
|
"/absolute/project/.env.linkbunny"
|
|
46
46
|
]
|
|
@@ -59,7 +59,7 @@ For [VS Code](https://code.visualstudio.com/docs/agents/reference/mcp-configurat
|
|
|
59
59
|
"linkbunny": {
|
|
60
60
|
"type": "stdio",
|
|
61
61
|
"command": "npx",
|
|
62
|
-
"args": ["-y", "@linkbunny/mcp@0.1.
|
|
62
|
+
"args": ["-y", "@linkbunny/mcp@0.1.6"],
|
|
63
63
|
"envFile": "${workspaceFolder}/.env.linkbunny"
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -82,7 +82,7 @@ An explicit file takes precedence over an inherited token; invalid files never f
|
|
|
82
82
|
|
|
83
83
|
A package cached or downloaded by npm is not proof that a server is registered. Check initialization separately after saving the registration. Tokens saved in a secret store or repository environment file are not automatically loaded by the client.
|
|
84
84
|
|
|
85
|
-
In the fresh session, ask the configured client to perform `initialize`, `tools/list`, `resources/list`, and `resources/read` for `linkbunny://guidance/skill`, then call `get_status` once. Expect server version `0.1.
|
|
85
|
+
In the fresh session, ask the configured client to perform `initialize`, `tools/list`, `resources/list`, and `resources/read` for `linkbunny://guidance/skill`, then call `get_status` once. Expect server version `0.1.6`, exactly the six tools below, the guidance text, and a status result. `get_status` is read-only and proves token admission without reserving anything. Do not call any other tool to test configuration: even `check_in` can reserve an assignment. If a client cannot list or read resources, report that part as unverified; reading the web skill does not verify MCP resource access.
|
|
86
86
|
|
|
87
87
|
Discovery and resource reads make no LinkBunny API requests. Startup validates token format locally, so only `get_status` proves API token admission and shows subscription state. For isolated discovery-only testing, use the synthetic example `LINKBUNNY_API_TOKEN=synthetic-discovery-only-not-a-real-token-00000000`. It passes the local format check but cannot authorize real API activity. Never substitute a production token merely to test package startup. Discovery-only tests must not invoke `create_profile`, `update_profile`, `check_in`, `report_assignment`, or `reject_assignment`.
|
|
88
88
|
|
package/dist/index.js
CHANGED
|
@@ -18,7 +18,7 @@ var STATUS_PAGE_LIMIT = 100;
|
|
|
18
18
|
var REQUEST_TIMEOUT_MS = 1e4;
|
|
19
19
|
var MAX_RESPONSE_BYTES = 1048576;
|
|
20
20
|
var SKILL_URI = "linkbunny://guidance/skill";
|
|
21
|
-
var VERSION = "0.1.
|
|
21
|
+
var VERSION = "0.1.6";
|
|
22
22
|
|
|
23
23
|
// src/privacy.ts
|
|
24
24
|
function redact({
|
|
@@ -32,7 +32,7 @@ function redact({
|
|
|
32
32
|
var errorMessages = {
|
|
33
33
|
invalid_request: "Request payload or path is invalid. Review the canonical guidance and correct the authorized request.",
|
|
34
34
|
unauthorized: "Credential rejected. Ask the owner to check their token in the private Account page.",
|
|
35
|
-
forbidden: "
|
|
35
|
+
forbidden: "The free exchange limit is reached or a paid LinkBunny subscription is required through https://getlinkbunny.com/account. Account and billing operations are not available through this MCP.",
|
|
36
36
|
not_found: "The owned resource is unavailable. Confirm the retained canonical ID with the owner.",
|
|
37
37
|
profile_conflict: "A live profile already exists for this domain. Recover its canonical ID from the private Account page.",
|
|
38
38
|
idempotency_conflict: "A previous report or rejection has different details. Do not change details to force a retry.",
|
|
@@ -323,7 +323,7 @@ function createServer({
|
|
|
323
323
|
{
|
|
324
324
|
name: "create_profile",
|
|
325
325
|
title: "Create profile",
|
|
326
|
-
description: "Register an owner-managed site only after specific registration authorization.
|
|
326
|
+
description: "Register an owner-managed site only after specific registration authorization. Free accounts get one site. Retain the returned canonical profileID privately; await review without polling.",
|
|
327
327
|
input: createInput,
|
|
328
328
|
response: createResponse,
|
|
329
329
|
method: "POST",
|
|
@@ -478,13 +478,17 @@ function statusNextAction(result) {
|
|
|
478
478
|
}
|
|
479
479
|
const { account, profiles, nextOffset } = result.data;
|
|
480
480
|
const noAutomaticWrites = "Do not run any other LinkBunny tool automatically.";
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
481
|
+
const freeExchange = account.subscriptionActive ? [] : [
|
|
482
|
+
`This account has no paid subscription, so it uses the free exchange: one site, one reported placement and one received backlink. Each direction stops on its own: after the first placement is successfully reported, check_in issues no new assignments, and after the first backlink is reported, the site is no longer picked as a target for new assignments; the other direction stays open. Already issued assignments can still be reported within their normal window and settle, so either count can go above one. A 403 from create_profile or check_in means a free exchange limit is reached; the owner can subscribe at ${ACCOUNT_URL}.`
|
|
483
|
+
];
|
|
484
484
|
if (account.profileCount === 0) {
|
|
485
|
-
return
|
|
485
|
+
return [
|
|
486
|
+
...freeExchange,
|
|
487
|
+
`No sites are registered. Registering a site with create_profile needs the owner's specific registration authorization. ${noAutomaticWrites}`
|
|
488
|
+
].join(" ");
|
|
486
489
|
}
|
|
487
490
|
const steps = [
|
|
491
|
+
...freeExchange,
|
|
488
492
|
...profiles.some((profile) => profile.pendingAssignment) ? [
|
|
489
493
|
"Present each existing pending assignment to the owner for review instead of checking in to reserve more. Publication, report, and rejection each need separate owner authorization."
|
|
490
494
|
] : [],
|
package/dist/skill.md
CHANGED
|
@@ -15,10 +15,15 @@ publishing through the site's normal workflow, or rejects the request.
|
|
|
15
15
|
|
|
16
16
|
1. A LinkBunny account is required. The human may create an account or log in at
|
|
17
17
|
`https://getlinkbunny.com`; an agent may do so only with explicitly authorized browser control.
|
|
18
|
-
2.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
2. A new account can try one exchange free: one site, one completed link placement and one received
|
|
19
|
+
backlink, no card. Each direction stops on its own: after the site's first placement is
|
|
20
|
+
successfully reported, it gets no new assignments to place, and after its first backlink is
|
|
21
|
+
reported, it is no longer picked as the target of new assignments. The other direction stays
|
|
22
|
+
open. Assignments already issued can still be reported within their normal window and settle,
|
|
23
|
+
so a free site can end up with more than one placement or backlink. The paid subscription removes that limit and covers up to 10 sites. The human
|
|
24
|
+
must authorize starting, changing, or cancelling billing; an authorized browser-capable agent may
|
|
25
|
+
operate the website UI for that approved action.
|
|
26
|
+
3. Create an API token in the account UI. The human must authorize
|
|
22
27
|
each creation, rotation, or revocation; an authorized browser-capable agent may perform it. Copy
|
|
23
28
|
the token once into an owner-approved secret store and keep it secret.
|
|
24
29
|
4. Use that token for the public API workflow below. The public API does not provide signup, login,
|
|
@@ -35,14 +40,15 @@ may carry out an authorized API or site action only within the approval given.
|
|
|
35
40
|
When the owner asks you to set up LinkBunny and has no account or API token yet:
|
|
36
41
|
|
|
37
42
|
1. Plan locally. Inspect the project and propose one HTTPS target URL and one HTTPS area of the
|
|
38
|
-
site where nofollow links can go, both on the same registrable domain. Tell the owner the
|
|
39
|
-
($20/month for up to 10 sites, no per-link
|
|
40
|
-
match, that some check-ins return no request,
|
|
41
|
-
no LinkBunny API call while there is no token.
|
|
43
|
+
site where nofollow links can go, both on the same registrable domain. Tell the owner the first
|
|
44
|
+
exchange is free for one site, the price after that ($20/month for up to 10 sites, no per-link
|
|
45
|
+
fees), that profiles are reviewed before they can match, that some check-ins return no request,
|
|
46
|
+
and which later steps need their approval. Make no LinkBunny API call while there is no token.
|
|
42
47
|
2. With the owner's OK, open `https://getlinkbunny.com/login?redirect=%2Faccount%3Fsetup%3Dagent`
|
|
43
48
|
in their browser, or print it when you cannot open a browser (for example in a cloud or remote
|
|
44
49
|
session). Opening the link is not browser control: do not read, click, fill, or otherwise
|
|
45
|
-
operate the page. The owner signs in
|
|
50
|
+
operate the page. The owner signs in and creates the API token there. Subscribing is optional
|
|
51
|
+
for the free exchange.
|
|
46
52
|
3. With the owner's OK, create a project file `.env.linkbunny` containing only an empty
|
|
47
53
|
`LINKBUNNY_API_TOKEN=` line and make sure Git ignores it, or use another secret store the owner
|
|
48
54
|
chooses. The owner pastes the token there. Never ask for the token in chat, and never print,
|
|
@@ -51,12 +57,12 @@ When the owner asks you to set up LinkBunny and has no account or API token yet:
|
|
|
51
57
|
token out of command arguments and output, for example:
|
|
52
58
|
`tr -d '\r' < .env.linkbunny | sed -nE 's/^(export )?LINKBUNNY_API_TOKEN="?([A-Za-z0-9._~-]+)"?$/header = "Authorization: Bearer \2"/p' | curl -sS -w '\nHTTP %{http_code}\n' --config - https://api.getlinkbunny.com/v1/me`.
|
|
53
59
|
If the LinkBunny MCP server is already configured with that token file, its `get_status` tool
|
|
54
|
-
makes the same read.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
says it is done again.
|
|
58
|
-
5.
|
|
59
|
-
|
|
60
|
+
makes the same read. A `200` response confirms setup. `subscriptionActive: false` with
|
|
61
|
+
`siteLimit: 1` means the account is on the free exchange; `subscriptionActive: true` means the
|
|
62
|
+
paid plan is active. `401` means the token is missing or wrong in the file. Do not poll; wait
|
|
63
|
+
until the owner says it is done again.
|
|
64
|
+
5. After a `200` status read, continue with "A — Create a profile". Ask the owner before
|
|
65
|
+
registering the site.
|
|
60
66
|
|
|
61
67
|
## Activation and approval gates
|
|
62
68
|
|
|
@@ -83,7 +89,8 @@ plain-language descriptions are enough.
|
|
|
83
89
|
## Safety contract
|
|
84
90
|
|
|
85
91
|
- API base: `https://api.getlinkbunny.com/v1`.
|
|
86
|
-
- Put the account token in `Authorization: Bearer <token>`.
|
|
92
|
+
- Put the account token in `Authorization: Bearer <token>`. With curl, pass that header through
|
|
93
|
+
standard input (`--config -`), as in the examples, so the token never appears in command arguments.
|
|
87
94
|
- Keep tokens in an owner-approved secret store. Never print, echo, log, paste into chat, include in screenshots, write to source/config tracked by Git, or commit them.
|
|
88
95
|
- Register only sites the owner is authorized to manage.
|
|
89
96
|
- Never send repository, hosting, deployment, or provider credentials to LinkBunny.
|
|
@@ -111,8 +118,8 @@ Present an existing pending assignment for owner review rather than checking in
|
|
|
111
118
|
|
|
112
119
|
After specific authorization, `POST /profiles` with `targetURL`, `requestedAnchor`, one documented
|
|
113
120
|
`category`, and `inventoryPrefix`. Both URLs must be public HTTPS URLs without credentials or
|
|
114
|
-
fragments, on the same registrable domain.
|
|
115
|
-
successful Domain Rating lookup returns `201` with status `pending_review`. LinkBunny reviews each
|
|
121
|
+
fragments, on the same registrable domain. Without a subscription, an account can register one
|
|
122
|
+
site for its free exchange. A successful Domain Rating lookup returns `201` with status `pending_review`. LinkBunny reviews each
|
|
116
123
|
new profile once before it can match as a source or target. Tell the owner review is pending and
|
|
117
124
|
suggest asking the agent to check again after the advisory interval or once approval is confirmed.
|
|
118
125
|
Immediately record and retain the response's canonical `profileID` in the owner's approved private
|
|
@@ -143,11 +150,13 @@ only when no more specific category fits:
|
|
|
143
150
|
After specific authorization, `PATCH /profiles/:profileID` accepts the same fields. Changing the
|
|
144
151
|
target or inventory URL materially resets the profile to Domain Rating lookup and one-time review;
|
|
145
152
|
it cannot match during re-review. Anchor- or category-only edits preserve an existing approval.
|
|
153
|
+
Without a subscription, a URL change made within an hour of the site's creation or previous URL
|
|
154
|
+
change waits up to an hour for its Domain Rating lookup and stays `pending` until then.
|
|
146
155
|
|
|
147
156
|
### B — Check in
|
|
148
157
|
|
|
149
|
-
After specific authorization, `POST /profiles/:profileID/check-in`.
|
|
150
|
-
|
|
158
|
+
After specific authorization, `POST /profiles/:profileID/check-in`. Without a subscription, the
|
|
159
|
+
free exchange allows check-ins until the site's one placement is reported. The response contains the same existing pending assignment, one newly
|
|
151
160
|
reserved assignment, or `{ "assignment": null }`. A profile awaiting review instead returns
|
|
152
161
|
`{ "assignment": null, "status": "profile_pending_review", "message": "This site is awaiting marketplace review.", "retryAfterSeconds": 3600 }`;
|
|
153
162
|
the one-hour interval is advisory, and every check-in still requires specific owner authorization.
|
|
@@ -184,7 +193,8 @@ calendar day. Pending, rejected, and expired/unreported assignments do not consu
|
|
|
184
193
|
capacity; eligibility resets at the next UTC midnight.
|
|
185
194
|
|
|
186
195
|
```sh
|
|
187
|
-
|
|
196
|
+
printf 'header = "Authorization: Bearer %s"\n' "$LINKBUNNY_API_TOKEN" |
|
|
197
|
+
curl -X POST --config - \
|
|
188
198
|
"https://api.getlinkbunny.com/v1/profiles/$LINKBUNNY_PROFILE_ID/check-in"
|
|
189
199
|
```
|
|
190
200
|
|
|
@@ -238,11 +248,13 @@ may return `409 terminal_conflict`. Expected errors use `{ "error": { "code", "m
|
|
|
238
248
|
400 invalid request, 401 credential failure, 403 entitlement failure, 404 unavailable owned
|
|
239
249
|
resource, or 409 terminal conflict.
|
|
240
250
|
|
|
241
|
-
When create-profile
|
|
242
|
-
|
|
243
|
-
`https://getlinkbunny.com/account
|
|
244
|
-
|
|
245
|
-
|
|
251
|
+
When create-profile, check-in, or profile update returns a `403` that mentions the free exchange or a
|
|
252
|
+
subscription, relay its message: the free exchange covers one site and one reported placement, and
|
|
253
|
+
the paid subscription at `https://getlinkbunny.com/account` adds more. A free site that already
|
|
254
|
+
placed its link still stays eligible to receive its one backlink when a match comes up. Do not
|
|
255
|
+
imply that signup, billing, or token creation is available through the public API. Report and
|
|
256
|
+
reject never depend on a subscription: any issued assignment can be reported or reconsidered
|
|
257
|
+
within the usual windows, including after entitlement ends or the free exchange is used.
|
|
246
258
|
|
|
247
259
|
Pending assignments expire at their seven-day deadline. Expiration releases the reservation but
|
|
248
260
|
does not permanently exclude the directional pair. Report completion for the exact original
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linkbunny/mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"lockfileVersion": 2,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@linkbunny/mcp",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.6",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@modelcontextprotocol/server": "2.0.0",
|
package/package.json
CHANGED