tripinned-mcp 1.0.9 → 1.0.15
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/dist/index.js +83 -5
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
4
5
|
import { z } from "zod";
|
|
5
6
|
import { TRIP_LIMIT, ITEM_PER_DAY_LIMIT } from "./limits.js";
|
|
7
|
+
const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
8
|
+
const SERVER_VERSION = packageJson.version ?? "0.0.0";
|
|
6
9
|
const API_KEY = process.env.TRIPINNED_API_KEY;
|
|
7
10
|
const BASE_URL = process.env.TRIPINNED_API_URL ?? "https://api.tripinned.com/v1";
|
|
8
11
|
if (!API_KEY) {
|
|
@@ -32,9 +35,21 @@ async function call(method, path, body) {
|
|
|
32
35
|
}
|
|
33
36
|
return data;
|
|
34
37
|
}
|
|
35
|
-
const
|
|
36
|
-
name: "
|
|
37
|
-
|
|
38
|
+
const locationSchema = z.object({
|
|
39
|
+
name: z.string().max(100).optional().describe("Optional location label"),
|
|
40
|
+
lat: z.coerce.number().min(-90).max(90).describe("Location latitude"),
|
|
41
|
+
lng: z.coerce.number().min(-180).max(180).describe("Location longitude"),
|
|
42
|
+
order: z.coerce.number().int().optional().describe("Route order, 0-based"),
|
|
43
|
+
});
|
|
44
|
+
const server = new McpServer({ name: "tripinned", version: SERVER_VERSION }, {
|
|
45
|
+
instructions: `
|
|
46
|
+
Tripinned trip planning rules:
|
|
47
|
+
- Stopovers and layovers (transit stops between flights) must be created as a SINGLE item, not separate items. Combine them into one item with a clear title (e.g. "Layover at Dubai Airport").
|
|
48
|
+
- Each item belongs to exactly one day; do not split a single event across multiple days.
|
|
49
|
+
- When adding flight segments, group the full route (origin → stopover → destination) as one item if the user is just passing through.
|
|
50
|
+
- To show an item in the route map, set show_on_route to true. For place items, provide either lat/lng for a single location or locations for up to 10 ordered place stops. For flights, provide origin/destination IATA codes in from_place and to_place; no separate coordinates are needed. For non-flight transport, provide from_place/from_lat/from_lng and/or to_place/to_lat/to_lng.
|
|
51
|
+
- Hotel stays (check-in and check-out) must be created as a SINGLE item. Do not create separate items for check-in and check-out. Place the item on the check-in day and include both dates in the description (e.g. "Hotel ABC — Check-in Mar 5, Check-out Mar 8").
|
|
52
|
+
`.trim(),
|
|
38
53
|
});
|
|
39
54
|
// ── Trips ────────────────────────────────────────────────────────────────────
|
|
40
55
|
server.tool("list_trips", "내 여행 플랜 목록을 가져옵니다.", {}, async () => {
|
|
@@ -99,21 +114,34 @@ server.tool("add_item", `특정 Day에 일정을 추가합니다. Day당 최대
|
|
|
99
114
|
end_time: z.string().optional().describe("종료 시간 (HH:MM)"),
|
|
100
115
|
end_day_offset: z.coerce.number().int().min(0).optional().describe("도착일 오프셋 (0=당일, 1=다음날)"),
|
|
101
116
|
place: z.string().optional().describe("장소명"),
|
|
117
|
+
lat: z.coerce.number().min(-90).max(90).optional().describe("장소 위도 (경로 지도 표시용)"),
|
|
118
|
+
lng: z.coerce.number().min(-180).max(180).optional().describe("장소 경도 (경로 지도 표시용)"),
|
|
119
|
+
timezone: z.string().optional().describe("장소 IANA 시간대 (예: Asia/Seoul)"),
|
|
120
|
+
show_on_route: z.boolean().optional().describe("경로 보기에 표시할지 여부"),
|
|
102
121
|
memo: z.string().max(1000).optional().describe("메모"),
|
|
103
122
|
from_place: z
|
|
104
123
|
.string()
|
|
105
124
|
.optional()
|
|
106
125
|
.describe("출발지 IATA 코드 (항공편 전용, 예: ICN)"),
|
|
126
|
+
from_lat: z.coerce.number().min(-90).max(90).optional().describe("Non-flight transport origin latitude"),
|
|
127
|
+
from_lng: z.coerce.number().min(-180).max(180).optional().describe("Non-flight transport origin longitude"),
|
|
128
|
+
locations: z
|
|
129
|
+
.array(locationSchema)
|
|
130
|
+
.max(10)
|
|
131
|
+
.optional()
|
|
132
|
+
.describe("Ordered place locations for one place item, max 10"),
|
|
107
133
|
to_place: z
|
|
108
134
|
.string()
|
|
109
135
|
.optional()
|
|
110
136
|
.describe("도착지 IATA 코드 (항공편 전용, 예: MXP)"),
|
|
137
|
+
to_lat: z.coerce.number().min(-90).max(90).optional().describe("Non-flight transport destination latitude"),
|
|
138
|
+
to_lng: z.coerce.number().min(-180).max(180).optional().describe("Non-flight transport destination longitude"),
|
|
111
139
|
layovers: z
|
|
112
140
|
.string()
|
|
113
141
|
.optional()
|
|
114
142
|
.describe('경유지 JSON 배열 (항공편 전용). 필드: iata, tz, arrTime(HH:MM), arrOffset, depTime(HH:MM), depOffset, depFlightNo?, airportChange?, depIata?, depTz?'),
|
|
115
143
|
flight_no: z.string().optional().describe("항공편 번호 (예: KE447)"),
|
|
116
|
-
}, async ({ trip_id, day_id, title, icon, start_time, end_time, end_day_offset, place, memo, from_place, to_place, layovers, flight_no, }) => {
|
|
144
|
+
}, async ({ trip_id, day_id, title, icon, start_time, end_time, end_day_offset, place, lat, lng, locations, timezone, show_on_route, memo, from_place, from_lat, from_lng, to_place, to_lat, to_lng, layovers, flight_no, }) => {
|
|
117
145
|
const body = { title };
|
|
118
146
|
if (icon)
|
|
119
147
|
body.icon = icon;
|
|
@@ -125,12 +153,30 @@ server.tool("add_item", `특정 Day에 일정을 추가합니다. Day당 최대
|
|
|
125
153
|
body.endDayOffset = end_day_offset;
|
|
126
154
|
if (place)
|
|
127
155
|
body.place = place;
|
|
156
|
+
if (lat !== undefined)
|
|
157
|
+
body.lat = lat;
|
|
158
|
+
if (lng !== undefined)
|
|
159
|
+
body.lng = lng;
|
|
160
|
+
if (locations !== undefined)
|
|
161
|
+
body.locations = locations;
|
|
162
|
+
if (timezone !== undefined)
|
|
163
|
+
body.timezone = timezone;
|
|
164
|
+
if (show_on_route !== undefined)
|
|
165
|
+
body.showOnRoute = show_on_route;
|
|
128
166
|
if (memo)
|
|
129
167
|
body.memo = memo;
|
|
130
168
|
if (from_place)
|
|
131
169
|
body.fromPlace = from_place;
|
|
170
|
+
if (from_lat !== undefined)
|
|
171
|
+
body.fromLat = from_lat;
|
|
172
|
+
if (from_lng !== undefined)
|
|
173
|
+
body.fromLng = from_lng;
|
|
132
174
|
if (to_place)
|
|
133
175
|
body.toPlace = to_place;
|
|
176
|
+
if (to_lat !== undefined)
|
|
177
|
+
body.toLat = to_lat;
|
|
178
|
+
if (to_lng !== undefined)
|
|
179
|
+
body.toLng = to_lng;
|
|
134
180
|
if (layovers)
|
|
135
181
|
body.layovers = layovers;
|
|
136
182
|
if (flight_no)
|
|
@@ -148,22 +194,36 @@ server.tool("update_item", "일정을 수정합니다. 변경할 필드만 보
|
|
|
148
194
|
end_time: z.string().optional().describe("종료 시간 (HH:MM)"),
|
|
149
195
|
end_day_offset: z.coerce.number().int().min(0).optional().describe("도착일 오프셋 (0=당일, 1=다음날)"),
|
|
150
196
|
place: z.string().optional().describe("장소명"),
|
|
197
|
+
lat: z.coerce.number().min(-90).max(90).nullable().optional().describe("장소 위도 (경로 지도 표시용, null로 초기화 가능)"),
|
|
198
|
+
lng: z.coerce.number().min(-180).max(180).nullable().optional().describe("장소 경도 (경로 지도 표시용, null로 초기화 가능)"),
|
|
199
|
+
timezone: z.string().nullable().optional().describe("장소 IANA 시간대 (예: Asia/Seoul, null로 초기화 가능)"),
|
|
200
|
+
show_on_route: z.boolean().optional().describe("경로 보기에 표시할지 여부"),
|
|
151
201
|
memo: z.string().max(1000).optional().describe("메모"),
|
|
152
202
|
from_place: z
|
|
153
203
|
.string()
|
|
154
204
|
.optional()
|
|
155
205
|
.describe("출발지 IATA 코드 (항공편 전용, 예: ICN)"),
|
|
206
|
+
from_lat: z.coerce.number().min(-90).max(90).nullable().optional().describe("Non-flight transport origin latitude, or null to clear"),
|
|
207
|
+
from_lng: z.coerce.number().min(-180).max(180).nullable().optional().describe("Non-flight transport origin longitude, or null to clear"),
|
|
208
|
+
locations: z
|
|
209
|
+
.array(locationSchema)
|
|
210
|
+
.max(10)
|
|
211
|
+
.nullable()
|
|
212
|
+
.optional()
|
|
213
|
+
.describe("Ordered place locations for one place item, max 10; null clears them"),
|
|
156
214
|
to_place: z
|
|
157
215
|
.string()
|
|
158
216
|
.optional()
|
|
159
217
|
.describe("도착지 IATA 코드 (항공편 전용, 예: MXP)"),
|
|
218
|
+
to_lat: z.coerce.number().min(-90).max(90).nullable().optional().describe("Non-flight transport destination latitude, or null to clear"),
|
|
219
|
+
to_lng: z.coerce.number().min(-180).max(180).nullable().optional().describe("Non-flight transport destination longitude, or null to clear"),
|
|
160
220
|
layovers: z
|
|
161
221
|
.string()
|
|
162
222
|
.nullable()
|
|
163
223
|
.optional()
|
|
164
224
|
.describe('경유지 JSON 배열 (항공편 전용). 필드: iata, tz, arrTime(HH:MM), arrOffset, depTime(HH:MM), depOffset, depFlightNo?, airportChange?, depIata?, depTz?. null로 초기화 가능'),
|
|
165
225
|
flight_no: z.string().optional().describe("항공편 번호 (예: KE447)"),
|
|
166
|
-
}, async ({ trip_id, day_id, item_id, title, icon, start_time, end_time, end_day_offset, place, memo, from_place, to_place, layovers, flight_no, }) => {
|
|
226
|
+
}, async ({ trip_id, day_id, item_id, title, icon, start_time, end_time, end_day_offset, place, lat, lng, locations, timezone, show_on_route, memo, from_place, from_lat, from_lng, to_place, to_lat, to_lng, layovers, flight_no, }) => {
|
|
167
227
|
const body = {};
|
|
168
228
|
if (title !== undefined)
|
|
169
229
|
body.title = title;
|
|
@@ -177,12 +237,30 @@ server.tool("update_item", "일정을 수정합니다. 변경할 필드만 보
|
|
|
177
237
|
body.endDayOffset = end_day_offset;
|
|
178
238
|
if (place !== undefined)
|
|
179
239
|
body.place = place;
|
|
240
|
+
if (lat !== undefined)
|
|
241
|
+
body.lat = lat;
|
|
242
|
+
if (lng !== undefined)
|
|
243
|
+
body.lng = lng;
|
|
244
|
+
if (locations !== undefined)
|
|
245
|
+
body.locations = locations ?? [];
|
|
246
|
+
if (timezone !== undefined)
|
|
247
|
+
body.timezone = timezone;
|
|
248
|
+
if (show_on_route !== undefined)
|
|
249
|
+
body.showOnRoute = show_on_route;
|
|
180
250
|
if (memo !== undefined)
|
|
181
251
|
body.memo = memo;
|
|
182
252
|
if (from_place !== undefined)
|
|
183
253
|
body.fromPlace = from_place;
|
|
254
|
+
if (from_lat !== undefined)
|
|
255
|
+
body.fromLat = from_lat;
|
|
256
|
+
if (from_lng !== undefined)
|
|
257
|
+
body.fromLng = from_lng;
|
|
184
258
|
if (to_place !== undefined)
|
|
185
259
|
body.toPlace = to_place;
|
|
260
|
+
if (to_lat !== undefined)
|
|
261
|
+
body.toLat = to_lat;
|
|
262
|
+
if (to_lng !== undefined)
|
|
263
|
+
body.toLng = to_lng;
|
|
186
264
|
if (layovers !== undefined)
|
|
187
265
|
body.layovers = layovers;
|
|
188
266
|
if (flight_no !== undefined)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tripinned-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.15",
|
|
4
4
|
"description": "MCP server for Tripinned — manage your travel plans with AI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"scripts": {
|
|
14
14
|
"build": "tsc",
|
|
15
15
|
"dev": "node --loader ts-node/esm src/index.ts",
|
|
16
|
+
"prepack": "npm run build",
|
|
16
17
|
"start": "node dist/index.js"
|
|
17
18
|
},
|
|
18
19
|
"dependencies": {
|