Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 740da6d

Browse files
authored
chore: create .mcpb bundles for the MCP servers (#9870)
* create .mcpb bundles for the MCP server. * fix build * include an icon for each * get mcpb up to spec * remove from CI * add mcpb build command to README * update README * update bundle configs * update descriptions * include README and LICENSE in bundle * add openWorldHint: true annotation * add links for Claude Desktop in docs * remove dev dep @anthropic-ai/mcpb * formatting
1 parent 6bcb47f commit 740da6d

10 files changed

Lines changed: 385 additions & 15 deletions

File tree

packages/dev/mcp/react-aria/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ Follow Windsurf MCP [documentation](https://docs.windsurf.com/windsurf/cascade/m
109109
| `get_react_aria_page_info` | `{ page_name: string }` | Return page description and list of section titles. |
110110
| `get_react_aria_page` | `{ page_name: string, section_name?: string }` | Return full page markdown, or only the specified section. |
111111

112+
## Privacy Policy
113+
114+
See [Adobe's privacy policy](https://www.adobe.com/privacy/policy.html).
115+
112116
## Development
113117

114118
### Testing locally
@@ -135,4 +139,19 @@ Update your MCP client configuration to use the local MCP server:
135139
}
136140
}
137141
}
142+
```
143+
144+
## Bundling
145+
146+
To build an [MCP Bundle (MCPB)](https://github.com/modelcontextprotocol/mcpb), first prepare the staging directory:
147+
148+
```bash
149+
yarn workspace @react-spectrum/s2-docs generate:mcpb
150+
```
151+
152+
Then validate and pack using the [`@anthropic-ai/mcpb`](https://www.npmjs.com/package/@anthropic-ai/mcpb) CLI (paths are printed by the script above):
153+
154+
```bash
155+
npx @anthropic-ai/mcpb validate <staging-dir>
156+
npx @anthropic-ai/mcpb pack <staging-dir> <output-path>
138157
```

packages/dev/mcp/react-aria/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env node
22
/// <reference types="node" />
3-
import {errorToString} from '../../shared/src/utils.js';
3+
import {errorToString, readPackageVersion} from '../../shared/src/utils.js';
44
import {startServer} from '../../shared/src/server.js';
55

66
// CLI entry for React Aria
@@ -13,7 +13,7 @@ import {startServer} from '../../shared/src/server.js';
1313
);
1414
process.exit(0);
1515
}
16-
await startServer('react-aria', '0.1.0');
16+
await startServer('react-aria', readPackageVersion(import.meta.url));
1717
} catch (err) {
1818
console.error(errorToString(err));
1919
process.exit(1);

packages/dev/mcp/s2/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ Follow Windsurf MCP [documentation](https://docs.windsurf.com/windsurf/cascade/m
111111
| `search_s2_icons` | `{ terms: string \| string[] }` | Search S2 workflow icon names. |
112112
| `search_s2_illustrations` | `{ terms: string \| string[] }` | Search S2 illustration names. |
113113

114+
## Privacy Policy
115+
116+
See [Adobe's privacy policy](https://www.adobe.com/privacy/policy.html).
117+
114118
## Development
115119

116120
### Testing locally
@@ -137,4 +141,19 @@ Update your MCP client configuration to use the local MCP server:
137141
}
138142
}
139143
}
144+
```
145+
146+
## Bundling
147+
148+
To build an [MCP Bundle (MCPB)](https://github.com/modelcontextprotocol/mcpb), first prepare the staging directory:
149+
150+
```bash
151+
yarn workspace @react-spectrum/s2-docs generate:mcpb
152+
```
153+
154+
Then validate and pack using the [`@anthropic-ai/mcpb`](https://www.npmjs.com/package/@anthropic-ai/mcpb) CLI (paths are printed by the script above):
155+
156+
```bash
157+
npx @anthropic-ai/mcpb validate <staging-dir>
158+
npx @anthropic-ai/mcpb pack <staging-dir> <output-path>
140159
```

packages/dev/mcp/s2/src/index.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env node
22
/// <reference types="node" />
3-
import {errorToString} from '../../shared/src/utils.js';
3+
import {errorToString, readPackageVersion} from '../../shared/src/utils.js';
44
import {
55
listIconNames,
66
listIllustrationNames,
@@ -23,14 +23,15 @@ import {z} from 'zod';
2323
process.exit(0);
2424
}
2525

26-
await startServer('s2', '0.1.0', (server: McpServer) => {
26+
await startServer('s2', readPackageVersion(import.meta.url), (server: McpServer) => {
2727
server.registerTool(
2828
'search_s2_icons',
2929
{
3030
title: 'Search S2 icons',
3131
description:
3232
'Searches the S2 workflow icon set by one or more terms; returns matching icon names.',
33-
inputSchema: {terms: z.union([z.string(), z.array(z.string())])}
33+
inputSchema: {terms: z.union([z.string(), z.array(z.string())])},
34+
annotations: {readOnlyHint: true, openWorldHint: true}
3435
},
3536
async ({terms}) => {
3637
const allNames = listIconNames();
@@ -94,7 +95,8 @@ import {z} from 'zod';
9495
title: 'Search S2 illustrations',
9596
description:
9697
'Searches the S2 illustrations set by one or more terms; returns matching illustration names.',
97-
inputSchema: {terms: z.union([z.string(), z.array(z.string())])}
98+
inputSchema: {terms: z.union([z.string(), z.array(z.string())])},
99+
annotations: {readOnlyHint: true, openWorldHint: true}
98100
},
99101
async ({terms}) => {
100102
const allNames = listIllustrationNames();
@@ -158,7 +160,8 @@ import {z} from 'zod';
158160
title: 'Get style macro property values',
159161
description:
160162
'Returns the allowed values for a given S2 style macro property (including expanded color/spacing value lists where applicable).',
161-
inputSchema: {propertyName: z.string()}
163+
inputSchema: {propertyName: z.string()},
164+
annotations: {readOnlyHint: true, openWorldHint: true}
162165
},
163166
async ({propertyName}) => {
164167
const name = String(propertyName ?? '').trim();

packages/dev/mcp/shared/src/server.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ export async function startServer(
3333
? 'List React Spectrum (@react-spectrum/s2) docs pages'
3434
: 'List React Aria docs pages',
3535
description: `Returns a list of available pages in the ${library} docs.`,
36-
inputSchema: {includeDescription: z.boolean().optional()}
36+
inputSchema: {includeDescription: z.boolean().optional()},
37+
annotations: {readOnlyHint: true, openWorldHint: true}
3738
},
3839
async ({includeDescription}) => {
3940
const pages = await buildPageIndex(library);
@@ -53,7 +54,8 @@ export async function startServer(
5354
{
5455
title: 'Get page info',
5556
description: 'Returns page description and list of sections for a given page.',
56-
inputSchema: {page_name: z.string()}
57+
inputSchema: {page_name: z.string()},
58+
annotations: {readOnlyHint: true, openWorldHint: true}
5759
},
5860
async ({page_name}) => {
5961
const ref = await resolvePageRef(library, page_name);
@@ -73,7 +75,8 @@ export async function startServer(
7375
title: 'Get page markdown',
7476
description:
7577
'Returns the full markdown content for a page, or a specific section if provided.',
76-
inputSchema: {page_name: z.string(), section_name: z.string().optional()}
78+
inputSchema: {page_name: z.string(), section_name: z.string().optional()},
79+
annotations: {readOnlyHint: true, openWorldHint: true}
7780
},
7881
async ({page_name, section_name}) => {
7982
const ref = await resolvePageRef(library, page_name);

packages/dev/mcp/shared/src/utils.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
import {dirname, join} from 'path';
2+
import {fileURLToPath} from 'url';
3+
import {readFileSync} from 'fs';
4+
5+
// Resolves the version from the nearest package.json above the CLI entry point.
6+
// Both the npm-published layout (dist/<lib>/src/index.js) and the .mcpb bundle
7+
// layout (server/<lib>/src/index.js) place package.json three levels up.
8+
export function readPackageVersion(entryUrl: string): string {
9+
const entryDir = dirname(fileURLToPath(entryUrl));
10+
const pkgPath = join(entryDir, '..', '..', '..', 'package.json');
11+
try {
12+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
13+
if (typeof pkg.version === 'string' && pkg.version.length > 0) {
14+
return pkg.version;
15+
}
16+
} catch {
17+
// fall through
18+
}
19+
return '0.0.0';
20+
}
21+
122
export function errorToString(err: unknown): string {
223
if (err && typeof err === 'object' && 'stack' in err && typeof (err as any).stack === 'string') {
324
return (err as any).stack as string;

packages/dev/s2-docs/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
"build:s2": "LIBRARY=s2 parcel build 'pages/s2/**/*.mdx' --config .parcelrc-s2-docs --dist-dir dist/s2 --cache-dir ../../../.parcel-cache/s2",
1111
"build:react-aria": "LIBRARY=react-aria parcel build 'pages/react-aria/**/*.mdx' --config .parcelrc-s2-docs --dist-dir dist/react-aria --cache-dir ../../../.parcel-cache/react-aria",
1212
"generate:og": "node scripts/generateOGImages.mjs",
13-
"generate:md": "node scripts/generateMarkdownDocs.mjs"
13+
"generate:md": "node scripts/generateMarkdownDocs.mjs",
14+
"generate:mcpb": "node scripts/generateMcpb.mjs"
1415
},
1516
"dependencies": {
1617
"@babel/parser": "^7.26.3",

packages/dev/s2-docs/pages/react-aria/ai.mdx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const tags = ['ai', 'mcp', 'agent', 'skills', 'llms.txt', 'markdown'];
1717
[Node.js](https://nodejs.org/) must be installed on your system to run the MCP server.
1818

1919
<Tabs aria-label="MCP Clients" density="compact">
20-
<TabList><Tab id="cursor">Cursor</Tab><Tab id="vscode">VS Code</Tab><Tab id="claude-code">Claude Code</Tab><Tab id="codex">Codex</Tab><Tab id="gemini-cli">Gemini CLI</Tab><Tab id="other">Other</Tab></TabList>
20+
<TabList><Tab id="cursor">Cursor</Tab><Tab id="vscode">VS Code</Tab><Tab id="claude-desktop">Claude Code (Desktop)</Tab><Tab id="claude-code">Claude Code (CLI)</Tab><Tab id="codex">Codex</Tab><Tab id="gemini-cli">Gemini CLI</Tab><Tab id="other">Other</Tab></TabList>
2121
<TabPanel id="cursor">
2222
Click the button to install:
2323

@@ -64,6 +64,13 @@ export const tags = ['ai', 'mcp', 'agent', 'skills', 'llms.txt', 'markdown'];
6464
}
6565
```
6666
</TabPanel>
67+
<TabPanel id="claude-desktop">
68+
Click the button to add to Claude Desktop:
69+
70+
<Link href="claude://claude.ai/directory/connectors/ant.dir.gh.adobe.react-aria" aria-label="Add to Claude Desktop">
71+
<img src="https://img.shields.io/badge/Claude_Desktop-Claude_Desktop?style=flat-square&label=Add%20to%20Claude%20Desktop&color=D97757" alt="Add to Claude Desktop" />
72+
</Link>
73+
</TabPanel>
6774
<TabPanel id="claude-code">
6875
Use the Claude Code CLI to add the server:
6976

@@ -121,4 +128,4 @@ Add the `.md` extension to the URL to get the markdown version of a page. Additi
121128

122129
## llms.txt
123130

124-
The <Link href="llms.txt" target="_blank">llms.txt</Link> file contains a list of all the markdown pages available in the React Aria documentation.
131+
The <Link href="llms.txt" target="_blank">llms.txt</Link> file contains a list of all the markdown pages available in the React Aria documentation.

packages/dev/s2-docs/pages/s2/ai.mdx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ export const tags = ['ai', 'mcp', 'agent', 'skills', 'llms.txt', 'markdown'];
1717
[Node.js](https://nodejs.org/) must be installed on your system to run the MCP server.
1818

1919
<Tabs aria-label="MCP Clients" density="compact">
20-
<TabList><Tab id="cursor">Cursor</Tab><Tab id="vscode">VS Code</Tab><Tab id="claude-code">Claude Code</Tab><Tab id="codex">Codex</Tab><Tab id="gemini-cli">Gemini CLI</Tab><Tab id="other">Other</Tab></TabList>
20+
<TabList><Tab id="cursor">Cursor</Tab><Tab id="vscode">VS Code</Tab><Tab id="claude-desktop">Claude Code (Desktop)</Tab><Tab id="claude-code">Claude Code (CLI)</Tab><Tab id="codex">Codex</Tab><Tab id="gemini-cli">Gemini CLI</Tab><Tab id="other">Other</Tab></TabList>
2121
<TabPanel id="cursor">
2222
Click the button to install:
23-
23+
2424
<Link href="cursor://anysphere.cursor-deeplink/mcp/install?name=React%20Spectrum%20(S2)&config=eyJjb21tYW5kIjoibnB4IEByZWFjdC1zcGVjdHJ1bS9tY3BAbGF0ZXN0In0%3D" aria-label="Add to Cursor">
2525
<picture>
2626
<source srcSet="https://cursor.com/deeplink/mcp-install-dark.svg" media="(prefers-color-scheme: light)" />
@@ -64,6 +64,13 @@ export const tags = ['ai', 'mcp', 'agent', 'skills', 'llms.txt', 'markdown'];
6464
}
6565
```
6666
</TabPanel>
67+
<TabPanel id="claude-desktop">
68+
Click the button to add to Claude Desktop:
69+
70+
<Link href="claude://claude.ai/directory/connectors/ant.dir.gh.adobe.s2" aria-label="Add to Claude Desktop">
71+
<img src="https://img.shields.io/badge/Claude_Desktop-Claude_Desktop?style=flat-square&label=Add%20to%20Claude%20Desktop&color=D97757" alt="Add to Claude Desktop" />
72+
</Link>
73+
</TabPanel>
6774
<TabPanel id="claude-code">
6875
Use the Claude Code CLI to add the server:
6976

0 commit comments

Comments
 (0)