tripinned-mcp 1.0.10 → 1.0.17

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.
Files changed (2) hide show
  1. package/dist/index.js +77 -5
  2. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -1,9 +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";
6
- import { version } from "../package.json";
7
+ const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
8
+ const SERVER_VERSION = packageJson.version ?? "0.0.0";
7
9
  const API_KEY = process.env.TRIPINNED_API_KEY;
8
10
  const BASE_URL = process.env.TRIPINNED_API_URL ?? "https://api.tripinned.com/v1";
9
11
  if (!API_KEY) {
@@ -33,13 +35,20 @@ async function call(method, path, body) {
33
35
  }
34
36
  return data;
35
37
  }
36
- const server = new McpServer({ name: "tripinned", version }, {
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 }, {
37
45
  instructions: `
38
46
  Tripinned trip planning rules:
39
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").
40
48
  - Each item belongs to exactly one day; do not split a single event across multiple days.
41
49
  - When adding flight segments, group the full route (origin → stopover → destination) as one item if the user is just passing through.
42
- - 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").
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 must use icon HOTEL with check-in in start_time, check-out in end_time, and end_day_offset for the check-out day. The API creates a check-in/check-out group with one shared groupId; add_item returns the check-in item and get_trip shows both items.
43
52
  `.trim(),
44
53
  });
45
54
  // ── Trips ────────────────────────────────────────────────────────────────────
@@ -105,21 +114,34 @@ server.tool("add_item", `특정 Day에 일정을 추가합니다. Day당 최대
105
114
  end_time: z.string().optional().describe("종료 시간 (HH:MM)"),
106
115
  end_day_offset: z.coerce.number().int().min(0).optional().describe("도착일 오프셋 (0=당일, 1=다음날)"),
107
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("경로 보기에 표시할지 여부"),
108
121
  memo: z.string().max(1000).optional().describe("메모"),
109
122
  from_place: z
110
123
  .string()
111
124
  .optional()
112
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"),
113
133
  to_place: z
114
134
  .string()
115
135
  .optional()
116
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"),
117
139
  layovers: z
118
140
  .string()
119
141
  .optional()
120
142
  .describe('경유지 JSON 배열 (항공편 전용). 필드: iata, tz, arrTime(HH:MM), arrOffset, depTime(HH:MM), depOffset, depFlightNo?, airportChange?, depIata?, depTz?'),
121
143
  flight_no: z.string().optional().describe("항공편 번호 (예: KE447)"),
122
- }, 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, }) => {
123
145
  const body = { title };
124
146
  if (icon)
125
147
  body.icon = icon;
@@ -131,12 +153,30 @@ server.tool("add_item", `특정 Day에 일정을 추가합니다. Day당 최대
131
153
  body.endDayOffset = end_day_offset;
132
154
  if (place)
133
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;
134
166
  if (memo)
135
167
  body.memo = memo;
136
168
  if (from_place)
137
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;
138
174
  if (to_place)
139
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;
140
180
  if (layovers)
141
181
  body.layovers = layovers;
142
182
  if (flight_no)
@@ -154,22 +194,36 @@ server.tool("update_item", "일정을 수정합니다. 변경할 필드만 보
154
194
  end_time: z.string().optional().describe("종료 시간 (HH:MM)"),
155
195
  end_day_offset: z.coerce.number().int().min(0).optional().describe("도착일 오프셋 (0=당일, 1=다음날)"),
156
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("경로 보기에 표시할지 여부"),
157
201
  memo: z.string().max(1000).optional().describe("메모"),
158
202
  from_place: z
159
203
  .string()
160
204
  .optional()
161
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"),
162
214
  to_place: z
163
215
  .string()
164
216
  .optional()
165
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"),
166
220
  layovers: z
167
221
  .string()
168
222
  .nullable()
169
223
  .optional()
170
224
  .describe('경유지 JSON 배열 (항공편 전용). 필드: iata, tz, arrTime(HH:MM), arrOffset, depTime(HH:MM), depOffset, depFlightNo?, airportChange?, depIata?, depTz?. null로 초기화 가능'),
171
225
  flight_no: z.string().optional().describe("항공편 번호 (예: KE447)"),
172
- }, 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, }) => {
173
227
  const body = {};
174
228
  if (title !== undefined)
175
229
  body.title = title;
@@ -183,12 +237,30 @@ server.tool("update_item", "일정을 수정합니다. 변경할 필드만 보
183
237
  body.endDayOffset = end_day_offset;
184
238
  if (place !== undefined)
185
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;
186
250
  if (memo !== undefined)
187
251
  body.memo = memo;
188
252
  if (from_place !== undefined)
189
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;
190
258
  if (to_place !== undefined)
191
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;
192
264
  if (layovers !== undefined)
193
265
  body.layovers = layovers;
194
266
  if (flight_no !== undefined)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tripinned-mcp",
3
- "version": "1.0.10",
3
+ "version": "1.0.17",
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": {