@findtime/mcp-server 3.25.15 → 3.25.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.
- package/README.md +14 -6
- package/examples/claude-desktop-config.json +5 -4
- package/examples/cline-mcp.json +5 -4
- package/examples/codex-mcp.json +5 -4
- package/examples/cursor-mcp.json +5 -4
- package/examples/windsurf-mcp.json +5 -4
- package/help-catalog.cjs +252 -0
- package/package.json +2 -8
- package/server.js +53 -65
package/README.md
CHANGED
|
@@ -124,15 +124,16 @@ Best meeting time between New York, Sydney, and Mumbai?
|
|
|
124
124
|
|
|
125
125
|
## Local development
|
|
126
126
|
|
|
127
|
-
Run the
|
|
127
|
+
Run the workspace version directly:
|
|
128
128
|
|
|
129
129
|
```bash
|
|
130
|
-
npm start
|
|
130
|
+
npm run mcp:start
|
|
131
131
|
```
|
|
132
132
|
|
|
133
133
|
The server attempts to load `.env.development.local`, `.env.development`, `.env.local`, and `.env` from:
|
|
134
134
|
|
|
135
135
|
- the current working directory
|
|
136
|
+
- `services/mcp-server`
|
|
136
137
|
- the repo root
|
|
137
138
|
|
|
138
139
|
## Tests
|
|
@@ -140,13 +141,13 @@ The server attempts to load `.env.development.local`, `.env.development`, `.env.
|
|
|
140
141
|
Protocol and transport tests:
|
|
141
142
|
|
|
142
143
|
```bash
|
|
143
|
-
npm test
|
|
144
|
+
npm run test:mcp-server
|
|
144
145
|
```
|
|
145
146
|
|
|
146
147
|
Live production-parity smoke tests:
|
|
147
148
|
|
|
148
149
|
```bash
|
|
149
|
-
npm run test:smoke
|
|
150
|
+
npm run test:mcp-server:smoke
|
|
150
151
|
```
|
|
151
152
|
|
|
152
153
|
The smoke suite checks:
|
|
@@ -168,7 +169,7 @@ The canonical public source for this package now lives in:
|
|
|
168
169
|
- npm: `@findtime/mcp-server`
|
|
169
170
|
- Official MCP Registry: `https://registry.modelcontextprotocol.io/?q=io.github.hkchao%2Ffindtime-mcp-server`
|
|
170
171
|
|
|
171
|
-
Publish and version updates should happen from
|
|
172
|
+
Publish and version updates should happen from that public repo, not from this private app repo.
|
|
172
173
|
|
|
173
174
|
Standard publish flow in the public repo:
|
|
174
175
|
|
|
@@ -178,4 +179,11 @@ npm pack --dry-run
|
|
|
178
179
|
npm publish --access public
|
|
179
180
|
```
|
|
180
181
|
|
|
181
|
-
|
|
182
|
+
The equivalent local verification checks in this repo are:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
npm run test:mcp-server
|
|
186
|
+
npm run mcp:pack
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Treat this repo as the implementation source that originally produced the MCP package, not as the canonical public release source.
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
"mcpServers": {
|
|
3
3
|
"findtime": {
|
|
4
4
|
"type": "stdio",
|
|
5
|
-
"command": "
|
|
6
|
-
"args": [
|
|
5
|
+
"command": "node",
|
|
6
|
+
"args": [
|
|
7
|
+
"/absolute/path/to/findtime-io-mcp/services/mcp-server/src/server.js"
|
|
8
|
+
],
|
|
7
9
|
"env": {
|
|
8
10
|
"FINDTIME_MCP_CLIENT_TYPE": "claude-desktop",
|
|
9
|
-
"FINDTIME_TIME_API_BASE_URL": "https://time-api.findtime.io"
|
|
10
|
-
"FINDTIME_TIME_API_KEY": "YOUR_FINDTIME_SECRET_KEY"
|
|
11
|
+
"FINDTIME_TIME_API_BASE_URL": "https://time-api.findtime.io"
|
|
11
12
|
}
|
|
12
13
|
}
|
|
13
14
|
}
|
package/examples/cline-mcp.json
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
"mcpServers": {
|
|
3
3
|
"findtime": {
|
|
4
4
|
"type": "stdio",
|
|
5
|
-
"command": "
|
|
6
|
-
"args": [
|
|
5
|
+
"command": "node",
|
|
6
|
+
"args": [
|
|
7
|
+
"/absolute/path/to/findtime-io-mcp/services/mcp-server/src/server.js"
|
|
8
|
+
],
|
|
7
9
|
"env": {
|
|
8
10
|
"FINDTIME_MCP_CLIENT_TYPE": "cline",
|
|
9
|
-
"FINDTIME_TIME_API_BASE_URL": "https://time-api.findtime.io"
|
|
10
|
-
"FINDTIME_TIME_API_KEY": "YOUR_FINDTIME_SECRET_KEY"
|
|
11
|
+
"FINDTIME_TIME_API_BASE_URL": "https://time-api.findtime.io"
|
|
11
12
|
}
|
|
12
13
|
}
|
|
13
14
|
}
|
package/examples/codex-mcp.json
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
"mcpServers": {
|
|
3
3
|
"findtime": {
|
|
4
4
|
"type": "stdio",
|
|
5
|
-
"command": "
|
|
6
|
-
"args": [
|
|
5
|
+
"command": "node",
|
|
6
|
+
"args": [
|
|
7
|
+
"/absolute/path/to/findtime-io-mcp/services/mcp-server/src/server.js"
|
|
8
|
+
],
|
|
7
9
|
"env": {
|
|
8
10
|
"FINDTIME_MCP_CLIENT_TYPE": "codex",
|
|
9
|
-
"FINDTIME_TIME_API_BASE_URL": "https://time-api.findtime.io"
|
|
10
|
-
"FINDTIME_TIME_API_KEY": "YOUR_FINDTIME_SECRET_KEY"
|
|
11
|
+
"FINDTIME_TIME_API_BASE_URL": "https://time-api.findtime.io"
|
|
11
12
|
}
|
|
12
13
|
}
|
|
13
14
|
}
|
package/examples/cursor-mcp.json
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
"mcpServers": {
|
|
3
3
|
"findtime": {
|
|
4
4
|
"type": "stdio",
|
|
5
|
-
"command": "
|
|
6
|
-
"args": [
|
|
5
|
+
"command": "node",
|
|
6
|
+
"args": [
|
|
7
|
+
"/absolute/path/to/findtime-io-mcp/services/mcp-server/src/server.js"
|
|
8
|
+
],
|
|
7
9
|
"env": {
|
|
8
10
|
"FINDTIME_MCP_CLIENT_TYPE": "cursor",
|
|
9
|
-
"FINDTIME_TIME_API_BASE_URL": "https://time-api.findtime.io"
|
|
10
|
-
"FINDTIME_TIME_API_KEY": "YOUR_FINDTIME_SECRET_KEY"
|
|
11
|
+
"FINDTIME_TIME_API_BASE_URL": "https://time-api.findtime.io"
|
|
11
12
|
}
|
|
12
13
|
}
|
|
13
14
|
}
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
"mcpServers": {
|
|
3
3
|
"findtime": {
|
|
4
4
|
"type": "stdio",
|
|
5
|
-
"command": "
|
|
6
|
-
"args": [
|
|
5
|
+
"command": "node",
|
|
6
|
+
"args": [
|
|
7
|
+
"/absolute/path/to/findtime-io-mcp/services/mcp-server/src/server.js"
|
|
8
|
+
],
|
|
7
9
|
"env": {
|
|
8
10
|
"FINDTIME_MCP_CLIENT_TYPE": "windsurf",
|
|
9
|
-
"FINDTIME_TIME_API_BASE_URL": "https://time-api.findtime.io"
|
|
10
|
-
"FINDTIME_TIME_API_KEY": "YOUR_FINDTIME_SECRET_KEY"
|
|
11
|
+
"FINDTIME_TIME_API_BASE_URL": "https://time-api.findtime.io"
|
|
11
12
|
}
|
|
12
13
|
}
|
|
13
14
|
}
|
package/help-catalog.cjs
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const HELP_ALIASES = ['help', '?', '/help', '/findtime help', '/findtime ?'];
|
|
4
|
+
|
|
5
|
+
const HELP_INTENTS = [
|
|
6
|
+
{
|
|
7
|
+
id: 'current_time',
|
|
8
|
+
label: 'Current time',
|
|
9
|
+
notes: 'Returns local date/time, timezone, UTC offset, and DST context when relevant.',
|
|
10
|
+
examples: {
|
|
11
|
+
default: ['What time is it now in Tokyo?', 'now in Tokyo, Sydney, Dubai'],
|
|
12
|
+
slack: ['/findtime now in Tokyo Sydney Dubai'],
|
|
13
|
+
discord: ['now in Tokyo, Paris, and Vancouver'],
|
|
14
|
+
chrome: ['now in Tokyo Sydney Dubai'],
|
|
15
|
+
mcp: ['What time is it now in Tokyo?']
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: 'timezone_lookup',
|
|
20
|
+
label: 'Timezone lookup',
|
|
21
|
+
notes: 'Returns the canonical IANA timezone plus the current abbreviation.',
|
|
22
|
+
examples: {
|
|
23
|
+
default: ['What timezone is Auckland in?', 'Tokyo time zone'],
|
|
24
|
+
slack: ['/findtime Tokyo time zone'],
|
|
25
|
+
discord: ['Tokyo time zone'],
|
|
26
|
+
chrome: ['Singapore time zone'],
|
|
27
|
+
mcp: ['What timezone is Auckland in?']
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: 'timezone_abbreviation_lookup',
|
|
32
|
+
label: 'Timezone abbreviation',
|
|
33
|
+
notes: 'Returns the current abbreviation plus the canonical IANA timezone.',
|
|
34
|
+
examples: {
|
|
35
|
+
default: ['What timezone abbreviation is Auckland?', 'What does CST mean?'],
|
|
36
|
+
slack: ['/findtime what does CST mean?'],
|
|
37
|
+
discord: ['what does CST mean?'],
|
|
38
|
+
chrome: ['timezone in Dubai'],
|
|
39
|
+
mcp: ['What timezone abbreviation is Auckland?']
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'time_conversion',
|
|
44
|
+
label: 'Time conversion',
|
|
45
|
+
notes: 'Includes local dates because conversions often cross calendar days.',
|
|
46
|
+
examples: {
|
|
47
|
+
default: ['If it is 3:30pm in London, what time is it in Sydney?', 'What is 5pm San Francisco time in Tokyo?', '9am New York to Berlin'],
|
|
48
|
+
slack: ['/findtime 3pm London to NYC'],
|
|
49
|
+
discord: ['8pm PST to London and Tokyo'],
|
|
50
|
+
chrome: ['3pm London to NYC', '9am PST to IST'],
|
|
51
|
+
mcp: ['If it is 3:30pm in London, what time is it in Sydney?', 'What is 5pm San Francisco time in Tokyo?']
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: 'dst_status',
|
|
56
|
+
label: 'Daylight saving',
|
|
57
|
+
notes: 'Returns DST status and transition context when available.',
|
|
58
|
+
examples: {
|
|
59
|
+
default: ['Is Mexico City on DST?', 'Does London observe daylight saving?', 'When do clocks change in New York?'],
|
|
60
|
+
slack: ['/findtime does London observe daylight saving?'],
|
|
61
|
+
discord: ['does London observe daylight saving?'],
|
|
62
|
+
chrome: ['does Sydney observe daylight saving?', 'DST in New York'],
|
|
63
|
+
mcp: ['Is Mexico City on DST?']
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: 'overlap_hours',
|
|
68
|
+
label: 'Overlap hours',
|
|
69
|
+
notes: 'Useful for distributed-team availability, handoff planning, and group coordination.',
|
|
70
|
+
examples: {
|
|
71
|
+
default: ['What working hours overlap for San Francisco, Berlin, and Tokyo?', 'working hours overlap for New York and London'],
|
|
72
|
+
slack: ['/findtime working hours overlap for San Francisco Berlin Tokyo'],
|
|
73
|
+
discord: ['when are New York and Sydney both free?'],
|
|
74
|
+
chrome: ['new york london tokyo'],
|
|
75
|
+
mcp: ['What working hours overlap for San Francisco, Berlin, and Tokyo?']
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: 'meeting_time_search',
|
|
80
|
+
label: 'Meeting time search',
|
|
81
|
+
notes: 'Returns ranked meeting windows and tradeoffs across participants.',
|
|
82
|
+
examples: {
|
|
83
|
+
default: ['Find a good meeting time for San Francisco, Berlin, and Sydney.', 'Best meeting time for San Francisco, Berlin, and Sydney next week', 'find the best meeting times between Helsinki and Osaka'],
|
|
84
|
+
slack: ['/findtime find a good meeting time for San Francisco Berlin Sydney'],
|
|
85
|
+
discord: ['best time for Los Angeles, Berlin, and Seoul'],
|
|
86
|
+
chrome: ['meeting Helsinki Dubai Chicago Taipei'],
|
|
87
|
+
mcp: ['Find a good meeting time for San Francisco, Berlin, and Sydney.']
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
id: 'abbreviation_disambiguation',
|
|
92
|
+
label: 'Abbreviation disambiguation',
|
|
93
|
+
notes: 'Timezone abbreviations are aliases, not canonical identifiers.',
|
|
94
|
+
examples: {
|
|
95
|
+
default: ['What does CST mean for a customer in China versus a customer in Chicago?', 'Convert 9am CST to London'],
|
|
96
|
+
slack: ['/findtime Convert 9am CST to London'],
|
|
97
|
+
discord: ['9am CST to London'],
|
|
98
|
+
chrome: ['EST to GMT'],
|
|
99
|
+
mcp: ['What does CST mean for a customer in China versus a customer in Chicago?']
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
id: 'location_disambiguation',
|
|
104
|
+
label: 'Location disambiguation',
|
|
105
|
+
notes: 'Ambiguous place names should return clarification or country-aware choices instead of silent guessing.',
|
|
106
|
+
examples: {
|
|
107
|
+
default: ['What time is it in Victoria?', 'What time is it in Springfield?'],
|
|
108
|
+
slack: ['/findtime what time is it in Victoria?'],
|
|
109
|
+
discord: ['what time is it in Victoria?'],
|
|
110
|
+
chrome: ['time in Victoria'],
|
|
111
|
+
mcp: ['What time is it in Victoria?']
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
const HELP_SURFACES = {
|
|
117
|
+
default: {
|
|
118
|
+
title: 'Ask about time anywhere',
|
|
119
|
+
summary: 'Convert times, compare cities, check current time, inspect time zones, resolve ambiguity, or find a meeting slot.'
|
|
120
|
+
},
|
|
121
|
+
slack: {
|
|
122
|
+
title: 'Use /findtime for time intelligence',
|
|
123
|
+
summary: 'Convert times, compare teammates, check time zones, and find meeting windows directly in Slack.'
|
|
124
|
+
},
|
|
125
|
+
discord: {
|
|
126
|
+
title: 'Coordinate across time zones',
|
|
127
|
+
summary: 'Convert raid times, compare party locations, check current time, or find a time that works for your group.',
|
|
128
|
+
commandAliases: ['/findtime', '/ft']
|
|
129
|
+
},
|
|
130
|
+
telegram: {
|
|
131
|
+
title: 'Ask findtime.io about time',
|
|
132
|
+
summary: 'Convert times, check cities, inspect time zones, and coordinate across locations from Telegram.'
|
|
133
|
+
},
|
|
134
|
+
chrome: {
|
|
135
|
+
title: 'What you can do',
|
|
136
|
+
summary: 'Convert times, open live pages on findtime.io, and plan across cities from the browser.'
|
|
137
|
+
},
|
|
138
|
+
mcp: {
|
|
139
|
+
title: 'findtime.io Time Help',
|
|
140
|
+
summary: 'Use findtime.io MCP for accurate timezone, DST, conversion, overlap-hours, and cross-timezone meeting-time intelligence.'
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const AMBIGUITY_EXAMPLES = [
|
|
145
|
+
{
|
|
146
|
+
query: 'What time is it in Springfield?',
|
|
147
|
+
expectedBehavior: 'Ask for clarification or provide likely matches because many cities share this name.'
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
query: 'Convert 9am CST to London.',
|
|
151
|
+
expectedBehavior: 'Clarify whether CST means China Standard Time, Central Standard Time, Cuba Standard Time, or another regional meaning when context is insufficient.'
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
query: 'Schedule a meeting for Paris and Sydney next Friday.',
|
|
155
|
+
expectedBehavior: 'Resolve Paris, France unless context suggests otherwise; include date and local-time tradeoffs.'
|
|
156
|
+
}
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
const FAILURE_POLICY = [
|
|
160
|
+
'If a findtime.io MCP call fails, say the MCP call failed and include the visible error.',
|
|
161
|
+
'Do not present fallback timezone or DST calculations as if they came from findtime.io MCP.',
|
|
162
|
+
'For high-stakes scheduling, retry or ask the user before using fallback reasoning.'
|
|
163
|
+
];
|
|
164
|
+
|
|
165
|
+
function normalizeSurface(surface) {
|
|
166
|
+
const normalized = String(surface || '').trim().toLowerCase();
|
|
167
|
+
if (normalized === 'discord-bot') return 'discord';
|
|
168
|
+
if (normalized === 'slack-bot') return 'slack';
|
|
169
|
+
if (normalized === 'telegram-bot') return 'telegram';
|
|
170
|
+
if (normalized === 'web-chat' || normalized === 'webapp' || normalized === 'web') return 'default';
|
|
171
|
+
return HELP_SURFACES[normalized] ? normalized : 'default';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function examplesForSurface(intent, surface) {
|
|
175
|
+
const key = normalizeSurface(surface);
|
|
176
|
+
const examples = intent.examples || {};
|
|
177
|
+
return examples[key] || examples.default || [];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function flattenExamples(surface = 'default', { max = 12 } = {}) {
|
|
181
|
+
const examples = [];
|
|
182
|
+
for (const intent of HELP_INTENTS) {
|
|
183
|
+
for (const example of examplesForSurface(intent, surface)) {
|
|
184
|
+
if (!examples.includes(example)) examples.push(example);
|
|
185
|
+
if (examples.length >= max) return examples;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return examples;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function mergeExamples(primary = [], secondary = [], { max = 18 } = {}) {
|
|
192
|
+
const examples = [];
|
|
193
|
+
for (const example of [...primary, ...secondary]) {
|
|
194
|
+
if (!examples.includes(example)) examples.push(example);
|
|
195
|
+
if (examples.length >= max) return examples;
|
|
196
|
+
}
|
|
197
|
+
return examples;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function buildHelpPayload({ surface = 'default', includeAllIntents = false } = {}) {
|
|
201
|
+
const normalizedSurface = normalizeSurface(surface);
|
|
202
|
+
const surfaceMeta = HELP_SURFACES[normalizedSurface] || HELP_SURFACES.default;
|
|
203
|
+
const defaultSuggestions = flattenExamples('default', { max: 18 });
|
|
204
|
+
const surfaceSuggestions = normalizedSurface === 'default'
|
|
205
|
+
? []
|
|
206
|
+
: flattenExamples(normalizedSurface, { max: 6 });
|
|
207
|
+
const suggestions = normalizedSurface === 'default'
|
|
208
|
+
? defaultSuggestions
|
|
209
|
+
: mergeExamples(surfaceSuggestions, defaultSuggestions, { max: 18 });
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
version: 'answer-help.v1',
|
|
213
|
+
surface: normalizedSurface,
|
|
214
|
+
title: surfaceMeta.title,
|
|
215
|
+
summary: surfaceMeta.summary,
|
|
216
|
+
commandAliases: surfaceMeta.commandAliases || [],
|
|
217
|
+
aliases: HELP_ALIASES,
|
|
218
|
+
suggestions,
|
|
219
|
+
surfaceSuggestions,
|
|
220
|
+
defaultSuggestions,
|
|
221
|
+
intents: HELP_INTENTS.map((intent) => ({
|
|
222
|
+
intent: intent.id,
|
|
223
|
+
label: intent.label,
|
|
224
|
+
notes: intent.notes,
|
|
225
|
+
examples: includeAllIntents
|
|
226
|
+
? mergeExamples(examplesForSurface(intent, normalizedSurface), examplesForSurface(intent, 'default'), { max: 6 })
|
|
227
|
+
: mergeExamples(examplesForSurface(intent, normalizedSurface), examplesForSurface(intent, 'default'), { max: 2 }),
|
|
228
|
+
surfaceExamples: normalizedSurface === 'default' ? [] : examplesForSurface(intent, normalizedSurface),
|
|
229
|
+
defaultExamples: examplesForSurface(intent, 'default'),
|
|
230
|
+
example: (examplesForSurface(intent, normalizedSurface)[0] || examplesForSurface(intent, 'default')[0] || '')
|
|
231
|
+
})),
|
|
232
|
+
ambiguityExamples: AMBIGUITY_EXAMPLES,
|
|
233
|
+
failurePolicy: FAILURE_POLICY
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function isHelpQuery(value) {
|
|
238
|
+
const normalized = String(value || '').trim().replace(/\s+/g, ' ').toLowerCase();
|
|
239
|
+
return HELP_ALIASES.includes(normalized);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
module.exports = {
|
|
243
|
+
HELP_ALIASES,
|
|
244
|
+
HELP_INTENTS,
|
|
245
|
+
HELP_SURFACES,
|
|
246
|
+
AMBIGUITY_EXAMPLES,
|
|
247
|
+
FAILURE_POLICY,
|
|
248
|
+
normalizeSurface,
|
|
249
|
+
flattenExamples,
|
|
250
|
+
buildHelpPayload,
|
|
251
|
+
isHelpQuery
|
|
252
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@findtime/mcp-server",
|
|
3
|
-
"version": "3.25.
|
|
3
|
+
"version": "3.25.17",
|
|
4
4
|
"mcpName": "io.github.hkchao/findtime-mcp-server",
|
|
5
5
|
"description": "Production-parity MCP server for the findtime.io Time API",
|
|
6
6
|
"bin": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"type": "commonjs",
|
|
11
11
|
"files": [
|
|
12
12
|
"server.js",
|
|
13
|
+
"help-catalog.cjs",
|
|
13
14
|
"README.md",
|
|
14
15
|
"SKILL.md",
|
|
15
16
|
"examples"
|
|
@@ -31,14 +32,7 @@
|
|
|
31
32
|
"publishConfig": {
|
|
32
33
|
"access": "public"
|
|
33
34
|
},
|
|
34
|
-
"repository": {
|
|
35
|
-
"type": "git",
|
|
36
|
-
"url": "git+https://github.com/hkchao/findtime-mcp-server.git"
|
|
37
|
-
},
|
|
38
35
|
"homepage": "https://findtime.io/developers/mcp/",
|
|
39
|
-
"bugs": {
|
|
40
|
-
"url": "https://github.com/hkchao/findtime-mcp-server/issues"
|
|
41
|
-
},
|
|
42
36
|
"keywords": [
|
|
43
37
|
"mcp",
|
|
44
38
|
"model-context-protocol",
|
package/server.js
CHANGED
|
@@ -417,13 +417,47 @@ function getVisibleToolDefinitions() {
|
|
|
417
417
|
return TOOL_DEFINITIONS.filter((tool) => ANSWER_ONLY_TOOL_NAMES.has(tool.name));
|
|
418
418
|
}
|
|
419
419
|
|
|
420
|
+
function loadHelpCatalog() {
|
|
421
|
+
const candidatePaths = [
|
|
422
|
+
path.join(PACKAGE_ROOT, 'help-catalog.cjs'),
|
|
423
|
+
path.join(REPO_ROOT, 'src', 'shared', 'findtimeHelpCatalog.cjs')
|
|
424
|
+
];
|
|
425
|
+
for (const catalogPath of candidatePaths) {
|
|
426
|
+
try {
|
|
427
|
+
return require(catalogPath);
|
|
428
|
+
} catch (_error) {
|
|
429
|
+
// Try the next candidate. Published MCP packages use PACKAGE_ROOT.
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return {
|
|
433
|
+
buildHelpPayload: () => ({
|
|
434
|
+
version: 'answer-help.v1',
|
|
435
|
+
surface: 'mcp',
|
|
436
|
+
title: 'findtime.io Time Help',
|
|
437
|
+
summary: 'Use findtime.io MCP for accurate timezone, DST, conversion, overlap-hours, and cross-timezone meeting-time intelligence.',
|
|
438
|
+
suggestions: [
|
|
439
|
+
'What time is it now in Tokyo?',
|
|
440
|
+
'What timezone is Auckland in?',
|
|
441
|
+
'If it is 3:30pm in London, what time is it in Sydney?',
|
|
442
|
+
'What working hours overlap for San Francisco, Berlin, and Tokyo?',
|
|
443
|
+
'Find a good meeting time for San Francisco, Berlin, and Sydney.'
|
|
444
|
+
],
|
|
445
|
+
intents: [],
|
|
446
|
+
ambiguityExamples: [],
|
|
447
|
+
failurePolicy: []
|
|
448
|
+
})
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
|
|
420
452
|
function buildFindtimeHelpPayload() {
|
|
453
|
+
const help = loadHelpCatalog().buildHelpPayload({ surface: 'mcp', includeAllIntents: true });
|
|
421
454
|
return {
|
|
455
|
+
...help,
|
|
422
456
|
ok: true,
|
|
423
457
|
tool: 'get_findtime_help',
|
|
424
458
|
recommendedTool: 'answer_time_question',
|
|
425
459
|
recommendedMode: 'FINDTIME_MCP_TOOL_MODE=answer-only',
|
|
426
|
-
summary:
|
|
460
|
+
summary: `${help.summary} In enterprise bots, route natural-language questions through answer_time_question.`,
|
|
427
461
|
install: {
|
|
428
462
|
command: 'npx',
|
|
429
463
|
args: ['-y', '@findtime/mcp-server'],
|
|
@@ -434,62 +468,6 @@ function buildFindtimeHelpPayload() {
|
|
|
434
468
|
FINDTIME_MCP_TOOL_MODE: 'answer-only'
|
|
435
469
|
}
|
|
436
470
|
},
|
|
437
|
-
intents: [
|
|
438
|
-
{
|
|
439
|
-
intent: 'current_time',
|
|
440
|
-
example: 'What time is it now in Tokyo?',
|
|
441
|
-
notes: 'Returns local date/time, timezone, UTC offset, and DST context when relevant.'
|
|
442
|
-
},
|
|
443
|
-
{
|
|
444
|
-
intent: 'timezone_lookup',
|
|
445
|
-
example: 'What is the IANA timezone for San Francisco?',
|
|
446
|
-
notes: 'Use IANA timezone IDs as canonical identifiers.'
|
|
447
|
-
},
|
|
448
|
-
{
|
|
449
|
-
intent: 'time_conversion',
|
|
450
|
-
example: 'Convert 3pm next Tuesday in New York to London, Berlin, and Singapore.',
|
|
451
|
-
notes: 'Include local dates because conversions often cross calendar days.'
|
|
452
|
-
},
|
|
453
|
-
{
|
|
454
|
-
intent: 'dst_status',
|
|
455
|
-
example: 'Is Mexico City on DST?',
|
|
456
|
-
notes: 'Returns DST status and transition context when available.'
|
|
457
|
-
},
|
|
458
|
-
{
|
|
459
|
-
intent: 'overlap_hours',
|
|
460
|
-
example: 'What working hours overlap for San Francisco, Berlin, and Tokyo?',
|
|
461
|
-
notes: 'Useful for distributed-team availability and handoff planning.'
|
|
462
|
-
},
|
|
463
|
-
{
|
|
464
|
-
intent: 'meeting_time_search',
|
|
465
|
-
example: 'Find a good 45-minute meeting time next week for San Francisco, Berlin, and Sydney.',
|
|
466
|
-
notes: 'Returns ranked meeting windows and tradeoffs across participants.'
|
|
467
|
-
},
|
|
468
|
-
{
|
|
469
|
-
intent: 'abbreviation_disambiguation',
|
|
470
|
-
example: 'What does CST mean for a customer in China versus a customer in Chicago?',
|
|
471
|
-
notes: 'Timezone abbreviations are aliases, not canonical identifiers.'
|
|
472
|
-
},
|
|
473
|
-
{
|
|
474
|
-
intent: 'location_disambiguation',
|
|
475
|
-
example: 'What time is it in Victoria?',
|
|
476
|
-
notes: 'Ambiguous place names should return clarification or country-aware choices instead of silent guessing.'
|
|
477
|
-
}
|
|
478
|
-
],
|
|
479
|
-
ambiguityExamples: [
|
|
480
|
-
{
|
|
481
|
-
query: 'What time is it in Springfield?',
|
|
482
|
-
expectedBehavior: 'Ask for clarification or provide likely matches because many cities share this name.'
|
|
483
|
-
},
|
|
484
|
-
{
|
|
485
|
-
query: 'Convert 9am CST to London.',
|
|
486
|
-
expectedBehavior: 'Clarify whether CST means China Standard Time, Central Standard Time, Cuba Standard Time, or another regional meaning when context is insufficient.'
|
|
487
|
-
},
|
|
488
|
-
{
|
|
489
|
-
query: 'Schedule a meeting for Paris and Sydney next Friday.',
|
|
490
|
-
expectedBehavior: 'Resolve Paris, France unless context suggests otherwise; include date and local-time tradeoffs.'
|
|
491
|
-
}
|
|
492
|
-
],
|
|
493
471
|
answerApiPattern: {
|
|
494
472
|
tool: 'answer_time_question',
|
|
495
473
|
arguments: {
|
|
@@ -497,15 +475,24 @@ function buildFindtimeHelpPayload() {
|
|
|
497
475
|
userTimezone: 'America/Los_Angeles',
|
|
498
476
|
locale: 'en-US'
|
|
499
477
|
}
|
|
500
|
-
}
|
|
501
|
-
failurePolicy: [
|
|
502
|
-
'If a findtime.io MCP call fails, say the MCP call failed and include the visible error.',
|
|
503
|
-
'Do not present fallback timezone or DST calculations as if they came from findtime.io MCP.',
|
|
504
|
-
'For high-stakes scheduling, retry or ask the user before using fallback reasoning.'
|
|
505
|
-
]
|
|
478
|
+
}
|
|
506
479
|
};
|
|
507
480
|
}
|
|
508
481
|
|
|
482
|
+
function formatFindtimeHelpText(help) {
|
|
483
|
+
const lines = [
|
|
484
|
+
'findtime.io Time Help',
|
|
485
|
+
'',
|
|
486
|
+
'Try asking:',
|
|
487
|
+
...help.intents.map((entry) => `- ${entry.example}`),
|
|
488
|
+
'',
|
|
489
|
+
'If a place or timezone is ambiguous, findtime.io will ask for clarification. For example:',
|
|
490
|
+
...help.ambiguityExamples.map((entry) => `- ${entry.query} -> ${entry.expectedBehavior}`),
|
|
491
|
+
];
|
|
492
|
+
|
|
493
|
+
return lines.join('\n');
|
|
494
|
+
}
|
|
495
|
+
|
|
509
496
|
function safeReadJson(filePath) {
|
|
510
497
|
try {
|
|
511
498
|
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
@@ -1199,14 +1186,15 @@ function createFindtimeMcpServer(options = {}) {
|
|
|
1199
1186
|
}
|
|
1200
1187
|
|
|
1201
1188
|
if (name === 'get_findtime_help') {
|
|
1189
|
+
const help = buildFindtimeHelpPayload();
|
|
1202
1190
|
return {
|
|
1203
1191
|
content: [
|
|
1204
1192
|
{
|
|
1205
1193
|
type: 'text',
|
|
1206
|
-
text:
|
|
1194
|
+
text: formatFindtimeHelpText(help)
|
|
1207
1195
|
}
|
|
1208
1196
|
],
|
|
1209
|
-
structuredContent:
|
|
1197
|
+
structuredContent: help
|
|
1210
1198
|
};
|
|
1211
1199
|
}
|
|
1212
1200
|
|