@radsoft/markbase-mcp 2026.11.2 → 2026.11.4
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 +16 -0
- package/dist/index.js +29 -2
- package/package.json +1 -1
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -40,12 +40,28 @@ claude mcp add markbase --env MARKBASE_API_KEY=mb_your_key -- npx -y @radsoft/ma
|
|
|
40
40
|
| Tool | What it does |
|
|
41
41
|
|------|--------------|
|
|
42
42
|
| `check_trademark_availability` | Screen a proposed name against the register, ranked by similarity with a risk level |
|
|
43
|
+
| `screen_trademark_names` | Screen 1–25 names in input order, with a shared optional class; Launch or Scale required |
|
|
43
44
|
| `search_trademarks` | Typo-tolerant search across 14M+ marks, filterable by class and status |
|
|
44
45
|
| `get_trademark` | Full record for one mark by serial number |
|
|
45
46
|
| `get_trademark_timeline` | Prosecution history — filing, publication, office actions, registration |
|
|
46
47
|
| `search_by_owner` | Every mark held by a company or person |
|
|
47
48
|
| `list_trademark_classes` | All 45 Nice classes, so the model can pick the right one before screening |
|
|
48
49
|
|
|
50
|
+
Bulk screening counts every submitted name toward monthly and per-minute limits,
|
|
51
|
+
including duplicates. Invalid or rejected batches do not consume monthly quota.
|
|
52
|
+
Accepted batches consume their full name count even if a search fails; the tool
|
|
53
|
+
does not retry automatically.
|
|
54
|
+
|
|
55
|
+
## Unreleased: opt-in phonetic clearance
|
|
56
|
+
|
|
57
|
+
Both screening tools accept an optional `phonetic` boolean in the source server.
|
|
58
|
+
The API mode is not enabled in production yet. Omit the parameter to keep current
|
|
59
|
+
spelling-only behavior; an unavailable opt-in request returns 503 rather than
|
|
60
|
+
silently downgrading. Successful opt-in responses preserve the API's separate
|
|
61
|
+
matching metadata and exact, spelling, and phonetic evidence. Scores and risk
|
|
62
|
+
thresholds keep their existing spelling meaning. See the
|
|
63
|
+
[unreleased contract](../docs/phonetic-clearance.md) for limits and publication steps.
|
|
64
|
+
|
|
49
65
|
## Why the class matters
|
|
50
66
|
|
|
51
67
|
Trademark rights are scoped to goods and services. DELTA coexists as an airline, a
|
package/dist/index.js
CHANGED
|
@@ -56,7 +56,7 @@ server.registerTool("search_trademarks", {
|
|
|
56
56
|
});
|
|
57
57
|
server.registerTool("check_trademark_availability", {
|
|
58
58
|
title: "Screen a proposed name against existing trademarks",
|
|
59
|
-
description: "Screen a proposed brand or company name against the US register and get existing marks
|
|
59
|
+
description: "Screen a proposed brand or company name against the US register and get ranked existing marks, each with a spelling similarity score and risk level. Use this BEFORE suggesting a company, product, or brand name to a user. This is a first-pass screening signal, not legal advice.",
|
|
60
60
|
inputSchema: {
|
|
61
61
|
name: z.string().describe("The proposed brand or company name to screen"),
|
|
62
62
|
trademark_class: z
|
|
@@ -64,21 +64,26 @@ server.registerTool("check_trademark_availability", {
|
|
|
64
64
|
.optional()
|
|
65
65
|
.describe("Nice class 1-45 for the goods/services. Strongly recommended — trademark rights are class-scoped, so screening without one produces noise."),
|
|
66
66
|
limit: z.number().int().min(1).max(25).optional().describe("Max conflicts to return, default 10"),
|
|
67
|
+
phonetic: z.boolean().optional().describe("Unreleased opt-in phonetic matching. Omit or use false for spelling-only results; true returns 503 while unavailable."),
|
|
67
68
|
},
|
|
68
|
-
}, async ({ name, trademark_class, limit }) => {
|
|
69
|
+
}, async ({ name, trademark_class, limit, phonetic }) => {
|
|
69
70
|
try {
|
|
70
71
|
const p = new URLSearchParams({ mark: name, limit: String(limit ?? 10) });
|
|
71
72
|
if (trademark_class)
|
|
72
73
|
p.set("class", padClass(trademark_class));
|
|
74
|
+
if (phonetic !== undefined)
|
|
75
|
+
p.set("phonetic", String(phonetic));
|
|
73
76
|
const data = await request(`/clearance?${p}`);
|
|
74
77
|
return ok({
|
|
75
78
|
proposed_name: data.mark,
|
|
76
79
|
scoped_to_class: data.class ? String(parseInt(data.class, 10)) : null,
|
|
77
80
|
highest_risk: data.highest_risk,
|
|
81
|
+
...(data.matching ? { matching: data.matching } : {}),
|
|
78
82
|
conflicts: data.matches.map((m) => ({
|
|
79
83
|
...shapeMark(m),
|
|
80
84
|
similarity: m.similarity,
|
|
81
85
|
risk: m.risk,
|
|
86
|
+
...(m.signals ? { signals: m.signals } : {}),
|
|
82
87
|
})),
|
|
83
88
|
disclaimer: "Screening signal only. Likelihood of confusion is a legal judgment and this does not cover state or common-law rights. Recommend a trademark attorney before adopting a name.",
|
|
84
89
|
});
|
|
@@ -87,6 +92,28 @@ server.registerTool("check_trademark_availability", {
|
|
|
87
92
|
return fail(err);
|
|
88
93
|
}
|
|
89
94
|
});
|
|
95
|
+
server.registerTool("screen_trademark_names", {
|
|
96
|
+
title: "Screen a batch of proposed names",
|
|
97
|
+
description: "Screen 1–25 proposed names against the US register using a Launch or Scale key. Each submitted name consumes one monthly request, including duplicate names. An admitted batch consumes quota even if a search fails; do not retry automatically. Results keep input order. This is preliminary screening, not legal advice or a registration guarantee.",
|
|
98
|
+
inputSchema: {
|
|
99
|
+
names: z.array(z.string().trim().min(1).max(200)).min(1).max(25).describe("Proposed names in the order to screen"),
|
|
100
|
+
trademark_class: z.string().regex(/^\d{1,3}$/).refine((value) => Number(value) >= 1 && Number(value) <= 45).optional()
|
|
101
|
+
.describe("Shared Nice class 1–45 for the goods or services"),
|
|
102
|
+
limit: z.number().int().min(1).max(50).optional().describe("Maximum matches per name; default 20"),
|
|
103
|
+
phonetic: z.boolean().optional().describe("Unreleased opt-in phonetic matching for every name. Omit or use false for spelling-only results; true returns 503 while unavailable."),
|
|
104
|
+
},
|
|
105
|
+
}, async ({ names, trademark_class, limit, phonetic }) => {
|
|
106
|
+
try {
|
|
107
|
+
const data = await request("/clearance/batch", {
|
|
108
|
+
method: "POST",
|
|
109
|
+
body: JSON.stringify({ marks: names, class: trademark_class ? padClass(trademark_class) : undefined, limit, phonetic }),
|
|
110
|
+
});
|
|
111
|
+
return ok({ ...data, disclaimer: "Screening signal only. Consult a trademark attorney before adopting a name." });
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
return fail(error);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
90
117
|
server.registerTool("get_trademark", {
|
|
91
118
|
title: "Get a trademark by serial number",
|
|
92
119
|
description: "Fetch the complete record for one US trademark by its USPTO serial number, including owner, status, classes, and goods/services.",
|
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
"source": "github",
|
|
10
10
|
"subfolder": "mcp"
|
|
11
11
|
},
|
|
12
|
-
"version": "2026.11.
|
|
12
|
+
"version": "2026.11.4",
|
|
13
13
|
"packages": [
|
|
14
14
|
{
|
|
15
15
|
"registryType": "npm",
|
|
16
16
|
"identifier": "@radsoft/markbase-mcp",
|
|
17
|
-
"version": "2026.11.
|
|
17
|
+
"version": "2026.11.4",
|
|
18
18
|
"transport": {
|
|
19
19
|
"type": "stdio"
|
|
20
20
|
},
|