contractor-license-mcp-server 0.6.3 → 0.6.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jack Underwood
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -164,8 +164,8 @@ Each license verification consumes **1 credit**, whether the result is fresh or
164
164
  ## Development
165
165
 
166
166
  ```bash
167
- git clone https://github.com/jackunderwood/Contractor-License-Verification.git
168
- cd Contractor-License-Verification/mcp-server
167
+ git clone https://github.com/Noquarter6/contractor-license-mcp-server.git
168
+ cd contractor-license-mcp-server
169
169
  npm install
170
170
  npm run build
171
171
  npm test
package/dist/api.js CHANGED
@@ -15,11 +15,10 @@ export class ApiClient {
15
15
  this.http = axios.create({
16
16
  baseURL,
17
17
  headers: { "X-API-Key": apiKey },
18
- timeout: 120_000, // Scrapers can be slow (Playwright + portal load)
18
+ timeout: 120_000, // Portal lookups can be slow
19
19
  });
20
20
  }
21
21
  async verify(state, licenseNumber, trade) {
22
- // TODO: Add force_refresh param once backend supports cache bypass
23
22
  try {
24
23
  const { data } = await this.http.get("/verify", {
25
24
  params: { state, license: licenseNumber, trade },
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ function getConfig() {
12
12
  const apiKey = process.env.CLV_API_KEY;
13
13
  if (!apiUrl) {
14
14
  console.error("Error: CLV_API_URL environment variable is required.\n" +
15
- "Set it to your Contractor License Verification API URL (e.g. https://your-app.railway.app)");
15
+ "Set it to https://www.tradesapi.com");
16
16
  process.exit(1);
17
17
  }
18
18
  if (!apiKey) {
@@ -27,11 +27,16 @@ async function main() {
27
27
  const client = new ApiClient(apiUrl, apiKey);
28
28
  const server = new McpServer({
29
29
  name: "contractor-license-verification",
30
- version: "0.1.0",
30
+ version: "0.6.3",
31
31
  });
32
32
  server.registerTool("clv_verify_license", {
33
- description: "Verify a contractor's license by checking the state licensing board portal. " +
34
- "Returns license validity, holder name, status, expiration, and any disciplinary actions.",
33
+ description: "Verify a single contractor license against a state licensing board portal. " +
34
+ "Returns validity, licensee name, status, expiration date, and any disciplinary actions on file. " +
35
+ "Use this when you have a specific license number to check. " +
36
+ "Use clv_search_by_name instead when you only have a contractor's name. " +
37
+ "Results are cached for 24 hours; set force_refresh to bypass. " +
38
+ "Returns an error for unsupported states (check clv_list_supported_states first). " +
39
+ "This is a read-only lookup that does not modify any licensing data.",
35
40
  inputSchema: VerifyInputSchema,
36
41
  annotations: {
37
42
  readOnlyHint: true,
@@ -40,8 +45,11 @@ async function main() {
40
45
  },
41
46
  }, async (args) => handleVerify(client, args));
42
47
  server.registerTool("clv_batch_verify", {
43
- description: "Verify multiple contractor licenses in a single request (max 25). " +
44
- "Returns results for each license, with partial failure handling — individual failures don't block others.",
48
+ description: "Verify multiple contractor licenses in a single request (1-25 items). " +
49
+ "Each license is verified independently — individual failures do not block the batch. " +
50
+ "Use this instead of multiple clv_verify_license calls when checking more than one license. " +
51
+ "Returns a summary (succeeded/failed counts) plus per-license results with the same fields as clv_verify_license. " +
52
+ "Returns an error if the array is empty or exceeds 25 items.",
45
53
  inputSchema: BatchInputSchema,
46
54
  annotations: {
47
55
  readOnlyHint: true,
@@ -50,8 +58,10 @@ async function main() {
50
58
  },
51
59
  }, async (args) => handleBatchVerify(client, args));
52
60
  server.registerTool("clv_list_supported_states", {
53
- description: "List all US states currently supported for contractor license verification, " +
54
- "including portal URLs, health status, and available trades.",
61
+ description: "List all US states supported for contractor license verification, with portal URLs, health status, and available trades per state. " +
62
+ "Call this before verifying to confirm a state and trade combination is supported. " +
63
+ "Returns 45 states. Health status is 'healthy', 'degraded', or 'down'. " +
64
+ "Does not make any network requests — the state list is embedded in the server.",
55
65
  inputSchema: StatesInputSchema,
56
66
  annotations: {
57
67
  readOnlyHint: true,
@@ -61,7 +71,10 @@ async function main() {
61
71
  }, async (args) => handleListStates(args));
62
72
  server.registerTool("clv_search_by_name", {
63
73
  description: "Search for contractors by business or individual name in a state licensing database. " +
64
- "Returns matching contractors with license numbers, status, and confidence scores.",
74
+ "Use this when you have a contractor's name but not their license number. " +
75
+ "Use clv_verify_license instead when you already have the license number. " +
76
+ "Returns matching contractors with license numbers, status, and confidence scores (0-1). " +
77
+ "Partial name matches are supported. Results are capped by the limit parameter (default 20, max 50).",
65
78
  inputSchema: SearchInputSchema,
66
79
  annotations: {
67
80
  readOnlyHint: true,
package/dist/schemas.js CHANGED
@@ -5,43 +5,43 @@ const stateField = z
5
5
  .length(2)
6
6
  .transform((s) => s.toUpperCase())
7
7
  .refine((s) => US_STATE_CODES.has(s), { message: "Invalid US state code" })
8
- .describe("Two-letter US state code (e.g. 'CA', 'TX'). Use clv_list_supported_states to see available states.");
8
+ .describe("Two-letter US state code (e.g. 'CA', 'TX', 'FL'). 45 states supported. Call clv_list_supported_states to see the full list with available trades.");
9
9
  export const VerifyInputSchema = z.object({
10
10
  state: stateField,
11
11
  license_number: z
12
12
  .string()
13
13
  .min(1)
14
14
  .max(50)
15
- .describe("The contractor's license number as shown on their license card. Format varies by state."),
15
+ .describe("The contractor's license number exactly as issued (e.g. 'TACLA00000103C' for TX HVAC, '1098765' for CA). Format varies by state."),
16
16
  trade: z
17
17
  .string()
18
18
  .min(1)
19
19
  .max(50)
20
20
  .default("general")
21
- .describe("The trade/contractor type (e.g. 'General Contractor', 'Electrical'). Use clv_list_supported_states to see valid values per state."),
21
+ .describe("Trade category: 'general', 'electrical', 'plumbing', 'hvac', 'mechanical', 'residential', or 'home_inspection'. Defaults to 'general'. Available trades vary by state — check clv_list_supported_states."),
22
22
  force_refresh: z
23
23
  .boolean()
24
24
  .default(false)
25
- .describe("Bypass cache and fetch fresh data from the state portal."),
25
+ .describe("Bypass the 24-hour cache and re-fetch live from the state portal. Use when you need the most current data."),
26
26
  response_format: z
27
27
  .enum(["markdown", "json"])
28
28
  .default("markdown")
29
- .describe("Response format."),
29
+ .describe("Output format. 'markdown' is optimized for chat display with tables. 'json' returns structured data for programmatic use."),
30
30
  });
31
31
  export const BatchInputSchema = z.object({
32
32
  licenses: z
33
33
  .array(z.object({
34
34
  state: stateField,
35
- license_number: z.string().min(1).max(50).describe("License number."),
36
- trade: z.string().min(1).max(50).default("general").describe("Trade type."),
35
+ license_number: z.string().min(1).max(50).describe("License number exactly as issued. Format varies by state."),
36
+ trade: z.string().min(1).max(50).default("general").describe("Trade category (e.g. 'general', 'electrical', 'plumbing', 'hvac'). Defaults to 'general'."),
37
37
  }))
38
38
  .min(1)
39
39
  .max(25)
40
- .describe("Array of licenses to verify (1-25 items)."),
40
+ .describe("Array of licenses to verify (1-25 items). Each license is verified independently — a failure on one does not affect the others."),
41
41
  response_format: z
42
42
  .enum(["markdown", "json"])
43
43
  .default("markdown")
44
- .describe("Response format."),
44
+ .describe("Output format. 'markdown' is optimized for chat display. 'json' returns structured data for programmatic use."),
45
45
  });
46
46
  export const SearchInputSchema = z.object({
47
47
  state: stateField,
@@ -49,28 +49,28 @@ export const SearchInputSchema = z.object({
49
49
  .string()
50
50
  .min(2)
51
51
  .max(200)
52
- .describe("Business or individual name to search for in the state licensing database."),
52
+ .describe("Business or individual name to search for. Partial matches are supported (e.g. 'Anderson' will match 'Anderson Electric LLC')."),
53
53
  trade: z
54
54
  .string()
55
55
  .min(1)
56
56
  .max(50)
57
57
  .default("general")
58
- .describe("The trade/contractor type to filter by (e.g. 'General Contractor', 'Electrical'). Use clv_list_supported_states to see valid values per state."),
58
+ .describe("Trade category to filter by: 'general', 'electrical', 'plumbing', 'hvac', etc. Defaults to 'general'. Check clv_list_supported_states for valid trades per state."),
59
59
  limit: z
60
60
  .number()
61
61
  .int()
62
62
  .min(1)
63
63
  .max(50)
64
64
  .default(20)
65
- .describe("Maximum number of results to return."),
65
+ .describe("Maximum number of results to return. Defaults to 20, capped at 50."),
66
66
  response_format: z
67
67
  .enum(["markdown", "json"])
68
68
  .default("markdown")
69
- .describe("Response format."),
69
+ .describe("Output format. 'markdown' is optimized for chat display with tables. 'json' returns structured data for programmatic use."),
70
70
  });
71
71
  export const StatesInputSchema = z.object({
72
72
  response_format: z
73
73
  .enum(["markdown", "json"])
74
74
  .default("markdown")
75
- .describe("Response format."),
75
+ .describe("Output format. 'markdown' renders a table with state codes, names, status, and trades. 'json' returns a structured array."),
76
76
  });
@@ -3,8 +3,6 @@ import { CHARACTER_LIMIT } from "../constants.js";
3
3
  export async function handleBatchVerify(client, args) {
4
4
  const { licenses, response_format } = args;
5
5
  const results = [];
6
- // Sequential to respect backend rate limits.
7
- // TODO: Switch to POST /batch when backend supports it (Phase 2B).
8
6
  for (const item of licenses) {
9
7
  try {
10
8
  const result = await client.verify(item.state, item.license_number, item.trade);
@@ -1,6 +1,4 @@
1
1
  import { formatStatesList } from "../format.js";
2
- // Hardcoded until backend exposes a /states endpoint.
3
- // Last updated: 2026-03-27 — 43 working states.
4
2
  const SUPPORTED_STATES = [
5
3
  { code: "AK", name: "Alaska", portal: "https://www.commerce.alaska.gov/", status: "healthy", trades: ["general", "electrical", "mechanical"] },
6
4
  { code: "AL", name: "Alabama", portal: "https://genconbd.alabama.gov/", status: "healthy", trades: ["general", "electrical", "plumbing", "hvac", "residential"] },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "contractor-license-mcp-server",
3
- "version": "0.6.3",
3
+ "version": "0.6.4",
4
4
  "description": "Real-time contractor license verification across 45 US states. MCP server for Claude Desktop and other AI agents — verify license status, expiration, and disciplinary history directly against state licensing board portals.",
5
5
  "mcpName": "io.github.Noquarter6/contractor-license-mcp-server",
6
6
  "type": "module",