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.
Files changed (2) hide show
  1. package/dist/index.js +83 -5
  2. 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 server = new McpServer({
36
- name: "tripinned",
37
- version: "1.0.8",
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.9",
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": {