keycardai-mcp 0.1.0 → 0.1.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +41 -0
- data/README.md +0 -1
- data/lib/keycardai/mcp/metadata_app.rb +8 -27
- data/lib/keycardai/mcp/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e7fc6bb3654a0b2a8e00298d2d36cd551786982bbae2404600a5d9a8d01b479f
|
|
4
|
+
data.tar.gz: 34c7ed0e14be12c111614ab20c7667ef5de9c2882873fbb03c1c8d951dcf5791
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: deefc119bca708aff1721536932f0fc9b6c09ab463e0c2c980e8702c8fec3eced345b59473956ff4cd873358d8f59ea0c4e24059fe7edb36b61e99d1c953548a
|
|
7
|
+
data.tar.gz: f6c15892df3b646cddbc87c6f5c02f328b4da569f190b8c54da22e4a145a3b6d20780cc0d878c662148b68cfd38eba2aba657532e7e4cd7d8fcf228b1a064959
|
data/CHANGELOG.md
CHANGED
|
@@ -13,3 +13,44 @@ verification with RFC 6750 challenges, OAuth metadata endpoints (RFC 9728
|
|
|
13
13
|
protected-resource with path insertion, an RFC 8414 authorization-server proxy,
|
|
14
14
|
and JWKS), and an AuthProvider whose `grant` middleware exchanges the caller's
|
|
15
15
|
token per resource into an AccessContext. Wraps no MCP SDK.
|
|
16
|
+
|
|
17
|
+
## 0.1.1-keycardai-mcp (2026-09-16)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
- fix(keycardai-mcp): retire the 2025-03-26 metadata rewrites (SDK-3) (#37)
|
|
21
|
+
- MetadataApp no longer rewrites authorization_servers to the request origin
|
|
22
|
+
for MCP-Protocol-Version: 2025-03-26 and no longer appends resource=<origin>
|
|
23
|
+
to the proxied authorization_endpoint. Both documents match keycard-sdk-spec
|
|
24
|
+
oauth-metadata-endpoints spec-version 2.
|
|
25
|
+
- Co-authored-by: devin-ai-keycard <devin-ai@keycard.ai>
|
|
26
|
+
Co-authored-by: Larry Osakwe <larry@keycard.ai>
|
|
27
|
+
|
|
28
|
+
## 0.1.0-keycardai-mcp (2026-08-19)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
- feat(keycardai-mcp): Rack bearer middleware, metadata endpoints, AuthProvider grant (#8)
|
|
32
|
+
- Phase 2: the MCP server integration at the Rack seam, implementing
|
|
33
|
+
specs/server-bearer-auth/{bearer-token-verification-middleware,
|
|
34
|
+
oauth-metadata-endpoints,route-level-auth-gating}.md and
|
|
35
|
+
specs/delegated-access/grant-decorator.md:
|
|
36
|
+
- - RequireBearerAuth: fail-closed Rack middleware; 401 RFC 6750
|
|
37
|
+
challenge with the RFC 9728 resource_metadata URL, 400 on malformed
|
|
38
|
+
headers, 401 invalid_token on non-bearer schemes (TS/Go position),
|
|
39
|
+
403 insufficient_scope on missing required_scopes (all-of; gating is
|
|
40
|
+
this middleware re-applied per route, the TS/Go idiom), verified
|
|
41
|
+
AccessToken in the Rack env, construction error on a nil verifier
|
|
42
|
+
- MetadataApp: RFC 9728 protected-resource metadata with path
|
|
43
|
+
insertion, the 2025-03-26 MCP-version shim, RFC 8414 AS proxy with
|
|
44
|
+
the resource=<origin> rewrite (replace, not append), optional
|
|
45
|
+
/.well-known/jwks.json, permissive CORS + preflight, 502 on upstream
|
|
46
|
+
failure
|
|
47
|
+
- AuthProvider: zone + credential holder; token_verifier with
|
|
48
|
+
multi-zone issuers inferred from a self-describing credential;
|
|
49
|
+
grant(resources, user_identifier:, request_scopes:) middleware that
|
|
50
|
+
fails fast 401 without a verified token, never aborts on per-resource
|
|
51
|
+
errors, and merges stacked grants into one AccessContext;
|
|
52
|
+
exchange_tokens + exchange_tokens_for_zone
|
|
53
|
+
- Env accessors Keycardai::MCP.auth_info / .access_context
|
|
54
|
+
- No MCP SDK dependency, per the plan: compatibility with the official
|
|
55
|
+
mcp gem is proven by the phase 4 example server. Conformance suites map
|
|
56
|
+
to all four spec Testing tables (28 examples).
|
data/README.md
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Keycard integration for MCP servers in Ruby, attached at the Rack seam.
|
|
4
4
|
|
|
5
|
-
> **Preview.** APIs may change between minor versions while the surface settles.
|
|
6
5
|
> Conformance against the cross-SDK contract is tracked in the
|
|
7
6
|
> [conformance report](https://github.com/keycardai/ruby-sdk/blob/main/docs/conformance-report.md).
|
|
8
7
|
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "uri"
|
|
4
|
-
|
|
5
3
|
module Keycardai
|
|
6
4
|
module MCP
|
|
7
5
|
# Rack app serving the resource server's OAuth discovery surface
|
|
8
6
|
# (RFC 9728 / RFC 8414): the protected-resource metadata (with path
|
|
9
7
|
# insertion for sub-path mounts), a server-side proxy of the zone's
|
|
10
|
-
# authorization-server metadata (
|
|
11
|
-
#
|
|
8
|
+
# authorization-server metadata (returned unmodified), and optionally
|
|
9
|
+
# the server's own public JWKS.
|
|
12
10
|
# Responses carry permissive CORS so browser clients can read them.
|
|
13
11
|
#
|
|
14
12
|
# run Keycardai::MCP::MetadataApp.new(issuer: zone_url, scopes_supported: ["mcp:tools"])
|
|
@@ -16,7 +14,6 @@ module Keycardai
|
|
|
16
14
|
PROTECTED_RESOURCE_PATH = "/.well-known/oauth-protected-resource"
|
|
17
15
|
AUTHORIZATION_SERVER_PATH = "/.well-known/oauth-authorization-server"
|
|
18
16
|
JWKS_PATH = "/.well-known/jwks.json"
|
|
19
|
-
LEGACY_MCP_PROTOCOL_VERSION = "2025-03-26"
|
|
20
17
|
|
|
21
18
|
# @param issuer [String] the zone's issuer URL
|
|
22
19
|
# @param scopes_supported [Array<String>, nil]
|
|
@@ -46,7 +43,7 @@ module Keycardai
|
|
|
46
43
|
when %r{\A#{Regexp.escape(PROTECTED_RESOURCE_PATH)}(/.*)?\z}
|
|
47
44
|
protected_resource_response(env, Regexp.last_match(1).to_s)
|
|
48
45
|
when AUTHORIZATION_SERVER_PATH
|
|
49
|
-
authorization_server_response
|
|
46
|
+
authorization_server_response
|
|
50
47
|
when JWKS_PATH
|
|
51
48
|
jwks_response
|
|
52
49
|
else
|
|
@@ -68,10 +65,9 @@ module Keycardai
|
|
|
68
65
|
# origin + /mcp.
|
|
69
66
|
def protected_resource_response(env, resource_path)
|
|
70
67
|
origin = RackSupport.origin(env)
|
|
71
|
-
legacy = env["HTTP_MCP_PROTOCOL_VERSION"] == LEGACY_MCP_PROTOCOL_VERSION
|
|
72
68
|
document = {
|
|
73
69
|
"resource" => "#{origin}#{resource_path}",
|
|
74
|
-
"authorization_servers" => [
|
|
70
|
+
"authorization_servers" => [@issuer],
|
|
75
71
|
"bearer_methods_supported" => ["header"],
|
|
76
72
|
"scopes_supported" => @scopes_supported,
|
|
77
73
|
"resource_name" => @resource_name,
|
|
@@ -81,32 +77,17 @@ module Keycardai
|
|
|
81
77
|
RackSupport.json_response(document)
|
|
82
78
|
end
|
|
83
79
|
|
|
84
|
-
# RFC 8414 proxy of the zone's metadata,
|
|
85
|
-
#
|
|
86
|
-
|
|
87
|
-
def authorization_server_response(env)
|
|
80
|
+
# RFC 8414 proxy of the zone's metadata, returned unmodified. MCP
|
|
81
|
+
# 2025-06-18 clients send the RFC 8707 resource parameter themselves.
|
|
82
|
+
def authorization_server_response
|
|
88
83
|
metadata = Keycardai::OAuth.fetch_authorization_server_metadata(
|
|
89
84
|
@issuer, http_client: @http_client, timeout: @timeout
|
|
90
85
|
)
|
|
91
|
-
|
|
92
|
-
if document["authorization_endpoint"]
|
|
93
|
-
document["authorization_endpoint"] =
|
|
94
|
-
with_resource_param(document["authorization_endpoint"], RackSupport.origin(env))
|
|
95
|
-
end
|
|
96
|
-
RackSupport.json_response(document)
|
|
86
|
+
RackSupport.json_response(metadata.raw)
|
|
97
87
|
rescue Keycardai::Error
|
|
98
88
|
RackSupport.json_response({ "error" => "bad_gateway" }, status: 502)
|
|
99
89
|
end
|
|
100
90
|
|
|
101
|
-
def with_resource_param(endpoint, resource)
|
|
102
|
-
uri = URI(endpoint)
|
|
103
|
-
params = URI.decode_www_form(uri.query.to_s)
|
|
104
|
-
params.delete_if { |pair| pair.first == "resource" }
|
|
105
|
-
params << ["resource", resource]
|
|
106
|
-
uri.query = URI.encode_www_form(params)
|
|
107
|
-
uri.to_s
|
|
108
|
-
end
|
|
109
|
-
|
|
110
91
|
def jwks_response
|
|
111
92
|
return RackSupport.json_response({ "error" => "not_found" }, status: 404) unless @public_jwks
|
|
112
93
|
|