mcp-travelcode 1.0.5 → 1.0.6
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/build/http-server.d.ts +14 -6
- package/build/http-server.js +88 -29
- package/package.json +1 -1
package/build/http-server.d.ts
CHANGED
|
@@ -3,12 +3,20 @@
|
|
|
3
3
|
* HTTP entry point for the TravelCode MCP Server.
|
|
4
4
|
*
|
|
5
5
|
* Supports OAuth 2.1 via MCP spec:
|
|
6
|
-
* - Serves Protected Resource Metadata (RFC 9728) at
|
|
7
|
-
* -
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
6
|
+
* - Serves Protected Resource Metadata (RFC 9728) at the path-aware well-known
|
|
7
|
+
* URL (`/.well-known/oauth-protected-resource/mcp`) AND the legacy
|
|
8
|
+
* non-suffixed path for older clients.
|
|
9
|
+
* - Proxies Authorization Server Metadata (RFC 8414) at
|
|
10
|
+
* `/.well-known/oauth-authorization-server`. travel-code.com's nginx blocks
|
|
11
|
+
* `/.well-known/*` on its own origin, so MCP clients cannot discover AS
|
|
12
|
+
* metadata there directly. We advertise the sidecar itself as the AS in the
|
|
13
|
+
* Protected Resource Metadata document and serve the AS metadata here with
|
|
14
|
+
* the real upstream `authorization_endpoint` / `token_endpoint` /
|
|
15
|
+
* `registration_endpoint` / `revocation_endpoint` values. The browser and
|
|
16
|
+
* client hit those upstream URLs directly — only discovery is proxied.
|
|
17
|
+
* - Returns 401 with WWW-Authenticate on missing Bearer and on unknown session,
|
|
18
|
+
* so clients can restart the OAuth flow after a sidecar restart.
|
|
19
|
+
* - Creates per-session McpServer instances using the user's OAuth token.
|
|
12
20
|
*
|
|
13
21
|
* Stdio transport (src/index.ts) remains unchanged for backward compatibility.
|
|
14
22
|
*/
|
package/build/http-server.js
CHANGED
|
@@ -3,12 +3,20 @@
|
|
|
3
3
|
* HTTP entry point for the TravelCode MCP Server.
|
|
4
4
|
*
|
|
5
5
|
* Supports OAuth 2.1 via MCP spec:
|
|
6
|
-
* - Serves Protected Resource Metadata (RFC 9728) at
|
|
7
|
-
* -
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
6
|
+
* - Serves Protected Resource Metadata (RFC 9728) at the path-aware well-known
|
|
7
|
+
* URL (`/.well-known/oauth-protected-resource/mcp`) AND the legacy
|
|
8
|
+
* non-suffixed path for older clients.
|
|
9
|
+
* - Proxies Authorization Server Metadata (RFC 8414) at
|
|
10
|
+
* `/.well-known/oauth-authorization-server`. travel-code.com's nginx blocks
|
|
11
|
+
* `/.well-known/*` on its own origin, so MCP clients cannot discover AS
|
|
12
|
+
* metadata there directly. We advertise the sidecar itself as the AS in the
|
|
13
|
+
* Protected Resource Metadata document and serve the AS metadata here with
|
|
14
|
+
* the real upstream `authorization_endpoint` / `token_endpoint` /
|
|
15
|
+
* `registration_endpoint` / `revocation_endpoint` values. The browser and
|
|
16
|
+
* client hit those upstream URLs directly — only discovery is proxied.
|
|
17
|
+
* - Returns 401 with WWW-Authenticate on missing Bearer and on unknown session,
|
|
18
|
+
* so clients can restart the OAuth flow after a sidecar restart.
|
|
19
|
+
* - Creates per-session McpServer instances using the user's OAuth token.
|
|
12
20
|
*
|
|
13
21
|
* Stdio transport (src/index.ts) remains unchanged for backward compatibility.
|
|
14
22
|
*/
|
|
@@ -19,11 +27,21 @@ import { createServer } from "./server.js";
|
|
|
19
27
|
// --- Configuration ---
|
|
20
28
|
const PORT = parseInt(process.env.PORT || "3000", 10);
|
|
21
29
|
const API_BASE_URL = (process.env.TRAVELCODE_API_BASE_URL || "https://api.travel-code.com/v1").replace(/\/+$/, "");
|
|
22
|
-
|
|
23
|
-
//
|
|
30
|
+
// Upstream Authorization Server origin — where the real OAuth endpoints live.
|
|
31
|
+
// travel-code.com implements /oauth/authorize, /oauth/token, /oauth/register,
|
|
32
|
+
// /oauth/revoke but does NOT expose RFC 8414 metadata (nginx blocks
|
|
33
|
+
// /.well-known/*), so we proxy AS metadata from this sidecar.
|
|
34
|
+
const UPSTREAM_AS_ORIGIN = (process.env.OAUTH_ISSUER || "https://travel-code.com").replace(/\/+$/, "");
|
|
35
|
+
// Resource URI — the public URL of this MCP server (origin, no path).
|
|
24
36
|
// In production, set to the actual public URL (e.g. https://mcp.travel-code.com).
|
|
25
37
|
// Locally defaults to http://localhost:PORT.
|
|
26
|
-
const RESOURCE_URI = process.env.RESOURCE_URI || `http://localhost:${PORT}
|
|
38
|
+
const RESOURCE_URI = (process.env.RESOURCE_URI || `http://localhost:${PORT}`).replace(/\/+$/, "");
|
|
39
|
+
// MCP endpoint path — advertised as the canonical resource identifier in
|
|
40
|
+
// Protected Resource Metadata (RFC 9728) so audience binding (RFC 8707) works.
|
|
41
|
+
const MCP_PATH = "/mcp";
|
|
42
|
+
const MCP_RESOURCE_IDENTIFIER = `${RESOURCE_URI}${MCP_PATH}`;
|
|
43
|
+
// Path-aware PRM URL per RFC 9728 §3.1.
|
|
44
|
+
const PRM_URL = `${RESOURCE_URI}/.well-known/oauth-protected-resource${MCP_PATH}`;
|
|
27
45
|
const POLL_INTERVAL_MS = parseInt(process.env.TRAVELCODE_POLL_INTERVAL_MS || "2000", 10);
|
|
28
46
|
const POLL_TIMEOUT_MS = parseInt(process.env.TRAVELCODE_POLL_TIMEOUT_MS || "90000", 10);
|
|
29
47
|
const SCOPES_SUPPORTED = [
|
|
@@ -63,14 +81,55 @@ app.options(/.*/, (_req, res) => {
|
|
|
63
81
|
res.sendStatus(204);
|
|
64
82
|
});
|
|
65
83
|
// --- Protected Resource Metadata (RFC 9728) ---
|
|
84
|
+
//
|
|
85
|
+
// `resource` MUST match the URL the client is actually using (the MCP endpoint)
|
|
86
|
+
// so that audience binding / RFC 8707 resource indicators line up.
|
|
87
|
+
//
|
|
88
|
+
// `authorization_servers` points to the sidecar itself rather than the upstream
|
|
89
|
+
// travel-code.com origin, because the upstream blocks /.well-known/* at the
|
|
90
|
+
// edge. The client will discover AS metadata from us at
|
|
91
|
+
// `/.well-known/oauth-authorization-server` (served below), which in turn
|
|
92
|
+
// advertises the real upstream authorize/token/register/revoke endpoints.
|
|
93
|
+
const protectedResourceMetadata = {
|
|
94
|
+
resource: MCP_RESOURCE_IDENTIFIER,
|
|
95
|
+
authorization_servers: [RESOURCE_URI],
|
|
96
|
+
scopes_supported: SCOPES_SUPPORTED,
|
|
97
|
+
bearer_methods_supported: ["header"],
|
|
98
|
+
resource_name: "TravelCode MCP Server",
|
|
99
|
+
};
|
|
100
|
+
app.get("/.well-known/oauth-protected-resource/mcp", (_req, res) => {
|
|
101
|
+
res.json(protectedResourceMetadata);
|
|
102
|
+
});
|
|
103
|
+
// Legacy non-path-suffixed PRM for older clients that don't do
|
|
104
|
+
// path-aware discovery per RFC 9728 §3.1.
|
|
66
105
|
app.get("/.well-known/oauth-protected-resource", (_req, res) => {
|
|
67
|
-
res.json(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
106
|
+
res.json(protectedResourceMetadata);
|
|
107
|
+
});
|
|
108
|
+
// --- Authorization Server Metadata (RFC 8414) — proxied ---
|
|
109
|
+
//
|
|
110
|
+
// travel-code.com's nginx blocks /.well-known/oauth-authorization-server at
|
|
111
|
+
// the edge, so we host the document ourselves. `issuer` MUST match the URL at
|
|
112
|
+
// which this metadata was fetched (RFC 8414 §3.3) — that's RESOURCE_URI.
|
|
113
|
+
// The endpoints point to the real upstream OAuth server; the browser and
|
|
114
|
+
// token-endpoint calls go there directly.
|
|
115
|
+
const authorizationServerMetadata = {
|
|
116
|
+
issuer: RESOURCE_URI,
|
|
117
|
+
authorization_endpoint: `${UPSTREAM_AS_ORIGIN}/oauth/authorize`,
|
|
118
|
+
token_endpoint: `${UPSTREAM_AS_ORIGIN}/oauth/token`,
|
|
119
|
+
registration_endpoint: `${UPSTREAM_AS_ORIGIN}/oauth/register`,
|
|
120
|
+
revocation_endpoint: `${UPSTREAM_AS_ORIGIN}/oauth/revoke`,
|
|
121
|
+
scopes_supported: SCOPES_SUPPORTED,
|
|
122
|
+
response_types_supported: ["code"],
|
|
123
|
+
grant_types_supported: ["authorization_code", "refresh_token"],
|
|
124
|
+
token_endpoint_auth_methods_supported: ["none"],
|
|
125
|
+
code_challenge_methods_supported: ["S256"],
|
|
126
|
+
};
|
|
127
|
+
app.get("/.well-known/oauth-authorization-server", (_req, res) => {
|
|
128
|
+
res.json(authorizationServerMetadata);
|
|
129
|
+
});
|
|
130
|
+
// Some MCP clients also probe a path-suffixed variant.
|
|
131
|
+
app.get("/.well-known/oauth-authorization-server/mcp", (_req, res) => {
|
|
132
|
+
res.json(authorizationServerMetadata);
|
|
74
133
|
});
|
|
75
134
|
// --- Health check ---
|
|
76
135
|
app.get("/health", (_req, res) => {
|
|
@@ -81,11 +140,10 @@ app.get("/health", (_req, res) => {
|
|
|
81
140
|
});
|
|
82
141
|
});
|
|
83
142
|
// --- Helper: send 401 ---
|
|
84
|
-
function send401(res) {
|
|
85
|
-
const metadataUrl = `${RESOURCE_URI}/.well-known/oauth-protected-resource`;
|
|
143
|
+
function send401(res, description = "Bearer token required") {
|
|
86
144
|
res.status(401)
|
|
87
|
-
.set("WWW-Authenticate", `Bearer resource_metadata="${
|
|
88
|
-
.json({ error: "unauthorized", error_description:
|
|
145
|
+
.set("WWW-Authenticate", `Bearer resource_metadata="${PRM_URL}"`)
|
|
146
|
+
.json({ error: "unauthorized", error_description: description });
|
|
89
147
|
}
|
|
90
148
|
// --- MCP endpoint ---
|
|
91
149
|
app.all("/mcp", async (req, res) => {
|
|
@@ -105,10 +163,10 @@ app.all("/mcp", async (req, res) => {
|
|
|
105
163
|
if (sessionId) {
|
|
106
164
|
const session = sessions.get(sessionId);
|
|
107
165
|
if (!session) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
166
|
+
// Return 401 (not 404) so the client restarts the OAuth flow after a
|
|
167
|
+
// sidecar restart or TTL expiry, instead of giving up with a dead
|
|
168
|
+
// session ID.
|
|
169
|
+
send401(res, "Session not found or expired. Please re-authenticate.");
|
|
112
170
|
return;
|
|
113
171
|
}
|
|
114
172
|
await session.transport.handleRequest(req, res, req.body);
|
|
@@ -155,10 +213,11 @@ app.all("/mcp", async (req, res) => {
|
|
|
155
213
|
// --- Start ---
|
|
156
214
|
app.listen(PORT, () => {
|
|
157
215
|
console.log(`TravelCode MCP Server (HTTP) listening on port ${PORT}`);
|
|
158
|
-
console.log(`MCP endpoint:
|
|
159
|
-
console.log(`Resource
|
|
160
|
-
console.log(`
|
|
161
|
-
console.log(`
|
|
162
|
-
console.log(`
|
|
216
|
+
console.log(`MCP endpoint: ${MCP_RESOURCE_IDENTIFIER}`);
|
|
217
|
+
console.log(`Protected Resource: ${PRM_URL}`);
|
|
218
|
+
console.log(`AS metadata (proxy): ${RESOURCE_URI}/.well-known/oauth-authorization-server`);
|
|
219
|
+
console.log(`Upstream OAuth: ${UPSTREAM_AS_ORIGIN}/oauth/{authorize,token,register,revoke}`);
|
|
220
|
+
console.log(`API base URL: ${API_BASE_URL}`);
|
|
221
|
+
console.log(`Scopes: ${SCOPES_SUPPORTED.join(", ")}`);
|
|
163
222
|
});
|
|
164
223
|
//# sourceMappingURL=http-server.js.map
|
package/package.json
CHANGED