@findtime/mcp-server 3.25.6 → 3.25.8

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/package.json +1 -1
  2. package/server.js +124 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@findtime/mcp-server",
3
- "version": "3.25.6",
3
+ "version": "3.25.8",
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
@@ -19,6 +19,10 @@ loadEnvironmentFiles();
19
19
 
20
20
  const PACKAGE_METADATA = safeReadJson(LOCAL_PACKAGE_PATH) || safeReadJson(REPO_PACKAGE_PATH) || {};
21
21
  const SERVER_VERSION = PACKAGE_METADATA.version || '0.0.0';
22
+ const MCP_PACKAGE_NAME = PACKAGE_METADATA.name || '@findtime/mcp-server';
23
+ const MCP_NPM_REGISTRY_LATEST_URL = `https://registry.npmjs.org/${encodeURIComponent(MCP_PACKAGE_NAME)}/latest`;
24
+ const MCP_NPM_URL = `https://www.npmjs.com/package/${MCP_PACKAGE_NAME}`;
25
+ const MCP_REGISTRY_URL = 'https://registry.modelcontextprotocol.io/?q=io.github.hkchao%2Ffindtime-mcp-server';
22
26
  const DEFAULT_API_BASE_URL = firstNonEmpty(
23
27
  process.env.TIME_API_BASE_URL,
24
28
  process.env.FINDTIME_TIME_API_BASE_URL
@@ -33,6 +37,18 @@ const DEFAULT_API_KEY = firstNonEmpty(
33
37
  const TIMEZONE_HELPERS_PATH = path.join(REPO_ROOT, 'slack-bot', 'timezone-helpers.js');
34
38
 
35
39
  const TOOL_DEFINITIONS = [
40
+ {
41
+ name: 'get_api_diagnostics',
42
+ description: 'Return MCP and findtime Time API diagnostics, including the running MCP version, latest published MCP version, API base URL, auth configuration, and a live health check.',
43
+ inputSchema: {
44
+ type: 'object',
45
+ properties: {},
46
+ additionalProperties: false
47
+ },
48
+ buildRequest() {
49
+ return { path: '/health', params: new URLSearchParams() };
50
+ }
51
+ },
36
52
  {
37
53
  name: 'time_snapshot',
38
54
  description: 'Return the production time snapshot payload for one location or a list of locations.',
@@ -806,12 +822,120 @@ function createFindtimeMcpServer(options = {}) {
806
822
  }
807
823
  }
808
824
 
825
+ async function fetchLatestMcpVersion() {
826
+ const controller = new AbortController();
827
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
828
+
829
+ try {
830
+ const response = await fetchImpl(MCP_NPM_REGISTRY_LATEST_URL, {
831
+ method: 'GET',
832
+ headers: {
833
+ Accept: 'application/json'
834
+ },
835
+ signal: controller.signal
836
+ });
837
+
838
+ const rawBody = await response.text();
839
+ const parsedBody = tryParseJson(rawBody);
840
+
841
+ if (!response.ok) {
842
+ return {
843
+ ok: false,
844
+ status: response.status,
845
+ latestVersion: null
846
+ };
847
+ }
848
+
849
+ const latestVersion = parsedBody && typeof parsedBody.version === 'string'
850
+ ? parsedBody.version.trim()
851
+ : '';
852
+
853
+ if (!latestVersion) {
854
+ return {
855
+ ok: false,
856
+ status: response.status,
857
+ latestVersion: null
858
+ };
859
+ }
860
+
861
+ return {
862
+ ok: true,
863
+ status: response.status,
864
+ latestVersion
865
+ };
866
+ } catch (error) {
867
+ return {
868
+ ok: false,
869
+ status: null,
870
+ latestVersion: null,
871
+ networkError: error && error.name === 'AbortError'
872
+ ? `Request timed out after ${timeoutMs}ms`
873
+ : String(error && error.message ? error.message : error)
874
+ };
875
+ } finally {
876
+ clearTimeout(timeout);
877
+ }
878
+ }
879
+
809
880
  async function callTool(name, args = {}) {
810
881
  const tool = TOOL_DEFINITIONS_BY_NAME.get(name);
811
882
  if (!tool) {
812
883
  throw invalidParamsError(`Unknown tool: ${name}`);
813
884
  }
814
885
 
886
+ if (name === 'get_api_diagnostics') {
887
+ const request = tool.buildRequest(args || {});
888
+ const [apiResponse, latestMcpVersionCheck] = await Promise.all([
889
+ fetchJson(name, request),
890
+ fetchLatestMcpVersion()
891
+ ]);
892
+ const checkedAt = new Date().toISOString();
893
+ const latestVersion = latestMcpVersionCheck.latestVersion || null;
894
+ const diagnostics = {
895
+ ok: apiResponse.ok,
896
+ tool: name,
897
+ checkedAt,
898
+ mcpVersion: SERVER_VERSION,
899
+ mcpLatestVersion: latestVersion,
900
+ mcpUpToDate: latestVersion ? latestVersion === SERVER_VERSION : null,
901
+ mcpLatestVersionCheck: latestVersion ? 'ok' : 'failed',
902
+ mcpLatestVersionSource: latestVersion ? 'npm' : 'unavailable',
903
+ mcpLatestVersionHint: latestVersion
904
+ ? null
905
+ : 'Could not verify the latest published MCP version automatically. Check the npm package page or Official MCP Registry listing directly.',
906
+ mcpRegistryUrl: MCP_REGISTRY_URL,
907
+ mcpNpmUrl: MCP_NPM_URL,
908
+ apiBaseUrl,
909
+ apiConfigured: Boolean(apiBaseUrl),
910
+ apiAuthConfigured: Boolean(typeof apiKey === 'string' && apiKey.trim()),
911
+ clientType: firstNonEmpty(
912
+ process.env.FINDTIME_MCP_CLIENT_TYPE,
913
+ process.env.TIME_API_CLIENT_TYPE
914
+ ) || null,
915
+ apiHealthUrl: apiResponse.url,
916
+ apiStatus: apiResponse.status,
917
+ apiReachable: apiResponse.ok
918
+ };
919
+
920
+ if (apiResponse.ok) {
921
+ diagnostics.apiHealth = apiResponse.parsedBody;
922
+ } else {
923
+ diagnostics.error = apiResponse.parsedBody !== undefined ? apiResponse.parsedBody : apiResponse.rawBody;
924
+ if (apiResponse.networkError) {
925
+ diagnostics.networkError = apiResponse.networkError;
926
+ }
927
+ }
928
+
929
+ if (!latestVersion && latestMcpVersionCheck.networkError) {
930
+ diagnostics.mcpLatestVersionNetworkError = latestMcpVersionCheck.networkError;
931
+ }
932
+
933
+ return buildToolSuccessResult(name, diagnostics, {
934
+ endpoint: request.path,
935
+ url: apiResponse.url
936
+ });
937
+ }
938
+
815
939
  const request = tool.buildRequest(args || {});
816
940
  let apiResponse = await fetchJson(name, request);
817
941