@tcliplab/transit-mcp 0.1.1 → 0.1.3

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 (3) hide show
  1. package/README.md +27 -5
  2. package/build/index.js +43 -15
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,11 +1,33 @@
1
- # Transit API Server
1
+ # Transit API Server (UNOFFICIAL)
2
2
 
3
- An MCP server for Transit API, a Japanese public transit API built on `https://api.transit.ls8h.com/`.
3
+ An unofficial local MCP server for Transit API, a Japanese public transit API built on `https://api.transit.ls8h.com/`.
4
4
 
5
- This server wraps the REST API and is still unofficial.
5
+ In most cases, you should use the official MCP connector at `https://api.transit.ls8h.com/mcp`.
6
6
 
7
- I made this to search Japanese transit information seamlessly from LLM chat.
7
+ This package exists as a local `npm` / `npx`-based wrapper and as a base for future wrapper tools and experiments.
8
+ It also includes lightweight result previews, which may make the first-step UX a little smoother.
8
9
 
9
- ## LICENSE
10
+ Why `npx`: this package can be tried without a local install or a separate service setup.
11
+ It may be a small example of a local `npm` / `npx`-driven MCP wrapper.
12
+
13
+ ## Connect Codex
14
+
15
+ Add the server to your Codex config:
16
+
17
+ ```toml
18
+ [mcp_servers.transit_mcp]
19
+ command = "npx"
20
+ args = ["-y", "@tcliplab/transit-mcp@beta"]
21
+ ```
22
+
23
+ ## API Documentation
24
+
25
+ Transit API is available at `https://api.transit.ls8h.com/`.
26
+
27
+ The server uses the public REST API directly and does not require extra credentials.
28
+
29
+ ## About
30
+
31
+ This package is currently unofficial.
10
32
 
11
33
  No license is granted at this time. OSS release is planned after permission and scope are clarified.
package/build/index.js CHANGED
@@ -1,8 +1,20 @@
1
1
  #!/usr/bin/env node
2
+ import { readFileSync } from "node:fs";
2
3
  import { McpServer } from "@modelcontextprotocol/server";
3
4
  import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
4
5
  import * as z from "zod/v4";
5
6
  const API_BASE = "https://api.transit.ls8h.com";
7
+ const REQUEST_TIMEOUT_MS = 20_000;
8
+ function readPackageVersion() {
9
+ try {
10
+ const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
11
+ return typeof packageJson.version === "string" ? packageJson.version : "0.0.0";
12
+ }
13
+ catch {
14
+ return "0.0.0";
15
+ }
16
+ }
17
+ const SERVER_VERSION = readPackageVersion();
6
18
  function toSearchParams(params) {
7
19
  const searchParams = new URLSearchParams();
8
20
  for (const [key, value] of Object.entries(params)) {
@@ -19,24 +31,40 @@ async function fetchTransit(path, params = {}) {
19
31
  searchParams.forEach((value, key) => {
20
32
  url.searchParams.set(key, value);
21
33
  });
22
- const response = await fetch(url, {
23
- headers: {
24
- accept: "application/json",
25
- },
26
- });
27
- const rawText = await response.text();
28
- let data = rawText;
34
+ const controller = new AbortController();
35
+ const timeoutId = setTimeout(() => {
36
+ controller.abort();
37
+ }, REQUEST_TIMEOUT_MS);
29
38
  try {
30
- data = JSON.parse(rawText);
39
+ const response = await fetch(url, {
40
+ headers: {
41
+ accept: "application/json",
42
+ },
43
+ signal: controller.signal,
44
+ });
45
+ const rawText = await response.text();
46
+ let data = rawText;
47
+ try {
48
+ data = JSON.parse(rawText);
49
+ }
50
+ catch {
51
+ // Keep raw text when the endpoint does not return JSON.
52
+ }
53
+ if (!response.ok) {
54
+ const detail = typeof data === "string" ? data : JSON.stringify(data, null, 2);
55
+ throw new Error(`Transit API ${response.status} ${response.statusText}: ${detail}`);
56
+ }
57
+ return data;
31
58
  }
32
- catch {
33
- // Keep raw text when the endpoint does not return JSON.
59
+ catch (error) {
60
+ if (error instanceof Error && error.name === "AbortError") {
61
+ throw new Error(`Transit API request timed out after ${REQUEST_TIMEOUT_MS}ms`);
62
+ }
63
+ throw error;
34
64
  }
35
- if (!response.ok) {
36
- const detail = typeof data === "string" ? data : JSON.stringify(data, null, 2);
37
- throw new Error(`Transit API ${response.status} ${response.statusText}: ${detail}`);
65
+ finally {
66
+ clearTimeout(timeoutId);
38
67
  }
39
- return data;
40
68
  }
41
69
  function formatServiceSeconds(seconds) {
42
70
  const day = 86_400;
@@ -196,7 +224,7 @@ const stationInput = z.object({
196
224
  });
197
225
  const server = new McpServer({
198
226
  name: "transit-mcp",
199
- version: "1.0.0",
227
+ version: SERVER_VERSION,
200
228
  });
201
229
  server.registerTool("plan_journey", {
202
230
  title: "Plan journey",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tcliplab/transit-mcp",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "publishConfig": {