@findtime/mcp-server 3.25.18 → 3.25.20

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 (4) hide show
  1. package/README.md +25 -5
  2. package/SKILL.md +71 -0
  3. package/package.json +1 -1
  4. package/server.js +30 -0
package/README.md CHANGED
@@ -49,9 +49,12 @@ Optional environment variables:
49
49
  - `FINDTIME_TIME_API_BASE_URL`
50
50
  - `TIME_API_BASE_URL`
51
51
  - `TIME_API_TIMEOUT_MS`
52
+ - `FINDTIME_BINDING_TYPE` for optional install/workspace binding context. Supported values: `slack_team`, `workspace_id`, `install_id`.
53
+ - `FINDTIME_BINDING_VALUE` for the install/workspace identifier that matches the selected binding type.
54
+ - `FINDTIME_BINDING_HEADER` to override the header name directly for custom enterprise environments.
55
+ - `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. When present, the MCP wrapper forwards it to `time-api` as `X-Findtime-User-ID` for enterprise usage attribution.
52
56
  - `FINDTIME_MCP_CLIENT_TYPE`
53
57
  - `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.
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.
55
58
  - `FINDTIME_MCP_INSTRUMENTATION_ENABLED=false` to opt out of anonymous usage telemetry.
56
59
  - `FINDTIME_MCP_USAGE_TELEMETRY_URL` to override the default telemetry endpoint.
57
60
 
@@ -86,6 +89,9 @@ enabled = true
86
89
  FINDTIME_MCP_CLIENT_TYPE = "codex"
87
90
  FINDTIME_TIME_API_BASE_URL = "https://time-api.findtime.io"
88
91
  FINDTIME_TIME_API_KEY = "YOUR_FINDTIME_SECRET_KEY"
92
+ # Optional enterprise install binding:
93
+ # FINDTIME_BINDING_TYPE = "workspace_id"
94
+ # FINDTIME_BINDING_VALUE = "YOUR_WORKSPACE_ID"
89
95
  ```
90
96
 
91
97
  ### Claude Desktop
@@ -99,13 +105,13 @@ FINDTIME_TIME_API_KEY = "YOUR_FINDTIME_SECRET_KEY"
99
105
  "findtime": {
100
106
  "command": "npx",
101
107
  "args": ["-y", "@findtime/mcp-server"],
102
- "env": {
103
- "FINDTIME_TIME_API_BASE_URL": "https://time-api.findtime.io",
104
- "FINDTIME_TIME_API_KEY": "YOUR_FINDTIME_SECRET_KEY"
108
+ "env": {
109
+ "FINDTIME_TIME_API_BASE_URL": "https://time-api.findtime.io",
110
+ "FINDTIME_TIME_API_KEY": "YOUR_FINDTIME_SECRET_KEY"
111
+ }
105
112
  }
106
113
  }
107
114
  }
108
- }
109
115
  ```
110
116
 
111
117
  ### Claude CLI / Claude Code
@@ -138,6 +144,20 @@ If your Claude CLI uses JSON config instead, add:
138
144
  }
