trademark-mcp-server
Version:
A Model Context Protocol (MCP) server that provides tools for searching and retrieving USPTO trademark information using the TSDR API.
289 lines (262 loc) • 10.9 kB
JavaScript
;
// src/index.ts
var import_fastmcp = require("fastmcp");
var import_zod = require("zod");
var server = new import_fastmcp.FastMCP({
name: "trademark-mcp-server",
version: "1.0.0",
instructions: `
This MCP server provides tools for searching and retrieving USPTO trademark information using the TSDR API.
Available tools:
- trademark_search_by_serial: Search for trademarks by 8-digit serial number
- trademark_search_by_registration: Search for trademarks by 7-8 digit registration number
- trademark_status: Get detailed status information for a specific trademark
- trademark_image: Retrieve trademark image URLs
- trademark_documents: Get document bundle URLs for a trademark
The server uses the USPTO TSDR (Trademark Status & Document Retrieval) API to provide real-time trademark data.
Rate limits: 60 requests per minute for general API calls, 4 requests per minute for PDF/ZIP downloads.
IMPORTANT: An API key is required to access the USPTO TSDR API (since October 2020).
Set the USPTO_API_KEY environment variable with your API key from https://developer.uspto.gov/
`
});
var TSDR_BASE_URL = "https://tsdrapi.uspto.gov/ts/cd";
var API_KEY = process.env.USPTO_API_KEY;
function getHeaders() {
const headers = {
"User-Agent": "trademark-mcp-server/1.0.0"
};
if (API_KEY) {
headers["USPTO-API-KEY"] = API_KEY;
}
return headers;
}
function checkApiKey() {
if (!API_KEY) {
return "\u274C USPTO API key not configured. Please set the USPTO_API_KEY environment variable with your API key from https://account.uspto.gov/api-manager/";
}
return null;
}
server.addTool({
name: "trademark_search_by_serial",
description: "Search for trademark information using a serial number",
parameters: import_zod.z.object({
serialNumber: import_zod.z.string().min(8).max(8).describe("8-digit trademark serial number"),
format: import_zod.z.enum(["json", "xml"]).default("json").describe("Response format")
}),
annotations: {
title: "Trademark Search by Serial Number",
readOnlyHint: true,
openWorldHint: true
},
execute: async (args) => {
const apiKeyError = checkApiKey();
if (apiKeyError) {
return apiKeyError;
}
try {
const fileExtension = args.format === "json" ? "json" : "xml";
const url = `${TSDR_BASE_URL}/casestatus/sn${args.serialNumber}/info.${fileExtension}`;
const response = await fetch(url, {
headers: getHeaders()
});
if (!response.ok) {
const errorText = await response.text();
if (errorText.includes("need to register for an API key")) {
throw new Error(`\u{1F511} USPTO API Authentication Issue
The USPTO TSDR API is rejecting our API key. This could be due to:
1. **API Key Activation Delay**: New keys may need 24-48 hours to activate
2. **Endpoint Restrictions**: Individual record endpoints may be temporarily disabled
3. **Authentication Method**: The API might require a different authentication format
**Your API Key**: ${API_KEY ? `${API_KEY.substring(0, 8)}...` : "Not set"}
**Next Steps**:
\u2022 Contact USPTO support: APIhelp@uspto.gov
\u2022 Include your API key and this error message
\u2022 Ask specifically about individual record endpoint access
**Alternative**: Try bulk data download endpoints if available.`);
}
throw new Error(`USPTO API returned ${response.status}: ${response.statusText}. Error: ${errorText}`);
}
if (args.format === "json") {
const jsonData = await response.json();
return JSON.stringify(jsonData, null, 2);
} else {
const xmlData = await response.text();
return xmlData;
}
} catch (error) {
return `Error fetching trademark data: ${error instanceof Error ? error.message : String(error)}`;
}
}
});
server.addTool({
name: "trademark_status",
description: "Get comprehensive status information for a trademark by serial number",
parameters: import_zod.z.object({
serialNumber: import_zod.z.string().min(8).max(8).describe("8-digit trademark serial number")
}),
annotations: {
title: "Trademark Status Lookup",
readOnlyHint: true,
openWorldHint: true
},
execute: async (args) => {
const apiKeyError = checkApiKey();
if (apiKeyError) {
return apiKeyError;
}
try {
const url = `${TSDR_BASE_URL}/casestatus/sn${args.serialNumber}/content`;
const response = await fetch(url, {
headers: getHeaders()
});
if (!response.ok) {
const errorText = await response.text();
if (errorText.includes("need to register for an API key")) {
throw new Error(`\u{1F511} USPTO API Authentication Issue
The USPTO TSDR API is rejecting our API key. This could be due to:
1. **API Key Activation Delay**: New keys may need 24-48 hours to activate
2. **Endpoint Restrictions**: Individual record endpoints may be temporarily disabled
3. **Authentication Method**: The API might require a different authentication format
**Your API Key**: ${API_KEY ? `${API_KEY.substring(0, 8)}...` : "Not set"}
**Next Steps**:
\u2022 Contact USPTO support: APIhelp@uspto.gov
\u2022 Include your API key and this error message
\u2022 Ask specifically about individual record endpoint access
**Alternative**: Try bulk data download endpoints if available.`);
}
throw new Error(`USPTO API returned ${response.status}: ${response.statusText}. Error: ${errorText}`);
}
const htmlContent = await response.text();
const titleMatch = htmlContent.match(/<title>(.*?)<\/title>/i);
const title = titleMatch ? titleMatch[1] : "No title found";
return `Trademark Status Report for Serial Number: ${args.serialNumber}
Title: ${title}
Full HTML content available at: ${url}
Note: This tool returns the HTML content from the USPTO. For structured data, use trademark_search_by_serial instead.`;
} catch (error) {
return `Error fetching trademark status: ${error instanceof Error ? error.message : String(error)}`;
}
}
});
server.addTool({
name: "trademark_image",
description: "Get the image URL for a trademark by serial number",
parameters: import_zod.z.object({
serialNumber: import_zod.z.string().min(8).max(8).describe("8-digit trademark serial number")
}),
annotations: {
title: "Trademark Image Retrieval",
readOnlyHint: true,
openWorldHint: true
},
execute: async (args) => {
const apiKeyError = checkApiKey();
if (apiKeyError) {
return apiKeyError;
}
try {
const imageUrl = `${TSDR_BASE_URL}/rawImage/${args.serialNumber}`;
const response = await fetch(imageUrl, {
method: "HEAD",
headers: getHeaders()
});
if (!response.ok) {
return `No image found for trademark serial number: ${args.serialNumber}`;
}
return `Trademark image URL for serial number ${args.serialNumber}: ${imageUrl}
You can view this image by opening the URL in a web browser.`;
} catch (error) {
return `Error retrieving trademark image: ${error instanceof Error ? error.message : String(error)}`;
}
}
});
server.addTool({
name: "trademark_documents",
description: "Get the document bundle URL for a trademark by serial number",
parameters: import_zod.z.object({
serialNumber: import_zod.z.string().min(8).max(8).describe("8-digit trademark serial number")
}),
annotations: {
title: "Trademark Documents Bundle",
readOnlyHint: true,
openWorldHint: true
},
execute: async (args) => {
const apiKeyError = checkApiKey();
if (apiKeyError) {
return apiKeyError;
}
try {
const documentsUrl = `${TSDR_BASE_URL}/casedocs/bundle.pdf?sn=${args.serialNumber}`;
return `Document bundle URL for trademark serial number ${args.serialNumber}: ${documentsUrl}
This URL provides a PDF containing all documents related to this trademark application.
Note: Document downloads are rate-limited to 4 requests per minute per API key.`;
} catch (error) {
return `Error generating document bundle URL: ${error instanceof Error ? error.message : String(error)}`;
}
}
});
server.addTool({
name: "trademark_search_by_registration",
description: "Search for trademark information using a registration number",
parameters: import_zod.z.object({
registrationNumber: import_zod.z.string().min(7).max(8).describe("7-8 digit trademark registration number"),
format: import_zod.z.enum(["json", "xml"]).default("json").describe("Response format")
}),
annotations: {
title: "Trademark Search by Registration Number",
readOnlyHint: true,
openWorldHint: true
},
execute: async (args) => {
const apiKeyError = checkApiKey();
if (apiKeyError) {
return apiKeyError;
}
try {
const fileExtension = args.format === "json" ? "json" : "xml";
const url = `${TSDR_BASE_URL}/casestatus/rn${args.registrationNumber}/info.${fileExtension}`;
const response = await fetch(url, {
headers: getHeaders()
});
if (!response.ok) {
const errorText = await response.text();
if (errorText.includes("need to register for an API key")) {
throw new Error(`\u{1F511} USPTO API Authentication Issue
The USPTO TSDR API is rejecting our API key. This could be due to:
1. **API Key Activation Delay**: New keys may need 24-48 hours to activate
2. **Endpoint Restrictions**: Individual record endpoints may be temporarily disabled
3. **Authentication Method**: The API might require a different authentication format
**Your API Key**: ${API_KEY ? `${API_KEY.substring(0, 8)}...` : "Not set"}
**Next Steps**:
\u2022 Contact USPTO support: APIhelp@uspto.gov
\u2022 Include your API key and this error message
\u2022 Ask specifically about individual record endpoint access
**Alternative**: Try bulk data download endpoints if available.`);
}
throw new Error(`USPTO API returned ${response.status}: ${response.statusText}. Error: ${errorText}`);
}
if (args.format === "json") {
const jsonData = await response.json();
return JSON.stringify(jsonData, null, 2);
} else {
const xmlData = await response.text();
return xmlData;
}
} catch (error) {
return `Error fetching trademark data by registration number: ${error instanceof Error ? error.message : String(error)}`;
}
}
});
var src_default = server;
// src/server.ts
var port = process.env.PORT ? parseInt(process.env.PORT) : 8080;
src_default.start({
transportType: "httpStream",
httpStream: {
port
}
});
console.log(`\u{1F680} Trademark MCP Server running on http://localhost:${port}`);
console.log(`\u{1F4CB} Health check: http://localhost:${port}/health`);
console.log(`\u{1F50D} MCP endpoint: http://localhost:${port}/mcp`);