@unclick/restcountries-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.
- package/LICENSE +15 -0
- package/README.md +38 -0
- package/dist/index.js +116 -0
- package/dist/restcountries-tool.js +140 -0
- package/package.json +44 -0
- package/server.json +33 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Apache License 2.0
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 UnClick / malamutemayhem
|
|
4
|
+
|
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
you may not use this file except in compliance with the License.
|
|
7
|
+
You may obtain a copy of the License at
|
|
8
|
+
|
|
9
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
|
|
11
|
+
Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
See the License for the specific language governing permissions and
|
|
15
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# REST Countries MCP by UnClick
|
|
2
|
+
|
|
3
|
+
Country data: flags, currencies, languages, regions, capitals, and more. No API key.
|
|
4
|
+
|
|
5
|
+
> By UnClick. 180+ tools plus persistent agent memory in one install: https://unclick.world
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
Installs straight from GitHub, no npm account needed.
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"mcpServers": {
|
|
14
|
+
"restcountries": {
|
|
15
|
+
"command": "npx",
|
|
16
|
+
"args": ["-y", "https://github.com/malamutemayhem/unclick/releases/download/standalone-mcps-latest/restcountries.tgz"]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Tools
|
|
23
|
+
|
|
24
|
+
- `country_all`
|
|
25
|
+
- `country_by_name`
|
|
26
|
+
- `country_by_code`
|
|
27
|
+
- `country_by_region`
|
|
28
|
+
- `country_by_currency`
|
|
29
|
+
- `country_by_language`
|
|
30
|
+
|
|
31
|
+
## Want the rest?
|
|
32
|
+
|
|
33
|
+
This is one connector. [UnClick](https://unclick.world) bundles 180+ tools plus
|
|
34
|
+
persistent cross-session agent memory in a single install.
|
|
35
|
+
|
|
36
|
+
## License
|
|
37
|
+
|
|
38
|
+
Apache-2.0
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// REST Countries MCP. Standalone MCP server by UnClick.
|
|
3
|
+
// By UnClick. 180+ tools plus persistent agent memory in one install: https://unclick.world
|
|
4
|
+
//
|
|
5
|
+
// Generated from the UnClick connector by scripts/generate-standalone-mcp.mjs.
|
|
6
|
+
// Edit the connector in the UnClick monorepo, not here.
|
|
7
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
8
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
10
|
+
import { countryAll, countryByName, countryByCode, countryByRegion, countryByCurrency, countryByLanguage, } from "./restcountries-tool.js";
|
|
11
|
+
const TOOLS = [
|
|
12
|
+
{
|
|
13
|
+
name: "country_all",
|
|
14
|
+
description: "Get all countries from REST Countries.",
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: "object",
|
|
17
|
+
additionalProperties: false,
|
|
18
|
+
properties: {
|
|
19
|
+
fields: { type: "string" },
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "country_by_name",
|
|
25
|
+
description: "Get a country by name from REST Countries.",
|
|
26
|
+
inputSchema: {
|
|
27
|
+
type: "object",
|
|
28
|
+
additionalProperties: false,
|
|
29
|
+
properties: {
|
|
30
|
+
name: { type: "string" },
|
|
31
|
+
fullText: { type: "boolean" },
|
|
32
|
+
},
|
|
33
|
+
required: ["name"],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "country_by_code",
|
|
38
|
+
description: "Get a country by code from REST Countries.",
|
|
39
|
+
inputSchema: {
|
|
40
|
+
type: "object",
|
|
41
|
+
additionalProperties: false,
|
|
42
|
+
properties: {
|
|
43
|
+
code: { type: "string" },
|
|
44
|
+
},
|
|
45
|
+
required: ["code"],
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "country_by_region",
|
|
50
|
+
description: "Get countries by region from REST Countries.",
|
|
51
|
+
inputSchema: {
|
|
52
|
+
type: "object",
|
|
53
|
+
additionalProperties: false,
|
|
54
|
+
properties: {
|
|
55
|
+
region: { type: "string" },
|
|
56
|
+
},
|
|
57
|
+
required: ["region"],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: "country_by_currency",
|
|
62
|
+
description: "Get countries by currency from REST Countries.",
|
|
63
|
+
inputSchema: {
|
|
64
|
+
type: "object",
|
|
65
|
+
additionalProperties: false,
|
|
66
|
+
properties: {
|
|
67
|
+
currency: { type: "string" },
|
|
68
|
+
},
|
|
69
|
+
required: ["currency"],
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: "country_by_language",
|
|
74
|
+
description: "Get countries by language from REST Countries.",
|
|
75
|
+
inputSchema: {
|
|
76
|
+
type: "object",
|
|
77
|
+
additionalProperties: false,
|
|
78
|
+
properties: {
|
|
79
|
+
language: { type: "string" },
|
|
80
|
+
},
|
|
81
|
+
required: ["language"],
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
];
|
|
85
|
+
const HANDLERS = {
|
|
86
|
+
country_all: (args) => countryAll(args),
|
|
87
|
+
country_by_name: (args) => countryByName(args),
|
|
88
|
+
country_by_code: (args) => countryByCode(args),
|
|
89
|
+
country_by_region: (args) => countryByRegion(args),
|
|
90
|
+
country_by_currency: (args) => countryByCurrency(args),
|
|
91
|
+
country_by_language: (args) => countryByLanguage(args),
|
|
92
|
+
};
|
|
93
|
+
const server = new Server({ name: "io.github.malamutemayhem/restcountries", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
94
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
95
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
96
|
+
const handler = HANDLERS[req.params.name];
|
|
97
|
+
if (!handler) {
|
|
98
|
+
return { content: [{ type: "text", text: `Unknown tool: ${req.params.name}` }], isError: true };
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const result = await handler((req.params.arguments ?? {}));
|
|
102
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
106
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
async function main() {
|
|
110
|
+
const transport = new StdioServerTransport();
|
|
111
|
+
await server.connect(transport);
|
|
112
|
+
}
|
|
113
|
+
main().catch((err) => {
|
|
114
|
+
process.stderr.write(`[restcountries-mcp] fatal: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
});
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// REST Countries integration for the UnClick MCP server.
|
|
2
|
+
// Uses the REST Countries public API via fetch - no auth required.
|
|
3
|
+
// Data covers all 250 countries with rich metadata.
|
|
4
|
+
const RESTCOUNTRIES_BASE = "https://restcountries.com/v3.1";
|
|
5
|
+
// --- API helper ---
|
|
6
|
+
async function rcFetch(path, fields) {
|
|
7
|
+
const url = new URL(`${RESTCOUNTRIES_BASE}${path}`);
|
|
8
|
+
if (fields)
|
|
9
|
+
url.searchParams.set("fields", fields);
|
|
10
|
+
const response = await fetch(url.toString(), {
|
|
11
|
+
headers: { "Accept": "application/json" },
|
|
12
|
+
});
|
|
13
|
+
if (response.status === 404) {
|
|
14
|
+
return { results: [], note: "No countries matched the query." };
|
|
15
|
+
}
|
|
16
|
+
if (!response.ok) {
|
|
17
|
+
const text = await response.text().catch(() => "");
|
|
18
|
+
throw new Error(`HTTP ${response.status} from REST Countries API${text ? `: ${text}` : ""}`);
|
|
19
|
+
}
|
|
20
|
+
return response.json();
|
|
21
|
+
}
|
|
22
|
+
// --- Normalizer ---
|
|
23
|
+
function normalizeCountry(c) {
|
|
24
|
+
const name = c.name;
|
|
25
|
+
const currencies = c.currencies;
|
|
26
|
+
const languages = c.languages;
|
|
27
|
+
const capital = c.capital;
|
|
28
|
+
const flags = c.flags;
|
|
29
|
+
const maps = c.maps;
|
|
30
|
+
return {
|
|
31
|
+
name: name?.common ?? c.name,
|
|
32
|
+
official_name: name?.official ?? null,
|
|
33
|
+
cca2: c.cca2 ?? null,
|
|
34
|
+
cca3: c.cca3 ?? null,
|
|
35
|
+
capital: capital?.[0] ?? null,
|
|
36
|
+
region: c.region ?? null,
|
|
37
|
+
subregion: c.subregion ?? null,
|
|
38
|
+
population: c.population ?? null,
|
|
39
|
+
area: c.area ?? null,
|
|
40
|
+
flag_emoji: c.flag ?? null,
|
|
41
|
+
flag_url: flags?.png ?? null,
|
|
42
|
+
google_maps: maps?.googleMaps ?? null,
|
|
43
|
+
currencies: currencies
|
|
44
|
+
? Object.entries(currencies).map(([code, info]) => ({
|
|
45
|
+
code,
|
|
46
|
+
name: info.name,
|
|
47
|
+
symbol: info.symbol,
|
|
48
|
+
}))
|
|
49
|
+
: null,
|
|
50
|
+
languages: languages ? Object.values(languages) : null,
|
|
51
|
+
timezones: c.timezones ?? null,
|
|
52
|
+
continents: c.continents ?? null,
|
|
53
|
+
landlocked: c.landlocked ?? null,
|
|
54
|
+
borders: c.borders ?? null,
|
|
55
|
+
independent: c.independent ?? null,
|
|
56
|
+
un_member: c.unMember ?? null,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// --- Operations ---
|
|
60
|
+
export async function countryAll(args) {
|
|
61
|
+
const fields = String(args.fields ?? "").trim();
|
|
62
|
+
const data = await rcFetch("/all", fields || undefined);
|
|
63
|
+
if (data && typeof data === "object" && "results" in data)
|
|
64
|
+
return data;
|
|
65
|
+
const countries = data;
|
|
66
|
+
return {
|
|
67
|
+
count: countries.length,
|
|
68
|
+
countries: countries.map(normalizeCountry),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export async function countryByName(args) {
|
|
72
|
+
const name = String(args.name ?? "").trim();
|
|
73
|
+
if (!name)
|
|
74
|
+
throw new Error("name is required.");
|
|
75
|
+
const data = await rcFetch(`/name/${encodeURIComponent(name)}`);
|
|
76
|
+
if (data && typeof data === "object" && "results" in data)
|
|
77
|
+
return data;
|
|
78
|
+
const countries = data;
|
|
79
|
+
return {
|
|
80
|
+
count: countries.length,
|
|
81
|
+
countries: countries.map(normalizeCountry),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export async function countryByCode(args) {
|
|
85
|
+
const code = String(args.code ?? "").trim().toUpperCase();
|
|
86
|
+
if (!code)
|
|
87
|
+
throw new Error("code is required (ISO 2 or 3 letter code).");
|
|
88
|
+
const data = await rcFetch(`/alpha/${encodeURIComponent(code)}`);
|
|
89
|
+
if (data && typeof data === "object" && "results" in data)
|
|
90
|
+
return data;
|
|
91
|
+
// /alpha/{code} can return a single object or array
|
|
92
|
+
const arr = Array.isArray(data) ? data : [data];
|
|
93
|
+
return {
|
|
94
|
+
count: arr.length,
|
|
95
|
+
countries: arr.map(normalizeCountry),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export async function countryByRegion(args) {
|
|
99
|
+
const region = String(args.region ?? "").trim();
|
|
100
|
+
const validRegions = ["Africa", "Americas", "Asia", "Europe", "Oceania", "Antarctic"];
|
|
101
|
+
if (!region)
|
|
102
|
+
throw new Error(`region is required. Valid values: ${validRegions.join(", ")}.`);
|
|
103
|
+
const data = await rcFetch(`/region/${encodeURIComponent(region)}`);
|
|
104
|
+
if (data && typeof data === "object" && "results" in data)
|
|
105
|
+
return data;
|
|
106
|
+
const countries = data;
|
|
107
|
+
return {
|
|
108
|
+
region,
|
|
109
|
+
count: countries.length,
|
|
110
|
+
countries: countries.map(normalizeCountry),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
export async function countryByCurrency(args) {
|
|
114
|
+
const currency = String(args.currency ?? "").trim().toUpperCase();
|
|
115
|
+
if (!currency)
|
|
116
|
+
throw new Error("currency is required (e.g. USD, EUR).");
|
|
117
|
+
const data = await rcFetch(`/currency/${encodeURIComponent(currency)}`);
|
|
118
|
+
if (data && typeof data === "object" && "results" in data)
|
|
119
|
+
return data;
|
|
120
|
+
const countries = data;
|
|
121
|
+
return {
|
|
122
|
+
currency,
|
|
123
|
+
count: countries.length,
|
|
124
|
+
countries: countries.map(normalizeCountry),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
export async function countryByLanguage(args) {
|
|
128
|
+
const language = String(args.language ?? "").trim().toLowerCase();
|
|
129
|
+
if (!language)
|
|
130
|
+
throw new Error("language is required (e.g. english, spanish).");
|
|
131
|
+
const data = await rcFetch(`/lang/${encodeURIComponent(language)}`);
|
|
132
|
+
if (data && typeof data === "object" && "results" in data)
|
|
133
|
+
return data;
|
|
134
|
+
const countries = data;
|
|
135
|
+
return {
|
|
136
|
+
language,
|
|
137
|
+
count: countries.length,
|
|
138
|
+
countries: countries.map(normalizeCountry),
|
|
139
|
+
};
|
|
140
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@unclick/restcountries-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"mcpName": "io.github.malamutemayhem/restcountries",
|
|
5
|
+
"description": "Country data: flags, currencies, languages, regions, capitals, and more. No API key. By UnClick (https://unclick.world).",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"mcp",
|
|
8
|
+
"model-context-protocol",
|
|
9
|
+
"unclick",
|
|
10
|
+
"countries",
|
|
11
|
+
"geography",
|
|
12
|
+
"restcountries"
|
|
13
|
+
],
|
|
14
|
+
"author": "UnClick (https://unclick.world)",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"bin": {
|
|
17
|
+
"restcountries-mcp": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"main": "./dist/index.js",
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md",
|
|
23
|
+
"server.json"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsc",
|
|
27
|
+
"start": "node dist/index.js",
|
|
28
|
+
"prepublishOnly": "npm run build"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.15.1"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"typescript": "^5.6.0",
|
|
35
|
+
"@types/node": "^22.0.0"
|
|
36
|
+
},
|
|
37
|
+
"license": "Apache-2.0",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/malamutemayhem/unclick.git",
|
|
41
|
+
"directory": "packages/standalone/restcountries-mcp"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://unclick.world"
|
|
44
|
+
}
|
package/server.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.malamutemayhem/restcountries",
|
|
4
|
+
"title": "REST Countries MCP by UnClick",
|
|
5
|
+
"description": "Country data: flags, currencies, languages, regions, capitals, and more. No API key. By UnClick.",
|
|
6
|
+
"version": "0.1.0",
|
|
7
|
+
"websiteUrl": "https://unclick.world",
|
|
8
|
+
"icons": [
|
|
9
|
+
{
|
|
10
|
+
"src": "https://unclick.world/favicon.png",
|
|
11
|
+
"mimeType": "image/png",
|
|
12
|
+
"sizes": [
|
|
13
|
+
"512x512"
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"repository": {
|
|
18
|
+
"url": "https://github.com/malamutemayhem/unclick.git",
|
|
19
|
+
"source": "github",
|
|
20
|
+
"subfolder": "packages/standalone/restcountries-mcp"
|
|
21
|
+
},
|
|
22
|
+
"packages": [
|
|
23
|
+
{
|
|
24
|
+
"registryType": "npm",
|
|
25
|
+
"identifier": "@unclick/restcountries-mcp",
|
|
26
|
+
"version": "0.1.0",
|
|
27
|
+
"runtimeHint": "npx",
|
|
28
|
+
"transport": {
|
|
29
|
+
"type": "stdio"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|