139
145
  ```
140
146
 
147
+ For enterprise installs, the minimum credential is always the API key. If the install environment has a stable workspace or installation identifier, optionally add:
148
+
149
+ ```text
150
+ FINDTIME_BINDING_TYPE=workspace_id
151
+ FINDTIME_BINDING_VALUE=YOUR_WORKSPACE_ID
152
+ ```
153
+
154
+ For Slack-specific installs:
155
+
156
+ ```text
157
+ FINDTIME_BINDING_TYPE=slack_team
158
+ FINDTIME_BINDING_VALUE=T01ABC123
159
+ ```
160
+
141
161
  ## Verify installation
142
162
 
143
163
  Use an explicit tool-call prompt first:
package/SKILL.md CHANGED
@@ -70,6 +70,50 @@ When packaging instructions for another agent, include this `SKILL.md` file with
70
70
 
71
71
  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.
72
72
 
73
+ For enterprise installs, always try to provide a stable caller identifier.
74
+
75
+ How to pass it:
76
+
77
+ - if you control the MCP wrapper config, set `FINDTIME_MCP_INSTALL_ID` or `FINDTIME_MCP_CLIENT_ID`; the wrapper forwards that value to `time-api.findtime.io` as `X-Findtime-User-ID`
78
+ - if you control direct HTTP calls to `time-api.findtime.io`, send the same value explicitly in the `X-Findtime-User-ID` header
79
+ - if you also have a stable thread or conversation identifier, send it as `X-Findtime-Conversation-ID`
80
+
81
+ Use this for enterprise usage attribution.
82
+
83
+ Example MCP wrapper config:
84
+
85
+ ```text
86
+ FINDTIME_TIME_API_KEY=your_enterprise_api_key
87
+ FINDTIME_MCP_CLIENT_TYPE=enterprise-bot
88
+ FINDTIME_MCP_INSTALL_ID=install_12345
89
+ ```
90
+
91
+ With that config, the MCP wrapper calls `time-api.findtime.io` using:
92
+
93
+ ```text
94
+ X-API-Key: your_enterprise_api_key
95
+ X-Findtime-User-ID: install_12345
96
+ ```
97
+
98
+ Direct HTTP example when you are not using the MCP wrapper:
99
+
100
+ ```text
101
+ GET /api/time/current?query=Tokyo
102
+ Authorization: Bearer <your_enterprise_api_key>
103
+ X-Findtime-User-ID: install_12345
104
+ X-Findtime-Conversation-ID: thread_abc
105
+ ```
106
+
107
+ Use the most stable first-party identifier you have:
108
+
109
+ - preferred: a true per-user ID from your platform or agent runtime
110
+ - next best: a stable conversation or thread ID when the same user may appear across multiple sessions
111
+ - next best: your platform's install ID or deployment ID
112
+ - fallback: your platform's stable workspace or tenant ID
113
+ - last resort: a stable agent client ID
114
+
115
+ If no true per-user ID is available, it is acceptable for `X-Findtime-User-ID` to carry an install ID or other stable caller identity instead of a human end-user ID.
116
+
73
117
  For enterprise bots that should avoid model-level tool selection across the lower-level APIs, set:
74
118
 
75
119
  ```text
@@ -122,6 +166,33 @@ When using `findtime.io` MCP:
122
166
  - Mention DST only when relevant to the user's question or when it explains an offset difference.
123
167
  - Keep answers concise unless the user asks for details.
124
168
 
169
+ ## Voice
170
+
171
+ Use a calm, practical, time-aware voice when presenting `findtime.io` MCP results.
172
+
173
+ - Lead with the exact answer first: time, date, timezone, offset, or recommended meeting window.
174
+ - Add useful human context when it matters, such as tomorrow/yesterday, early morning, late evening, overnight, weekend, or outside working hours.
175
+ - Be lightly opinionated about awkward meeting times. Call out who pays the scheduling cost.
176
+ - Keep humor subtle and brief. Personality should make the answer easier to use, not less precise.
177
+ - Never let voice obscure the source facts, uncertainty, ambiguity, timezone ID, local date, offset, or DST status.
178
+ - If the tool returns ambiguity or a clarification request, preserve that first and do not guess for the sake of sounding helpful.
179
+
180
+ Good style:
181
+
182
+ ```text
183
+ 3:00 PM in New York on Tuesday is 8:00 PM in London and 5:00 AM Wednesday in Tokyo.
184
+
185
+ London gets an easy evening slot. Tokyo crosses into the next morning, so that one is only friendly for very early risers.
186
+ ```
187
+
188
+ Meeting style:
189
+
190
+ ```text
191
+ Best fit: 8:00 AM San Francisco / 5:00 PM Berlin / 1:00 AM Sydney.
192
+
193
+ That works cleanly for San Francisco and Berlin, but Sydney takes the late-night hit. If Sydney needs sane hours, rotate the burden or move this async.
194
+ ```
195
+
125
196
  ## Failure Policy
126
197
 
127
198
  If the `findtime.io` MCP server is configured but a tool call fails:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@findtime/mcp-server",
