@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.
- package/README.md +27 -5
- package/build/index.js +43 -15
- 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
|
-
|
|
5
|
+
In most cases, you should use the official MCP connector at `https://api.transit.ls8h.com/mcp`.
|
|
6
6
|
|
|
7
|
-
|
|
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
|
-
|
|
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
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
36
|
-
|
|
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:
|
|
227
|
+
version: SERVER_VERSION,
|
|
200
228
|
});
|
|
201
229
|
server.registerTool("plan_journey", {
|
|
202
230
|
title: "Plan journey",
|