@findtime/mcp-server 3.25.13 → 3.25.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/README.md +2 -0
- package/SKILL.md +16 -1
- package/package.json +1 -1
- package/server.js +135 -2
package/README.md
CHANGED
|
@@ -14,6 +14,7 @@ Published surfaces:
|
|
|
14
14
|
## Tool surface
|
|
15
15
|
|
|
16
16
|
- `answer_time_question`
|
|
17
|
+
- `get_findtime_help`
|
|
17
18
|
- `time_snapshot`
|
|
18
19
|
- `get_current_time`
|
|
19
20
|
- `get_dst_schedule`
|
|
@@ -49,6 +50,7 @@ Optional environment variables:
|
|
|
49
50
|
- `TIME_API_BASE_URL`
|
|
50
51
|
- `TIME_API_TIMEOUT_MS`
|
|
51
52
|
- `FINDTIME_MCP_CLIENT_TYPE`
|
|
53
|
+
- `FINDTIME_MCP_TOOL_MODE=answer-only` to expose only `answer_time_question`, `get_findtime_help`, and `get_api_diagnostics` for enterprise bots that should route every natural-language request through the answer API.
|
|
52
54
|
- `FINDTIME_MCP_CLIENT_ID` or `FINDTIME_MCP_INSTALL_ID` to provide a stable client identifier. If omitted, the server creates one locally under the user's state directory.
|
|
53
55
|
- `FINDTIME_MCP_INSTRUMENTATION_ENABLED=false` to opt out of anonymous usage telemetry.
|
|
54
56
|
- `FINDTIME_MCP_USAGE_TELEMETRY_URL` to override the default telemetry endpoint.
|
package/SKILL.md
CHANGED
|
@@ -72,9 +72,23 @@ Most MCP clients need the same core config:
|
|
|
72
72
|
|
|
73
73
|
For a company bot or server-side agent, set `FINDTIME_MCP_CLIENT_TYPE` to a stable identifier such as `company-bot`, and provide a stable install ID with `FINDTIME_MCP_CLIENT_ID` or `FINDTIME_MCP_INSTALL_ID` when possible.
|
|
74
74
|
|
|
75
|
+
For enterprise bots that should avoid model-level tool selection across the lower-level APIs, set:
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
FINDTIME_MCP_TOOL_MODE=answer-only
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
In answer-only mode, the MCP server exposes only:
|
|
82
|
+
|
|
83
|
+
- `answer_time_question`
|
|
84
|
+
- `get_findtime_help`
|
|
85
|
+
- `get_api_diagnostics`
|
|
86
|
+
|
|
87
|
+
Use this mode when the bot should route all natural-language time requests through the answer API first.
|
|
88
|
+
|
|
75
89
|
## Tool Selection
|
|
76
90
|
|
|
77
|
-
Use `answer_time_question` first for natural-language or ambiguous prompts.
|
|
91
|
+
Use `answer_time_question` first for natural-language or ambiguous prompts. In enterprise bot deployments, prefer `FINDTIME_MCP_TOOL_MODE=answer-only` so the agent sees `answer_time_question` as the default execution path instead of choosing lower-level tools directly.
|
|
78
92
|
|
|
79
93
|
Examples:
|
|
80
94
|
|
|
@@ -86,6 +100,7 @@ Examples:
|
|
|
86
100
|
|
|
87
101
|
Use specific tools when the agent has already parsed the task into structured inputs:
|
|
88
102
|
|
|
103
|
+
- `get_findtime_help`: examples of supported intents, answer API usage, ambiguity handling, and enterprise deployment guidance
|
|
89
104
|
- `get_current_time`: current local time for a known city, country, timezone, or location
|
|
90
105
|
- `convert_time`: convert a known date/time from one place or timezone to another
|
|
91
106
|
- `get_dst_schedule`: DST status and transition dates
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -49,6 +49,7 @@ const DEFAULT_API_KEY = firstNonEmpty(
|
|
|
49
49
|
process.env.FINDTIME_TIME_API_KEY
|
|
50
50
|
);
|
|
51
51
|
const TIMEZONE_HELPERS_PATH = path.join(REPO_ROOT, 'slack-bot', 'timezone-helpers.js');
|
|
52
|
+
const ANSWER_ONLY_TOOL_NAMES = new Set(['answer_time_question', 'get_findtime_help', 'get_api_diagnostics']);
|
|
52
53
|
|
|
53
54
|
const TOOL_DEFINITIONS = [
|
|
54
55
|
{
|
|
@@ -104,6 +105,18 @@ const TOOL_DEFINITIONS = [
|
|
|
104
105
|
return { path: '/health', params: new URLSearchParams() };
|
|
105
106
|
}
|
|
106
107
|
},
|
|
108
|
+
{
|
|
109
|
+
name: 'get_findtime_help',
|
|
110
|
+
description: 'Return enterprise-friendly findtime.io MCP usage help, including supported time-intelligence intents, answer API examples, ambiguity handling examples, and recommended answer-only deployment guidance.',
|
|
111
|
+
inputSchema: {
|
|
112
|
+
type: 'object',
|
|
113
|
+
properties: {},
|
|
114
|
+
additionalProperties: false
|
|
115
|
+
},
|
|
116
|
+
buildRequest() {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
},
|
|
107
120
|
{
|
|
108
121
|
name: 'time_snapshot',
|
|
109
122
|
description: 'Return the production time snapshot payload for one location or a list of locations.',
|
|
@@ -385,6 +398,114 @@ const TOOL_DEFINITIONS_BY_NAME = new Map(TOOL_DEFINITIONS.map((tool) => [tool.na
|
|
|
385
398
|
let cachedResolveLocation;
|
|
386
399
|
let cachedMcpClientId;
|
|
387
400
|
|
|
401
|
+
function getMcpToolMode() {
|
|
402
|
+
const mode = String(firstNonEmpty(
|
|
403
|
+
process.env.FINDTIME_MCP_TOOL_MODE,
|
|
404
|
+
process.env.FINDTIME_MCP_TOOLS
|
|
405
|
+
) || 'full').trim().toLowerCase();
|
|
406
|
+
|
|
407
|
+
return ['answer-only', 'answer_only', 'answer'].includes(mode)
|
|
408
|
+
? 'answer-only'
|
|
409
|
+
: 'full';
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function getVisibleToolDefinitions() {
|
|
413
|
+
if (getMcpToolMode() !== 'answer-only') {
|
|
414
|
+
return TOOL_DEFINITIONS;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return TOOL_DEFINITIONS.filter((tool) => ANSWER_ONLY_TOOL_NAMES.has(tool.name));
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function buildFindtimeHelpPayload() {
|
|
421
|
+
return {
|
|
422
|
+
ok: true,
|
|
423
|
+
tool: 'get_findtime_help',
|
|
424
|
+
recommendedTool: 'answer_time_question',
|
|
425
|
+
recommendedMode: 'FINDTIME_MCP_TOOL_MODE=answer-only',
|
|
426
|
+
summary: 'Use findtime.io MCP for accurate timezone, DST, conversion, overlap-hours, and cross-timezone meeting-time intelligence. In enterprise bots, route natural-language questions through answer_time_question.',
|
|
427
|
+
install: {
|
|
428
|
+
command: 'npx',
|
|
429
|
+
args: ['-y', '@findtime/mcp-server'],
|
|
430
|
+
requiredEnv: ['FINDTIME_TIME_API_KEY'],
|
|
431
|
+
recommendedEnterpriseEnv: {
|
|
432
|
+
FINDTIME_TIME_API_BASE_URL: 'https://time-api.findtime.io',
|
|
433
|
+
FINDTIME_MCP_CLIENT_TYPE: 'company-bot',
|
|
434
|
+
FINDTIME_MCP_TOOL_MODE: 'answer-only'
|
|
435
|
+
}
|
|
436
|
+
},
|
|
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
|
+
answerApiPattern: {
|
|
494
|
+
tool: 'answer_time_question',
|
|
495
|
+
arguments: {
|
|
496
|
+
query: 'Best meeting time for San Francisco, Berlin, and Sydney next week',
|
|
497
|
+
userTimezone: 'America/Los_Angeles',
|
|
498
|
+
locale: 'en-US'
|
|
499
|
+
}
|
|
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
|
+
]
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
|
|
388
509
|
function safeReadJson(filePath) {
|
|
389
510
|
try {
|
|
390
511
|
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
@@ -1073,10 +1194,22 @@ function createFindtimeMcpServer(options = {}) {
|
|
|
1073
1194
|
|
|
1074
1195
|
async function callTool(name, args = {}) {
|
|
1075
1196
|
const tool = TOOL_DEFINITIONS_BY_NAME.get(name);
|
|
1076
|
-
if (!tool) {
|
|
1197
|
+
if (!tool || (getMcpToolMode() === 'answer-only' && !ANSWER_ONLY_TOOL_NAMES.has(name))) {
|
|
1077
1198
|
throw invalidParamsError(`Unknown tool: ${name}`);
|
|
1078
1199
|
}
|
|
1079
1200
|
|
|
1201
|
+
if (name === 'get_findtime_help') {
|
|
1202
|
+
return {
|
|
1203
|
+
content: [
|
|
1204
|
+
{
|
|
1205
|
+
type: 'text',
|
|
1206
|
+
text: `get_findtime_help response\n${JSON.stringify(buildFindtimeHelpPayload(), null, 2)}`
|
|
1207
|
+
}
|
|
1208
|
+
],
|
|
1209
|
+
structuredContent: buildFindtimeHelpPayload()
|
|
1210
|
+
};
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1080
1213
|
if (name === 'get_api_diagnostics') {
|
|
1081
1214
|
const request = tool.buildRequest(args || {});
|
|
1082
1215
|
const [apiResponse, latestMcpVersionCheck] = await Promise.all([
|
|
@@ -1231,7 +1364,7 @@ function createFindtimeMcpServer(options = {}) {
|
|
|
1231
1364
|
|
|
1232
1365
|
if (method === 'tools/list') {
|
|
1233
1366
|
return createSuccessResponse(message.id, {
|
|
1234
|
-
tools:
|
|
1367
|
+
tools: getVisibleToolDefinitions().map(({ name, description, inputSchema }) => ({
|
|
1235
1368
|
name,
|
|
1236
1369
|
description,
|
|
1237
1370
|
inputSchema
|