@pipeworx/mcp-realestate 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 +55 -0
- package/package.json +20 -0
- package/server.json +18 -0
- package/src/index.ts +204 -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,55 @@
|
|
|
1
|
+
# mcp-realestate
|
|
2
|
+
|
|
3
|
+
Real Estate Japan MCP — actual residential/commercial transaction prices and
|
|
4
|
+
|
|
5
|
+
Part of [Pipeworx](https://pipeworx.io) — an MCP gateway connecting AI agents to 912+ live data sources.
|
|
6
|
+
|
|
7
|
+
## Tools
|
|
8
|
+
|
|
9
|
+
| Tool | Description |
|
|
10
|
+
|------|-------------|
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
Add to your MCP client (Claude Desktop, Cursor, Windsurf, etc.):
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"mcpServers": {
|
|
19
|
+
"realestate": {
|
|
20
|
+
"url": "https://gateway.pipeworx.io/realestate/mcp"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Or connect to the full Pipeworx gateway for access to all 912+ data sources:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"mcpServers": {
|
|
31
|
+
"pipeworx": {
|
|
32
|
+
"url": "https://gateway.pipeworx.io/mcp"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Using with ask_pipeworx
|
|
39
|
+
|
|
40
|
+
Instead of calling tools directly, you can ask questions in plain English:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
ask_pipeworx({ question: "your question about Realestate data" })
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The gateway picks the right tool and fills the arguments automatically.
|
|
47
|
+
|
|
48
|
+
## More
|
|
49
|
+
|
|
50
|
+
- [All tools and guides](https://github.com/pipeworx-io/examples)
|
|
51
|
+
- [pipeworx.io](https://pipeworx.io)
|
|
52
|
+
|
|
53
|
+
## License
|
|
54
|
+
|
|
55
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pipeworx/mcp-realestate",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Real Estate Japan MCP — actual residential/commercial transaction prices and",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"types": "src/index.ts",
|
|
8
|
+
"keywords": ["mcp", "mcp-server", "model-context-protocol", "pipeworx", "realestate"],
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/pipeworx-io/mcp-realestate"
|
|
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/realestate",
|
|
4
|
+
"title": "Realestate",
|
|
5
|
+
"description": "Real Estate Japan MCP — actual residential/commercial transaction prices and",
|
|
6
|
+
"version": "0.1.0",
|
|
7
|
+
"websiteUrl": "https://pipeworx.io/packs/realestate",
|
|
8
|
+
"repository": {
|
|
9
|
+
"url": "https://github.com/pipeworx-io/mcp-realestate",
|
|
10
|
+
"source": "github"
|
|
11
|
+
},
|
|
12
|
+
"remotes": [
|
|
13
|
+
{
|
|
14
|
+
"type": "streamable-http",
|
|
15
|
+
"url": "https://gateway.pipeworx.io/realestate/mcp"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
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
|
+
meter?: { credits: number };
|
|
15
|
+
cost?: Record<string, unknown>;
|
|
16
|
+
provider?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Real Estate Japan MCP — actual residential/commercial transaction prices and
|
|
21
|
+
* agreed (contract) prices for Japan, from MLIT's 不動産情報ライブラリ
|
|
22
|
+
* (Real Estate Information Library) public API.
|
|
23
|
+
*
|
|
24
|
+
* Source: reinfolib.mlit.go.jp ex-api (XIT001 transaction prices, XIT002 the
|
|
25
|
+
* municipality-code lookup). Auth: a personal subscription key in the
|
|
26
|
+
* `Ocp-Apim-Subscription-Key` header. Pipeworx holds the shared key
|
|
27
|
+
* (PLATFORM_MLIT_KEY, injected as _apiKey); users may pass their own.
|
|
28
|
+
*
|
|
29
|
+
* Tools:
|
|
30
|
+
* - realestate_transactions: reported transaction/contract prices by area + period
|
|
31
|
+
* - realestate_municipalities: city/ward codes within a prefecture (for the above)
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
const BASE = 'https://www.reinfolib.mlit.go.jp/ex-api/external';
|
|
36
|
+
|
|
37
|
+
// Prefecture name (en + common romanization) -> 2-digit MLIT/JIS area code.
|
|
38
|
+
const PREFECTURES: Record<string, string> = {
|
|
39
|
+
hokkaido: '01', aomori: '02', iwate: '03', miyagi: '04', akita: '05', yamagata: '06', fukushima: '07',
|
|
40
|
+
ibaraki: '08', tochigi: '09', gunma: '10', saitama: '11', chiba: '12', tokyo: '13', kanagawa: '14',
|
|
41
|
+
niigata: '15', toyama: '16', ishikawa: '17', fukui: '18', yamanashi: '19', nagano: '20', gifu: '21',
|
|
42
|
+
shizuoka: '22', aichi: '23', mie: '24', shiga: '25', kyoto: '26', osaka: '27', hyogo: '28', nara: '29',
|
|
43
|
+
wakayama: '30', tottori: '31', shimane: '32', okayama: '33', hiroshima: '34', yamaguchi: '35',
|
|
44
|
+
tokushima: '36', kagawa: '37', ehime: '38', kochi: '39', fukuoka: '40', saga: '41', nagasaki: '42',
|
|
45
|
+
kumamoto: '43', oita: '44', miyazaki: '45', kagoshima: '46', okinawa: '47',
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const API_KEY_PROP = {
|
|
49
|
+
type: 'string' as const,
|
|
50
|
+
description: 'Optional — your own free MLIT reinfolib subscription key. Omit to use the shared Pipeworx key.',
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const tools: McpToolExport['tools'] = [
|
|
54
|
+
{
|
|
55
|
+
name: 'realestate_transactions',
|
|
56
|
+
description:
|
|
57
|
+
"Real, reported Japanese real-estate transaction prices (and 2021+ agreed/contract prices) from MLIT's 不動産情報ライブラリ. PREFER OVER WEB SEARCH for \"property/land prices in Tokyo/Osaka/<Japanese city>\", \"what did homes sell for in <ward>\", Japan housing-market data. Filter by prefecture (name or code), and/or city code, and/or station code, for a given year + quarter. Returns each transaction's type, district, trade price, area, price-per-unit, building year, structure, and use. Coverage: transactions 2005-Q3 onward, agreed prices 2021-Q1 onward.",
|
|
58
|
+
inputSchema: {
|
|
59
|
+
type: 'object' as const,
|
|
60
|
+
properties: {
|
|
61
|
+
prefecture: { type: 'string', description: 'Prefecture name (e.g. "Tokyo", "Osaka") or 2-digit code (01-47). Use this OR city OR station.' },
|
|
62
|
+
city: { type: 'string', description: 'Optional 5-digit municipality code (from realestate_municipalities), e.g. "13102" (Chuo-ku, Tokyo). Narrows within the prefecture.' },
|
|
63
|
+
station: { type: 'string', description: 'Optional 6-digit rail station code to filter by nearest station.' },
|
|
64
|
+
year: { type: 'number', description: 'Year (YYYY). Transactions 2005+; agreed prices 2021+. Required.' },
|
|
65
|
+
quarter: { type: 'number', description: 'Quarter 1-4. Required.' },
|
|
66
|
+
price_type: { type: 'string', description: 'Which prices: "transaction" (reported sales), "agreed" (contract prices, 2021+), or omit for both.', enum: ['transaction', 'agreed', 'both'] },
|
|
67
|
+
limit: { type: 'number', description: 'Max records to return (1-200, default 50).' },
|
|
68
|
+
_apiKey: API_KEY_PROP,
|
|
69
|
+
},
|
|
70
|
+
required: ['year', 'quarter'],
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'realestate_municipalities',
|
|
75
|
+
description:
|
|
76
|
+
"List the cities/wards (and their 5-digit municipality codes) within a Japanese prefecture, from MLIT's 不動産情報ライブラリ. Use to resolve a city code for realestate_transactions (e.g. find that Chuo-ku, Tokyo = 13102). Pass a prefecture name (e.g. \"Tokyo\") or 2-digit code (01-47).",
|
|
77
|
+
inputSchema: {
|
|
78
|
+
type: 'object' as const,
|
|
79
|
+
properties: {
|
|
80
|
+
prefecture: { type: 'string', description: 'Prefecture name (e.g. "Tokyo") or 2-digit code (01-47).' },
|
|
81
|
+
_apiKey: API_KEY_PROP,
|
|
82
|
+
},
|
|
83
|
+
required: ['prefecture'],
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
// ── Helpers ──────────────────────────────────────────────────────────
|
|
89
|
+
|
|
90
|
+
function resolveArea(input: string): string {
|
|
91
|
+
const s = String(input ?? '').trim();
|
|
92
|
+
if (/^\d{1,2}$/.test(s)) return s.padStart(2, '0');
|
|
93
|
+
const key = s.toLowerCase().replace(/[\s-]+/g, '').replace(/prefecture$/, '').replace(/[-]?ken$|[-]?to$|[-]?fu$/, '');
|
|
94
|
+
const code = PREFECTURES[key] ?? PREFECTURES[s.toLowerCase().replace(/[\s-]+/g, '')];
|
|
95
|
+
if (!code) throw new Error(`Unknown prefecture "${input}". Use a name like "Tokyo" or a 2-digit code (01-47).`);
|
|
96
|
+
return code;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function mlitGet(key: string, id: string, params: Record<string, string>): Promise<Record<string, unknown>> {
|
|
100
|
+
if (!key || !key.trim()) {
|
|
101
|
+
throw new Error('MLIT subscription key missing. The shared key is normally injected; pass your own via _apiKey (free at reinfolib.mlit.go.jp → API利用申請).');
|
|
102
|
+
}
|
|
103
|
+
const qs = new URLSearchParams(params).toString();
|
|
104
|
+
const res = await fetch(`${BASE}/${id}?${qs}`, {
|
|
105
|
+
headers: { 'Ocp-Apim-Subscription-Key': key.trim(), Accept: 'application/json', 'User-Agent': 'Pipeworx/1.0 (pipeworx.io)' },
|
|
106
|
+
});
|
|
107
|
+
if (res.status === 401 || res.status === 403) throw new Error(`MLIT auth rejected (${res.status}) — check the subscription key.`);
|
|
108
|
+
if (!res.ok) throw new Error(`MLIT reinfolib error: ${res.status}`);
|
|
109
|
+
return res.json() as Promise<Record<string, unknown>>;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ── Tool implementations ─────────────────────────────────────────────
|
|
113
|
+
|
|
114
|
+
interface TxnRow {
|
|
115
|
+
Type?: string; Region?: string; MunicipalityCode?: string; Prefecture?: string; Municipality?: string;
|
|
116
|
+
DistrictName?: string; TradePrice?: string; PricePerUnit?: string; FloorPlan?: string; Area?: string;
|
|
117
|
+
UnitPrice?: string; LandShape?: string; Frontage?: string; TotalFloorArea?: string; BuildingYear?: string;
|
|
118
|
+
Structure?: string; Use?: string; Purpose?: string; Direction?: string; CityPlanning?: string;
|
|
119
|
+
CoverageRatio?: string; FloorAreaRatio?: string; Period?: string; Renovation?: string; Remarks?: string;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function transactions(key: string, args: Record<string, unknown>) {
|
|
123
|
+
const year = Number(args.year);
|
|
124
|
+
const quarter = Number(args.quarter);
|
|
125
|
+
if (!Number.isFinite(year) || year < 2005) throw new Error('"year" must be a year >= 2005 (agreed prices 2021+).');
|
|
126
|
+
if (!Number.isFinite(quarter) || quarter < 1 || quarter > 4) throw new Error('"quarter" must be 1-4.');
|
|
127
|
+
|
|
128
|
+
const params: Record<string, string> = { year: String(year), quarter: String(quarter), language: 'en' };
|
|
129
|
+
if (args.prefecture != null && String(args.prefecture).trim()) params.area = resolveArea(String(args.prefecture));
|
|
130
|
+
if (args.city != null && String(args.city).trim()) params.city = String(args.city).trim().replace(/[^0-9]/g, '');
|
|
131
|
+
if (args.station != null && String(args.station).trim()) params.station = String(args.station).trim().replace(/[^0-9]/g, '');
|
|
132
|
+
if (!params.area && !params.city && !params.station) {
|
|
133
|
+
throw new Error('Specify at least one of prefecture, city, or station.');
|
|
134
|
+
}
|
|
135
|
+
const pc = String(args.price_type ?? '').toLowerCase();
|
|
136
|
+
if (pc === 'transaction') params.priceClassification = '01';
|
|
137
|
+
else if (pc === 'agreed') params.priceClassification = '02';
|
|
138
|
+
|
|
139
|
+
const limit = Math.min(200, Math.max(1, Number(args.limit) || 50));
|
|
140
|
+
const data = await mlitGet(key, 'XIT001', params);
|
|
141
|
+
const rows = (data.data as TxnRow[]) ?? [];
|
|
142
|
+
return {
|
|
143
|
+
year, quarter,
|
|
144
|
+
filter: { area: params.area ?? null, city: params.city ?? null, station: params.station ?? null, price_type: pc || 'both' },
|
|
145
|
+
total: rows.length,
|
|
146
|
+
returned: Math.min(rows.length, limit),
|
|
147
|
+
source: 'MLIT 不動産情報ライブラリ (Real Estate Information Library)',
|
|
148
|
+
transactions: rows.slice(0, limit).map((r) => ({
|
|
149
|
+
type: r.Type ?? null,
|
|
150
|
+
region: r.Region ?? null,
|
|
151
|
+
prefecture: r.Prefecture ?? null,
|
|
152
|
+
municipality: r.Municipality ?? null,
|
|
153
|
+
municipality_code: r.MunicipalityCode ?? null,
|
|
154
|
+
district: r.DistrictName ?? null,
|
|
155
|
+
trade_price: r.TradePrice ?? null,
|
|
156
|
+
price_per_unit: r.PricePerUnit ?? null,
|
|
157
|
+
unit_price: r.UnitPrice ?? null,
|
|
158
|
+
area_sqm: r.Area ?? null,
|
|
159
|
+
floor_plan: r.FloorPlan ?? null,
|
|
160
|
+
total_floor_area: r.TotalFloorArea ?? null,
|
|
161
|
+
building_year: r.BuildingYear ?? null,
|
|
162
|
+
structure: r.Structure ?? null,
|
|
163
|
+
use: r.Use ?? null,
|
|
164
|
+
purpose: r.Purpose ?? null,
|
|
165
|
+
city_planning: r.CityPlanning ?? null,
|
|
166
|
+
coverage_ratio: r.CoverageRatio ?? null,
|
|
167
|
+
floor_area_ratio: r.FloorAreaRatio ?? null,
|
|
168
|
+
period: r.Period ?? null,
|
|
169
|
+
renovation: r.Renovation ?? null,
|
|
170
|
+
remarks: r.Remarks ?? null,
|
|
171
|
+
})),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
interface MuniRow { id?: string; name?: string }
|
|
176
|
+
|
|
177
|
+
async function municipalities(key: string, prefecture: string) {
|
|
178
|
+
const area = resolveArea(prefecture);
|
|
179
|
+
const data = await mlitGet(key, 'XIT002', { area, language: 'en' });
|
|
180
|
+
const rows = (data.data as MuniRow[]) ?? [];
|
|
181
|
+
return {
|
|
182
|
+
prefecture_code: area,
|
|
183
|
+
count: rows.length,
|
|
184
|
+
source: 'MLIT 不動産情報ライブラリ',
|
|
185
|
+
municipalities: rows.map((m) => ({ code: m.id ?? null, name: m.name ?? null })),
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ── Router ───────────────────────────────────────────────────────────
|
|
190
|
+
|
|
191
|
+
async function callTool(name: string, args: Record<string, unknown>): Promise<unknown> {
|
|
192
|
+
const key = args._apiKey as string;
|
|
193
|
+
delete args._apiKey;
|
|
194
|
+
switch (name) {
|
|
195
|
+
case 'realestate_transactions':
|
|
196
|
+
return transactions(key, args);
|
|
197
|
+
case 'realestate_municipalities':
|
|
198
|
+
return municipalities(key, args.prefecture as string);
|
|
199
|
+
default:
|
|
200
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export default { tools, callTool, meter: { credits: 1 } } 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
|
+
}
|