@codespar/mcp-coordinadora 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/README.md +51 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +309 -0
- package/package.json +25 -0
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# MCP Coordinadora
|
|
2
|
+
|
|
3
|
+
MCP server for **Coordinadora** — one of Colombia's largest courier and logistics companies, offering domestic and international shipping services.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Set your credentials
|
|
9
|
+
export COORDINADORA_API_KEY="your-api-key"
|
|
10
|
+
export COORDINADORA_NIT="your-nit"
|
|
11
|
+
|
|
12
|
+
# Run via stdio
|
|
13
|
+
npx tsx packages/colombia/coordinadora/src/index.ts
|
|
14
|
+
|
|
15
|
+
# Run via HTTP
|
|
16
|
+
npx tsx packages/colombia/coordinadora/src/index.ts --http
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Environment Variables
|
|
20
|
+
|
|
21
|
+
| Variable | Required | Description |
|
|
22
|
+
|----------|----------|-------------|
|
|
23
|
+
| `COORDINADORA_API_KEY` | Yes | API key from Coordinadora |
|
|
24
|
+
| `COORDINADORA_NIT` | Yes | Company NIT number |
|
|
25
|
+
| `MCP_HTTP` | No | Set to `"true"` to enable HTTP transport |
|
|
26
|
+
| `MCP_PORT` | No | HTTP port (default: 3000) |
|
|
27
|
+
|
|
28
|
+
## Tools
|
|
29
|
+
|
|
30
|
+
| Tool | Description |
|
|
31
|
+
|------|-------------|
|
|
32
|
+
| `create_shipment` | Create a new shipment |
|
|
33
|
+
| `get_shipment` | Get shipment details by guide number |
|
|
34
|
+
| `track_shipment` | Track a shipment |
|
|
35
|
+
| `get_rates` | Get shipping rates/quotes |
|
|
36
|
+
| `list_cities` | List available cities for shipping |
|
|
37
|
+
| `create_pickup` | Schedule a pickup |
|
|
38
|
+
| `get_coverage` | Check coverage for a location |
|
|
39
|
+
| `cancel_shipment` | Cancel a shipment |
|
|
40
|
+
|
|
41
|
+
## Auth
|
|
42
|
+
|
|
43
|
+
Uses **API key + NIT header** authentication. Both the API key and company NIT are sent as custom headers with every request.
|
|
44
|
+
|
|
45
|
+
## API Reference
|
|
46
|
+
|
|
47
|
+
- [Coordinadora API Docs](https://www.coordinadora.com/portafolio-de-servicios/soluciones-tecnologicas/)
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
**Enterprise?** Contact us at [codespar.com](https://codespar.com) for dedicated support, custom integrations, and SLAs.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* MCP Server for Coordinadora — major Colombian courier/logistics company.
|
|
4
|
+
*
|
|
5
|
+
* Tools:
|
|
6
|
+
* - create_shipment: Create a new shipment
|
|
7
|
+
* - get_shipment: Get shipment details
|
|
8
|
+
* - track_shipment: Track a shipment
|
|
9
|
+
* - get_rates: Get shipping rates/quotes
|
|
10
|
+
* - list_cities: List available cities for shipping
|
|
11
|
+
* - create_pickup: Schedule a pickup
|
|
12
|
+
* - get_coverage: Check coverage for a location
|
|
13
|
+
* - cancel_shipment: Cancel a shipment
|
|
14
|
+
*
|
|
15
|
+
* Environment:
|
|
16
|
+
* COORDINADORA_API_KEY — API key
|
|
17
|
+
* COORDINADORA_NIT — Company NIT number
|
|
18
|
+
*/
|
|
19
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* MCP Server for Coordinadora — major Colombian courier/logistics company.
|
|
4
|
+
*
|
|
5
|
+
* Tools:
|
|
6
|
+
* - create_shipment: Create a new shipment
|
|
7
|
+
* - get_shipment: Get shipment details
|
|
8
|
+
* - track_shipment: Track a shipment
|
|
9
|
+
* - get_rates: Get shipping rates/quotes
|
|
10
|
+
* - list_cities: List available cities for shipping
|
|
11
|
+
* - create_pickup: Schedule a pickup
|
|
12
|
+
* - get_coverage: Check coverage for a location
|
|
13
|
+
* - cancel_shipment: Cancel a shipment
|
|
14
|
+
*
|
|
15
|
+
* Environment:
|
|
16
|
+
* COORDINADORA_API_KEY — API key
|
|
17
|
+
* COORDINADORA_NIT — Company NIT number
|
|
18
|
+
*/
|
|
19
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
20
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
21
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
22
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
23
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
24
|
+
const API_KEY = process.env.COORDINADORA_API_KEY || "";
|
|
25
|
+
const NIT = process.env.COORDINADORA_NIT || "";
|
|
26
|
+
const BASE_URL = "https://api.coordinadora.com/v1";
|
|
27
|
+
async function coordinadoraRequest(method, path, body) {
|
|
28
|
+
const headers = {
|
|
29
|
+
"Content-Type": "application/json",
|
|
30
|
+
"Accept": "application/json",
|
|
31
|
+
"x-api-key": API_KEY,
|
|
32
|
+
"x-nit": NIT,
|
|
33
|
+
};
|
|
34
|
+
const res = await fetch(`${BASE_URL}${path}`, {
|
|
35
|
+
method,
|
|
36
|
+
headers,
|
|
37
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
38
|
+
});
|
|
39
|
+
if (!res.ok) {
|
|
40
|
+
const err = await res.text();
|
|
41
|
+
throw new Error(`Coordinadora API ${res.status}: ${err}`);
|
|
42
|
+
}
|
|
43
|
+
return res.json();
|
|
44
|
+
}
|
|
45
|
+
const server = new Server({ name: "mcp-coordinadora", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
46
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
47
|
+
tools: [
|
|
48
|
+
{
|
|
49
|
+
name: "create_shipment",
|
|
50
|
+
description: "Create a new shipment",
|
|
51
|
+
inputSchema: {
|
|
52
|
+
type: "object",
|
|
53
|
+
properties: {
|
|
54
|
+
origin_city: { type: "string", description: "Origin city DANE code" },
|
|
55
|
+
destination_city: { type: "string", description: "Destination city DANE code" },
|
|
56
|
+
sender: {
|
|
57
|
+
type: "object",
|
|
58
|
+
description: "Sender information",
|
|
59
|
+
properties: {
|
|
60
|
+
name: { type: "string", description: "Sender name" },
|
|
61
|
+
phone: { type: "string", description: "Sender phone" },
|
|
62
|
+
address: { type: "string", description: "Sender address" },
|
|
63
|
+
nit: { type: "string", description: "Sender NIT/CC" },
|
|
64
|
+
},
|
|
65
|
+
required: ["name", "phone", "address"],
|
|
66
|
+
},
|
|
67
|
+
recipient: {
|
|
68
|
+
type: "object",
|
|
69
|
+
description: "Recipient information",
|
|
70
|
+
properties: {
|
|
71
|
+
name: { type: "string", description: "Recipient name" },
|
|
72
|
+
phone: { type: "string", description: "Recipient phone" },
|
|
73
|
+
address: { type: "string", description: "Recipient address" },
|
|
74
|
+
nit: { type: "string", description: "Recipient NIT/CC" },
|
|
75
|
+
},
|
|
76
|
+
required: ["name", "phone", "address"],
|
|
77
|
+
},
|
|
78
|
+
packages: {
|
|
79
|
+
type: "array",
|
|
80
|
+
description: "Packages to ship",
|
|
81
|
+
items: {
|
|
82
|
+
type: "object",
|
|
83
|
+
properties: {
|
|
84
|
+
weight: { type: "number", description: "Weight in kg" },
|
|
85
|
+
height: { type: "number", description: "Height in cm" },
|
|
86
|
+
width: { type: "number", description: "Width in cm" },
|
|
87
|
+
length: { type: "number", description: "Length in cm" },
|
|
88
|
+
declared_value: { type: "number", description: "Declared value in COP" },
|
|
89
|
+
content: { type: "string", description: "Package content description" },
|
|
90
|
+
},
|
|
91
|
+
required: ["weight"],
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
service_type: { type: "string", description: "Service type (standard, express, same_day)" },
|
|
95
|
+
payment_type: { type: "string", description: "Payment type (prepaid, collect, contra_entrega)" },
|
|
96
|
+
},
|
|
97
|
+
required: ["origin_city", "destination_city", "sender", "recipient", "packages"],
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: "get_shipment",
|
|
102
|
+
description: "Get shipment details by guide number",
|
|
103
|
+
inputSchema: {
|
|
104
|
+
type: "object",
|
|
105
|
+
properties: { guideNumber: { type: "string", description: "Guide number (numero de guia)" } },
|
|
106
|
+
required: ["guideNumber"],
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: "track_shipment",
|
|
111
|
+
description: "Track a shipment by guide number",
|
|
112
|
+
inputSchema: {
|
|
113
|
+
type: "object",
|
|
114
|
+
properties: { guideNumber: { type: "string", description: "Guide number" } },
|
|
115
|
+
required: ["guideNumber"],
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: "get_rates",
|
|
120
|
+
description: "Get shipping rates/quotes",
|
|
121
|
+
inputSchema: {
|
|
122
|
+
type: "object",
|
|
123
|
+
properties: {
|
|
124
|
+
origin_city: { type: "string", description: "Origin city DANE code" },
|
|
125
|
+
destination_city: { type: "string", description: "Destination city DANE code" },
|
|
126
|
+
weight: { type: "number", description: "Weight in kg" },
|
|
127
|
+
height: { type: "number", description: "Height in cm" },
|
|
128
|
+
width: { type: "number", description: "Width in cm" },
|
|
129
|
+
length: { type: "number", description: "Length in cm" },
|
|
130
|
+
declared_value: { type: "number", description: "Declared value in COP" },
|
|
131
|
+
},
|
|
132
|
+
required: ["origin_city", "destination_city", "weight"],
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "list_cities",
|
|
137
|
+
description: "List available cities for shipping",
|
|
138
|
+
inputSchema: {
|
|
139
|
+
type: "object",
|
|
140
|
+
properties: {
|
|
141
|
+
department: { type: "string", description: "Filter by department name" },
|
|
142
|
+
search: { type: "string", description: "Search by city name" },
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: "create_pickup",
|
|
148
|
+
description: "Schedule a pickup at an address",
|
|
149
|
+
inputSchema: {
|
|
150
|
+
type: "object",
|
|
151
|
+
properties: {
|
|
152
|
+
address: { type: "string", description: "Pickup address" },
|
|
153
|
+
city: { type: "string", description: "City DANE code" },
|
|
154
|
+
contact_name: { type: "string", description: "Contact person name" },
|
|
155
|
+
contact_phone: { type: "string", description: "Contact phone" },
|
|
156
|
+
date: { type: "string", description: "Pickup date (YYYY-MM-DD)" },
|
|
157
|
+
time_from: { type: "string", description: "Pickup window start (HH:MM)" },
|
|
158
|
+
time_to: { type: "string", description: "Pickup window end (HH:MM)" },
|
|
159
|
+
packages_count: { type: "number", description: "Number of packages" },
|
|
160
|
+
total_weight: { type: "number", description: "Total weight in kg" },
|
|
161
|
+
},
|
|
162
|
+
required: ["address", "city", "contact_name", "contact_phone", "date"],
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: "get_coverage",
|
|
167
|
+
description: "Check if a location is within coverage area",
|
|
168
|
+
inputSchema: {
|
|
169
|
+
type: "object",
|
|
170
|
+
properties: {
|
|
171
|
+
city: { type: "string", description: "City DANE code or name" },
|
|
172
|
+
postal_code: { type: "string", description: "Postal code" },
|
|
173
|
+
},
|
|
174
|
+
required: ["city"],
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: "cancel_shipment",
|
|
179
|
+
description: "Cancel a shipment",
|
|
180
|
+
inputSchema: {
|
|
181
|
+
type: "object",
|
|
182
|
+
properties: {
|
|
183
|
+
guideNumber: { type: "string", description: "Guide number" },
|
|
184
|
+
reason: { type: "string", description: "Cancellation reason" },
|
|
185
|
+
},
|
|
186
|
+
required: ["guideNumber"],
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
}));
|
|
191
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
192
|
+
const { name, arguments: args } = request.params;
|
|
193
|
+
try {
|
|
194
|
+
switch (name) {
|
|
195
|
+
case "create_shipment":
|
|
196
|
+
return { content: [{ type: "text", text: JSON.stringify(await coordinadoraRequest("POST", "/guias", {
|
|
197
|
+
ciudadOrigen: args?.origin_city,
|
|
198
|
+
ciudadDestino: args?.destination_city,
|
|
199
|
+
remitente: args?.sender,
|
|
200
|
+
destinatario: args?.recipient,
|
|
201
|
+
bultos: args?.packages,
|
|
202
|
+
tipoServicio: args?.service_type,
|
|
203
|
+
tipoPago: args?.payment_type,
|
|
204
|
+
}), null, 2) }] };
|
|
205
|
+
case "get_shipment":
|
|
206
|
+
return { content: [{ type: "text", text: JSON.stringify(await coordinadoraRequest("GET", `/guias/${args?.guideNumber}`), null, 2) }] };
|
|
207
|
+
case "track_shipment":
|
|
208
|
+
return { content: [{ type: "text", text: JSON.stringify(await coordinadoraRequest("GET", `/guias/${args?.guideNumber}/tracking`), null, 2) }] };
|
|
209
|
+
case "get_rates": {
|
|
210
|
+
const payload = {
|
|
211
|
+
ciudadOrigen: args?.origin_city,
|
|
212
|
+
ciudadDestino: args?.destination_city,
|
|
213
|
+
peso: args?.weight,
|
|
214
|
+
};
|
|
215
|
+
if (args?.height)
|
|
216
|
+
payload.alto = args.height;
|
|
217
|
+
if (args?.width)
|
|
218
|
+
payload.ancho = args.width;
|
|
219
|
+
if (args?.length)
|
|
220
|
+
payload.largo = args.length;
|
|
221
|
+
if (args?.declared_value)
|
|
222
|
+
payload.valorDeclarado = args.declared_value;
|
|
223
|
+
return { content: [{ type: "text", text: JSON.stringify(await coordinadoraRequest("POST", "/cotizaciones", payload), null, 2) }] };
|
|
224
|
+
}
|
|
225
|
+
case "list_cities": {
|
|
226
|
+
const params = new URLSearchParams();
|
|
227
|
+
if (args?.department)
|
|
228
|
+
params.set("departamento", args.department);
|
|
229
|
+
if (args?.search)
|
|
230
|
+
params.set("buscar", args.search);
|
|
231
|
+
return { content: [{ type: "text", text: JSON.stringify(await coordinadoraRequest("GET", `/ciudades?${params}`), null, 2) }] };
|
|
232
|
+
}
|
|
233
|
+
case "create_pickup":
|
|
234
|
+
return { content: [{ type: "text", text: JSON.stringify(await coordinadoraRequest("POST", "/recolecciones", {
|
|
235
|
+
direccion: args?.address,
|
|
236
|
+
ciudad: args?.city,
|
|
237
|
+
contactoNombre: args?.contact_name,
|
|
238
|
+
contactoTelefono: args?.contact_phone,
|
|
239
|
+
fecha: args?.date,
|
|
240
|
+
horaDesde: args?.time_from,
|
|
241
|
+
horaHasta: args?.time_to,
|
|
242
|
+
cantidadBultos: args?.packages_count,
|
|
243
|
+
pesoTotal: args?.total_weight,
|
|
244
|
+
}), null, 2) }] };
|
|
245
|
+
case "get_coverage": {
|
|
246
|
+
const params = new URLSearchParams();
|
|
247
|
+
params.set("ciudad", args?.city);
|
|
248
|
+
if (args?.postal_code)
|
|
249
|
+
params.set("codigoPostal", args.postal_code);
|
|
250
|
+
return { content: [{ type: "text", text: JSON.stringify(await coordinadoraRequest("GET", `/cobertura?${params}`), null, 2) }] };
|
|
251
|
+
}
|
|
252
|
+
case "cancel_shipment": {
|
|
253
|
+
const payload = {};
|
|
254
|
+
if (args?.reason)
|
|
255
|
+
payload.motivo = args.reason;
|
|
256
|
+
return { content: [{ type: "text", text: JSON.stringify(await coordinadoraRequest("DELETE", `/guias/${args?.guideNumber}`, payload), null, 2) }] };
|
|
257
|
+
}
|
|
258
|
+
default:
|
|
259
|
+
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
catch (err) {
|
|
263
|
+
return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
async function main() {
|
|
267
|
+
if (process.argv.includes("--http") || process.env.MCP_HTTP === "true") {
|
|
268
|
+
const { default: express } = await import("express");
|
|
269
|
+
const { randomUUID } = await import("node:crypto");
|
|
270
|
+
const app = express();
|
|
271
|
+
app.use(express.json());
|
|
272
|
+
const transports = new Map();
|
|
273
|
+
app.get("/health", (_req, res) => res.json({ status: "ok", sessions: transports.size }));
|
|
274
|
+
app.post("/mcp", async (req, res) => {
|
|
275
|
+
const sid = req.headers["mcp-session-id"];
|
|
276
|
+
if (sid && transports.has(sid)) {
|
|
277
|
+
await transports.get(sid).handleRequest(req, res, req.body);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (!sid && isInitializeRequest(req.body)) {
|
|
281
|
+
const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
|
|
282
|
+
t.onclose = () => { if (t.sessionId)
|
|
283
|
+
transports.delete(t.sessionId); };
|
|
284
|
+
const s = new Server({ name: "mcp-coordinadora", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
285
|
+
server._requestHandlers.forEach((v, k) => s._requestHandlers.set(k, v));
|
|
286
|
+
server._notificationHandlers?.forEach((v, k) => s._notificationHandlers.set(k, v));
|
|
287
|
+
await s.connect(t);
|
|
288
|
+
await t.handleRequest(req, res, req.body);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
res.status(400).json({ jsonrpc: "2.0", error: { code: -32000, message: "Bad Request" }, id: null });
|
|
292
|
+
});
|
|
293
|
+
app.get("/mcp", async (req, res) => { const sid = req.headers["mcp-session-id"]; if (sid && transports.has(sid))
|
|
294
|
+
await transports.get(sid).handleRequest(req, res);
|
|
295
|
+
else
|
|
296
|
+
res.status(400).send("Invalid session"); });
|
|
297
|
+
app.delete("/mcp", async (req, res) => { const sid = req.headers["mcp-session-id"]; if (sid && transports.has(sid))
|
|
298
|
+
await transports.get(sid).handleRequest(req, res);
|
|
299
|
+
else
|
|
300
|
+
res.status(400).send("Invalid session"); });
|
|
301
|
+
const port = Number(process.env.MCP_PORT) || 3000;
|
|
302
|
+
app.listen(port, () => { console.error(`MCP HTTP server on http://localhost:${port}/mcp`); });
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
const transport = new StdioServerTransport();
|
|
306
|
+
await server.connect(transport);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
main().catch(console.error);
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@codespar/mcp-coordinadora",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for Coordinadora — Colombian courier and logistics",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mcp-coordinadora": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": ["dist"],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"start": "node dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/node": "^22.0.0",
|
|
20
|
+
"typescript": "^5.8.0"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"keywords": ["coordinadora", "courier", "logistics", "colombia", "shipping", "mcp"],
|
|
24
|
+
"mcpName": "io.github.codespar/mcp-coordinadora"
|
|
25
|
+
}
|