@nshipster/sosumi 1.0.8 → 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/src/lib/ard.ts ADDED
@@ -0,0 +1,77 @@
1
+ import { PUBLISHER_DID, PUBLISHER_DOMAIN } from "./identity"
2
+ import { SKILL_NAME } from "./skill"
3
+
4
+ export const AI_CATALOG_MEDIA_TYPE = "application/ai-catalog+json"
5
+
6
+ export interface AiCatalogEntry {
7
+ identifier: string
8
+ displayName: string
9
+ type: string
10
+ url: string
11
+ description: string
12
+ representativeQueries: string[]
13
+ }
14
+
15
+ export interface AiCatalog {
16
+ specVersion: string
17
+ host: {
18
+ displayName: string
19
+ identifier: string
20
+ documentationUrl: string
21
+ }
22
+ entries: AiCatalogEntry[]
23
+ }
24
+
25
+ /** Build the ARD capability manifest for the origin serving the request. */
26
+ export function buildAiCatalog(origin: string): AiCatalog {
27
+ return {
28
+ specVersion: "1.0",
29
+ host: {
30
+ displayName: PUBLISHER_DOMAIN,
31
+ identifier: PUBLISHER_DID,
32
+ documentationUrl: `${origin}/`,
33
+ },
34
+ entries: [
35
+ {
36
+ identifier: `urn:air:${PUBLISHER_DOMAIN}:server:mcp`,
37
+ displayName: "Sosumi MCP Server",
38
+ type: "application/mcp-server-card+json",
39
+ url: `${origin}/.well-known/mcp/server-card.json`,
40
+ description:
41
+ "Searches and fetches Apple Developer documentation, Human Interface Guidelines, WWDC transcripts, and public Swift-DocC pages.",
42
+ representativeQueries: [
43
+ "Search Apple Developer documentation for URLSession",
44
+ "Fetch the SwiftUI View documentation as Markdown",
45
+ "Get the transcript for a WWDC session",
46
+ "Read a public Swift-DocC documentation page",
47
+ ],
48
+ },
49
+ {
50
+ identifier: `urn:air:${PUBLISHER_DOMAIN}:agent:documentation`,
51
+ displayName: "Sosumi Documentation Agent",
52
+ type: "application/a2a-agent-card+json",
53
+ url: `${origin}/.well-known/agent-card.json`,
54
+ description:
55
+ "An A2A agent for searching and fetching Apple and Swift-DocC documentation as Markdown.",
56
+ representativeQueries: [
57
+ "Find Apple documentation about Swift actors",
58
+ "Fetch /documentation/swiftui/view as Markdown",
59
+ "Fetch the transcript for /videos/play/wwdc2021/10133",
60
+ ],
61
+ },
62
+ {
63
+ identifier: `urn:air:${PUBLISHER_DOMAIN}:skill:${SKILL_NAME}`,
64
+ displayName: "Sosumi Agent Skill",
65
+ type: 'text/markdown; profile="urn:air:agent-skills"',
66
+ url: `${origin}/.well-known/agent-skills/${SKILL_NAME}/SKILL.md`,
67
+ description:
68
+ "Instructions for using Sosumi to research Apple APIs, design guidance, videos, and Swift-DocC documentation.",
69
+ representativeQueries: [
70
+ "Research an Apple framework API before writing code",
71
+ "Look up current SwiftUI documentation",
72
+ "Find Apple's design guidance for an interface",
73
+ ],
74
+ },
75
+ ],
76
+ }
77
+ }
@@ -0,0 +1,105 @@
1
+ import { jwkToKeyID, type Signer } from "web-bot-auth"
2
+ import { helpers, signerFromJWK } from "web-bot-auth/crypto"
3
+
4
+ /** The public half of the application's Ed25519 signing key. */
5
+ export interface PublicSigningKey {
6
+ kty: "OKP"
7
+ crv: "Ed25519"
8
+ x: string
9
+ kid: string
10
+ use: string
11
+ }
12
+
13
+ export interface SigningKeyConfig {
14
+ signer: Signer
15
+ publicKey: PublicSigningKey
16
+ }
17
+
18
+ interface Ed25519PrivateJwk {
19
+ kty: "OKP"
20
+ crv: "Ed25519"
21
+ x: string
22
+ d: string
23
+ }
24
+
25
+ let cache: { key: string; config: Promise<SigningKeyConfig> } | null = null
26
+
27
+ /** Configure the application-wide signing key from a serialized private JWK. */
28
+ export function configureSigningKey(serializedKey?: string): void {
29
+ const key = serializedKey?.trim()
30
+ if (!key) {
31
+ cache = null
32
+ return
33
+ }
34
+
35
+ if (cache?.key === key) {
36
+ return
37
+ }
38
+
39
+ cache = { key, config: buildSigningKeyConfig(key) }
40
+ }
41
+
42
+ /** Return the configured signer and public key, or null if unavailable. */
43
+ export async function currentSigningKey(): Promise<SigningKeyConfig | null> {
44
+ const entry = cache
45
+ if (!entry) {
46
+ return null
47
+ }
48
+
49
+ try {
50
+ return await entry.config
51
+ } catch (error) {
52
+ console.error("auth: failed to load signing key", error)
53
+ if (cache === entry) {
54
+ cache = null
55
+ }
56
+ return null
57
+ }
58
+ }
59
+
60
+ /** Return a copy of the configured public signing key, if available. */
61
+ export async function currentPublicSigningKey(): Promise<PublicSigningKey | null> {
62
+ const config = await currentSigningKey()
63
+ return config ? { ...config.publicKey } : null
64
+ }
65
+
66
+ function parseSigningJwk(key: string): Ed25519PrivateJwk {
67
+ let jwk: JsonWebKey
68
+ try {
69
+ jwk = JSON.parse(key) as JsonWebKey
70
+ } catch (error) {
71
+ throw new Error(`WEB_BOT_AUTH_KEY is not valid JSON: ${(error as Error).message}`)
72
+ }
73
+
74
+ if (
75
+ jwk.kty !== "OKP" ||
76
+ jwk.crv !== "Ed25519" ||
77
+ typeof jwk.x !== "string" ||
78
+ jwk.x.length === 0 ||
79
+ typeof jwk.d !== "string" ||
80
+ jwk.d.length === 0
81
+ ) {
82
+ throw new Error(
83
+ "WEB_BOT_AUTH_KEY must be an Ed25519 private JSON Web Key (kty=OKP, crv=Ed25519, with x and d).",
84
+ )
85
+ }
86
+
87
+ return { kty: jwk.kty, crv: jwk.crv, x: jwk.x, d: jwk.d }
88
+ }
89
+
90
+ async function buildSigningKeyConfig(key: string): Promise<SigningKeyConfig> {
91
+ const jwk = parseSigningJwk(key)
92
+ const signer = await signerFromJWK(jwk)
93
+ const kid = await jwkToKeyID(jwk, helpers.WEBCRYPTO_SHA256, helpers.BASE64URL_DECODE)
94
+
95
+ return {
96
+ signer,
97
+ publicKey: {
98
+ kty: jwk.kty,
99
+ crv: jwk.crv,
100
+ x: jwk.x,
101
+ kid,
102
+ use: "sig",
103
+ },
104
+ }
105
+ }
package/src/lib/did.ts ADDED
@@ -0,0 +1,40 @@
1
+ import { currentPublicSigningKey } from "./auth"
2
+ import { PUBLISHER_DID, PUBLISHER_ORIGIN } from "./identity"
3
+
4
+ export const DID_DOCUMENT_MEDIA_TYPE = "application/did+ld+json"
5
+
6
+ /**
7
+ * Build the did:web document for Sosumi's stable publisher identity.
8
+ * The verification method reuses the public half of the application signing
9
+ * key, which is configured out of band and never committed. Returns `null`
10
+ * rather than publishing an unverifiable identity when that key is unavailable.
11
+ */
12
+ export async function buildDidDocument() {
13
+ const publicKey = await currentPublicSigningKey()
14
+ if (!publicKey) {
15
+ return null
16
+ }
17
+
18
+ const verificationMethodId = `${PUBLISHER_DID}#${publicKey.kid}`
19
+
20
+ return {
21
+ "@context": ["https://www.w3.org/ns/did/v1", "https://w3id.org/security/suites/jws-2020/v1"],
22
+ id: PUBLISHER_DID,
23
+ alsoKnownAs: [PUBLISHER_ORIGIN],
24
+ verificationMethod: [
25
+ {
26
+ id: verificationMethodId,
27
+ type: "JsonWebKey2020",
28
+ controller: PUBLISHER_DID,
29
+ publicKeyJwk: {
30
+ kty: publicKey.kty,
31
+ crv: publicKey.crv,
32
+ x: publicKey.x,
33
+ kid: publicKey.kid,
34
+ },
35
+ },
36
+ ],
37
+ authentication: [verificationMethodId],
38
+ assertionMethod: [verificationMethodId],
39
+ }
40
+ }
@@ -516,7 +516,7 @@ function renderHIGTocItems(items: HIGTocItem[], headingLevel: number): string {
516
516
  let markdown = ""
517
517
 
518
518
  for (const item of items) {
519
- if (item.type === "module" || item.type === "symbol") {
519
+ if (item.type === "module" || item.type === "symbol" || item.type === "collection") {
520
520
  // Ensure blank line before heading when preceding content was a list
521
521
  if (markdown && !markdown.endsWith("\n\n")) {
522
522
  markdown += "\n"
@@ -29,7 +29,7 @@ export interface HIGTocItem {
29
29
  icon?: string
30
30
  path: string
31
31
  title: string
32
- type: "module" | "symbol" | "article"
32
+ type: "module" | "symbol" | "collection" | "article"
33
33
  }
34
34
 
35
35
  /**
@@ -0,0 +1,4 @@
1
+ /** Stable publisher identity, intentionally independent of the request origin. */
2
+ export const PUBLISHER_DOMAIN = "sosumi.ai"
3
+ export const PUBLISHER_DID = `did:web:${PUBLISHER_DOMAIN}`
4
+ export const PUBLISHER_ORIGIN = `https://${PUBLISHER_DOMAIN}`
package/src/lib/mcp.ts CHANGED
@@ -4,16 +4,27 @@ import { z } from "zod"
4
4
  import type { ExternalPolicyEnv } from "./external"
5
5
  import { fetchExternalDocumentationMarkdown } from "./external"
6
6
  import { fetchHIGPageData, renderHIGFromJSON } from "./hig"
7
+ import { PUBLISHER_DOMAIN } from "./identity"
7
8
  import { fetchJSONData, renderFromJSON } from "./reference"
8
9
  import { searchAppleDeveloperDocs } from "./search"
9
10
  import { generateAppleDocUrl, normalizeDocumentationPath } from "./url"
10
11
  import { fetchVideoTranscriptMarkdown } from "./video"
11
12
 
12
13
  export const MCP_SERVER_INFO = {
13
- name: "sosumi.ai",
14
+ name: PUBLISHER_DOMAIN,
14
15
  version: "1.0.0",
15
16
  } as const
16
17
 
18
+ /**
19
+ * Guidance for clients, kept out of the documentation itself
20
+ * so that fetched pages stay 1:1 with Apple's content.
21
+ */
22
+ export const MCP_SERVER_INSTRUCTIONS =
23
+ "Sosumi returns Apple's documentation as Markdown. " +
24
+ 'The conformance conditions in Apple\'s data, such as "Conforms when `Element` conforms to `Hashable`.", ' +
25
+ "can be missing or wrong. " +
26
+ "When a condition matters, read the declaration in the module's `.swiftinterface` file in the SDK."
27
+
17
28
  export interface ToolHttpBinding {
18
29
  path?: string
19
30
  pathFrom?: string
@@ -114,7 +125,7 @@ export const TOOL_DEFINITIONS = {
114
125
  } satisfies Record<string, ToolMeta>
115
126
 
116
127
  export function createMcpServer(externalPolicyEnv: ExternalPolicyEnv = {}) {
117
- const server = new McpServer(MCP_SERVER_INFO)
128
+ const server = new McpServer(MCP_SERVER_INFO, { instructions: MCP_SERVER_INSTRUCTIONS })
118
129
 
119
130
  const search = TOOL_DEFINITIONS.searchAppleDocumentation
120
131
  server.registerTool(
@@ -614,7 +614,13 @@ function renderRelationships(
614
614
  markdown += `## ${rel.title}\n\n`
615
615
  for (const id of rel.identifiers) {
616
616
  const { title, url } = resolveLink(id, variants, references, externalOrigin)
617
- markdown += `- [${title}](${url})\n`
617
+ const condition = renderCondition(
618
+ references?.[id],
619
+ "conformancePrefix",
620
+ references,
621
+ externalOrigin,
622
+ )
623
+ markdown += `- [${title}](${url})${condition ? ` ${condition}` : ""}\n`
618
624
  }
619
625
  markdown += "\n"
620
626
  }
@@ -623,6 +629,30 @@ function renderRelationships(
623
629
  return markdown
624
630
  }
625
631
 
632
+ /**
633
+ * Render the condition on a reference, or an empty string when it has none.
634
+ * Relationship sections use the conformance prefix
635
+ * ("Conforms when `Element` conforms to `Hashable`."),
636
+ * and topic and see also sections use the availability prefix
637
+ * ("Available when `Element` conforms to `Hashable`.").
638
+ */
639
+ function renderCondition(
640
+ reference: ContentItem | undefined,
641
+ prefixKey: "conformancePrefix" | "availabilityPrefix",
642
+ references?: Record<string, ContentItem>,
643
+ externalOrigin?: string,
644
+ ): string {
645
+ const constraints = reference?.conformance?.constraints
646
+ if (!constraints?.length) {
647
+ return ""
648
+ }
649
+ const prefix = reference?.conformance?.[prefixKey] ?? []
650
+ const inlineContent = prefix.length
651
+ ? [...prefix, { type: "text", text: " " }, ...constraints]
652
+ : constraints
653
+ return renderInlineContent(inlineContent, references, 0, externalOrigin).trim()
654
+ }
655
+
626
656
  /**
627
657
  * Render topic sections
628
658
  */
@@ -652,6 +682,15 @@ function renderTopicSections(
652
682
  if (abstract) {
653
683
  markdown += ` ${abstract}`
654
684
  }
685
+ const condition = renderCondition(
686
+ references?.[id],
687
+ "availabilityPrefix",
688
+ references,
689
+ externalOrigin,
690
+ )
691
+ if (condition) {
692
+ markdown += ` ${condition}`
693
+ }
655
694
  markdown += "\n"
656
695
  }
657
696
  markdown += "\n"
@@ -730,7 +769,13 @@ function renderSeeAlso(
730
769
  markdown += `## ${section.title}\n\n`
731
770
  for (const id of section.identifiers) {
732
771
  const { title, url } = resolveLink(id, variants, references, externalOrigin)
733
- markdown += `- [${title}](${url})\n`
772
+ const condition = renderCondition(
773
+ references?.[id],
774
+ "availabilityPrefix",
775
+ references,
776
+ externalOrigin,
777
+ )
778
+ markdown += `- [${title}](${url})${condition ? ` ${condition}` : ""}\n`
734
779
  }
735
780
  markdown += "\n"
736
781
  }
package/src/lib/types.ts CHANGED
@@ -90,6 +90,17 @@ export interface ContentItem {
90
90
 
91
91
  /** Present on reference entries for deprecated symbols. */
92
92
  deprecated?: boolean
93
+
94
+ /**
95
+ * Present on reference entries for conditional conformances and members
96
+ * (e.g. "Conforms when `Element` conforms to `Hashable`."
97
+ * or "Available when `Element` conforms to `Hashable`.").
98
+ */
99
+ conformance?: {
100
+ conformancePrefix?: ContentItem[]
101
+ availabilityPrefix?: ContentItem[]
102
+ constraints?: ContentItem[]
103
+ }
93
104
  }
94
105
 
95
106
  // ============================================================================
@@ -22,12 +22,11 @@
22
22
  import {
23
23
  directoryResponseHeaders,
24
24
  HTTP_MESSAGE_SIGNATURES_DIRECTORY,
25
- jwkToKeyID,
26
25
  MediaType,
27
- type Signer,
28
26
  signatureHeaders,
29
27
  } from "web-bot-auth"
30
- import { helpers, signerFromJWK } from "web-bot-auth/crypto"
28
+ import { configureSigningKey, currentSigningKey, type PublicSigningKey } from "./auth"
29
+ import { PUBLISHER_ORIGIN } from "./identity"
31
30
 
32
31
  export interface WebBotAuthEnv {
33
32
  /**
@@ -45,7 +44,7 @@ export const DIRECTORY_PATH = HTTP_MESSAGE_SIGNATURES_DIRECTORY
45
44
  /** Content type for the key directory response. */
46
45
  export const DIRECTORY_MEDIA_TYPE = MediaType.HTTP_MESSAGE_SIGNATURES_DIRECTORY
47
46
 
48
- const DEFAULT_SIGNATURE_AGENT = "https://sosumi.ai"
47
+ const DEFAULT_SIGNATURE_AGENT = PUBLISHER_ORIGIN
49
48
 
50
49
  /**
51
50
  * How long an outbound request signature stays valid.
@@ -56,28 +55,7 @@ const OUTBOUND_SIGNATURE_TTL_MS = 5 * 60 * 1000
56
55
  /** How long the directory self-signature stays valid. */
57
56
  const DIRECTORY_SIGNATURE_TTL_MS = 60 * 60 * 1000
58
57
 
59
- /** A public JSON Web Key as published in the directory (RFC 8037 Ed25519). */
60
- export interface DirectoryKey {
61
- kty?: string
62
- crv?: string
63
- x?: string
64
- kid: string
65
- use: string
66
- }
67
-
68
- interface WebBotAuthConfig {
69
- signer: Signer
70
- publicKey: DirectoryKey
71
- agent: string
72
- }
73
-
74
- /**
75
- * Cached, parsed configuration.
76
- * The signing key is application-global (the same for every request),
77
- * so it is memoized across requests in the isolate
78
- * and only rebuilt when the underlying secret or agent changes.
79
- */
80
- let cache: { key: string; agent: string; config: Promise<WebBotAuthConfig> } | null = null
58
+ let signatureAgent: string | null = null
81
59
 
82
60
  /**
83
61
  * Prime the signing configuration from the environment.
@@ -86,91 +64,8 @@ let cache: { key: string; agent: string; config: Promise<WebBotAuthConfig> } | n
86
64
  */
87
65
  export function configureWebBotAuth(env: WebBotAuthEnv): void {
88
66
  const key = env.WEB_BOT_AUTH_KEY?.trim()
89
- if (!key) {
90
- cache = null
91
- return
92
- }
93
-
94
- const agent = env.SIGNATURE_AGENT?.trim() || DEFAULT_SIGNATURE_AGENT
95
- if (cache && cache.key === key && cache.agent === agent) {
96
- return
97
- }
98
-
99
- cache = { key, agent, config: buildConfig(key, agent) }
100
- }
101
-
102
- interface Ed25519PrivateJwk {
103
- kty: "OKP"
104
- crv: "Ed25519"
105
- x: string
106
- d: string
107
- }
108
-
109
- /**
110
- * Parse and validate the signing secret as an Ed25519 OKP private JWK.
111
- * Fails closed on anything malformed
112
- * so a misconfigured `WEB_BOT_AUTH_KEY` never yields a broken directory key
113
- * or unverifiable signatures.
114
- */
115
- function parseSigningJwk(key: string): Ed25519PrivateJwk {
116
- let jwk: JsonWebKey
117
- try {
118
- jwk = JSON.parse(key) as JsonWebKey
119
- } catch (error) {
120
- throw new Error(`WEB_BOT_AUTH_KEY is not valid JSON: ${(error as Error).message}`)
121
- }
122
-
123
- if (
124
- jwk.kty !== "OKP" ||
125
- jwk.crv !== "Ed25519" ||
126
- typeof jwk.x !== "string" ||
127
- jwk.x.length === 0 ||
128
- typeof jwk.d !== "string" ||
129
- jwk.d.length === 0
130
- ) {
131
- throw new Error(
132
- "WEB_BOT_AUTH_KEY must be an Ed25519 private JSON Web Key (kty=OKP, crv=Ed25519, with x and d).",
133
- )
134
- }
135
-
136
- return { kty: jwk.kty, crv: jwk.crv, x: jwk.x, d: jwk.d }
137
- }
138
-
139
- async function buildConfig(key: string, agent: string): Promise<WebBotAuthConfig> {
140
- const jwk = parseSigningJwk(key)
141
- const signer = await signerFromJWK(jwk)
142
- const kid = await jwkToKeyID(jwk, helpers.WEBCRYPTO_SHA256, helpers.BASE64URL_DECODE)
143
-
144
- // Publish only the public half of the key (never the `d` parameter).
145
- const publicKey: DirectoryKey = {
146
- kty: jwk.kty,
147
- crv: jwk.crv,
148
- x: jwk.x,
149
- kid,
150
- use: "sig",
151
- }
152
-
153
- return { signer, publicKey, agent }
154
- }
155
-
156
- async function currentConfig(): Promise<WebBotAuthConfig | null> {
157
- const entry = cache
158
- if (!entry) {
159
- return null
160
- }
161
-
162
- try {
163
- return await entry.config
164
- } catch (error) {
165
- console.error("web-bot-auth: failed to load signing key", error)
166
- // Drop the failed config so the next request rebuilds it,
167
- // rather than serving the cached rejection until the isolate restarts.
168
- // Guard against clobbering a newer config set by a concurrent reconfigure.
169
- if (cache === entry) {
170
- cache = null
171
- }
172
- return null
173
- }
67
+ configureSigningKey(key)
68
+ signatureAgent = key ? env.SIGNATURE_AGENT?.trim() || DEFAULT_SIGNATURE_AGENT : null
174
69
  }
175
70
 
176
71
  /** Serialize a value as an RFC 8941 structured-field string (a quoted string). */
@@ -179,7 +74,7 @@ function structuredString(value: string): string {
179
74
  }
180
75
 
181
76
  export interface DirectoryResponse {
182
- body: { keys: DirectoryKey[] }
77
+ body: { keys: PublicSigningKey[] }
183
78
  headers: Record<string, string>
184
79
  }
185
80
 
@@ -193,8 +88,8 @@ export interface DirectoryResponse {
193
88
  * demonstrating control of the published key.
194
89
  */
195
90
  export async function webBotAuthDirectory(requestUrl: string): Promise<DirectoryResponse | null> {
196
- const config = await currentConfig()
197
- if (!config) {
91
+ const signingKey = await currentSigningKey()
92
+ if (!signingKey) {
198
93
  return null
199
94
  }
200
95
 
@@ -205,10 +100,13 @@ export async function webBotAuthDirectory(requestUrl: string): Promise<Directory
205
100
  request: { method: "GET", url: requestUrl, headers: {} as Record<string, string> },
206
101
  }
207
102
 
208
- const signature = await directoryResponseHeaders(message, [config.signer], { created, expires })
103
+ const signature = await directoryResponseHeaders(message, [signingKey.signer], {
104
+ created,
105
+ expires,
106
+ })
209
107
 
210
108
  return {
211
- body: { keys: [config.publicKey] },
109
+ body: { keys: [signingKey.publicKey] },
212
110
  headers: {
213
111
  "Content-Type": DIRECTORY_MEDIA_TYPE,
214
112
  Signature: signature.Signature,
@@ -228,13 +126,14 @@ export async function webBotAuthHeaders(
228
126
  method: string,
229
127
  url: string | URL,
230
128
  ): Promise<Record<string, string>> {
231
- const config = await currentConfig()
232
- if (!config) {
129
+ const signingKey = await currentSigningKey()
130
+ const agentValue = signatureAgent
131
+ if (!signingKey || !agentValue) {
233
132
  return {}
234
133
  }
235
134
 
236
135
  try {
237
- const agent = structuredString(config.agent)
136
+ const agent = structuredString(agentValue)
238
137
  // `signature-agent` must be present on the message
239
138
  // so it is covered by the signature;
240
139
  // the same value is returned for the outbound request.
@@ -245,7 +144,7 @@ export async function webBotAuthHeaders(
245
144
  const expires = new Date(created.getTime() + OUTBOUND_SIGNATURE_TTL_MS)
246
145
  const signature = await signatureHeaders(
247
146
  { method, url: url.toString(), headers },
248
- config.signer,
147
+ signingKey.signer,
249
148
  { created, expires },
250
149
  )
251
150
 
package/wrangler.jsonc CHANGED
@@ -17,12 +17,24 @@
17
17
  "assets": {
18
18
  "binding": "ASSETS",
19
19
  "directory": "./public",
20
- "run_worker_first": ["/", "/.well-known/agent-card.json", "/.well-known/agent-skills/*"]
20
+ "run_worker_first": [
21
+ "/",
22
+ "/.well-known/agent-card.json",
23
+ "/.well-known/agent-skills/*",
24
+ "/.well-known/mcp-registry-auth",
25
+ "/.well-known/security.txt",
26
+ "/message:send",
27
+ "/message:stream",
28
+ "/tasks",
29
+ "/tasks/*",
30
+ "/extendedAgentCard"
31
+ ]
21
32
  },
22
33
  "observability": {
23
34
  "enabled": true
24
35
  },
25
36
  "vars": {
26
- "NODE_ENV": "production"
37
+ "NODE_ENV": "production",
38
+ "MCP_REGISTRY_PUBLIC_KEY": "sL6l9eXq+RN7+VuAVWZGnSacjsgix0TVW5FDLYlBUpg="
27
39
  }
28
40
  }