@nshipster/sosumi 1.0.9 → 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 CHANGED
@@ -86,6 +86,28 @@ you can run this command to proxy over stdio:
86
86
 
87
87
  See [the website](https://sosumi.ai/#clients) for client-specific instructions.
88
88
 
89
+ #### Plugin
90
+
91
+ The [`plugin`](plugin) folder packages the MCP server and the [agent skill](#ai-agent-skill)
92
+ for Claude Code, Cursor, and [Agent Plugins](https://agent-plugins.org) clients
93
+ such as ChatGPT and Codex.
94
+
95
+ In Claude Code, run:
96
+
97
+ ```text
98
+ /plugin marketplace add NSHipster/sosumi.ai
99
+ /plugin install sosumi@sosumi-ai
100
+ ```
101
+
102
+ `public/SKILL.md` is the source of the skill.
103
+ After you edit it, copy it into the plugin:
104
+
105
+ ```bash
106
+ cp public/SKILL.md plugin/skills/sosumi/SKILL.md
107
+ ```
108
+
109
+ A test fails when the two files differ.
110
+
89
111
  #### Available Tools
90
112
 
91
113
  - `searchAppleDocumentation` - Searches Apple Developer documentation
@@ -334,6 +356,32 @@ Publishing is handled by `.github/workflows/release.yml`.
334
356
  - Release step: `gh release create "$TAG_NAME" --generate-notes`
335
357
  - Publish auth: npm trusted publishing via OIDC (`id-token: write`)
336
358
  - Publish command: `npm publish --provenance --access public`
359
+ - MCP Registry: after `npm publish`, the workflow publishes `server.json`
360
+ to the [MCP Registry](https://registry.modelcontextprotocol.io)
361
+ as `ai.sosumi/sosumi`.
362
+
363
+ Keep `version` in `server.json` equal to `version` in `package.json`;
364
+ a test fails when they differ.
365
+
366
+ The registry step uses
367
+ [HTTP authentication](https://github.com/modelcontextprotocol/registry/blob/main/docs/modelcontextprotocol-io/authentication.mdx#http-authentication)
368
+ for the `sosumi.ai` domain.
369
+ It skips with a notice until the `MCP_REGISTRY_PRIVATE_KEY` secret is set.
370
+ To set it up, generate an Ed25519 key pair with OpenSSL 3
371
+ (macOS's LibreSSL does not support Ed25519):
372
+
373
+ ```bash
374
+ openssl genpkey -algorithm Ed25519 -out key.pem
375
+ # Public key: add to "vars" in wrangler.jsonc as MCP_REGISTRY_PUBLIC_KEY, then deploy
376
+ openssl pkey -in key.pem -pubout -outform DER | tail -c 32 | base64
377
+ # Private key (hex): save as the MCP_REGISTRY_PRIVATE_KEY repository secret
378
+ openssl pkey -in key.pem -noout -text | grep -A3 "priv:" | tail -n +2 | tr -d ' :\n'
379
+ ```
380
+
381
+ The Worker then serves `v=MCPv1; k=ed25519; p=<public key>`
382
+ at `/.well-known/mcp-registry-auth`.
383
+ Without the variable, that path returns 404.
384
+ Delete `key.pem` after you save both values.
337
385
 
338
386
  ## License
339
387
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nshipster/sosumi",
3
- "version": "1.0.9",
3
+ "version": "1.1.0",
4
4
  "description": "Making Apple docs AI-readable",
5
5
  "license": "MIT",
6
6
  "author": "NSHipster",
package/public/SKILL.md CHANGED
@@ -85,6 +85,7 @@ Use these when Sosumi is configured as an MCP server (`https://sosumi.ai/mcp`):
85
85
  - Fetch targeted symbol pages for coding questions.
86
86
  - Keep source links in answers so users can verify details quickly.
87
87
  - Use Sosumi paths directly in responses whenever referencing Apple documentation pages.
88
+ - Don't rely on conformance conditions, such as "Conforms when `Element` conforms to `Hashable`." Apple's documentation data leaves some out and gets some wrong. When a condition matters, read the declaration in the module's `.swiftinterface` file in the SDK.
88
89
 
89
90
  ## Troubleshooting
90
91
 
package/public/index.html CHANGED
@@ -181,6 +181,29 @@
181
181
  border: 1px solid var(--accent);
182
182
  }
183
183
 
184
+ .install-buttons {
185
+ display: flex;
186
+ flex-wrap: wrap;
187
+ gap: 0.75rem;
188
+ }
189
+
190
+ a.link-button {
191
+ display: inline-block;
192
+ padding: 0.375rem 0.875rem;
193
+ border-radius: 0.5rem;
194
+ border: 1px solid var(--border);
195
+ background: var(--surface);
196
+ color: var(--text);
197
+ font-weight: 500;
198
+ transition: border 0.2s;
199
+ }
200
+
201
+ a.link-button:hover {
202
+ color: var(--text);
203
+ text-decoration: none;
204
+ border-color: var(--accent);
205
+ }
206
+
184
207
  figure {
185
208
  margin: 0;
186
209
  }
@@ -726,7 +749,18 @@
726
749
 
727
750
  <div class="tab-pane" id="vscode-tab">
728
751
  <h3>VSCode</h3>
729
- <p>Create a <code>.vscode/mcp.json</code> file in your workspace and enter the following
752
+ <p>Click a button below to install the sosumi MCP server:</p>
753
+
754
+ <p class="install-buttons">
755
+ <a class="link-button"
756
+ href="https://vscode.dev/redirect?url=vscode%3Amcp%2Finstall%3F%257B%2522name%2522%253A%2522sosumi%2522%252C%2522type%2522%253A%2522http%2522%252C%2522url%2522%253A%2522https%253A%252F%252Fsosumi.ai%252Fmcp%2522%257D">Install
757
+ in VS Code</a>
758
+ <a class="link-button"
759
+ href="https://vscode.dev/redirect?url=vscode-insiders%3Amcp%2Finstall%3F%257B%2522name%2522%253A%2522sosumi%2522%252C%2522type%2522%253A%2522http%2522%252C%2522url%2522%253A%2522https%253A%252F%252Fsosumi.ai%252Fmcp%2522%257D">Install
760
+ in VS Code Insiders</a>
761
+ </p>
762
+
763
+ <p>Or create a <code>.vscode/mcp.json</code> file in your workspace and enter the following
730
764
  configuration:</p>
731
765
 
732
766
  <pre><code class="mcp-config"><span class="dimmed"><span class="punctuation">{</span></span>
@@ -761,11 +795,17 @@
761
795
 
762
796
  <div class="tab-pane" id="claude-code-tab">
763
797
  <h3>Claude Code</h3>
764
- <p>Run the following command in your terminal:</p>
798
+ <p>Install the sosumi plugin, which adds the MCP server and the Sosumi skill.
799
+ Run these commands in Claude Code:</p>
800
+ <pre><code>/plugin marketplace add NSHipster/sosumi.ai
801
+ /plugin install sosumi@sosumi-ai</code></pre>
802
+ <p>To add only the MCP server, run this command in your terminal:</p>
765
803
  <pre><code>claude mcp add --transport http sosumi https://sosumi.ai/mcp</code></pre>
766
- <p>For more information, see the
804
+ <p>For more information, see the Claude Code documentation for
805
+ <a href="https://code.claude.com/docs/en/plugins/install"
806
+ rel="noopener noreferrer">plugins</a> and
767
807
  <a href="https://docs.anthropic.com/en/docs/claude-code/mcp"
768
- rel="noopener noreferrer">Claude Code MCP documentation</a>.
808
+ rel="noopener noreferrer">MCP</a>.
769
809
  </p>
770
810
  </div>
771
811
 
@@ -1043,6 +1083,11 @@ curl -o ~/.agents/skills/sosumi/SKILL.md https://sosumi.ai/SKILL.md</code></pre>
1043
1083
  <p>
1044
1084
  <strong>Contact:</strong> <a href="mailto:info@sosumi.ai">info@sosumi.ai</a>
1045
1085
  </p>
1086
+ <p>
1087
+ <a href="/privacy">Privacy</a> ·
1088
+ <a href="/terms">Terms</a> ·
1089
+ <a href="/support">Support</a>
1090
+ </p>
1046
1091
  </div>
1047
1092
 
1048
1093
  <aside>
package/public/llms.txt CHANGED
@@ -64,7 +64,12 @@ Open this deep link to automatically install the sosumi MCP server:
64
64
 
65
65
  ### VSCode
66
66
 
67
- Create a `.vscode/mcp.json` file in your workspace and enter the following configuration:
67
+ Open one of these links to install the sosumi MCP server:
68
+
69
+ - [Install in VS Code](https://vscode.dev/redirect?url=vscode%3Amcp%2Finstall%3F%257B%2522name%2522%253A%2522sosumi%2522%252C%2522type%2522%253A%2522http%2522%252C%2522url%2522%253A%2522https%253A%252F%252Fsosumi.ai%252Fmcp%2522%257D)
70
+ - [Install in VS Code Insiders](https://vscode.dev/redirect?url=vscode-insiders%3Amcp%2Finstall%3F%257B%2522name%2522%253A%2522sosumi%2522%252C%2522type%2522%253A%2522http%2522%252C%2522url%2522%253A%2522https%253A%252F%252Fsosumi.ai%252Fmcp%2522%257D)
71
+
72
+ Or create a `.vscode/mcp.json` file in your workspace and enter the following configuration:
68
73
 
69
74
  ```json
70
75
  {
@@ -88,7 +93,15 @@ Create a `.vscode/mcp.json` file in your workspace and enter the following confi
88
93
 
89
94
  ### Claude Code
90
95
 
91
- Run the following command in your terminal:
96
+ Install the sosumi plugin, which adds the MCP server and the Sosumi skill.
97
+ Run these commands in Claude Code:
98
+
99
+ ```text
100
+ /plugin marketplace add NSHipster/sosumi.ai
101
+ /plugin install sosumi@sosumi-ai
102
+ ```
103
+
104
+ To add only the MCP server, run this command in your terminal:
92
105
 
93
106
  ```shell
94
107
  claude mcp add --transport http sosumi https://sosumi.ai/mcp
@@ -336,3 +349,5 @@ For external Swift-DocC hosts, sosumi honors `robots.txt` rules
336
349
  and opt-out response directives such as `X-Robots-Tag: noai`.
337
350
 
338
351
  **Contact:** <info@sosumi.ai>
352
+
353
+ [Privacy](https://sosumi.ai/privacy) · [Terms](https://sosumi.ai/terms) · [Support](https://sosumi.ai/support)
@@ -0,0 +1,104 @@
1
+ /* Shared styles for the policy pages. Colors and type match index.html. */
2
+ :root {
3
+ color-scheme: light dark;
4
+
5
+ --font-sans: -apple-system, BlinkMacSystemFont, 'SF Pro Text', sans-serif;
6
+ --font-mono: 'SF Mono', monospace;
7
+
8
+ --text: oklch(15% 0 0);
9
+ --text-secondary: oklch(35% 0 0);
10
+ --text-tertiary: oklch(55% 0 0);
11
+
12
+ --surface: oklch(98% 0 0);
13
+ --surface-inset: oklch(95% 0.02 70);
14
+
15
+ --border: oklch(20% 0.1 70);
16
+
17
+ --link: oklch(65% 0.15 70);
18
+ --accent: oklch(78% 0.18 70);
19
+ }
20
+
21
+ @media (prefers-color-scheme: dark) {
22
+ :root {
23
+ --text: oklch(92% 0.02 70);
24
+ --text-secondary: oklch(75% 0.05 70);
25
+ --text-tertiary: oklch(55% 0.08 70);
26
+
27
+ --surface: oklch(18% 0 0);
28
+ --surface-inset: oklch(12% 0.02 70);
29
+
30
+ --border: oklch(25% 0.1 70);
31
+ --accent: oklch(85% 0.2 70);
32
+ }
33
+ }
34
+
35
+ body {
36
+ font-family: var(--font-sans);
37
+ color: var(--text);
38
+ background-color: var(--surface);
39
+ max-width: 52rem;
40
+ margin: 3rem auto;
41
+ padding: 1.5rem;
42
+ line-height: 1.6;
43
+ }
44
+
45
+ body > header {
46
+ margin-bottom: 2rem;
47
+ }
48
+
49
+ body > header p {
50
+ margin: 0;
51
+ font-size: 1.5rem;
52
+ font-weight: bold;
53
+ }
54
+
55
+ body > header a {
56
+ color: var(--text);
57
+ }
58
+
59
+ h1 {
60
+ font-size: 2.5rem;
61
+ margin-bottom: 0.25rem;
62
+ }
63
+
64
+ h2 {
65
+ margin-top: 2rem;
66
+ margin-bottom: 0.625rem;
67
+ color: var(--text-secondary);
68
+ }
69
+
70
+ .updated {
71
+ color: var(--text-tertiary);
72
+ margin-top: 0;
73
+ }
74
+
75
+ code {
76
+ background: var(--surface-inset);
77
+ padding: 0.125rem;
78
+ font-family: var(--font-mono);
79
+ overflow-wrap: break-word;
80
+ }
81
+
82
+ a {
83
+ color: var(--link);
84
+ text-decoration: none;
85
+ transition: color 0.2s ease;
86
+ }
87
+
88
+ a:hover {
89
+ color: var(--accent);
90
+ text-decoration: underline;
91
+ }
92
+
93
+ footer {
94
+ margin-top: 3.75rem;
95
+ padding-top: 1.25rem;
96
+ border-top: 1px solid var(--border);
97
+ color: var(--text-tertiary);
98
+ font-size: 0.875rem;
99
+ }
100
+
101
+ ::selection {
102
+ background-color: var(--accent);
103
+ color: var(--surface);
104
+ }
@@ -0,0 +1,111 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Privacy Policy - sosumi.ai</title>
8
+ <meta name="description" content="What sosumi.ai collects, who receives it, and how long it is kept.">
9
+ <link rel="canonical" href="https://sosumi.ai/privacy">
10
+ <meta name="theme-color" content="#d68d6e" media="(prefers-color-scheme: light)">
11
+ <meta name="theme-color" content="#ec9b7f" media="(prefers-color-scheme: dark)">
12
+ <link rel="icon" href="/favicon.svg" type="image/svg+xml">
13
+ <link rel="alternate icon" href="/favicon.ico">
14
+ <link rel="stylesheet" href="/page.css">
15
+ </head>
16
+
17
+ <body>
18
+ <header>
19
+ <p><a href="/">sosumi.ai</a></p>
20
+ </header>
21
+
22
+ <main>
23
+ <h1>Privacy Policy</h1>
24
+ <p class="updated">Last updated November 7, 2026</p>
25
+
26
+ <p>
27
+ Sosumi is a free service that converts Apple Developer documentation to Markdown.
28
+ It has no accounts, and it doesn't ask for your name, email address, or other personal information.
29
+ This page applies to the website, the HTTP API, and the MCP server at <code>https://sosumi.ai/mcp</code>.
30
+ </p>
31
+
32
+ <h2>What Sosumi keeps</h2>
33
+ <p>
34
+ Sosumi doesn't set cookies.
35
+ It doesn't have a database,
36
+ and it doesn't save the requests it receives or the pages it returns.
37
+ </p>
38
+ <p>
39
+ Sosumi runs on Cloudflare Workers with
40
+ <a href="https://developers.cloudflare.com/workers/observability/logs/workers-logs/" rel="noopener noreferrer">Workers Logs</a>
41
+ turned on.
42
+ For each request, Cloudflare logs the request's URL and other request and response details,
43
+ and it logs any errors that Sosumi reports.
44
+ A URL can include what you asked for, such as a documentation path or a search query.
45
+ Cloudflare keeps these logs for up to 7 days.
46
+ Sosumi's maintainers can read them to find and fix problems.
47
+ </p>
48
+
49
+ <h2>Who receives your requests</h2>
50
+ <p>
51
+ To answer a request, Sosumi's servers fetch the page from developer.apple.com
52
+ or send your search query to Apple's documentation search service.
53
+ For an external Swift-DocC page, Sosumi fetches from the host in the URL you give.
54
+ Those services receive the path, query, or URL from Sosumi's servers.
55
+ Sosumi makes these requests with its own headers.
56
+ It doesn't forward your IP address, cookies, or request headers.
57
+ </p>
58
+ <p>
59
+ When you use Sosumi through an MCP client or plugin,
60
+ your client sends Sosumi the tool's input,
61
+ such as a search query, documentation path, or URL.
62
+ Sosumi handles that input in the same way as any other request.
63
+ </p>
64
+ <p>
65
+ Responses include <code>Cache-Control</code> headers.
66
+ Cloudflare, your client, and proxies between you and Sosumi can keep a copy of a response
67
+ for as long as those headers allow.
68
+ For documentation and search responses, that is at most 24 hours.
69
+ </p>
70
+ <p>
71
+ The home page shows Cursor's install button images, which load from cursor.com.
72
+ When you view the home page, your browser requests those images from Cursor.
73
+ </p>
74
+ <p>
75
+ The <code>sosumi</code> command-line tool runs on your computer.
76
+ It fetches documentation directly from Apple and doesn't send requests to sosumi.ai.
77
+ </p>
78
+
79
+ <h2>Monitoring</h2>
80
+ <p>
81
+ A separate monitoring service checks that sosumi.ai works every 5 minutes.
82
+ It saves whether the last check passed.
83
+ When a check fails or recovers, it saves an alert email with the check's error message
84
+ until the email is sent to the maintainers.
85
+ It doesn't save anything about visitors.
86
+ </p>
87
+
88
+ <h2>Changes and questions</h2>
89
+ <p>
90
+ When this policy changes, the date at the top of this page changes too.
91
+ The page's history is public in the
92
+ <a href="https://github.com/NSHipster/sosumi.ai/commits/main/public/privacy.html" rel="noopener noreferrer">sosumi.ai repository</a>.
93
+ For questions, see <a href="/support">Support</a>.
94
+ </p>
95
+ </main>
96
+
97
+ <footer>
98
+ <p>
99
+ <a href="/privacy">Privacy</a> ·
100
+ <a href="/terms">Terms</a> ·
101
+ <a href="/support">Support</a> ·
102
+ <a href="https://github.com/NSHipster/sosumi.ai" rel="noopener noreferrer">GitHub</a>
103
+ </p>
104
+ <p>
105
+ Sosumi is an unofficial, independent project
106
+ and is not affiliated with or endorsed by Apple Inc.
107
+ </p>
108
+ </footer>
109
+ </body>
110
+
111
+ </html>
@@ -3,4 +3,13 @@
3
3
  <url>
4
4
  <loc>https://sosumi.ai/</loc>
5
5
  </url>
6
+ <url>
7
+ <loc>https://sosumi.ai/privacy</loc>
8
+ </url>
9
+ <url>
10
+ <loc>https://sosumi.ai/terms</loc>
11
+ </url>
12
+ <url>
13
+ <loc>https://sosumi.ai/support</loc>
14
+ </url>
6
15
  </urlset>
@@ -0,0 +1,65 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Support - sosumi.ai</title>
8
+ <meta name="description" content="How to get help with sosumi.ai and report problems.">
9
+ <link rel="canonical" href="https://sosumi.ai/support">
10
+ <meta name="theme-color" content="#d68d6e" media="(prefers-color-scheme: light)">
11
+ <meta name="theme-color" content="#ec9b7f" media="(prefers-color-scheme: dark)">
12
+ <link rel="icon" href="/favicon.svg" type="image/svg+xml">
13
+ <link rel="alternate icon" href="/favicon.ico">
14
+ <link rel="stylesheet" href="/page.css">
15
+ </head>
16
+
17
+ <body>
18
+ <header>
19
+ <p><a href="/">sosumi.ai</a></p>
20
+ </header>
21
+
22
+ <main>
23
+ <h1>Support</h1>
24
+
25
+ <h2>Report a problem or ask a question</h2>
26
+ <p>
27
+ Open an issue on
28
+ <a href="https://github.com/NSHipster/sosumi.ai/issues" rel="noopener noreferrer">GitHub</a>.
29
+ Include the URL or MCP tool input that you used,
30
+ the client that you used (for example, Claude Code, Cursor, or ChatGPT),
31
+ and what you expected to see.
32
+ </p>
33
+
34
+ <h2>Contact</h2>
35
+ <p>
36
+ For anything you don't want to post in public, including security reports,
37
+ email <a href="mailto:info@sosumi.ai">info@sosumi.ai</a>.
38
+ This is the contact in
39
+ <a href="/.well-known/security.txt"><code>security.txt</code></a>.
40
+ </p>
41
+
42
+ <h2>Set up</h2>
43
+ <p>
44
+ Instructions for each MCP client, the command-line tool, and the agent skill are on the
45
+ <a href="/#mcp">home page</a>.
46
+ If your host serves Swift-DocC documentation and you want Sosumi to stop fetching it,
47
+ see <a href="/#bot">how Sosumi fetches pages</a>.
48
+ </p>
49
+ </main>
50
+
51
+ <footer>
52
+ <p>
53
+ <a href="/privacy">Privacy</a> ·
54
+ <a href="/terms">Terms</a> ·
55
+ <a href="/support">Support</a> ·
56
+ <a href="https://github.com/NSHipster/sosumi.ai" rel="noopener noreferrer">GitHub</a>
57
+ </p>
58
+ <p>
59
+ Sosumi is an unofficial, independent project
60
+ and is not affiliated with or endorsed by Apple Inc.
61
+ </p>
62
+ </footer>
63
+ </body>
64
+
65
+ </html>
@@ -0,0 +1,73 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Terms of Use - sosumi.ai</title>
8
+ <meta name="description" content="The terms for using sosumi.ai.">
9
+ <link rel="canonical" href="https://sosumi.ai/terms">
10
+ <meta name="theme-color" content="#d68d6e" media="(prefers-color-scheme: light)">
11
+ <meta name="theme-color" content="#ec9b7f" media="(prefers-color-scheme: dark)">
12
+ <link rel="icon" href="/favicon.svg" type="image/svg+xml">
13
+ <link rel="alternate icon" href="/favicon.ico">
14
+ <link rel="stylesheet" href="/page.css">
15
+ </head>
16
+
17
+ <body>
18
+ <header>
19
+ <p><a href="/">sosumi.ai</a></p>
20
+ </header>
21
+
22
+ <main>
23
+ <h1>Terms of Use</h1>
24
+ <p class="updated">Last updated November 7, 2026</p>
25
+
26
+ <p>
27
+ These terms apply to the website, the HTTP API, and the MCP server at <code>https://sosumi.ai/mcp</code>.
28
+ </p>
29
+
30
+ <h2>The service</h2>
31
+ <p>
32
+ Sosumi is free to use.
33
+ It is provided "as is", without warranty of any kind.
34
+ It can change, slow down, or stop at any time, and its output can be incomplete or wrong.
35
+ Check important details against the original page, which each response links to.
36
+ </p>
37
+
38
+ <h2>The code</h2>
39
+ <p>
40
+ Sosumi's source code is available under the
41
+ <a href="https://github.com/NSHipster/sosumi.ai/blob/main/LICENSE.md" rel="noopener noreferrer">MIT license</a>.
42
+ </p>
43
+
44
+ <h2>The documentation</h2>
45
+ <p>
46
+ Documentation content belongs to its owners.
47
+ Apple Inc. owns the content of Apple Developer documentation,
48
+ the Human Interface Guidelines, and Apple's videos.
49
+ The owners of external Swift-DocC sites own their content.
50
+ Sosumi converts a page only when you request it, and it doesn't grant you any rights to that content.
51
+ </p>
52
+
53
+ <h2>Your use</h2>
54
+ <p>
55
+ Your use of Sosumi must comply with Apple's terms of use, the terms of any other site you request, and applicable law.
56
+ </p>
57
+ </main>
58
+
59
+ <footer>
60
+ <p>
61
+ <a href="/privacy">Privacy</a> ·
62
+ <a href="/terms">Terms</a> ·
63
+ <a href="/support">Support</a> ·
64
+ <a href="https://github.com/NSHipster/sosumi.ai" rel="noopener noreferrer">GitHub</a>
65
+ </p>
66
+ <p>
67
+ Sosumi is an unofficial, independent project
68
+ and is not affiliated with or endorsed by Apple Inc.
69
+ </p>
70
+ </footer>
71
+ </body>
72
+
73
+ </html>
package/src/index.ts CHANGED
@@ -64,6 +64,7 @@ interface Env {
64
64
  EXTERNAL_DOC_HOST_BLOCKLIST?: string
65
65
  WEB_BOT_AUTH_KEY?: string
66
66
  SIGNATURE_AGENT?: string
67
+ MCP_REGISTRY_PUBLIC_KEY?: string
67
68
  }
68
69
 
69
70
  const app = new Hono<{ Bindings: Env }>()
@@ -276,6 +277,22 @@ app.get("/.well-known/security.txt", (c) => {
276
277
  )
277
278
  })
278
279
 
280
+ // Proves control of the sosumi.ai namespace to the MCP Registry
281
+ // (`mcp-publisher login http`). The registry doesn't follow redirects.
282
+ app.get("/.well-known/mcp-registry-auth", (c) => {
283
+ const publicKey = c.env.MCP_REGISTRY_PUBLIC_KEY?.trim()
284
+ const headers = {
285
+ "Content-Type": "text/plain; charset=utf-8",
286
+ "Cache-Control": "no-store",
287
+ }
288
+
289
+ if (!publicKey) {
290
+ return c.text("Not Found", 404, headers)
291
+ }
292
+
293
+ return c.text(`v=MCPv1; k=ed25519; p=${publicKey}`, 200, headers)
294
+ })
295
+
279
296
  app.on("GET", ["/.well-known/ard.json", "/.well-known/ai-catalog.json"], (c) => {
280
297
  const origin = new URL(c.req.url).origin
281
298
 
@@ -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
  /**
package/src/lib/mcp.ts CHANGED
@@ -15,6 +15,16 @@ export const MCP_SERVER_INFO = {
15
15
  version: "1.0.0",
16
16
  } as const
17
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
+
18
28
  export interface ToolHttpBinding {
19
29
  path?: string
20
30
  pathFrom?: string
@@ -115,7 +125,7 @@ export const TOOL_DEFINITIONS = {
115
125
  } satisfies Record<string, ToolMeta>
116
126
 
117
127
  export function createMcpServer(externalPolicyEnv: ExternalPolicyEnv = {}) {
118
- const server = new McpServer(MCP_SERVER_INFO)
128
+ const server = new McpServer(MCP_SERVER_INFO, { instructions: MCP_SERVER_INSTRUCTIONS })
119
129
 
120
130
  const search = TOOL_DEFINITIONS.searchAppleDocumentation
121
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
  // ============================================================================
package/wrangler.jsonc CHANGED
@@ -21,6 +21,7 @@
21
21
  "/",
22
22
  "/.well-known/agent-card.json",
23
23
  "/.well-known/agent-skills/*",
24
+ "/.well-known/mcp-registry-auth",
24
25
  "/.well-known/security.txt",
25
26
  "/message:send",
26
27
  "/message:stream",
@@ -33,6 +34,7 @@
33
34
  "enabled": true
34
35
  },
35
36
  "vars": {
36
- "NODE_ENV": "production"
37
+ "NODE_ENV": "production",
38
+ "MCP_REGISTRY_PUBLIC_KEY": "sL6l9eXq+RN7+VuAVWZGnSacjsgix0TVW5FDLYlBUpg="
37
39
  }
38
40
  }