@meertrack/mcp 1.0.2 → 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 +27 -7
- package/dist/auth.d.ts +74 -15
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +139 -15
- package/dist/auth.js.map +1 -1
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -1
- package/dist/transports/http.d.ts +8 -1
- package/dist/transports/http.d.ts.map +1 -1
- package/dist/transports/http.js +31 -4
- package/dist/transports/http.js.map +1 -1
- package/dist/types.d.ts +179 -179
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +45 -36
- package/dist/types.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ read-only tools** and **3 prompt workflows**. No backend changes, same
|
|
|
22
22
|
| **Setup time** | 30 seconds (paste a JSON block) | 10 seconds (paste a URL) |
|
|
23
23
|
| **Best for** | Individual Pro customers; all Claude Desktop plans; any IDE on your laptop | Team/Enterprise custom connectors; Claude.ai web; remote-capable IDEs |
|
|
24
24
|
| **Runs where** | Your machine (`npx -y @meertrack/mcp`) | Meertrack's Fly.io fleet (`https://mcp.meertrack.com/mcp`) |
|
|
25
|
-
| **Auth** | `MEERTRACK_API_KEY` env var | `Authorization: Bearer mt_live_…` header |
|
|
25
|
+
| **Auth** | `MEERTRACK_API_KEY` env var | OAuth 2.1 (browser flow, recommended) or `Authorization: Bearer mt_live_…` header |
|
|
26
26
|
| **Plan gating** | Works on Claude Pro, Team, Enterprise | Claude Desktop "Add custom connector" is **Team/Enterprise only** |
|
|
27
27
|
|
|
28
28
|
**If you're on Claude Pro, use the local (stdio) path.** The "Add custom
|
|
@@ -172,14 +172,32 @@ All remote clients point at the same URL:
|
|
|
172
172
|
https://mcp.meertrack.com/mcp
|
|
173
173
|
```
|
|
174
174
|
|
|
175
|
-
|
|
176
|
-
|
|
175
|
+
Two auth paths are supported:
|
|
176
|
+
|
|
177
|
+
- **OAuth 2.1 (recommended)** — spec-conformant MCP clients discover the
|
|
178
|
+
authorization server at `/.well-known/oauth-protected-resource`, perform
|
|
179
|
+
Dynamic Client Registration at `https://meertrack.com/oauth/register`, and
|
|
180
|
+
drive the full PKCE-gated authorize → token flow. The user clicks
|
|
181
|
+
"Connect", signs in at `meertrack.com`, hits Allow on the consent screen,
|
|
182
|
+
and is done. No key handling. Access tokens are 10-minute JWTs
|
|
183
|
+
(RS256, `aud=https://mcp.meertrack.com/mcp`); refresh tokens are rotated
|
|
184
|
+
per OAuth 2.1 §4.3.1.
|
|
185
|
+
- **`Authorization: Bearer mt_live_…`** — paste a static API key for custom
|
|
186
|
+
connectors, CLI scripts, and any client that doesn't implement OAuth
|
|
187
|
+
discovery yet.
|
|
188
|
+
|
|
189
|
+
Both paths terminate at the same workspace; pick whichever your client
|
|
190
|
+
supports.
|
|
177
191
|
|
|
178
192
|
### Claude Desktop (Team / Enterprise only: "Add custom connector")
|
|
179
193
|
|
|
180
|
-
Settings → Connectors → **Add custom connector** → paste the URL above
|
|
181
|
-
|
|
182
|
-
|
|
194
|
+
Settings → Connectors → **Add custom connector** → paste the URL above.
|
|
195
|
+
**Do not paste a bearer token** — leave the token field empty and click Add.
|
|
196
|
+
Claude Desktop will open a browser window to `meertrack.com` for login and
|
|
197
|
+
consent; on Allow, the connector surfaces the 8 tools automatically.
|
|
198
|
+
|
|
199
|
+
The "Add custom connector" button is not visible on Pro; use the stdio path
|
|
200
|
+
above instead.
|
|
183
201
|
|
|
184
202
|
### Claude.ai web (Connectors)
|
|
185
203
|
|
|
@@ -296,10 +314,12 @@ MCP tool schemas are part of the public API contract; agents cache them. So:
|
|
|
296
314
|
- **PATCH**: bug fixes, description improvements, internal refactors with no
|
|
297
315
|
schema impact.
|
|
298
316
|
|
|
299
|
-
See [CHANGELOG.md](CHANGELOG.md) for the release history
|
|
317
|
+
See [CHANGELOG.md](CHANGELOG.md) for the release history, and
|
|
318
|
+
[docs/RELEASING.md](docs/RELEASING.md) for the maintainer publish procedure.
|
|
300
319
|
|
|
301
320
|
## Security & privacy
|
|
302
321
|
|
|
322
|
+
- **Privacy policy**: [https://meertrack.com/privacy](https://meertrack.com/privacy)
|
|
303
323
|
- [SECURITY.md](SECURITY.md): disclosure policy (`security@meertrack.com`),
|
|
304
324
|
in-scope surface.
|
|
305
325
|
- [docs/PRIVACY.md](docs/PRIVACY.md): the MCP layer is stateless; bearers are
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Bearer resolution for both transports.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* - `http` : key comes from the `Authorization` header per request, with a
|
|
6
|
-
* `?api_key=` query-string fallback for clients that can't set
|
|
7
|
-
* custom headers (some Claude Desktop builds, claude.ai web).
|
|
4
|
+
* Two auth shapes are accepted on the HTTP transport:
|
|
8
5
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
6
|
+
* 1. `mt_live_…` API keys — direct, long-lived bearers minted by the
|
|
7
|
+
* Meertrack app. Used by stdio (from `MEERTRACK_API_KEY` at startup),
|
|
8
|
+
* custom-connector users who paste a key, and direct API consumers.
|
|
9
|
+
*
|
|
10
|
+
* 2. OAuth 2.1 access tokens (JWTs) — minted by the Meertrack authorization
|
|
11
|
+
* server at `https://meertrack.com/oauth/token`. Used by Claude's
|
|
12
|
+
* Connectors Directory and any other MCP client that does OAuth
|
|
13
|
+
* discovery. Validated locally against the AS's JWKS (cached).
|
|
14
|
+
*
|
|
15
|
+
* Discrimination is by prefix: anything starting with `mt_live_` goes through
|
|
16
|
+
* the API-key path (regex-validated, forwarded verbatim to upstream). Anything
|
|
17
|
+
* else is treated as a JWT (signature + `iss` + `aud` + `exp` verified, then
|
|
18
|
+
* forwarded verbatim to upstream, which MUST also accept JWTs).
|
|
19
|
+
*
|
|
20
|
+
* In both modes, the bearer is forwarded verbatim — the upstream API is the
|
|
21
|
+
* single source of truth for authorization decisions. Local JWT verification
|
|
22
|
+
* on the MCP server is required by MCP spec §Authorization so we can emit a
|
|
23
|
+
* spec-conformant 401 with `WWW-Authenticate: resource_metadata=…` without a
|
|
24
|
+
* round trip.
|
|
12
25
|
*/
|
|
13
26
|
export declare const API_KEY_PREFIX = "mt_live_";
|
|
14
27
|
/** True iff `value` begins with `mt_live_`. Does not assert length/charset. */
|
|
@@ -29,13 +42,17 @@ export declare class InvalidApiKeyError extends Error {
|
|
|
29
42
|
export declare function resolveEnvApiKey(env?: NodeJS.ProcessEnv): string;
|
|
30
43
|
/**
|
|
31
44
|
* HTTP mode: resolution outcome per request. Successful cases carry the
|
|
32
|
-
* forwardable bearer
|
|
33
|
-
* a spec-conformant 401 (WWW-Authenticate header
|
|
45
|
+
* forwardable bearer and which auth type was used. Failures carry everything
|
|
46
|
+
* the transport needs to emit a spec-conformant 401 (WWW-Authenticate header
|
|
47
|
+
* value included).
|
|
34
48
|
*/
|
|
35
49
|
export type HttpAuthResolution = {
|
|
36
50
|
ok: true;
|
|
37
|
-
|
|
51
|
+
bearer: string;
|
|
52
|
+
authType: "api_key" | "oauth";
|
|
38
53
|
source: "header" | "query";
|
|
54
|
+
/** For OAuth: verified JWT claims. Undefined for api_key path. */
|
|
55
|
+
claims?: JwtClaims;
|
|
39
56
|
} | {
|
|
40
57
|
ok: false;
|
|
41
58
|
status: 401;
|
|
@@ -43,6 +60,24 @@ export type HttpAuthResolution = {
|
|
|
43
60
|
message: string;
|
|
44
61
|
wwwAuthenticate: string;
|
|
45
62
|
};
|
|
63
|
+
/** Subset of JWT claims we care about after OAuth verification. */
|
|
64
|
+
export interface JwtClaims {
|
|
65
|
+
sub: string;
|
|
66
|
+
company_id: string;
|
|
67
|
+
scope?: string;
|
|
68
|
+
iss: string;
|
|
69
|
+
aud: string;
|
|
70
|
+
exp: number;
|
|
71
|
+
iat: number;
|
|
72
|
+
}
|
|
73
|
+
export interface OAuthConfig {
|
|
74
|
+
/** Expected `iss` claim. Must match exactly. */
|
|
75
|
+
issuer: string;
|
|
76
|
+
/** Expected `aud` claim. RFC 8707 audience binding — this is the canonical MCP URI. */
|
|
77
|
+
audience: string;
|
|
78
|
+
/** JWKS URL on the authorization server. Keys are fetched + cached by `jose`. */
|
|
79
|
+
jwksUrl: string;
|
|
80
|
+
}
|
|
46
81
|
export interface HttpAuthContext {
|
|
47
82
|
/** Case-insensitive header lookup — works for `Headers`, plain objects, and Hono's helpers. */
|
|
48
83
|
header: (name: string) => string | null | undefined;
|
|
@@ -50,6 +85,11 @@ export interface HttpAuthContext {
|
|
|
50
85
|
searchParams: URLSearchParams;
|
|
51
86
|
/** Public URL of the `/.well-known/oauth-protected-resource` document. */
|
|
52
87
|
protectedResourceMetadataUrl: string;
|
|
88
|
+
/**
|
|
89
|
+
* OAuth configuration. When undefined, only `mt_live_…` keys are accepted
|
|
90
|
+
* (pre-OAuth deployments and tests that don't care about JWT paths).
|
|
91
|
+
*/
|
|
92
|
+
oauth?: OAuthConfig;
|
|
53
93
|
}
|
|
54
94
|
/**
|
|
55
95
|
* Extract a bearer from an incoming HTTP request. Header wins over query when
|
|
@@ -57,19 +97,38 @@ export interface HttpAuthContext {
|
|
|
57
97
|
* carrying a fully-formed `WWW-Authenticate` header value — the transport
|
|
58
98
|
* emits the 401 without touching the upstream API.
|
|
59
99
|
*/
|
|
60
|
-
export declare function extractHttpBearer(ctx: HttpAuthContext): HttpAuthResolution
|
|
100
|
+
export declare function extractHttpBearer(ctx: HttpAuthContext): Promise<HttpAuthResolution>;
|
|
61
101
|
/** Extract a bearer token from an `Authorization` header value, or `null` if absent/malformed. */
|
|
62
102
|
export declare function parseBearerHeader(value: string): string | null;
|
|
63
103
|
/**
|
|
64
|
-
* MCP
|
|
65
|
-
* MUST advertise where the client can find Protected Resource Metadata.
|
|
66
|
-
* use this to discover
|
|
67
|
-
*
|
|
104
|
+
* MCP spec §Authorization / RFC 9728: 401 responses on the HTTP transport
|
|
105
|
+
* MUST advertise where the client can find Protected Resource Metadata.
|
|
106
|
+
* Clients use this to discover the authorization server(s) and initiate the
|
|
107
|
+
* OAuth 2.1 flow.
|
|
68
108
|
*/
|
|
69
109
|
export declare function buildWwwAuthenticateHeader(protectedResourceMetadataUrl: string): string;
|
|
70
110
|
/**
|
|
71
111
|
* Redact every `mt_live_…` token in `value`. Apply to any string before
|
|
72
112
|
* writing it to logs or error messages. Also redacts `Bearer mt_live_…`.
|
|
113
|
+
* JWTs are not redacted here — they're not secrets in the same way (signed,
|
|
114
|
+
* short-lived, audience-bound) — but avoid logging them anyway.
|
|
73
115
|
*/
|
|
74
116
|
export declare function redactApiKeys(value: string): string;
|
|
117
|
+
/** Test-only: clear the JWKS cache between tests. */
|
|
118
|
+
export declare function __resetJwksCache(): void;
|
|
119
|
+
type OAuthVerification = {
|
|
120
|
+
ok: true;
|
|
121
|
+
claims: JwtClaims;
|
|
122
|
+
} | {
|
|
123
|
+
ok: false;
|
|
124
|
+
message: string;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Verify an OAuth access token locally. Checks signature (via JWKS), `iss`,
|
|
128
|
+
* `aud` (exact string match, RFC 8707 audience binding — this is what
|
|
129
|
+
* prevents token passthrough between resources), and `exp`. Returns the
|
|
130
|
+
* subset of claims the transport cares about, or a user-safe error message.
|
|
131
|
+
*/
|
|
132
|
+
export declare function verifyOAuthToken(token: string, config: OAuthConfig): Promise<OAuthVerification>;
|
|
133
|
+
export {};
|
|
75
134
|
//# sourceMappingURL=auth.d.ts.map
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AASH,eAAO,MAAM,cAAc,aAAa,CAAC;AAKzC,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED,8DAA8D;AAC9D,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAcR;AAED;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IACE,EAAE,EAAE,IAAI,CAAC;IACT,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,SAAS,GAAG,OAAO,CAAC;IAC9B,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAC;IAC3B,kEAAkE;IAClE,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB,GACD;IACE,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,GAAG,CAAC;IACZ,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEN,mEAAmE;AACnE,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,WAAW;IAC1B,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,uFAAuF;IACvF,QAAQ,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,+FAA+F;IAC/F,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACpD,gDAAgD;IAChD,YAAY,EAAE,eAAe,CAAC;IAC9B,0EAA0E;IAC1E,4BAA4B,EAAE,MAAM,CAAC;IACrC;;;OAGG;IACH,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,eAAe,GACnB,OAAO,CAAC,kBAAkB,CAAC,CAyD7B;AAED,kGAAkG;AAClG,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK9D;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,4BAA4B,EAAE,MAAM,GAAG,MAAM,CAEvF;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEnD;AAmBD,qDAAqD;AACrD,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED,KAAK,iBAAiB,GAClB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,GAC/B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnC;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,iBAAiB,CAAC,CAkB5B"}
|
package/dist/auth.js
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Bearer resolution for both transports.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* - `http` : key comes from the `Authorization` header per request, with a
|
|
6
|
-
* `?api_key=` query-string fallback for clients that can't set
|
|
7
|
-
* custom headers (some Claude Desktop builds, claude.ai web).
|
|
4
|
+
* Two auth shapes are accepted on the HTTP transport:
|
|
8
5
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
6
|
+
* 1. `mt_live_…` API keys — direct, long-lived bearers minted by the
|
|
7
|
+
* Meertrack app. Used by stdio (from `MEERTRACK_API_KEY` at startup),
|
|
8
|
+
* custom-connector users who paste a key, and direct API consumers.
|
|
9
|
+
*
|
|
10
|
+
* 2. OAuth 2.1 access tokens (JWTs) — minted by the Meertrack authorization
|
|
11
|
+
* server at `https://meertrack.com/oauth/token`. Used by Claude's
|
|
12
|
+
* Connectors Directory and any other MCP client that does OAuth
|
|
13
|
+
* discovery. Validated locally against the AS's JWKS (cached).
|
|
14
|
+
*
|
|
15
|
+
* Discrimination is by prefix: anything starting with `mt_live_` goes through
|
|
16
|
+
* the API-key path (regex-validated, forwarded verbatim to upstream). Anything
|
|
17
|
+
* else is treated as a JWT (signature + `iss` + `aud` + `exp` verified, then
|
|
18
|
+
* forwarded verbatim to upstream, which MUST also accept JWTs).
|
|
19
|
+
*
|
|
20
|
+
* In both modes, the bearer is forwarded verbatim — the upstream API is the
|
|
21
|
+
* single source of truth for authorization decisions. Local JWT verification
|
|
22
|
+
* on the MCP server is required by MCP spec §Authorization so we can emit a
|
|
23
|
+
* spec-conformant 401 with `WWW-Authenticate: resource_metadata=…` without a
|
|
24
|
+
* round trip.
|
|
12
25
|
*/
|
|
26
|
+
import { createRemoteJWKSet, errors as joseErrors, jwtVerify, } from "jose";
|
|
13
27
|
export const API_KEY_PREFIX = "mt_live_";
|
|
14
28
|
/** Full pattern: `mt_live_` followed by base64url characters. */
|
|
15
29
|
const API_KEY_PATTERN = /^mt_live_[A-Za-z0-9_-]+$/;
|
|
@@ -55,7 +69,7 @@ export function resolveEnvApiKey(env = process.env) {
|
|
|
55
69
|
* carrying a fully-formed `WWW-Authenticate` header value — the transport
|
|
56
70
|
* emits the 401 without touching the upstream API.
|
|
57
71
|
*/
|
|
58
|
-
export function extractHttpBearer(ctx) {
|
|
72
|
+
export async function extractHttpBearer(ctx) {
|
|
59
73
|
const headerValue = ctx.header("authorization") ?? ctx.header("Authorization");
|
|
60
74
|
const fromHeader = headerValue ? parseBearerHeader(headerValue) : null;
|
|
61
75
|
const fromQuery = ctx.searchParams.get("api_key");
|
|
@@ -67,11 +81,18 @@ export function extractHttpBearer(ctx) {
|
|
|
67
81
|
ok: false,
|
|
68
82
|
status: 401,
|
|
69
83
|
code: "unauthorized",
|
|
70
|
-
message: "Missing
|
|
84
|
+
message: "Missing credentials. Send `Authorization: Bearer <mt_live_… or OAuth access token>` (preferred) or `?api_key=mt_live_…` as a query-string fallback.",
|
|
71
85
|
wwwAuthenticate,
|
|
72
86
|
};
|
|
73
87
|
}
|
|
74
|
-
|
|
88
|
+
// Path A: legacy `mt_live_…` API key. Regex-validate prefix + forward verbatim.
|
|
89
|
+
if (hasApiKeyPrefix(candidate)) {
|
|
90
|
+
return { ok: true, bearer: candidate, authType: "api_key", source };
|
|
91
|
+
}
|
|
92
|
+
// Path B: OAuth JWT. Requires OAuth config on the transport; if unset we
|
|
93
|
+
// treat unknown-prefix bearers as invalid so pre-OAuth deployments don't
|
|
94
|
+
// silently accept garbage.
|
|
95
|
+
if (!ctx.oauth) {
|
|
75
96
|
return {
|
|
76
97
|
ok: false,
|
|
77
98
|
status: 401,
|
|
@@ -80,7 +101,23 @@ export function extractHttpBearer(ctx) {
|
|
|
80
101
|
wwwAuthenticate,
|
|
81
102
|
};
|
|
82
103
|
}
|
|
83
|
-
|
|
104
|
+
const verification = await verifyOAuthToken(candidate, ctx.oauth);
|
|
105
|
+
if (!verification.ok) {
|
|
106
|
+
return {
|
|
107
|
+
ok: false,
|
|
108
|
+
status: 401,
|
|
109
|
+
code: "unauthorized",
|
|
110
|
+
message: verification.message,
|
|
111
|
+
wwwAuthenticate,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
ok: true,
|
|
116
|
+
bearer: candidate,
|
|
117
|
+
authType: "oauth",
|
|
118
|
+
source,
|
|
119
|
+
claims: verification.claims,
|
|
120
|
+
};
|
|
84
121
|
}
|
|
85
122
|
/** Extract a bearer token from an `Authorization` header value, or `null` if absent/malformed. */
|
|
86
123
|
export function parseBearerHeader(value) {
|
|
@@ -91,10 +128,10 @@ export function parseBearerHeader(value) {
|
|
|
91
128
|
return token.length > 0 ? token : null;
|
|
92
129
|
}
|
|
93
130
|
/**
|
|
94
|
-
* MCP
|
|
95
|
-
* MUST advertise where the client can find Protected Resource Metadata.
|
|
96
|
-
* use this to discover
|
|
97
|
-
*
|
|
131
|
+
* MCP spec §Authorization / RFC 9728: 401 responses on the HTTP transport
|
|
132
|
+
* MUST advertise where the client can find Protected Resource Metadata.
|
|
133
|
+
* Clients use this to discover the authorization server(s) and initiate the
|
|
134
|
+
* OAuth 2.1 flow.
|
|
98
135
|
*/
|
|
99
136
|
export function buildWwwAuthenticateHeader(protectedResourceMetadataUrl) {
|
|
100
137
|
return `Bearer realm="meertrack", resource_metadata="${protectedResourceMetadataUrl}"`;
|
|
@@ -102,8 +139,95 @@ export function buildWwwAuthenticateHeader(protectedResourceMetadataUrl) {
|
|
|
102
139
|
/**
|
|
103
140
|
* Redact every `mt_live_…` token in `value`. Apply to any string before
|
|
104
141
|
* writing it to logs or error messages. Also redacts `Bearer mt_live_…`.
|
|
142
|
+
* JWTs are not redacted here — they're not secrets in the same way (signed,
|
|
143
|
+
* short-lived, audience-bound) — but avoid logging them anyway.
|
|
105
144
|
*/
|
|
106
145
|
export function redactApiKeys(value) {
|
|
107
146
|
return value.replace(/mt_live_[A-Za-z0-9_-]+/g, "mt_live_***");
|
|
108
147
|
}
|
|
148
|
+
// ─── OAuth JWT verification ──────────────────────────────────────────────────
|
|
149
|
+
/** Cached `jose` remote JWKS resolver, keyed by JWKS URL. */
|
|
150
|
+
const jwksCache = new Map();
|
|
151
|
+
function getJwks(jwksUrl) {
|
|
152
|
+
let jwks = jwksCache.get(jwksUrl);
|
|
153
|
+
if (!jwks) {
|
|
154
|
+
jwks = createRemoteJWKSet(new URL(jwksUrl), {
|
|
155
|
+
// `jose` handles its own cache internally with sensible defaults
|
|
156
|
+
// (cooldown on miss, 10 min cache). Don't re-wrap.
|
|
157
|
+
});
|
|
158
|
+
jwksCache.set(jwksUrl, jwks);
|
|
159
|
+
}
|
|
160
|
+
return jwks;
|
|
161
|
+
}
|
|
162
|
+
/** Test-only: clear the JWKS cache between tests. */
|
|
163
|
+
export function __resetJwksCache() {
|
|
164
|
+
jwksCache.clear();
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Verify an OAuth access token locally. Checks signature (via JWKS), `iss`,
|
|
168
|
+
* `aud` (exact string match, RFC 8707 audience binding — this is what
|
|
169
|
+
* prevents token passthrough between resources), and `exp`. Returns the
|
|
170
|
+
* subset of claims the transport cares about, or a user-safe error message.
|
|
171
|
+
*/
|
|
172
|
+
export async function verifyOAuthToken(token, config) {
|
|
173
|
+
const jwks = getJwks(config.jwksUrl);
|
|
174
|
+
try {
|
|
175
|
+
const { payload } = await jwtVerify(token, jwks, {
|
|
176
|
+
issuer: config.issuer,
|
|
177
|
+
audience: config.audience,
|
|
178
|
+
});
|
|
179
|
+
const claims = extractClaims(payload);
|
|
180
|
+
if (!claims) {
|
|
181
|
+
return {
|
|
182
|
+
ok: false,
|
|
183
|
+
message: "Access token is missing required claims (sub, company_id).",
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
return { ok: true, claims };
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
return { ok: false, message: classifyJwtError(err) };
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function extractClaims(payload) {
|
|
193
|
+
const sub = typeof payload.sub === "string" ? payload.sub : null;
|
|
194
|
+
const companyId = typeof payload["company_id"] === "string" ? payload["company_id"] : null;
|
|
195
|
+
const iss = typeof payload.iss === "string" ? payload.iss : null;
|
|
196
|
+
// `aud` can be a string or an array; jose's audience check has already
|
|
197
|
+
// validated it, so we normalize to the first match.
|
|
198
|
+
const aud = Array.isArray(payload.aud) ? payload.aud[0] ?? null : payload.aud ?? null;
|
|
199
|
+
const exp = typeof payload.exp === "number" ? payload.exp : null;
|
|
200
|
+
const iat = typeof payload.iat === "number" ? payload.iat : null;
|
|
201
|
+
if (!sub || !companyId || !iss || !aud || exp === null || iat === null)
|
|
202
|
+
return null;
|
|
203
|
+
const scope = typeof payload["scope"] === "string" ? payload["scope"] : undefined;
|
|
204
|
+
return {
|
|
205
|
+
sub,
|
|
206
|
+
company_id: companyId,
|
|
207
|
+
iss,
|
|
208
|
+
aud,
|
|
209
|
+
exp,
|
|
210
|
+
iat,
|
|
211
|
+
...(scope !== undefined ? { scope } : {}),
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Map `jose` errors to short, client-safe messages. Never leak signature
|
|
216
|
+
* details — "invalid token" is enough; anything more helps attackers.
|
|
217
|
+
*/
|
|
218
|
+
function classifyJwtError(err) {
|
|
219
|
+
if (err instanceof joseErrors.JWTExpired) {
|
|
220
|
+
return "Access token has expired. Refresh it at the authorization server.";
|
|
221
|
+
}
|
|
222
|
+
if (err instanceof joseErrors.JWTClaimValidationFailed) {
|
|
223
|
+
return "Access token claims failed validation (wrong issuer or audience).";
|
|
224
|
+
}
|
|
225
|
+
if (err instanceof joseErrors.JWSSignatureVerificationFailed) {
|
|
226
|
+
return "Access token signature is invalid.";
|
|
227
|
+
}
|
|
228
|
+
if (err instanceof joseErrors.JOSEError) {
|
|
229
|
+
return "Access token is malformed or could not be verified.";
|
|
230
|
+
}
|
|
231
|
+
return "Access token verification failed.";
|
|
232
|
+
}
|
|
109
233
|
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -53,10 +53,25 @@ async function startHttp() {
|
|
|
53
53
|
const prmUrl = process.env["MEERTRACK_MCP_PRM_URL"] ??
|
|
54
54
|
defaultProtectedResourceMetadataUrl(process.env["MEERTRACK_MCP_PUBLIC_HOST"] ?? `${hostname}:${port}`, onFly ? "https" : "http");
|
|
55
55
|
const baseUrl = process.env["MEERTRACK_API_BASE_URL"];
|
|
56
|
+
// OAuth 2.1 is opt-in via env. Issuer + audience + JWKS URL must all be set
|
|
57
|
+
// together; partial config is rejected loudly so a misconfigured deploy
|
|
58
|
+
// doesn't silently accept or reject tokens the wrong way.
|
|
59
|
+
const oauthIssuer = process.env["MEERTRACK_OAUTH_ISSUER"];
|
|
60
|
+
const oauthAudience = process.env["MEERTRACK_OAUTH_AUDIENCE"];
|
|
61
|
+
const oauthJwksUrl = process.env["MEERTRACK_OAUTH_JWKS_URL"];
|
|
62
|
+
const oauthAny = oauthIssuer ?? oauthAudience ?? oauthJwksUrl;
|
|
63
|
+
const oauthAll = oauthIssuer && oauthAudience && oauthJwksUrl;
|
|
64
|
+
if (oauthAny && !oauthAll) {
|
|
65
|
+
throw new Error("OAuth env is partially configured. Set all of MEERTRACK_OAUTH_ISSUER, MEERTRACK_OAUTH_AUDIENCE, MEERTRACK_OAUTH_JWKS_URL — or none.");
|
|
66
|
+
}
|
|
67
|
+
const oauth = oauthAll
|
|
68
|
+
? { issuer: oauthIssuer, audience: oauthAudience, jwksUrl: oauthJwksUrl }
|
|
69
|
+
: undefined;
|
|
56
70
|
const app = createHttpApp({
|
|
57
71
|
allowedOrigins,
|
|
58
72
|
protectedResourceMetadataUrl: prmUrl,
|
|
59
73
|
...(baseUrl !== undefined ? { baseUrl } : {}),
|
|
74
|
+
...(oauth !== undefined ? { oauth } : {}),
|
|
60
75
|
});
|
|
61
76
|
serve({ fetch: app.fetch, port, hostname }, (info) => {
|
|
62
77
|
process.stderr.write(`[meertrack-mcp] http listening on http://${info.address}:${info.port} (PRM: ${prmUrl}, origins: ${allowedOrigins.length})\n`);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EACL,aAAa,EACb,QAAQ,EACR,mCAAmC,GACpC,MAAM,sBAAsB,CAAC;AAE9B,MAAM,uBAAuB,GAAG;IAC9B,mBAAmB;IACnB,oBAAoB;IACpB,mBAAmB;CACpB,CAAC;AAEF,KAAK,UAAU,IAAI;IACjB,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,SAAS,CAAC;IAErF,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,SAAS,EAAE,CAAC;IACpB,CAAC;SAAM,CAAC;QACN,MAAM,UAAU,EAAE,CAAC;IACrB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,UAAU;IACvB,yEAAyE;IACzE,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,gBAAgB,EAAE,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,OAAO,IAAI,CAAC,CAAC;QACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,MAAM,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED,KAAK,UAAU,SAAS;IACtB,sEAAsE;IACtE,qCAAqC;IACrC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAEpD,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,CAAC;IACnD,MAAM,cAAc,GAAG,mBAAmB,CACxC,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,IAAI,uBAAuB,CAAC,IAAI,CAAC,GAAG,CAAC,CAClF,CAAC;IAEF,yEAAyE;IACzE,wEAAwE;IACxE,iEAAiE;IACjE,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC;IAEjD,MAAM,MAAM,GACV,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC;QACpC,mCAAmC,CACjC,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,IAAI,GAAG,QAAQ,IAAI,IAAI,EAAE,EACjE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CACzB,CAAC;IAEJ,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import { buildWwwAuthenticateHeader } from "../auth.js";
|
|
2
|
+
import { buildWwwAuthenticateHeader, type OAuthConfig } from "../auth.js";
|
|
3
3
|
import { type Logger } from "../logger.js";
|
|
4
4
|
/**
|
|
5
5
|
* Streamable HTTP transport (MCP spec 2025-11-25 §transports).
|
|
@@ -38,6 +38,13 @@ export interface CreateHttpAppOptions {
|
|
|
38
38
|
* Used verbatim — include scheme + host + path.
|
|
39
39
|
*/
|
|
40
40
|
protectedResourceMetadataUrl: string;
|
|
41
|
+
/**
|
|
42
|
+
* OAuth 2.1 configuration. When set:
|
|
43
|
+
* - non-`mt_live_` bearers are verified as JWTs against `jwksUrl`
|
|
44
|
+
* - PRM advertises `issuer` in `authorization_servers`
|
|
45
|
+
* When unset, only `mt_live_…` keys are accepted (pre-OAuth deployments).
|
|
46
|
+
*/
|
|
47
|
+
oauth?: OAuthConfig;
|
|
41
48
|
/** Optional fetch override for tests. Threaded into `buildServer`. */
|
|
42
49
|
fetchImpl?: typeof fetch;
|
|
43
50
|
/** Optional logger override (tests use a sink that captures lines). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/transports/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,EACL,0BAA0B,
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/transports/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,EACL,0BAA0B,EAG1B,KAAK,WAAW,EACjB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAA2B,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAEpE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,WAAW,oBAAoB;IACnC,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB;;;OAGG;IACH,4BAA4B,EAAE,MAAM,CAAC;IACrC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,sEAAsE;IACtE,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,WAAW,YAAY,CAAC;AACrC,eAAO,MAAM,QAAQ,SAAS,CAAC;AAC/B,eAAO,MAAM,QAAQ,0CAA0C,CAAC;AAEhE,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,8EAwD1D;AAiKD;;;;;GAKG;AACH,wBAAgB,mCAAmC,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAM,GAAG,OAAiB,GAAG,MAAM,CAE9G;AAcD,OAAO,EAAE,0BAA0B,EAAE,CAAC"}
|
package/dist/transports/http.js
CHANGED
|
@@ -11,9 +11,15 @@ export function createHttpApp(options) {
|
|
|
11
11
|
// Origin allowlist — runs before routing so it protects every path. The SDK
|
|
12
12
|
// also has deprecated built-in origin validation; we enforce it at the
|
|
13
13
|
// transport edge per the spec's current guidance.
|
|
14
|
+
//
|
|
15
|
+
// Loopback (`http://localhost:*`, `http://127.0.0.1:*`) is always allowed:
|
|
16
|
+
// this endpoint authenticates via Bearer tokens (not cookies), so DNS
|
|
17
|
+
// rebinding / cross-site cookie theft isn't the threat model, and MCP
|
|
18
|
+
// dev tools like the Inspector connect from a loopback origin on a
|
|
19
|
+
// user-chosen port.
|
|
14
20
|
app.use("*", async (c, next) => {
|
|
15
21
|
const origin = c.req.header("origin");
|
|
16
|
-
if (origin && !options.allowedOrigins.includes(origin)) {
|
|
22
|
+
if (origin && !isLoopbackOrigin(origin) && !options.allowedOrigins.includes(origin)) {
|
|
17
23
|
return c.json({ error: { code: "forbidden_origin", message: `Origin not allowed: ${origin}` } }, 403);
|
|
18
24
|
}
|
|
19
25
|
await next();
|
|
@@ -21,9 +27,19 @@ export function createHttpApp(options) {
|
|
|
21
27
|
app.get(HEALTH_PATH, (c) => c.json({ ok: true }));
|
|
22
28
|
app.get(PRM_PATH, (c) => c.json({
|
|
23
29
|
resource: prmResourceFor(options.protectedResourceMetadataUrl),
|
|
24
|
-
authorization_servers: [],
|
|
30
|
+
authorization_servers: options.oauth ? [options.oauth.issuer] : [],
|
|
25
31
|
bearer_methods_supported: ["header"],
|
|
26
32
|
}));
|
|
33
|
+
// Some MCP clients (older Inspector builds) fall back to fetching AS metadata
|
|
34
|
+
// from the RS's own `.well-known/oauth-authorization-server` instead of
|
|
35
|
+
// following the PRM's `authorization_servers` pointer. If we 404 here, the
|
|
36
|
+
// client then guesses token/authorize URLs on the RS host. Redirect to the
|
|
37
|
+
// real AS metadata URL when OAuth is configured.
|
|
38
|
+
app.get("/.well-known/oauth-authorization-server", (c) => {
|
|
39
|
+
if (!options.oauth)
|
|
40
|
+
return c.text("Not Found", 404);
|
|
41
|
+
return c.redirect(`${options.oauth.issuer.replace(/\/$/, "")}/.well-known/oauth-authorization-server`, 302);
|
|
42
|
+
});
|
|
27
43
|
const log = options.logger ?? defaultLogger;
|
|
28
44
|
app.post(MCP_PATH, (c) => handleMcpPost(c, options, log));
|
|
29
45
|
// Stateless — no server-initiated notifications, no session termination.
|
|
@@ -81,15 +97,16 @@ async function handleMcpPost(c, options, log) {
|
|
|
81
97
|
header: (name) => c.req.header(name) ?? null,
|
|
82
98
|
searchParams: new URL(c.req.url).searchParams,
|
|
83
99
|
protectedResourceMetadataUrl: prmUrl,
|
|
100
|
+
...(options.oauth !== undefined ? { oauth: options.oauth } : {}),
|
|
84
101
|
};
|
|
85
|
-
const resolution = extractHttpBearer(authCtx);
|
|
102
|
+
const resolution = await extractHttpBearer(authCtx);
|
|
86
103
|
if (!resolution.ok) {
|
|
87
104
|
finalize(401);
|
|
88
105
|
return unauthorizedResponse(resolution.message, resolution.wwwAuthenticate);
|
|
89
106
|
}
|
|
90
107
|
let upstreamRequestId = null;
|
|
91
108
|
const server = buildServer({
|
|
92
|
-
apiKey: resolution.
|
|
109
|
+
apiKey: resolution.bearer,
|
|
93
110
|
...(options.baseUrl !== undefined ? { baseUrl: options.baseUrl } : {}),
|
|
94
111
|
...(options.fetchImpl !== undefined ? { fetchImpl: options.fetchImpl } : {}),
|
|
95
112
|
onUpstreamResponse: ({ requestId }) => {
|
|
@@ -175,5 +192,15 @@ function unauthorizedResponse(message, wwwAuthenticate) {
|
|
|
175
192
|
export function defaultProtectedResourceMetadataUrl(host, protocol = "https") {
|
|
176
193
|
return `${protocol}://${host}${PRM_PATH}`;
|
|
177
194
|
}
|
|
195
|
+
function isLoopbackOrigin(origin) {
|
|
196
|
+
try {
|
|
197
|
+
const parsed = new URL(origin);
|
|
198
|
+
return (parsed.protocol === "http:" &&
|
|
199
|
+
(parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1"));
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
178
205
|
export { buildWwwAuthenticateHeader };
|
|
179
206
|
//# sourceMappingURL=http.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/transports/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAAE,wCAAwC,EAAE,MAAM,+DAA+D,CAAC;AACzH,OAAO,EACL,0BAA0B,EAC1B,iBAAiB,
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/transports/http.ts"],"names":[],"mappings":"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"}
|