@matchuplabs/nyc-api-mcp 0.1.0

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.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
5
+ import * as capabilityGuide from "./resources/capability-guide.js";
6
+ import * as inputFormatting from "./resources/input-formatting.js";
7
+ import * as schemaExamples from "./resources/schema-examples.js";
8
+ import * as coverageNotes from "./resources/coverage-notes.js";
9
+ import * as creditPolicy from "./resources/credit-policy.js";
10
+ import { TOOL_DEFINITION as resolvePropertyDef, TOOL_NAME as resolvePropertyName, resolvePropertyIdentifier, } from "./tools/resolve-property-identifier.js";
11
+ import { TOOL_DEFINITION as propertyIntelDef, TOOL_NAME as propertyIntelName, getPropertyIntelligence, } from "./tools/get-property-intelligence.js";
12
+ import { TOOL_DEFINITION as buildingViolationsDef, TOOL_NAME as buildingViolationsName, getBuildingViolations, } from "./tools/get-building-violations.js";
13
+ import { TOOL_DEFINITION as restaurantVenueIntelDef, TOOL_NAME as restaurantVenueIntelName, getRestaurantVenueIntel, } from "./tools/get-restaurant-venue-intel.js";
14
+ const server = new Server({ name: "nyc-api", version: "0.1.0" }, { capabilities: { tools: {}, resources: {} } });
15
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
16
+ tools: [resolvePropertyDef, propertyIntelDef, buildingViolationsDef, restaurantVenueIntelDef],
17
+ }));
18
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
19
+ const { name, arguments: args } = request.params;
20
+ try {
21
+ switch (name) {
22
+ case resolvePropertyName: {
23
+ const result = await resolvePropertyIdentifier((args ?? {}));
24
+ return {
25
+ content: [{ type: "text", text: result }],
26
+ };
27
+ }
28
+ case propertyIntelName: {
29
+ const result = await getPropertyIntelligence((args ?? {}));
30
+ return {
31
+ content: [{ type: "text", text: result }],
32
+ };
33
+ }
34
+ case buildingViolationsName: {
35
+ const result = await getBuildingViolations((args ?? {}));
36
+ return {
37
+ content: [{ type: "text", text: result }],
38
+ };
39
+ }
40
+ case restaurantVenueIntelName: {
41
+ const result = await getRestaurantVenueIntel((args ?? {}));
42
+ return {
43
+ content: [{ type: "text", text: result }],
44
+ };
45
+ }
46
+ default:
47
+ return {
48
+ content: [
49
+ {
50
+ type: "text",
51
+ text: JSON.stringify({
52
+ error: {
53
+ code: "UNKNOWN_TOOL",
54
+ message: `Tool "${name}" is not implemented.`,
55
+ suggested_next_step: "Check available tools via tools/list.",
56
+ },
57
+ }),
58
+ },
59
+ ],
60
+ isError: true,
61
+ };
62
+ }
63
+ }
64
+ catch (err) {
65
+ console.error(`Unhandled error in tool "${name}":`, err);
66
+ return {
67
+ content: [
68
+ {
69
+ type: "text",
70
+ text: JSON.stringify({
71
+ error: {
72
+ code: "UPSTREAM_TIMEOUT",
73
+ message: `Unexpected server error: ${err instanceof Error ? err.message : String(err)}`,
74
+ suggested_next_step: "Retry in a few seconds.",
75
+ },
76
+ }),
77
+ },
78
+ ],
79
+ isError: true,
80
+ };
81
+ }
82
+ });
83
+ const RESOURCES = [
84
+ capabilityGuide,
85
+ inputFormatting,
86
+ schemaExamples,
87
+ coverageNotes,
88
+ creditPolicy,
89
+ ];
90
+ server.setRequestHandler(ListResourcesRequestSchema, async () => ({
91
+ resources: RESOURCES.map((r) => ({
92
+ uri: r.RESOURCE_URI,
93
+ name: r.RESOURCE_NAME,
94
+ description: r.RESOURCE_DESCRIPTION,
95
+ mimeType: r.RESOURCE_URI === "nyc-api://schema-examples" ? "application/json" : "text/plain",
96
+ })),
97
+ }));
98
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
99
+ const { uri } = request.params;
100
+ const resource = RESOURCES.find((r) => r.RESOURCE_URI === uri);
101
+ if (!resource) {
102
+ throw new Error(`Resource not found: ${uri}`);
103
+ }
104
+ return {
105
+ contents: [
106
+ {
107
+ uri: resource.RESOURCE_URI,
108
+ mimeType: resource.RESOURCE_URI === "nyc-api://schema-examples" ? "application/json" : "text/plain",
109
+ text: resource.RESOURCE_CONTENT,
110
+ },
111
+ ],
112
+ };
113
+ });
114
+ async function main() {
115
+ if (!process.env.NYC_API_KEY) {
116
+ console.error("ERROR: NYC_API_KEY environment variable is not set. The server cannot function without it.");
117
+ console.error("Set it with: export NYC_API_KEY=your_api_key");
118
+ process.exit(1);
119
+ }
120
+ const transport = new StdioServerTransport();
121
+ await server.connect(transport);
122
+ console.error("NYC API MCP server running on stdio");
123
+ }
124
+ main().catch((error) => {
125
+ console.error("Fatal error:", error);
126
+ process.exit(1);
127
+ });
@@ -0,0 +1,2 @@
1
+ export declare function getApiKey(): string;
2
+ export declare function validateApiKeyPresent(): boolean;
@@ -0,0 +1,10 @@
1
+ export function getApiKey() {
2
+ const key = process.env.NYC_API_KEY;
3
+ if (!key) {
4
+ throw new Error("NYC_API_KEY environment variable is required. Get one at https://nycapi.app");
5
+ }
6
+ return key;
7
+ }
8
+ export function validateApiKeyPresent() {
9
+ return typeof process.env.NYC_API_KEY === "string" && process.env.NYC_API_KEY.length > 0;
10
+ }
@@ -0,0 +1,31 @@
1
+ export interface McpError {
2
+ error: {
3
+ code: string;
4
+ message: string;
5
+ suggested_next_step: string;
6
+ };
7
+ }
8
+ export declare const ErrorCodes: {
9
+ readonly MISSING_REQUIRED_IDENTIFIER: "MISSING_REQUIRED_IDENTIFIER";
10
+ readonly AMBIGUOUS_ADDRESS: "AMBIGUOUS_ADDRESS";
11
+ readonly AMBIGUOUS_VENUE: "AMBIGUOUS_VENUE";
12
+ readonly NOT_FOUND: "NOT_FOUND";
13
+ readonly UPSTREAM_TIMEOUT: "UPSTREAM_TIMEOUT";
14
+ readonly CREDIT_LIMIT_EXCEEDED: "CREDIT_LIMIT_EXCEEDED";
15
+ readonly INVALID_API_KEY: "INVALID_API_KEY";
16
+ readonly UNSUPPORTED_BOROUGH_FORMAT: "UNSUPPORTED_BOROUGH_FORMAT";
17
+ readonly RATE_LIMITED: "RATE_LIMITED";
18
+ };
19
+ export type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];
20
+ export declare function mcpError(code: ErrorCode, message?: string): McpError;
21
+ /**
22
+ * Map an API error (from NycApiClient) to a structured MCP error.
23
+ * Handles HTTP status codes and known error code strings.
24
+ */
25
+ export declare function mapApiErrorToMcp(apiError: {
26
+ error: {
27
+ code: string;
28
+ message: string;
29
+ status: number;
30
+ };
31
+ }): McpError;
@@ -0,0 +1,65 @@
1
+ export const ErrorCodes = {
2
+ MISSING_REQUIRED_IDENTIFIER: "MISSING_REQUIRED_IDENTIFIER",
3
+ AMBIGUOUS_ADDRESS: "AMBIGUOUS_ADDRESS",
4
+ AMBIGUOUS_VENUE: "AMBIGUOUS_VENUE",
5
+ NOT_FOUND: "NOT_FOUND",
6
+ UPSTREAM_TIMEOUT: "UPSTREAM_TIMEOUT",
7
+ CREDIT_LIMIT_EXCEEDED: "CREDIT_LIMIT_EXCEEDED",
8
+ INVALID_API_KEY: "INVALID_API_KEY",
9
+ UNSUPPORTED_BOROUGH_FORMAT: "UNSUPPORTED_BOROUGH_FORMAT",
10
+ RATE_LIMITED: "RATE_LIMITED",
11
+ };
12
+ const defaultSuggestions = {
13
+ MISSING_REQUIRED_IDENTIFIER: "Provide at least one identifier: address, BBL, or BIN.",
14
+ AMBIGUOUS_ADDRESS: "Add borough or zipcode to narrow the match.",
15
+ AMBIGUOUS_VENUE: "Add zipcode or CAMIS ID to narrow the match.",
16
+ NOT_FOUND: "Use resolve_property_identifier to verify the address or identifier.",
17
+ UPSTREAM_TIMEOUT: "Retry in a few seconds.",
18
+ CREDIT_LIMIT_EXCEEDED: "Purchase more credits at nycapi.app.",
19
+ INVALID_API_KEY: "Check your NYC_API_KEY environment variable.",
20
+ UNSUPPORTED_BOROUGH_FORMAT: "Use full borough name (e.g., 'Manhattan') or borough code (1-5).",
21
+ RATE_LIMITED: "Wait and retry.",
22
+ };
23
+ export function mcpError(code, message) {
24
+ return {
25
+ error: {
26
+ code,
27
+ message: message || code,
28
+ suggested_next_step: defaultSuggestions[code],
29
+ },
30
+ };
31
+ }
32
+ /**
33
+ * Map an API error (from NycApiClient) to a structured MCP error.
34
+ * Handles HTTP status codes and known error code strings.
35
+ */
36
+ export function mapApiErrorToMcp(apiError) {
37
+ const { code, message, status } = apiError.error;
38
+ // Map by HTTP status first, then by error code string
39
+ if (status === 401 || code === "missing_api_key" || code === "invalid_api_key" || code === "INVALID_API_KEY") {
40
+ return mcpError(ErrorCodes.INVALID_API_KEY, message);
41
+ }
42
+ if (status === 402 || code === "credits_exhausted" || code === "CREDIT_LIMIT_EXCEEDED") {
43
+ return mcpError(ErrorCodes.CREDIT_LIMIT_EXCEEDED, message);
44
+ }
45
+ if (status === 429 || code === "rate_limit_exceeded" || code === "RATE_LIMITED") {
46
+ return mcpError(ErrorCodes.RATE_LIMITED, message);
47
+ }
48
+ if (status === 404 || code === "not_found" || code === "NOT_FOUND") {
49
+ return mcpError(ErrorCodes.NOT_FOUND, message);
50
+ }
51
+ if (status === 400) {
52
+ if (code === "invalid_input" || code === "MISSING_REQUIRED_IDENTIFIER") {
53
+ return mcpError(ErrorCodes.MISSING_REQUIRED_IDENTIFIER, message);
54
+ }
55
+ if (code === "unsupported_borough" || code === "UNSUPPORTED_BOROUGH_FORMAT") {
56
+ return mcpError(ErrorCodes.UNSUPPORTED_BOROUGH_FORMAT, message);
57
+ }
58
+ return mcpError(ErrorCodes.MISSING_REQUIRED_IDENTIFIER, message);
59
+ }
60
+ if (status >= 500 || status === 0 || code === "UPSTREAM_TIMEOUT" || code === "UPSTREAM_ERROR" || code === "upstream_error") {
61
+ return mcpError(ErrorCodes.UPSTREAM_TIMEOUT, message);
62
+ }
63
+ // Fallback
64
+ return mcpError(ErrorCodes.UPSTREAM_TIMEOUT, message);
65
+ }
@@ -0,0 +1,75 @@
1
+ export declare class NycApiClient {
2
+ private baseUrl;
3
+ private apiKey;
4
+ constructor(baseUrl?: string, apiKey?: string);
5
+ private request;
6
+ propertyLookup(params: {
7
+ address?: string;
8
+ bbl?: string;
9
+ borough?: string;
10
+ block?: string;
11
+ lot?: string;
12
+ }): Promise<unknown>;
13
+ propertySearch(params: {
14
+ address?: string;
15
+ owner?: string;
16
+ borough?: string;
17
+ zipcode?: string;
18
+ building_class?: string;
19
+ limit?: number;
20
+ offset?: number;
21
+ }): Promise<unknown>;
22
+ venueHealth(params: {
23
+ name?: string;
24
+ address?: string;
25
+ zipcode?: string;
26
+ borough?: string;
27
+ camis?: string;
28
+ }): Promise<unknown>;
29
+ venueSearch(params: {
30
+ cuisine?: string;
31
+ grade?: string;
32
+ borough?: string;
33
+ zipcode?: string;
34
+ limit?: number;
35
+ offset?: number;
36
+ }): Promise<unknown>;
37
+ venuePermits(params: {
38
+ name?: string;
39
+ address?: string;
40
+ zipcode?: string;
41
+ }): Promise<unknown>;
42
+ violationsProfile(params: {
43
+ address: string;
44
+ borough?: string;
45
+ zipcode?: string;
46
+ block?: string;
47
+ lot?: string;
48
+ }): Promise<unknown>;
49
+ violationsDob(params: {
50
+ address?: string;
51
+ block?: string;
52
+ lot?: string;
53
+ borough?: string;
54
+ limit?: number;
55
+ offset?: number;
56
+ }): Promise<unknown>;
57
+ violationsHpd(params: {
58
+ address?: string;
59
+ block?: string;
60
+ lot?: string;
61
+ borough?: string;
62
+ zipcode?: string;
63
+ class?: string;
64
+ limit?: number;
65
+ offset?: number;
66
+ }): Promise<unknown>;
67
+ violationsComplaints(params: {
68
+ address: string;
69
+ borough?: string;
70
+ zipcode?: string;
71
+ complaint_type?: string;
72
+ limit?: number;
73
+ offset?: number;
74
+ }): Promise<unknown>;
75
+ }
@@ -0,0 +1,109 @@
1
+ export class NycApiClient {
2
+ baseUrl;
3
+ apiKey;
4
+ constructor(baseUrl, apiKey) {
5
+ this.baseUrl = baseUrl || process.env.NYC_API_BASE_URL || "https://nycapi.app";
6
+ this.apiKey = apiKey || process.env.NYC_API_KEY || "";
7
+ }
8
+ async request(path, params) {
9
+ const url = new URL(path, this.baseUrl);
10
+ for (const [key, value] of Object.entries(params)) {
11
+ if (value !== undefined) {
12
+ url.searchParams.set(key, String(value));
13
+ }
14
+ }
15
+ let response;
16
+ try {
17
+ response = await fetch(url.toString(), {
18
+ method: "GET",
19
+ headers: {
20
+ Authorization: `Bearer ${this.apiKey}`,
21
+ Accept: "application/json",
22
+ },
23
+ });
24
+ }
25
+ catch (err) {
26
+ return {
27
+ error: {
28
+ code: "UPSTREAM_TIMEOUT",
29
+ message: `Request to NYC API failed: ${err instanceof Error ? err.message : String(err)}`,
30
+ status: 0,
31
+ },
32
+ };
33
+ }
34
+ if (!response.ok) {
35
+ let body;
36
+ try {
37
+ body = await response.text();
38
+ }
39
+ catch {
40
+ body = "";
41
+ }
42
+ // Try to extract structured error code from JSON body
43
+ let errorCode = "UPSTREAM_ERROR";
44
+ let errorMessage = `NYC API returned ${response.status}: ${body}`;
45
+ try {
46
+ const parsed = JSON.parse(body);
47
+ if (parsed?.error?.code)
48
+ errorCode = parsed.error.code;
49
+ if (parsed?.error?.message)
50
+ errorMessage = parsed.error.message;
51
+ }
52
+ catch {
53
+ // Not JSON — use status-based mapping
54
+ }
55
+ const statusCodeMap = {
56
+ 400: "invalid_input",
57
+ 401: "invalid_api_key",
58
+ 402: "credits_exhausted",
59
+ 404: "not_found",
60
+ 429: "rate_limit_exceeded",
61
+ 500: "upstream_error",
62
+ 502: "upstream_error",
63
+ 503: "upstream_error",
64
+ };
65
+ // Prefer parsed error code, fall back to status-based code
66
+ if (errorCode === "UPSTREAM_ERROR" && statusCodeMap[response.status]) {
67
+ errorCode = statusCodeMap[response.status];
68
+ }
69
+ return {
70
+ error: {
71
+ code: errorCode,
72
+ message: errorMessage,
73
+ status: response.status,
74
+ },
75
+ };
76
+ }
77
+ return response.json();
78
+ }
79
+ // --- Property Intelligence ---
80
+ async propertyLookup(params) {
81
+ return this.request("/api/property/lookup", params);
82
+ }
83
+ async propertySearch(params) {
84
+ return this.request("/api/property/search", params);
85
+ }
86
+ // --- Restaurant & Venue Intel ---
87
+ async venueHealth(params) {
88
+ return this.request("/api/venue/health", params);
89
+ }
90
+ async venueSearch(params) {
91
+ return this.request("/api/venue/search", params);
92
+ }
93
+ async venuePermits(params) {
94
+ return this.request("/api/venue/permits", params);
95
+ }
96
+ // --- Building Violations ---
97
+ async violationsProfile(params) {
98
+ return this.request("/api/violations/profile", params);
99
+ }
100
+ async violationsDob(params) {
101
+ return this.request("/api/violations/dob", params);
102
+ }
103
+ async violationsHpd(params) {
104
+ return this.request("/api/violations/hpd", params);
105
+ }
106
+ async violationsComplaints(params) {
107
+ return this.request("/api/violations/complaints", params);
108
+ }
109
+ }
@@ -0,0 +1,11 @@
1
+ export declare enum UsageClass {
2
+ Resolver = "resolver",
3
+ SummaryLookup = "summary_lookup",
4
+ DetailLookup = "detail_lookup",
5
+ Estimate = "estimate"
6
+ }
7
+ export interface UsageEntry {
8
+ tool: string;
9
+ usageClass: UsageClass;
10
+ timestamp: Date;
11
+ }
@@ -0,0 +1,7 @@
1
+ export var UsageClass;
2
+ (function (UsageClass) {
3
+ UsageClass["Resolver"] = "resolver";
4
+ UsageClass["SummaryLookup"] = "summary_lookup";
5
+ UsageClass["DetailLookup"] = "detail_lookup";
6
+ UsageClass["Estimate"] = "estimate";
7
+ })(UsageClass || (UsageClass = {}));
@@ -0,0 +1,4 @@
1
+ export declare const RESOURCE_URI = "nyc-api://capability-guide";
2
+ export declare const RESOURCE_NAME = "NYC API Capability Guide";
3
+ export declare const RESOURCE_DESCRIPTION = "Explains what each tool does, when to use it, and the recommended decision tree for NYC property/building/venue questions.";
4
+ export declare const RESOURCE_CONTENT = "# NYC API MCP \u2014 Capability Guide\n\n## Tools Overview\n\n### 1. resolve_property_identifier\n**What it does:** Normalizes and validates a NYC property identifier. Accepts a partial address, BBL, or BIN and returns the normalized address, BBL, BIN, borough, and a confidence score.\n**When to use:** ALWAYS call this first before using get_property_intelligence or get_building_violations. It ensures you have the correct, unambiguous property. Returns multiple candidates if the input is ambiguous.\n**Cost:** Free or near-free \u2014 always resolve first.\n\n### 2. get_property_intelligence\n**What it does:** Returns ownership, sales history, liens, assessed value, rent stabilization status, zoning, and lot details for a NYC property.\n**When to use:** When the user asks about property ownership, value, sales, liens, zoning, or rent stabilization. Requires a BBL (get one from resolve_property_identifier first).\n**Cost:** 1 credit per call.\n\n### 3. get_building_violations\n**What it does:** Returns DOB and HPD violations, ECB penalties, complaints, and open permits for a building.\n**When to use:** When the user asks about building violations, complaints, permits, safety issues, or code enforcement. Requires a BIN or BBL.\n**Cost:** 1 credit per call.\n\n### 4. get_restaurant_venue_intel\n**What it does:** Returns health inspection grades, liquor license status, sidewalk cafe permits, and 311 complaints for a restaurant or venue.\n**When to use:** When the user asks about restaurant health grades, liquor licenses, venue permits, or food-related complaints. Accepts a name and/or address.\n**Cost:** 1 credit per call.\n\n## Decision Tree\n\n1. **Resolve first.** Always call resolve_property_identifier to normalize the address or identifier before any lookup.\n2. **Check confidence.** If confidence < 0.8 or multiple candidates are returned, ask the user to clarify.\n3. **Choose the right lookup:**\n - Property ownership, value, zoning, liens \u2192 get_property_intelligence\n - Building violations, permits, complaints \u2192 get_building_violations\n - Restaurant/venue health, liquor, permits \u2192 get_restaurant_venue_intel\n4. **Combine when needed.** You can call multiple lookup tools for the same property if the question spans domains (e.g., \"Tell me everything about 100 Broadway\").\n\n## Which Tool for Which Question?\n\n| Question type | Tool |\n|---|---|\n| Who owns this property? | get_property_intelligence |\n| What is this property worth? | get_property_intelligence |\n| Is this apartment rent stabilized? | get_property_intelligence |\n| Are there open violations? | get_building_violations |\n| Any DOB or HPD complaints? | get_building_violations |\n| What is the restaurant health grade? | get_restaurant_venue_intel |\n| Does this bar have a liquor license? | get_restaurant_venue_intel |\n| What's the BBL for this address? | resolve_property_identifier |\n";
@@ -0,0 +1,50 @@
1
+ export const RESOURCE_URI = "nyc-api://capability-guide";
2
+ export const RESOURCE_NAME = "NYC API Capability Guide";
3
+ export const RESOURCE_DESCRIPTION = "Explains what each tool does, when to use it, and the recommended decision tree for NYC property/building/venue questions.";
4
+ export const RESOURCE_CONTENT = `# NYC API MCP — Capability Guide
5
+
6
+ ## Tools Overview
7
+
8
+ ### 1. resolve_property_identifier
9
+ **What it does:** Normalizes and validates a NYC property identifier. Accepts a partial address, BBL, or BIN and returns the normalized address, BBL, BIN, borough, and a confidence score.
10
+ **When to use:** ALWAYS call this first before using get_property_intelligence or get_building_violations. It ensures you have the correct, unambiguous property. Returns multiple candidates if the input is ambiguous.
11
+ **Cost:** Free or near-free — always resolve first.
12
+
13
+ ### 2. get_property_intelligence
14
+ **What it does:** Returns ownership, sales history, liens, assessed value, rent stabilization status, zoning, and lot details for a NYC property.
15
+ **When to use:** When the user asks about property ownership, value, sales, liens, zoning, or rent stabilization. Requires a BBL (get one from resolve_property_identifier first).
16
+ **Cost:** 1 credit per call.
17
+
18
+ ### 3. get_building_violations
19
+ **What it does:** Returns DOB and HPD violations, ECB penalties, complaints, and open permits for a building.
20
+ **When to use:** When the user asks about building violations, complaints, permits, safety issues, or code enforcement. Requires a BIN or BBL.
21
+ **Cost:** 1 credit per call.
22
+
23
+ ### 4. get_restaurant_venue_intel
24
+ **What it does:** Returns health inspection grades, liquor license status, sidewalk cafe permits, and 311 complaints for a restaurant or venue.
25
+ **When to use:** When the user asks about restaurant health grades, liquor licenses, venue permits, or food-related complaints. Accepts a name and/or address.
26
+ **Cost:** 1 credit per call.
27
+
28
+ ## Decision Tree
29
+
30
+ 1. **Resolve first.** Always call resolve_property_identifier to normalize the address or identifier before any lookup.
31
+ 2. **Check confidence.** If confidence < 0.8 or multiple candidates are returned, ask the user to clarify.
32
+ 3. **Choose the right lookup:**
33
+ - Property ownership, value, zoning, liens → get_property_intelligence
34
+ - Building violations, permits, complaints → get_building_violations
35
+ - Restaurant/venue health, liquor, permits → get_restaurant_venue_intel
36
+ 4. **Combine when needed.** You can call multiple lookup tools for the same property if the question spans domains (e.g., "Tell me everything about 100 Broadway").
37
+
38
+ ## Which Tool for Which Question?
39
+
40
+ | Question type | Tool |
41
+ |---|---|
42
+ | Who owns this property? | get_property_intelligence |
43
+ | What is this property worth? | get_property_intelligence |
44
+ | Is this apartment rent stabilized? | get_property_intelligence |
45
+ | Are there open violations? | get_building_violations |
46
+ | Any DOB or HPD complaints? | get_building_violations |
47
+ | What is the restaurant health grade? | get_restaurant_venue_intel |
48
+ | Does this bar have a liquor license? | get_restaurant_venue_intel |
49
+ | What's the BBL for this address? | resolve_property_identifier |
50
+ `;
@@ -0,0 +1,4 @@
1
+ export declare const RESOURCE_URI = "nyc-api://coverage-notes";
2
+ export declare const RESOURCE_NAME = "NYC API Data Coverage Notes";
3
+ export declare const RESOURCE_DESCRIPTION = "Explains data sources, what each covers, refresh cycles, and limitations including rent stabilization caveats.";
4
+ export declare const RESOURCE_CONTENT = "# NYC API MCP \u2014 Data Coverage Notes\n\n## Data Sources\n\n### PLUTO (Primary Land Use Tax Lot Output)\n- **Covers:** Lot-level data \u2014 zoning, building class, lot area, built year, assessed value, number of units, owner name.\n- **Refresh:** Released roughly every 6 months by NYC DCP. Cached copies may lag by up to a month after release.\n- **Limitations:** Owner name is from tax records and may show LLCs or trusts rather than beneficial owners.\n\n### ACRIS (Automated City Register Information System)\n- **Covers:** Recorded property documents \u2014 deeds, mortgages, liens, satisfactions, UCC filings.\n- **Refresh:** Near real-time for newly recorded documents (1-3 business day lag). Historical records go back to 1966 for Manhattan, 1983 for other boroughs.\n- **Limitations:** Does not cover Staten Island (which uses a separate recording system). Document text is often scanned images, not structured data.\n\n### DOB (Department of Buildings)\n- **Covers:** Building permits, violations, complaints, certificates of occupancy, elevator inspections.\n- **Refresh:** Daily updates via NYC Open Data. Active permits and violations update within 24 hours.\n- **Limitations:** Some older violations may have incomplete descriptions. DOB NOW BIS data may differ slightly from legacy BIS data during transition.\n\n### HPD (Housing Preservation & Development)\n- **Covers:** Housing code violations, complaints, registrations, litigation, emergency repair program.\n- **Refresh:** Daily updates via NYC Open Data.\n- **Limitations:** Only covers residential properties. Does not include commercial-only buildings.\n\n### DOH (Department of Health)\n- **Covers:** Restaurant inspection results, grades (A/B/C), violation codes, closure orders.\n- **Refresh:** Updated regularly via NYC Open Data, typically within a week of an inspection.\n- **Limitations:** Grades may not reflect the very latest inspection if results are pending adjudication. Letter grade posting follows a specific cycle after initial/re-inspection.\n\n### DCA / DCWP (Department of Consumer and Worker Protection)\n- **Covers:** Business licenses, sidewalk cafe permits, newsstand permits.\n- **Refresh:** Updated periodically via NYC Open Data.\n- **Limitations:** Not all business types require DCA licenses. Coverage may not include newer permit categories.\n\n### 311\n- **Covers:** Complaint records for noise, illegal conversion, building conditions, rodents, street conditions, and more.\n- **Refresh:** Daily updates. Complaints are logged immediately but resolution may take weeks.\n- **Limitations:** Complaint data reflects what was reported, not necessarily verified conditions. High-volume complaint types (noise) may have duplicates.\n\n## Rent Stabilization Status\n\nRent stabilization data comes from DHCR (Division of Housing and Community Renewal) registration records and property tax records (RPAD/DOF).\n\n**What it means:** If a property is flagged as rent-stabilized, it has units subject to rent regulation \u2014 meaning rents, lease renewals, and eviction are governed by the Rent Stabilization Code.\n\n**Limitations:**\n- The most reliable public indicator is whether the property reports stabilized units in its annual tax filings (RPAD). This can lag by a year.\n- Individual apartment stabilization status cannot be determined from public bulk data alone \u2014 only building-level counts.\n- Properties may have a mix of stabilized and market-rate units.\n- DHCR does not provide a public API; building-level registration lists require FOIL requests.\n- De-regulation (when a unit leaves stabilization via high-rent vacancy or luxury decontrol) is not always reflected promptly.\n\n**Best practice:** Use the stabilization flag as a strong indicator, but advise users that individual unit status requires a DHCR rent history request.\n\n## General Freshness Expectations\n\n| Source | Typical refresh | Lag |\n|---|---|---|\n| DOB violations/permits | Daily | < 24 hours |\n| HPD violations/complaints | Daily | < 24 hours |\n| DOH restaurant inspections | Weekly | 1-7 days |\n| ACRIS documents | Near real-time | 1-3 business days |\n| PLUTO | Semi-annually | Up to 1 month after release |\n| 311 complaints | Daily | < 24 hours |\n| DCA/DCWP licenses | Periodic | 1-2 weeks |\n";
@@ -0,0 +1,69 @@
1
+ export const RESOURCE_URI = "nyc-api://coverage-notes";
2
+ export const RESOURCE_NAME = "NYC API Data Coverage Notes";
3
+ export const RESOURCE_DESCRIPTION = "Explains data sources, what each covers, refresh cycles, and limitations including rent stabilization caveats.";
4
+ export const RESOURCE_CONTENT = `# NYC API MCP — Data Coverage Notes
5
+
6
+ ## Data Sources
7
+
8
+ ### PLUTO (Primary Land Use Tax Lot Output)
9
+ - **Covers:** Lot-level data — zoning, building class, lot area, built year, assessed value, number of units, owner name.
10
+ - **Refresh:** Released roughly every 6 months by NYC DCP. Cached copies may lag by up to a month after release.
11
+ - **Limitations:** Owner name is from tax records and may show LLCs or trusts rather than beneficial owners.
12
+
13
+ ### ACRIS (Automated City Register Information System)
14
+ - **Covers:** Recorded property documents — deeds, mortgages, liens, satisfactions, UCC filings.
15
+ - **Refresh:** Near real-time for newly recorded documents (1-3 business day lag). Historical records go back to 1966 for Manhattan, 1983 for other boroughs.
16
+ - **Limitations:** Does not cover Staten Island (which uses a separate recording system). Document text is often scanned images, not structured data.
17
+
18
+ ### DOB (Department of Buildings)
19
+ - **Covers:** Building permits, violations, complaints, certificates of occupancy, elevator inspections.
20
+ - **Refresh:** Daily updates via NYC Open Data. Active permits and violations update within 24 hours.
21
+ - **Limitations:** Some older violations may have incomplete descriptions. DOB NOW BIS data may differ slightly from legacy BIS data during transition.
22
+
23
+ ### HPD (Housing Preservation & Development)
24
+ - **Covers:** Housing code violations, complaints, registrations, litigation, emergency repair program.
25
+ - **Refresh:** Daily updates via NYC Open Data.
26
+ - **Limitations:** Only covers residential properties. Does not include commercial-only buildings.
27
+
28
+ ### DOH (Department of Health)
29
+ - **Covers:** Restaurant inspection results, grades (A/B/C), violation codes, closure orders.
30
+ - **Refresh:** Updated regularly via NYC Open Data, typically within a week of an inspection.
31
+ - **Limitations:** Grades may not reflect the very latest inspection if results are pending adjudication. Letter grade posting follows a specific cycle after initial/re-inspection.
32
+
33
+ ### DCA / DCWP (Department of Consumer and Worker Protection)
34
+ - **Covers:** Business licenses, sidewalk cafe permits, newsstand permits.
35
+ - **Refresh:** Updated periodically via NYC Open Data.
36
+ - **Limitations:** Not all business types require DCA licenses. Coverage may not include newer permit categories.
37
+
38
+ ### 311
39
+ - **Covers:** Complaint records for noise, illegal conversion, building conditions, rodents, street conditions, and more.
40
+ - **Refresh:** Daily updates. Complaints are logged immediately but resolution may take weeks.
41
+ - **Limitations:** Complaint data reflects what was reported, not necessarily verified conditions. High-volume complaint types (noise) may have duplicates.
42
+
43
+ ## Rent Stabilization Status
44
+
45
+ Rent stabilization data comes from DHCR (Division of Housing and Community Renewal) registration records and property tax records (RPAD/DOF).
46
+
47
+ **What it means:** If a property is flagged as rent-stabilized, it has units subject to rent regulation — meaning rents, lease renewals, and eviction are governed by the Rent Stabilization Code.
48
+
49
+ **Limitations:**
50
+ - The most reliable public indicator is whether the property reports stabilized units in its annual tax filings (RPAD). This can lag by a year.
51
+ - Individual apartment stabilization status cannot be determined from public bulk data alone — only building-level counts.
52
+ - Properties may have a mix of stabilized and market-rate units.
53
+ - DHCR does not provide a public API; building-level registration lists require FOIL requests.
54
+ - De-regulation (when a unit leaves stabilization via high-rent vacancy or luxury decontrol) is not always reflected promptly.
55
+
56
+ **Best practice:** Use the stabilization flag as a strong indicator, but advise users that individual unit status requires a DHCR rent history request.
57
+
58
+ ## General Freshness Expectations
59
+
60
+ | Source | Typical refresh | Lag |
61
+ |---|---|---|
62
+ | DOB violations/permits | Daily | < 24 hours |
63
+ | HPD violations/complaints | Daily | < 24 hours |
64
+ | DOH restaurant inspections | Weekly | 1-7 days |
65
+ | ACRIS documents | Near real-time | 1-3 business days |
66
+ | PLUTO | Semi-annually | Up to 1 month after release |
67
+ | 311 complaints | Daily | < 24 hours |
68
+ | DCA/DCWP licenses | Periodic | 1-2 weeks |
69
+ `;
@@ -0,0 +1,4 @@
1
+ export declare const RESOURCE_URI = "nyc-api://credit-policy";
2
+ export declare const RESOURCE_NAME = "NYC API Credit & Usage Policy";
3
+ export declare const RESOURCE_DESCRIPTION = "Explains credit costs per tool, detail flags, and best practices to minimize usage.";
4
+ export declare const RESOURCE_CONTENT = "# NYC API MCP \u2014 Credit & Usage Policy\n\n## Credit Costs\n\n| Tool | Cost |\n|---|---|\n| resolve_property_identifier | Free (or near-free) |\n| get_property_intelligence | 1 credit per call |\n| get_building_violations | 1 credit per call |\n| get_restaurant_venue_intel | 1 credit per call |\n\n## Detail Flags\n\nSeveral lookup tools accept optional \"detail\" flags to control which data sections are returned (e.g., ownership, sales, violations). **Detail flags do not cost extra credits.** Whether you request one detail section or all of them, the call costs the same 1 credit.\n\nUse detail flags to get exactly the data you need \u2014 not to save credits, but to get a more focused, faster response.\n\n## Best Practices\n\n1. **Resolve first.** Always call resolve_property_identifier before a lookup tool. It is free and prevents wasted credits on bad identifiers.\n\n2. **Use summary mode.** When a tool offers a summary mode or default response, use it unless the user explicitly needs full detail. Summaries are faster and easier to work with.\n\n3. **Avoid duplicate calls.** If you already have results from a lookup tool for a given property, do not call it again for the same property in the same conversation \u2014 reuse the earlier response.\n\n4. **Combine questions.** If the user asks about ownership AND violations for the same property, call get_property_intelligence and get_building_violations in parallel \u2014 but only once each.\n\n5. **Check confidence before proceeding.** If resolve_property_identifier returns confidence < 0.8 or multiple candidates, ask the user to clarify before spending credits on a lookup.\n\n6. **Cache across turns.** If the user asks follow-up questions about the same property, reuse the data you already retrieved rather than making new API calls.\n\n## Summary\n\n- Resolver: always call first, always free.\n- Lookups: 1 credit each, detail flags are free.\n- Do not repeat calls. Resolve \u2192 confirm \u2192 lookup \u2192 reuse.\n";
@@ -0,0 +1,40 @@
1
+ export const RESOURCE_URI = "nyc-api://credit-policy";
2
+ export const RESOURCE_NAME = "NYC API Credit & Usage Policy";
3
+ export const RESOURCE_DESCRIPTION = "Explains credit costs per tool, detail flags, and best practices to minimize usage.";
4
+ export const RESOURCE_CONTENT = `# NYC API MCP — Credit & Usage Policy
5
+
6
+ ## Credit Costs
7
+
8
+ | Tool | Cost |
9
+ |---|---|
10
+ | resolve_property_identifier | Free (or near-free) |
11
+ | get_property_intelligence | 1 credit per call |
12
+ | get_building_violations | 1 credit per call |
13
+ | get_restaurant_venue_intel | 1 credit per call |
14
+
15
+ ## Detail Flags
16
+
17
+ Several lookup tools accept optional "detail" flags to control which data sections are returned (e.g., ownership, sales, violations). **Detail flags do not cost extra credits.** Whether you request one detail section or all of them, the call costs the same 1 credit.
18
+
19
+ Use detail flags to get exactly the data you need — not to save credits, but to get a more focused, faster response.
20
+
21
+ ## Best Practices
22
+
23
+ 1. **Resolve first.** Always call resolve_property_identifier before a lookup tool. It is free and prevents wasted credits on bad identifiers.
24
+
25
+ 2. **Use summary mode.** When a tool offers a summary mode or default response, use it unless the user explicitly needs full detail. Summaries are faster and easier to work with.
26
+
27
+ 3. **Avoid duplicate calls.** If you already have results from a lookup tool for a given property, do not call it again for the same property in the same conversation — reuse the earlier response.
28
+
29
+ 4. **Combine questions.** If the user asks about ownership AND violations for the same property, call get_property_intelligence and get_building_violations in parallel — but only once each.
30
+
31
+ 5. **Check confidence before proceeding.** If resolve_property_identifier returns confidence < 0.8 or multiple candidates, ask the user to clarify before spending credits on a lookup.
32
+
33
+ 6. **Cache across turns.** If the user asks follow-up questions about the same property, reuse the data you already retrieved rather than making new API calls.
34
+
35
+ ## Summary
36
+
37
+ - Resolver: always call first, always free.
38
+ - Lookups: 1 credit each, detail flags are free.
39
+ - Do not repeat calls. Resolve → confirm → lookup → reuse.
40
+ `;
@@ -0,0 +1,4 @@
1
+ export declare const RESOURCE_URI = "nyc-api://input-formatting";
2
+ export declare const RESOURCE_NAME = "NYC API Input Formatting Guide";
3
+ export declare const RESOURCE_DESCRIPTION = "Explains NYC address formats, BBL/BIN identifiers, and borough values accepted by the tools.";
4
+ export declare const RESOURCE_CONTENT = "# NYC API MCP \u2014 Input Formatting Guide\n\n## Address Format\nNYC addresses follow the pattern: HOUSE_NUMBER STREET_NAME\n\nExamples:\n- 100 BROADWAY\n- 350 FIFTH AVE\n- 1 CENTRE ST\n- 233 BROADWAY\n- 42-10 QUEENS BLVD (hyphenated house numbers are common in Queens)\n\nTips:\n- Street suffixes can be abbreviated (AVE, ST, BLVD, PL, DR) or spelled out.\n- Include the borough if the street name is not unique (e.g., \"100 BROADWAY, MANHATTAN\").\n- The resolver handles common misspellings and abbreviations.\n\n## BBL Format (Borough-Block-Lot)\nA 10-digit string that uniquely identifies a tax lot in NYC.\n\nStructure: B BBBBB LLLL\n- B (1 digit): Borough number\n- BBBBB (5 digits): Block number (zero-padded)\n- LLLL (4 digits): Lot number (zero-padded)\n\nBorough numbers:\n- 1 = Manhattan\n- 2 = Bronx\n- 3 = Brooklyn\n- 4 = Queens\n- 5 = Staten Island\n\nExamples:\n- 1000670001 \u2192 Manhattan, Block 00067, Lot 0001\n- 3012340056 \u2192 Brooklyn, Block 01234, Lot 0056\n- 4004561234 \u2192 Queens, Block 00456, Lot 1234\n\n## BIN Format (Building Identification Number)\nA 7-digit number that uniquely identifies a building in NYC.\n\nStructure: B NNNNNN\n- First digit is the borough number (same as BBL).\n- Remaining 6 digits identify the specific building.\n\nExamples:\n- 1001389 \u2192 a building in Manhattan\n- 3345678 \u2192 a building in Brooklyn\n\nNote: A single BBL (tax lot) can have multiple BINs (buildings), but each BIN belongs to exactly one BBL.\n\n## Borough Values\nThe tools accept borough names or abbreviations:\n\n| Full name | Abbreviation | Borough number |\n|---|---|---|\n| Manhattan | MN | 1 |\n| Bronx | BX | 2 |\n| Brooklyn | BK | 3 |\n| Queens | QN | 4 |\n| Staten Island | SI | 5 |\n\nBoth full names and abbreviations are case-insensitive.\n";