@pipeworx/mcp-census 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.
- package/LICENSE +21 -0
- package/README.md +34 -0
- package/package.json +20 -0
- package/server.json +18 -0
- package/src/index.ts +254 -0
- package/tsconfig.json +14 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pipeworx
|
|
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
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# mcp-census
|
|
2
|
+
|
|
3
|
+
Census MCP — U.S. Census Bureau housing-relevant APIs.
|
|
4
|
+
|
|
5
|
+
Part of the [Pipeworx](https://pipeworx.io) open MCP gateway.
|
|
6
|
+
|
|
7
|
+
## Tools
|
|
8
|
+
|
|
9
|
+
| Tool | Description |
|
|
10
|
+
|------|-------------|
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
Add to your MCP client config:
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"mcpServers": {
|
|
19
|
+
"census": {
|
|
20
|
+
"url": "https://gateway.pipeworx.io/census/mcp"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Or use the CLI:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx pipeworx use census
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## License
|
|
33
|
+
|
|
34
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pipeworx/mcp-census",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Census MCP — U.S. Census Bureau housing-relevant APIs.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"types": "src/index.ts",
|
|
8
|
+
"keywords": ["mcp", "mcp-server", "model-context-protocol", "pipeworx", "census"],
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/pipeworx-io/mcp-census"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"typecheck": "tsc --noEmit"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"typescript": "^5.7.0"
|
|
19
|
+
}
|
|
20
|
+
}
|
package/server.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.pipeworx-io/census",
|
|
4
|
+
"title": "census",
|
|
5
|
+
"description": "Census MCP — U.S. Census Bureau housing-relevant APIs.",
|
|
6
|
+
"version": "0.1.0",
|
|
7
|
+
"websiteUrl": "https://pipeworx.io/packs/census",
|
|
8
|
+
"repository": {
|
|
9
|
+
"url": "https://github.com/pipeworx-io/mcp-census",
|
|
10
|
+
"source": "github"
|
|
11
|
+
},
|
|
12
|
+
"remotes": [
|
|
13
|
+
{
|
|
14
|
+
"type": "streamable-http",
|
|
15
|
+
"url": "https://gateway.pipeworx.io/census/mcp"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
interface McpToolDefinition {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: 'object';
|
|
6
|
+
properties: Record<string, unknown>;
|
|
7
|
+
required?: string[];
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface McpToolExport {
|
|
12
|
+
tools: McpToolDefinition[];
|
|
13
|
+
callTool: (name: string, args: Record<string, unknown>) => Promise<unknown>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Census MCP — U.S. Census Bureau housing-relevant APIs.
|
|
18
|
+
*
|
|
19
|
+
* Tools:
|
|
20
|
+
* - census_acs: American Community Survey 5-year data (housing units, median home value, owner-occupied, etc.)
|
|
21
|
+
* - census_building_permits: Monthly building permits from the residential construction survey
|
|
22
|
+
* - census_housing_starts: New residential construction (starts, under construction, completions)
|
|
23
|
+
* - census_homeownership: Housing Vacancy Survey quarterly homeownership rates
|
|
24
|
+
* - census_available_datasets: List available Census Bureau datasets (no key required)
|
|
25
|
+
*
|
|
26
|
+
* BYO key: Census API key from https://api.census.gov/data/key_signup.html
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
const BASE = 'https://api.census.gov/data';
|
|
31
|
+
|
|
32
|
+
function extractKey(args: Record<string, unknown>): string {
|
|
33
|
+
const key = args._apiKey as string;
|
|
34
|
+
delete args._apiKey;
|
|
35
|
+
if (!key) throw new Error('Census API key required. Get one free at https://api.census.gov/data/key_signup.html');
|
|
36
|
+
return key;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function censusFetch(url: string): Promise<unknown> {
|
|
40
|
+
const res = await fetch(url);
|
|
41
|
+
if (!res.ok) {
|
|
42
|
+
const text = await res.text();
|
|
43
|
+
throw new Error(`Census API error (${res.status}): ${text}`);
|
|
44
|
+
}
|
|
45
|
+
return res.json();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Convert Census 2D array response into array of objects using first row as headers */
|
|
49
|
+
function tableToObjects(data: unknown): Record<string, string>[] {
|
|
50
|
+
if (!Array.isArray(data) || data.length < 2) return [];
|
|
51
|
+
const headers = data[0] as string[];
|
|
52
|
+
return data.slice(1).map((row: string[]) => {
|
|
53
|
+
const obj: Record<string, string> = {};
|
|
54
|
+
headers.forEach((h, i) => {
|
|
55
|
+
obj[h] = row[i];
|
|
56
|
+
});
|
|
57
|
+
return obj;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const tools: McpToolExport['tools'] = [
|
|
62
|
+
{
|
|
63
|
+
name: 'census_acs',
|
|
64
|
+
description:
|
|
65
|
+
'Get American Community Survey (ACS) 5-year data from the U.S. Census Bureau. The core dataset for housing statistics including total housing units, median home value, owner-occupied units, median rent, and more. Common variable codes: B25001_001E (total housing units), B25077_001E (median home value), B25003_002E (owner-occupied), B25003_003E (renter-occupied), B25064_001E (median gross rent), B25071_001E (median rent as % of income).',
|
|
66
|
+
inputSchema: {
|
|
67
|
+
type: 'object' as const,
|
|
68
|
+
properties: {
|
|
69
|
+
year: { type: 'number', description: 'Survey year (default 2022). ACS 5-year estimates available from 2009 onward.' },
|
|
70
|
+
variables: { type: 'string', description: 'Comma-separated variable codes to retrieve (e.g., "NAME,B25001_001E,B25077_001E"). Always include NAME for place names.' },
|
|
71
|
+
geography: { type: 'string', description: 'Geographic level and filter using Census "for" syntax (e.g., "state:06" for California, "county:*" for all counties, "state:*" for all states, "county:037&in=state:06" for LA County).' },
|
|
72
|
+
_apiKey: { type: 'string', description: 'Census API key' },
|
|
73
|
+
},
|
|
74
|
+
required: ['variables', 'geography', '_apiKey'],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'census_building_permits',
|
|
79
|
+
description:
|
|
80
|
+
'Get monthly building permits data from the Census Bureau residential construction survey. Tracks new privately-owned housing units authorized by building permits.',
|
|
81
|
+
inputSchema: {
|
|
82
|
+
type: 'object' as const,
|
|
83
|
+
properties: {
|
|
84
|
+
variables: { type: 'string', description: 'Comma-separated variables (e.g., "PERMIT" for total permits, "PERMIT_1UNIT" for single-family). Use census_available_datasets to discover variables.' },
|
|
85
|
+
time: { type: 'string', description: 'Time period (e.g., "2024-01" for January 2024, "from+2023-01+to+2024-01" for a range).' },
|
|
86
|
+
category_code: { type: 'string', description: 'Category filter (e.g., "TOTAL" for total, "1UNIT" for single-family). Optional.' },
|
|
87
|
+
_apiKey: { type: 'string', description: 'Census API key' },
|
|
88
|
+
},
|
|
89
|
+
required: ['variables', 'time', '_apiKey'],
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: 'census_housing_starts',
|
|
94
|
+
description:
|
|
95
|
+
'Get new residential construction data including housing starts, units under construction, and completions from the Census Bureau.',
|
|
96
|
+
inputSchema: {
|
|
97
|
+
type: 'object' as const,
|
|
98
|
+
properties: {
|
|
99
|
+
variables: { type: 'string', description: 'Comma-separated variables (e.g., "STARTS" for housing starts, "UNDER_CONSTRUCTION", "COMPLETIONS").' },
|
|
100
|
+
time: { type: 'string', description: 'Time period (e.g., "2024-01" for January 2024).' },
|
|
101
|
+
region: { type: 'string', description: 'Census region filter (e.g., "NE" for Northeast, "MW" for Midwest, "S" for South, "W" for West). Optional.' },
|
|
102
|
+
_apiKey: { type: 'string', description: 'Census API key' },
|
|
103
|
+
},
|
|
104
|
+
required: ['variables', 'time', '_apiKey'],
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: 'census_homeownership',
|
|
109
|
+
description:
|
|
110
|
+
'Get quarterly homeownership rates from the Census Bureau Housing Vacancy Survey (HVS). Reports the percentage of occupied housing units that are owner-occupied.',
|
|
111
|
+
inputSchema: {
|
|
112
|
+
type: 'object' as const,
|
|
113
|
+
properties: {
|
|
114
|
+
time: { type: 'string', description: 'Time period in YYYY-QN format (e.g., "2024-Q1" for Q1 2024). Use "from+2020-Q1+to+2024-Q1" for a range.' },
|
|
115
|
+
_apiKey: { type: 'string', description: 'Census API key' },
|
|
116
|
+
},
|
|
117
|
+
required: ['time', '_apiKey'],
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'census_available_datasets',
|
|
122
|
+
description:
|
|
123
|
+
'List available Census Bureau datasets. No API key required. Useful for discovering dataset identifiers, descriptions, and available variables before querying specific data.',
|
|
124
|
+
inputSchema: {
|
|
125
|
+
type: 'object' as const,
|
|
126
|
+
properties: {
|
|
127
|
+
_apiKey: { type: 'string', description: 'Census API key (not required for this endpoint but accepted for consistency)' },
|
|
128
|
+
},
|
|
129
|
+
required: [],
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
async function callTool(name: string, args: Record<string, unknown>): Promise<unknown> {
|
|
135
|
+
switch (name) {
|
|
136
|
+
case 'census_acs':
|
|
137
|
+
return censusAcs(args);
|
|
138
|
+
case 'census_building_permits':
|
|
139
|
+
return censusBuildingPermits(args);
|
|
140
|
+
case 'census_housing_starts':
|
|
141
|
+
return censusHousingStarts(args);
|
|
142
|
+
case 'census_homeownership':
|
|
143
|
+
return censusHomeownership(args);
|
|
144
|
+
case 'census_available_datasets':
|
|
145
|
+
return censusAvailableDatasets(args);
|
|
146
|
+
default:
|
|
147
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function censusAcs(args: Record<string, unknown>) {
|
|
152
|
+
const key = extractKey(args);
|
|
153
|
+
const year = (args.year as number) ?? 2022;
|
|
154
|
+
const variables = args.variables as string;
|
|
155
|
+
const geography = args.geography as string;
|
|
156
|
+
|
|
157
|
+
// Parse the geography into "for" and optional "in" parts
|
|
158
|
+
const parts = geography.split('&in=');
|
|
159
|
+
const forClause = parts[0];
|
|
160
|
+
const inClause = parts[1];
|
|
161
|
+
|
|
162
|
+
const params = new URLSearchParams({
|
|
163
|
+
get: variables,
|
|
164
|
+
for: forClause,
|
|
165
|
+
key,
|
|
166
|
+
});
|
|
167
|
+
if (inClause) params.set('in', inClause);
|
|
168
|
+
|
|
169
|
+
const data = await censusFetch(`${BASE}/${year}/acs/acs5?${params}`);
|
|
170
|
+
return { year, variables: variables.split(','), geography, results: tableToObjects(data) };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async function censusBuildingPermits(args: Record<string, unknown>) {
|
|
174
|
+
const key = extractKey(args);
|
|
175
|
+
const variables = args.variables as string;
|
|
176
|
+
const time = args.time as string;
|
|
177
|
+
const categoryCode = args.category_code as string | undefined;
|
|
178
|
+
|
|
179
|
+
const params = new URLSearchParams({
|
|
180
|
+
get: variables,
|
|
181
|
+
time,
|
|
182
|
+
key,
|
|
183
|
+
});
|
|
184
|
+
if (categoryCode) params.set('category_code', categoryCode);
|
|
185
|
+
|
|
186
|
+
const data = await censusFetch(`${BASE}/timeseries/eits/resconst?${params}`);
|
|
187
|
+
return { variables: variables.split(','), time, results: tableToObjects(data) };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function censusHousingStarts(args: Record<string, unknown>) {
|
|
191
|
+
const key = extractKey(args);
|
|
192
|
+
const variables = args.variables as string;
|
|
193
|
+
const time = args.time as string;
|
|
194
|
+
const region = args.region as string | undefined;
|
|
195
|
+
|
|
196
|
+
const params = new URLSearchParams({
|
|
197
|
+
get: variables,
|
|
198
|
+
time,
|
|
199
|
+
key,
|
|
200
|
+
});
|
|
201
|
+
if (region) params.set('geo', region);
|
|
202
|
+
|
|
203
|
+
const data = await censusFetch(`${BASE}/timeseries/eits/resconst?${params}`);
|
|
204
|
+
return { variables: variables.split(','), time, region: region ?? 'all', results: tableToObjects(data) };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function censusHomeownership(args: Record<string, unknown>) {
|
|
208
|
+
const key = extractKey(args);
|
|
209
|
+
const time = args.time as string;
|
|
210
|
+
|
|
211
|
+
const params = new URLSearchParams({
|
|
212
|
+
get: 'HOR',
|
|
213
|
+
for: 'us:*',
|
|
214
|
+
time,
|
|
215
|
+
key,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const data = await censusFetch(`${BASE}/timeseries/eits/hv?${params}`);
|
|
219
|
+
return { metric: 'homeownership_rate', time, results: tableToObjects(data) };
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async function censusAvailableDatasets(args: Record<string, unknown>) {
|
|
223
|
+
// Key is optional for this endpoint
|
|
224
|
+
delete args._apiKey;
|
|
225
|
+
|
|
226
|
+
const data = (await censusFetch(`${BASE}.json`)) as { dataset?: { title: string; description: string; c_vintage?: number; identifier: string }[] };
|
|
227
|
+
const datasets = (data.dataset ?? [])
|
|
228
|
+
.filter((d) => {
|
|
229
|
+
const title = (d.title ?? '').toLowerCase();
|
|
230
|
+
const desc = (d.description ?? '').toLowerCase();
|
|
231
|
+
return (
|
|
232
|
+
title.includes('housing') ||
|
|
233
|
+
title.includes('residential') ||
|
|
234
|
+
title.includes('acs') ||
|
|
235
|
+
title.includes('american community') ||
|
|
236
|
+
title.includes('vacancy') ||
|
|
237
|
+
title.includes('construction') ||
|
|
238
|
+
title.includes('permit') ||
|
|
239
|
+
desc.includes('housing') ||
|
|
240
|
+
desc.includes('residential')
|
|
241
|
+
);
|
|
242
|
+
})
|
|
243
|
+
.slice(0, 50)
|
|
244
|
+
.map((d) => ({
|
|
245
|
+
title: d.title,
|
|
246
|
+
description: d.description,
|
|
247
|
+
vintage: d.c_vintage ?? null,
|
|
248
|
+
identifier: d.identifier,
|
|
249
|
+
}));
|
|
250
|
+
|
|
251
|
+
return { total_housing_related: datasets.length, datasets };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export default { tools, callTool, meter: { credits: 5 } } satisfies McpToolExport;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"outDir": "dist",
|
|
10
|
+
"rootDir": "src",
|
|
11
|
+
"declaration": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src"]
|
|
14
|
+
}
|