gtfs-pro-mcp 1.0.0 → 1.1.0

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 CHANGED
@@ -88,6 +88,29 @@ Edit `claude_desktop_config.json` (Settings → Developer → Edit Config) and a
88
88
  Restart Claude Desktop, then ask: *"What bus stops are near \<a place in the
89
89
  service area\>?"* or *"When's the next bus from \<stop name\>?"*
90
90
 
91
+ > **Windows users:** Claude Desktop on Windows can't launch `npx` directly (it's a
92
+ > `.cmd` shim, not an executable), so the server silently fails to start and Claude
93
+ > falls back to web search. Wrap the command in `cmd` instead:
94
+ >
95
+ > ```json
96
+ > {
97
+ > "mcpServers": {
98
+ > "gtfs-pro-transit": {
99
+ > "command": "cmd",
100
+ > "args": ["/c", "npx", "-y", "gtfs-pro-mcp"],
101
+ > "env": {
102
+ > "GTFS_PRO_URL": "https://your-agency-site.org",
103
+ > "GTFS_PRO_FEED_ID": "default",
104
+ > "GTFS_PRO_AGENCY_NAME": "Your Transit Agency"
105
+ > }
106
+ > }
107
+ > }
108
+ > }
109
+ > ```
110
+ >
111
+ > After editing, fully **quit** Claude Desktop (right-click the system-tray icon →
112
+ > Quit — closing the window isn't enough) and reopen it.
113
+
91
114
  ## Use with ChatGPT / other MCP clients
92
115
 
93
116
  Any client that launches a local stdio MCP server works the same way — run
@@ -98,6 +121,46 @@ npm install -g gtfs-pro-mcp
98
121
  GTFS_PRO_URL=https://your-agency-site.org gtfs-pro-mcp
99
122
  ```
100
123
 
124
+ ## Remote / hosted connector (Streamable HTTP)
125
+
126
+ The stdio setup above only works in clients that launch a **local process** (classic
127
+ Claude Desktop, Cursor, etc.). The **Claude apps that use remote connectors** — the
128
+ newer desktop app and **claude.ai on the web** — instead add an MCP server by **URL**.
129
+ For those, run the server in **HTTP mode** and host it somewhere with a public HTTPS
130
+ address; then add that URL as a custom connector.
131
+
132
+ Run it in HTTP mode:
133
+
134
+ ```bash
135
+ GTFS_PRO_URL=https://your-agency-site.org \
136
+ GTFS_PRO_AGENCY_NAME="Your Transit Agency" \
137
+ PORT=3000 \
138
+ gtfs-pro-mcp-http # or: npm run start:http
139
+ ```
140
+
141
+ This serves the MCP endpoint at **`/mcp`** (and a `/healthz` check). Extra env:
142
+
143
+ | Env | Default | Notes |
144
+ |-----|---------|-------|
145
+ | `PORT` | `3000` | Most hosts inject this automatically |
146
+ | `MCP_AUTH_TOKEN` | — | Optional. If set, clients must send `Authorization: Bearer <token>`. Leave unset for an open server — the transit data is public. |
147
+
148
+ ### Deploy
149
+
150
+ - **Docker:** a `Dockerfile` is included — `docker build -t gtfs-pro-mcp . && docker run -p 3000:3000 -e GTFS_PRO_URL=… -e GTFS_PRO_AGENCY_NAME=… gtfs-pro-mcp`
151
+ - **Render / Railway / Fly.io (Node):** build `npm install && npm run build`, start `npm run start:http`, set the env vars. A `render.yaml` blueprint is included; Render gives you HTTPS automatically.
152
+ - **Your own VPS:** run behind nginx/Caddy with TLS, proxying to the Node port.
153
+
154
+ The data is public and read-only, so an open endpoint is fine; add `MCP_AUTH_TOKEN`
155
+ if you'd rather gate it.
156
+
157
+ ### Add it to Claude
158
+
159
+ In the Claude desktop app or claude.ai: **Settings → Connectors → Add custom
160
+ connector**, give it a name, and paste your server's URL ending in **`/mcp`**
161
+ (e.g. `https://gtfs-pro-mcp.onrender.com/mcp`). Then ask a transit question and
162
+ you'll see the `gtfs-pro-transit` tools used.
163
+
101
164
  ## Local development
102
165
 
103
166
  ```bash
@@ -109,12 +172,18 @@ node test/smoke.mjs # exercise all 9 tools against a live site
109
172
  ## How it fits together
110
173
 
111
174
  ```
112
- AI assistant ──MCP/stdio──► gtfs-pro-mcp ──HTTPS──► WP GTFS Pro REST API
113
- (Claude, GPT) (this package) /wp-json/wp-gtfs-pro/v1/*
175
+ Local (stdio):
176
+ AI client ──MCP/stdio──► gtfs-pro-mcp ──HTTPS──► WP GTFS Pro REST API
177
+ (Desktop) (local process) /wp-json/wp-gtfs-pro/v1/*
178
+
179
+ Remote (Streamable HTTP):
180
+ AI client ──MCP/HTTPS──► gtfs-pro-mcp-http ──HTTPS──► WP GTFS Pro REST API
181
+ (web/app) (hosted service /mcp) /wp-json/wp-gtfs-pro/v1/*
114
182
  ```
115
183
 
116
- A future release adds a Streamable-HTTP transport so the server can run as a
117
- hosted remote service (one endpoint per agency) instead of a local process.
184
+ Both transports share the same nine tools and config pick stdio for local
185
+ clients (Claude Desktop, Cursor) or HTTP for connector-based clients (the Claude
186
+ app, claude.ai web, ChatGPT).
118
187
 
119
188
  ## License
120
189
 
package/dist/http.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * GTFS Pro MCP server — Streamable HTTP entry point (remote / hosted transport).
4
+ *
5
+ * This lets the server run as a public service that AI clients add as a "custom
6
+ * connector" by URL — Claude (desktop + web), ChatGPT, etc. — with no local
7
+ * install. It implements the MCP Streamable HTTP transport with per-client
8
+ * sessions, exposes a single `/mcp` endpoint (POST to send, GET for the SSE
9
+ * stream, DELETE to end a session), plus `/healthz`.
10
+ *
11
+ * Config is the same as the stdio server (GTFS_PRO_URL, etc.). Extra env:
12
+ * PORT - listen port (default 3000; most hosts inject this)
13
+ * MCP_AUTH_TOKEN - optional. If set, clients must send `Authorization:
14
+ * Bearer <token>`. Leave unset for an open server (the
15
+ * underlying transit data is public anyway).
16
+ */
17
+ export {};
package/dist/http.js ADDED
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * GTFS Pro MCP server — Streamable HTTP entry point (remote / hosted transport).
4
+ *
5
+ * This lets the server run as a public service that AI clients add as a "custom
6
+ * connector" by URL — Claude (desktop + web), ChatGPT, etc. — with no local
7
+ * install. It implements the MCP Streamable HTTP transport with per-client
8
+ * sessions, exposes a single `/mcp` endpoint (POST to send, GET for the SSE
9
+ * stream, DELETE to end a session), plus `/healthz`.
10
+ *
11
+ * Config is the same as the stdio server (GTFS_PRO_URL, etc.). Extra env:
12
+ * PORT - listen port (default 3000; most hosts inject this)
13
+ * MCP_AUTH_TOKEN - optional. If set, clients must send `Authorization:
14
+ * Bearer <token>`. Leave unset for an open server (the
15
+ * underlying transit data is public anyway).
16
+ */
17
+ import express from "express";
18
+ import { randomUUID } from "node:crypto";
19
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
20
+ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
21
+ import { loadConfig } from "./config.js";
22
+ import { ApiClient } from "./api-client.js";
23
+ import { createServer } from "./server.js";
24
+ const config = loadConfig();
25
+ const api = new ApiClient(config); // shared across sessions → shared response cache
26
+ const PORT = Number(process.env.PORT || 3000);
27
+ const AUTH = (process.env.MCP_AUTH_TOKEN || "").trim();
28
+ const app = express();
29
+ app.use(express.json({ limit: "1mb" }));
30
+ // CORS — required for browser-based MCP clients (e.g. claude.ai web). The
31
+ // session id travels in the `mcp-session-id` header, so it must be allowed and
32
+ // exposed. Data is public, so origin is open.
33
+ app.use((req, res, next) => {
34
+ res.setHeader("Access-Control-Allow-Origin", req.headers.origin || "*");
35
+ res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
36
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, mcp-session-id, mcp-protocol-version, last-event-id");
37
+ res.setHeader("Access-Control-Expose-Headers", "mcp-session-id");
38
+ res.setHeader("Vary", "Origin");
39
+ if (req.method === "OPTIONS") {
40
+ res.sendStatus(204);
41
+ return;
42
+ }
43
+ next();
44
+ });
45
+ /** Optional bearer-token gate. Returns true if the request may proceed. */
46
+ function authorized(req, res) {
47
+ if (!AUTH)
48
+ return true;
49
+ if (req.headers.authorization === `Bearer ${AUTH}`)
50
+ return true;
51
+ res.status(401).json({
52
+ jsonrpc: "2.0",
53
+ error: { code: -32001, message: "Unauthorized" },
54
+ id: null,
55
+ });
56
+ return false;
57
+ }
58
+ app.get("/healthz", (_req, res) => {
59
+ res.json({ ok: true, agency: config.agencyName, site: config.gtfsProUrl });
60
+ });
61
+ app.get("/", (_req, res) => {
62
+ res.type("text/plain").send(`gtfs-pro-mcp (Streamable HTTP) for ${config.agencyName}. ` +
63
+ `MCP endpoint: POST ${"/mcp"}. Add this server's URL as a custom connector in your AI client.`);
64
+ });
65
+ // Live sessions, keyed by the transport's session id.
66
+ const transports = new Map();
67
+ const sid = (req) => {
68
+ const h = req.headers["mcp-session-id"];
69
+ return Array.isArray(h) ? h[0] : h;
70
+ };
71
+ // POST /mcp — client → server messages. An initialize request with no session
72
+ // id spins up a fresh session+server; everything else must carry a known id.
73
+ app.post("/mcp", async (req, res) => {
74
+ if (!authorized(req, res))
75
+ return;
76
+ const id = sid(req);
77
+ let transport = id ? transports.get(id) : undefined;
78
+ if (!transport) {
79
+ if (id || !isInitializeRequest(req.body)) {
80
+ res.status(400).json({
81
+ jsonrpc: "2.0",
82
+ error: { code: -32000, message: "Bad Request: no valid session ID for a non-initialize request" },
83
+ id: null,
84
+ });
85
+ return;
86
+ }
87
+ transport = new StreamableHTTPServerTransport({
88
+ sessionIdGenerator: () => randomUUID(),
89
+ onsessioninitialized: (newId) => {
90
+ transports.set(newId, transport);
91
+ },
92
+ });
93
+ transport.onclose = () => {
94
+ if (transport.sessionId)
95
+ transports.delete(transport.sessionId);
96
+ };
97
+ const server = createServer(config, api);
98
+ await server.connect(transport);
99
+ }
100
+ await transport.handleRequest(req, res, req.body);
101
+ });
102
+ // GET /mcp (open the SSE stream) and DELETE /mcp (end the session) both operate
103
+ // on an existing session.
104
+ async function existingSession(req, res) {
105
+ if (!authorized(req, res))
106
+ return;
107
+ const id = sid(req);
108
+ const transport = id ? transports.get(id) : undefined;
109
+ if (!transport) {
110
+ res.status(400).send("Invalid or missing session ID");
111
+ return;
112
+ }
113
+ await transport.handleRequest(req, res);
114
+ }
115
+ app.get("/mcp", existingSession);
116
+ app.delete("/mcp", existingSession);
117
+ app.listen(PORT, () => {
118
+ console.error(`gtfs-pro-mcp ready (http) — listening on :${PORT}, agency: ${config.agencyName}, ` +
119
+ `site: ${config.gtfsProUrl}${AUTH ? " [auth required]" : ""}`);
120
+ });
121
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * GTFS Pro MCP server — entry point.
3
+ * GTFS Pro MCP server — stdio entry point.
4
4
  *
5
- * Loads configuration, builds the API client, registers the nine transit tools,
6
- * and speaks MCP over stdio (the transport Claude Desktop / ChatGPT desktop use
7
- * to launch a local server). All diagnostics go to stderr so they never corrupt
8
- * the stdout JSON-RPC stream.
5
+ * Loads configuration, builds the server, and speaks MCP over stdio (the
6
+ * transport Claude Desktop / Cursor / ChatGPT desktop use to launch a local
7
+ * server). For the hosted/remote transport see `http.ts`. All diagnostics go to
8
+ * stderr so they never corrupt the stdout JSON-RPC stream.
9
9
  */
10
10
  export {};
package/dist/index.js CHANGED
@@ -1,32 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * GTFS Pro MCP server — entry point.
3
+ * GTFS Pro MCP server — stdio entry point.
4
4
  *
5
- * Loads configuration, builds the API client, registers the nine transit tools,
6
- * and speaks MCP over stdio (the transport Claude Desktop / ChatGPT desktop use
7
- * to launch a local server). All diagnostics go to stderr so they never corrupt
8
- * the stdout JSON-RPC stream.
5
+ * Loads configuration, builds the server, and speaks MCP over stdio (the
6
+ * transport Claude Desktop / Cursor / ChatGPT desktop use to launch a local
7
+ * server). For the hosted/remote transport see `http.ts`. All diagnostics go to
8
+ * stderr so they never corrupt the stdout JSON-RPC stream.
9
9
  */
10
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
11
10
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
12
11
  import { loadConfig } from "./config.js";
13
- import { ApiClient } from "./api-client.js";
14
- import { registerTools } from "./tools.js";
12
+ import { createServer } from "./server.js";
15
13
  async function main() {
16
14
  const config = loadConfig();
17
- const api = new ApiClient(config);
18
- const instructions = `This server provides real-time transit information for ${config.agencyName}.` +
19
- (config.agencyDescription ? ` ${config.agencyDescription}` : "") +
20
- ` Use the tools to look up bus stops, schedules, routes, live vehicle positions, ` +
21
- `and service alerts. Always use the tools for schedule and stop data — never guess ` +
22
- `times. Note: realtime tools (get_service_alerts, get_live_vehicles) returning an ` +
23
- `empty result is normal and means "nothing active right now," not "no service."`;
24
- const server = new McpServer({ name: "gtfs-pro-transit", version: "1.0.0" }, { instructions });
25
- registerTools(server, api);
15
+ const server = createServer(config);
26
16
  const transport = new StdioServerTransport();
27
17
  await server.connect(transport);
28
18
  // Visible in the client's MCP logs; harmless on stderr.
29
- console.error(`gtfs-pro-mcp ready — agency: ${config.agencyName}, site: ${config.gtfsProUrl}, ` +
19
+ console.error(`gtfs-pro-mcp ready (stdio) — agency: ${config.agencyName}, site: ${config.gtfsProUrl}, ` +
30
20
  `default feed: ${config.feedId}`);
31
21
  }
32
22
  main().catch((err) => {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,GAAG,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC;IAElC,MAAM,YAAY,GAChB,0DAA0D,MAAM,CAAC,UAAU,GAAG;QAC9E,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,kFAAkF;QAClF,oFAAoF;QACpF,mFAAmF;QACnF,gFAAgF,CAAC;IAEnF,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,OAAO,EAAE,EAC9C,EAAE,YAAY,EAAE,CACjB,CAAC;IAEF,aAAa,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAE3B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,wDAAwD;IACxD,OAAO,CAAC,KAAK,CACX,gCAAgC,MAAM,CAAC,UAAU,WAAW,MAAM,CAAC,UAAU,IAAI;QAC/E,iBAAiB,MAAM,CAAC,MAAM,EAAE,CACnC,CAAC;AACJ,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,iCAAkC,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IACzE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAEpC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,wDAAwD;IACxD,OAAO,CAAC,KAAK,CACX,wCAAwC,MAAM,CAAC,UAAU,WAAW,MAAM,CAAC,UAAU,IAAI;QACvF,iBAAiB,MAAM,CAAC,MAAM,EAAE,CACnC,CAAC;AACJ,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,iCAAkC,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IACzE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Shared MCP server factory. Both transports — stdio (`index.ts`) and
3
+ * Streamable HTTP (`http.ts`) — build their server through here, so the two
4
+ * surfaces expose the identical 9 tools and instructions.
5
+ */
6
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ import type { Config } from "./config.js";
8
+ import { ApiClient } from "./api-client.js";
9
+ /** The server-level instructions shown to the AI client. */
10
+ export declare function buildInstructions(config: Config): string;
11
+ /**
12
+ * Create a fully-wired McpServer. Pass a shared ApiClient so multiple HTTP
13
+ * sessions reuse one response cache; omit it for a standalone (stdio) process.
14
+ */
15
+ export declare function createServer(config: Config, api?: ApiClient): McpServer;
package/dist/server.js ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Shared MCP server factory. Both transports — stdio (`index.ts`) and
3
+ * Streamable HTTP (`http.ts`) — build their server through here, so the two
4
+ * surfaces expose the identical 9 tools and instructions.
5
+ */
6
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ import { ApiClient } from "./api-client.js";
8
+ import { registerTools } from "./tools.js";
9
+ /** The server-level instructions shown to the AI client. */
10
+ export function buildInstructions(config) {
11
+ return (`This server provides real-time transit information for ${config.agencyName}.` +
12
+ (config.agencyDescription ? ` ${config.agencyDescription}` : "") +
13
+ ` Use the tools to look up bus stops, schedules, routes, live vehicle positions, ` +
14
+ `and service alerts. Always use the tools for schedule and stop data — never guess ` +
15
+ `times. Note: realtime tools (get_service_alerts, get_live_vehicles) returning an ` +
16
+ `empty result is normal and means "nothing active right now," not "no service."`);
17
+ }
18
+ /**
19
+ * Create a fully-wired McpServer. Pass a shared ApiClient so multiple HTTP
20
+ * sessions reuse one response cache; omit it for a standalone (stdio) process.
21
+ */
22
+ export function createServer(config, api = new ApiClient(config)) {
23
+ const server = new McpServer({ name: "gtfs-pro-transit", version: "1.0.0" }, { instructions: buildInstructions(config) });
24
+ registerTools(server, api);
25
+ return server;
26
+ }
27
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,4DAA4D;AAC5D,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,OAAO,CACL,0DAA0D,MAAM,CAAC,UAAU,GAAG;QAC9E,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,kFAAkF;QAClF,oFAAoF;QACpF,mFAAmF;QACnF,gFAAgF,CACjF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,MAAiB,IAAI,SAAS,CAAC,MAAM,CAAC;IACjF,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,OAAO,EAAE,EAC9C,EAAE,YAAY,EAAE,iBAAiB,CAAC,MAAM,CAAC,EAAE,CAC5C,CAAC;IACF,aAAa,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gtfs-pro-mcp",
3
- "version": "1.0.0",
4
- "description": "Model Context Protocol server for any WP GTFS Pro transit site. Lets AI assistants query bus stops, schedules, routes, live vehicle positions, and service alerts from an agency's live GTFS data.",
3
+ "version": "1.1.0",
4
+ "description": "Model Context Protocol server for any WP GTFS Pro transit site. Lets AI assistants query bus stops, schedules, routes, live vehicle positions, and service alerts from an agency's live GTFS data. Runs locally (stdio) or as a hosted remote connector (Streamable HTTP).",
5
5
  "license": "GPL-2.0-or-later",
6
6
  "author": "Digital Mountaineers",
7
7
  "homepage": "https://github.com/digital-mountaineers/gtfs-pro-mcp#readme",
@@ -26,7 +26,8 @@
26
26
  ],
27
27
  "type": "module",
28
28
  "bin": {
29
- "gtfs-pro-mcp": "dist/index.js"
29
+ "gtfs-pro-mcp": "dist/index.js",
30
+ "gtfs-pro-mcp-http": "dist/http.js"
30
31
  },
31
32
  "files": [
32
33
  "dist",
@@ -41,13 +42,16 @@
41
42
  "build": "tsc",
42
43
  "watch": "tsc --watch",
43
44
  "start": "node dist/index.js",
45
+ "start:http": "node dist/http.js",
44
46
  "prepublishOnly": "npm run build"
45
47
  },
46
48
  "dependencies": {
47
49
  "@modelcontextprotocol/sdk": "^1.29.0",
50
+ "express": "^5.1.0",
48
51
  "zod": "^3.23.8"
49
52
  },
50
53
  "devDependencies": {
54
+ "@types/express": "^5.0.0",
51
55
  "@types/node": "^22.10.0",
52
56
  "typescript": "^5.7.0"
53
57
  }