Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesPull RequestsActionsSecurityInsightsSettings
✨ AI
More
Blame · Line-by-line history

well-known.ts

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

well-known.tsBlame118 lines · 1 contributor
369ad65ccanty labs1/**
2 * OAuth / agent discovery metadata under `/.well-known/*`.
3 *
4 * These endpoints let an AI agent (claude.ai remote connectors, Cursor,
5 * Copilot, etc.) auto-discover how to authenticate against Gluecron without
6 * any manual configuration. They advertise ONLY endpoints that actually
7 * exist today:
8 *
9 * - RFC 8414 Authorization Server Metadata
10 * GET /.well-known/oauth-authorization-server
11 * - RFC 9728 Protected Resource Metadata
12 * GET /.well-known/oauth-protected-resource
13 * GET /.well-known/oauth-protected-resource/mcp (resource-specific)
14 *
4c27627ccanty labs15 * `registration_endpoint` (RFC 7591 dynamic client registration) is now live
16 * at POST /oauth/register and advertised below. `introspection_endpoint`
17 * (RFC 7662) is still omitted until it ships — advertising a 404 would break
18 * clients that probe it. Add it here in the same change that builds it.
369ad65ccanty labs19 *
20 * Pure/read-only, no auth, no DB. Safe to serve to anyone.
21 */
22
23import { Hono } from "hono";
24import { config } from "../lib/config";
25import { SUPPORTED_SCOPES } from "../lib/oauth";
26
27const wellKnown = new Hono();
28
29/** Absolute URL on this deployment's public origin (no trailing slash). */
30function base(): string {
31 // config.appBaseUrl already strips a trailing slash.
32 return config.appBaseUrl;
33}
34
35/**
36 * RFC 8414 — OAuth 2.0 Authorization Server Metadata.
37 * The document a client reads to learn where /authorize and /token live,
38 * which PKCE methods are supported, and which scopes exist.
39 */
40wellKnown.get("/.well-known/oauth-authorization-server", (c) => {
41 const b = base();
42 return c.json(
43 {
44 issuer: b,
45 authorization_endpoint: `${b}/oauth/authorize`,
46 token_endpoint: `${b}/oauth/token`,
47 revocation_endpoint: `${b}/oauth/revoke`,
4c27627ccanty labs48 // RFC 7591 dynamic client registration — lets agents self-register.
49 registration_endpoint: `${b}/oauth/register`,
369ad65ccanty labs50 scopes_supported: SUPPORTED_SCOPES,
51 response_types_supported: ["code"],
52 grant_types_supported: ["authorization_code", "refresh_token"],
53 // PKCE (RFC 7636) — the provider verifies S256 and plain.
54 code_challenge_methods_supported: ["S256", "plain"],
55 token_endpoint_auth_methods_supported: [
56 "client_secret_basic",
57 "client_secret_post",
58 "none", // public clients (PKCE, no secret)
59 ],
60 revocation_endpoint_auth_methods_supported: [
61 "client_secret_basic",
62 "client_secret_post",
63 "none",
64 ],
65 service_documentation: `${b}/api/docs`,
66 },
67 200,
68 { "cache-control": "public, max-age=3600" }
69 );
70});
71
72/**
73 * RFC 9728 — OAuth 2.0 Protected Resource Metadata.
74 *
75 * Served both at the root path and at `/mcp` (resource-specific). A remote
76 * MCP connector reads this to learn which authorization server guards the
77 * `/mcp` resource and which scopes it accepts.
78 */
79function protectedResourceDoc(c: Parameters<Parameters<typeof wellKnown.get>[1]>[0]) {
80 const b = base();
81 return c.json(
82 {
83 resource: `${b}/mcp`,
84 authorization_servers: [b],
85 scopes_supported: SUPPORTED_SCOPES,
86 bearer_methods_supported: ["header"],
87 resource_documentation: `${b}/api/docs`,
88 },
89 200,
90 { "cache-control": "public, max-age=3600" }
91 );
92}
93
94wellKnown.get("/.well-known/oauth-protected-resource", protectedResourceDoc);
95// Resource-specific form (RFC 9728 §3.1) — some clients append the resource
96// path when the WWW-Authenticate challenge points at `<origin>/mcp`.
97wellKnown.get("/.well-known/oauth-protected-resource/mcp", protectedResourceDoc);
98
cb65da8ccanty labs99/**
100 * Official MCP Registry domain verification (HTTP method).
101 *
102 * registry.modelcontextprotocol.io verifies control of the `com.gluecron`
103 * namespace by fetching this file and checking the signature made with the
104 * matching PRIVATE key during `mcp-publisher login http`. The value below is
105 * the PUBLIC half of the publishing keypair — safe to serve to anyone.
106 * Override via MCP_REGISTRY_AUTH env when the keypair is rotated.
107 * Registry entry lives in ./server.json at the repo root.
108 */
109wellKnown.get("/.well-known/mcp-registry-auth", (c) =>
110 c.text(
111 process.env.MCP_REGISTRY_AUTH ||
112 "v=MCPv1; k=ed25519; p=7a15jFzQv2INI1Lc9Yg8+v2Dv3QNK+r83ZfpotOOOdw=",
113 200,
114 { "cache-control": "public, max-age=300" }
115 )
116);
117
369ad65ccanty labs118export default wellKnown;