3
- "version": "3.25.18",
3
+ "version": "3.25.20",
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": {
package/server.js CHANGED
@@ -48,6 +48,18 @@ const DEFAULT_API_KEY = firstNonEmpty(
48
48
  process.env.FINDTIME_MCP_API_KEY,
49
49
  process.env.FINDTIME_TIME_API_KEY
50
50
  );
51
+ const DEFAULT_BINDING_TYPE = firstNonEmpty(
52
+ process.env.FINDTIME_BINDING_TYPE,
53
+ process.env.FINDTIME_MCP_BINDING_TYPE
54
+ ) || '';
55
+ const DEFAULT_BINDING_VALUE = firstNonEmpty(
56
+ process.env.FINDTIME_BINDING_VALUE,
57
+ process.env.FINDTIME_MCP_BINDING_VALUE
58
+ ) || '';
59
+ const DEFAULT_BINDING_HEADER = firstNonEmpty(
60
+ process.env.FINDTIME_BINDING_HEADER,
61
+ process.env.FINDTIME_MCP_BINDING_HEADER
62
+ ) || '';
51
63
  const TIMEZONE_HELPERS_PATH = path.join(REPO_ROOT, 'slack-bot', 'timezone-helpers.js');
52
64
  const ANSWER_ONLY_TOOL_NAMES = new Set(['answer_time_question', 'get_findtime_help', 'get_api_diagnostics']);
53
65
 
@@ -1048,6 +1060,9 @@ function createFindtimeMcpServer(options = {}) {
1048
1060
  const apiBaseUrl = options.apiBaseUrl || DEFAULT_API_BASE_URL;
1049
1061
  const timeoutMs = Number.isFinite(options.timeoutMs) ? options.timeoutMs : DEFAULT_TIMEOUT_MS;
1050
1062
  const apiKey = options.apiKey === undefined ? DEFAULT_API_KEY : options.apiKey;
1063
+ const bindingType = options.bindingType === undefined ? DEFAULT_BINDING_TYPE : options.bindingType;
1064
+ const bindingValue = options.bindingValue === undefined ? DEFAULT_BINDING_VALUE : options.bindingValue;
1065
+ const bindingHeader = options.bindingHeader === undefined ? DEFAULT_BINDING_HEADER : options.bindingHeader;
1051
1066
  const serverName = options.serverName || 'findtime';
1052
1067
  const serverTitle = options.serverTitle || 'findtime Time API MCP';
1053
1068
  const resolveLocationImpl = options.resolveLocationImpl === undefined
@@ -1079,6 +1094,13 @@ function createFindtimeMcpServer(options = {}) {
1079
1094
  headers.Authorization = `Bearer ${apiKey.trim()}`;
1080
1095
  }
1081
1096
 
1097
+ const resolvedBindingHeader = typeof bindingHeader === 'string' && bindingHeader.trim()
1098
+ ? bindingHeader.trim()
1099
+ : normalizeBindingHeader(bindingType);
1100
+ if (resolvedBindingHeader && typeof bindingValue === 'string' && bindingValue.trim()) {
1101
+ headers[resolvedBindingHeader] = bindingValue.trim();
1102
+ }
1103
+
1082
1104
  const controller = new AbortController();
1083
1105
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
1084
1106
 
@@ -1403,6 +1425,14 @@ function createFindtimeMcpServer(options = {}) {
1403
1425
  };
1404
1426
  }
1405
1427
 
1428
+ function normalizeBindingHeader(bindingType) {
1429
+ const normalized = String(bindingType || '').trim().toLowerCase();
1430
+ if (normalized === 'slack_team') return 'X-Slack-Team-ID';
1431
+ if (normalized === 'workspace_id') return 'X-Findtime-Workspace-ID';
1432
+ if (normalized === 'install_id') return 'X-Findtime-Install-ID';
1433
+ return '';
1434
+ }
1435
+
1406
1436
  function tryParseJson(value) {
1407
1437
  if (typeof value !== 'string' || !value.trim()) {
1408
1438
  return undefined;