Skip to content

Commit 443d849

Browse files
committed
Refactor manifest schema to replace static_responses and client_extensions with _meta for improved organization
1 parent caa61bc commit 443d849

File tree

4 files changed

+62
-206
lines changed

4 files changed

+62
-206
lines changed

MANIFEST.md

Lines changed: 45 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -156,52 +156,52 @@ A full `manifest.json` with most of the optional fields looks like this:
156156
"max": 100
157157
}
158158
},
159-
"static_responses": {
160-
"initialize": {
161-
"capabilities": {},
162-
"instructions": "When the user wants to search files, use the search_files tool but only after asking them whether the files are local.",
163-
"protocolVersion": "2025-06-18",
164-
"serverInfo": {
165-
"name": "MyMCPExtension",
166-
"title": "My MCP Extension - Pro Edition",
167-
"version": "1.0.0"
168-
}
169-
},
170-
"tools/list": {
171-
"tools": [
172-
{
173-
"name": "search_files",
174-
"title": "File search",
175-
"description": "Search for files in a directory",
176-
"inputSchema": {
177-
"type": "object",
178-
"properties": {
179-
"fileSpec": {
180-
"type": "string",
181-
"description": "The file name to search for. Wildcards are supported"
182-
}
183-
},
184-
"required": ["fileSpec"]
185-
},
186-
"outputSchema": {
187-
"type": "object",
188-
"properties": {
189-
"searchResults": {
190-
"type": "array",
191-
"description": "The list of file paths that were found",
192-
"items": {
193-
"type": "string"
194-
}
195-
}
196-
},
159+
"_meta": {
160+
"com.microsoft.windows": {
161+
"package_family_name": "MyMcpMSIXPackage_51g09708xawrw",
162+
"static_responses": {
163+
"initialize": {
164+
"capabilities": {},
165+
"instructions": "When the user wants to search files, use the search_files tool but only after asking them whether the files are local.",
166+
"protocolVersion": "2025-06-18",
167+
"serverInfo": {
168+
"name": "MyMCPExtension",
169+
"title": "My MCP Extension - Pro Edition",
170+
"version": "1.0.0"
197171
}
172+
},
173+
"tools/list": {
174+
"tools": [
175+
{
176+
"name": "search_files",
177+
"title": "File search",
178+
"description": "Search for files in a directory",
179+
"inputSchema": {
180+
"type": "object",
181+
"properties": {
182+
"fileSpec": {
183+
"type": "string",
184+
"description": "The file name to search for. Wildcards are supported"
185+
}
186+
},
187+
"required": ["fileSpec"]
188+
},
189+
"outputSchema": {
190+
"type": "object",
191+
"properties": {
192+
"searchResults": {
193+
"type": "array",
194+
"description": "The list of file paths that were found",
195+
"items": {
196+
"type": "string"
197+
}
198+
}
199+
},
200+
}
201+
}
202+
]
198203
}
199-
]
200-
}
201-
},
202-
"client_extensions": {
203-
"windows": {
204-
"package_family_name": "MyMcpMSIXPackage_51g09708xawrw"
204+
},
205205
}
206206
}
207207
}
@@ -237,8 +237,7 @@ A full `manifest.json` with most of the optional fields looks like this:
237237
- **privacy_policies**: Array of URLs to privacy policies for external services that handle user data. Required when the extension connects to external services (first- or third-party) that process user data. Each URL should link to the respective service's privacy policy document
238238
- **compatibility**: Compatibility requirements (client app version, platforms, and runtime versions)
239239
- **user_config**: User-configurable options for the extension (see User Configuration section)
240-
- **static_responses**: A set of static response to protocol messages (for example, `initialize`, `tools/list`, etc.). This allows clients to avoid running a server when a static response is present (e.g. to get the set of tools with their input schemas). For more information, consult the [MCP specification](https://modelcontextprotocol.io/specification).
241-
- **client_extensions**: Platform-specific client integration metadata (e.g., Windows `package_family_name`, macOS bundle identifiers) enabling tighter OS/app store integration
240+
- **_meta**: Platform-specific client integration metadata (e.g., Windows `package_family_name`, macOS bundle identifiers) enabling tighter OS/app store integration. The keys in the `_meta` object are reverse-DNS namespaced, and the values are a dictionary of platform-specific metadata.
242241

243242
## Compatibility
244243

@@ -598,8 +597,6 @@ These fields describe the tools and prompts your MCP server provides. For server
598597

599598
Note: Resources are not included in the manifest because MCP resources are inherently dynamic - they represent URIs to data that the server discovers at runtime based on configuration, filesystem state, database connections, etc.
600599

601-
You can provide richer static tools by adding an `tools/list` entry inside the `static_responses` object.
602-
603600
### Static Declaration
604601

605602
For servers with a fixed set of capabilities, list them in arrays.
@@ -659,62 +656,3 @@ The `_generated` fields:
659656

660657
This helps implementing apps understand that querying the server at runtime will reveal more capabilities than what's declared in the manifest.
661658

662-
## Static responses
663-
664-
Servers can provide clients with static responses to protocol messages. Static responses let clients short-circuit server startup for discovery or platform integration metadata.
665-
This is useful to obtain rich tool schema information, server instructions, protocol version that the server supports, etc.
666-
The `static_responses` object is a dictionary where the key is the name of an MCP JSON-RPC method. For more information, consult the [MCP specification](https://modelcontextprotocol.io/specification).
667-
668-
If both the `tools/list` static response and the `tools` array are present, clients should use the tools described in `tools/list`.
669-
670-
Example:
671-
672-
```json
673-
{
674-
"static_responses": {
675-
"initialize": {
676-
"capabilities": {},
677-
"instructions": "When the user wants to search files, use the search_files tool but only after asking them whether the files are local.",
678-
"protocolVersion": "2025-06-18",
679-
"serverInfo": {
680-
"name": "MyMCPExtension",
681-
"title": "My MCP Extension - Pro Edition",
682-
"version": "1.0.0"
683-
}
684-
},
685-
"tools/list": {
686-
"tools": [
687-
{
688-
"name": "search_files",
689-
"title": "File search",
690-
"description": "Search for files in a directory",
691-
"inputSchema": {
692-
"type": "object",
693-
"properties": {
694-
"fileSpec": {
695-
"type": "string",
696-
"description": "The file name to search for. Wildcards are supported"
697-
}
698-
},
699-
"required": ["fileSpec"]
700-
},
701-
"outputSchema": {
702-
"type": "object",
703-
"properties": {
704-
"searchResults": {
705-
"type": "array",
706-
"description": "The list of file paths that were found",
707-
"items": {
708-
"type": "string"
709-
}
710-
}
711-
},
712-
}
713-
}
714-
]
715-
}
716-
},
717-
}
718-
```
719-
720-

src/schemas-loose.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,7 @@ export const McpbManifestSchema = z
107107
user_config: z
108108
.record(z.string(), McpbUserConfigurationOptionSchema)
109109
.optional(),
110-
static_responses: z.record(z.string(), z.any()).optional(),
111-
client_extensions: z.record(z.string(), z.record(z.string(), z.any())).optional(),
110+
_meta: z.record(z.string(), z.record(z.string(), z.any())).optional(),
112111
})
113112
.refine((data) => !!(data.dxt_version || data.manifest_version), {
114113
message:

src/schemas.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,7 @@ export const McpbManifestSchema = z
110110
user_config: z
111111
.record(z.string(), McpbUserConfigurationOptionSchema)
112112
.optional(),
113-
static_responses: z.record(z.string(), z.any()).optional(),
114-
client_extensions: z.record(z.string(), z.record(z.string(), z.any())).optional(),
113+
_meta: z.record(z.string(), z.record(z.string(), z.any())).optional(),
115114
})
116115
.refine((data) => !!(data.dxt_version || data.manifest_version), {
117116
message:

test/schemas.test.ts

Lines changed: 15 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ describe("McpbManifestSchema", () => {
137137
});
138138
});
139139

140-
describe("client_extensions", () => {
140+
describe("_meta", () => {
141141
const base = {
142142
manifest_version: "0.2",
143143
name: "client-ext-test",
@@ -151,23 +151,23 @@ describe("McpbManifestSchema", () => {
151151
},
152152
};
153153

154-
it("accepts valid client_extensions object with nested dictionaries", () => {
154+
it("accepts valid _meta object with nested dictionaries", () => {
155155
const manifest = {
156156
...base,
157-
client_extensions: {
158-
windows: { package_family_name: "Pkg_123", channel: "stable" },
159-
darwin: { bundle_id: "com.example.app", notarized: true },
157+
_meta: {
158+
"com.microsoft.windows": { package_family_name: "Pkg_123", channel: "stable" },
159+
"com.apple.darwin": { bundle_id: "com.example.app", notarized: true },
160160
},
161161
};
162162
const result = McpbManifestSchema.safeParse(manifest);
163163
expect(result.success).toBe(true);
164164
});
165165

166-
it("rejects primitive value in client_extensions entry", () => {
166+
it("rejects primitive value in _meta entry", () => {
167167
const manifest = {
168168
...base,
169-
client_extensions: {
170-
windows: "raw-string" as unknown as Record<string, unknown>,
169+
_meta: {
170+
"com.microsoft.windows": "raw-string" as unknown as Record<string, unknown>,
171171
},
172172
};
173173
const result = McpbManifestSchema.safeParse(manifest);
@@ -178,113 +178,33 @@ describe("McpbManifestSchema", () => {
178178
}
179179
});
180180

181-
it("rejects array value in client_extensions entry", () => {
181+
it("rejects array value in _meta entry", () => {
182182
const manifest = {
183183
...base,
184-
client_extensions: {
185-
linux: [] as unknown as Record<string, unknown>,
184+
_meta: {
185+
"com.apple.darwin": [] as unknown as Record<string, unknown>,
186186
},
187187
};
188188
const result = McpbManifestSchema.safeParse(manifest);
189189
expect(result.success).toBe(false);
190190
});
191191

192-
it("rejects null value in client_extensions entry", () => {
192+
it("rejects null value in _meta entry", () => {
193193
const manifest = {
194194
...base,
195-
client_extensions: {
195+
_meta: {
196196
custom: null as unknown as Record<string, unknown>,
197197
},
198198
};
199199
const result = McpbManifestSchema.safeParse(manifest);
200200
expect(result.success).toBe(false);
201201
});
202202

203-
it("allows empty object for client_extensions", () => {
204-
const manifest = { ...base, client_extensions: {} };
203+
it("allows empty object for _meta", () => {
204+
const manifest = { ...base, _meta: {} };
205205
const result = McpbManifestSchema.safeParse(manifest);
206206
expect(result.success).toBe(true);
207207
});
208208
});
209209

210-
describe("static_responses", () => {
211-
const base = {
212-
manifest_version: "0.2",
213-
name: "static-resp-test",
214-
version: "1.0.0",
215-
description: "Test manifest",
216-
author: { name: "Author" },
217-
server: {
218-
type: "node" as const,
219-
entry_point: "server/index.js",
220-
mcp_config: { command: "node", args: ["server/index.js"] },
221-
},
222-
};
223-
224-
it("accepts valid static_responses with object entries", () => {
225-
const manifest = {
226-
...base,
227-
static_responses: {
228-
initialize: {
229-
capabilities: {},
230-
protocolVersion: "2025-06-18",
231-
serverInfo: { name: "Test", version: "1.0.0" },
232-
},
233-
"tools/list": {
234-
tools: [
235-
{
236-
name: "search_files",
237-
description: "Search for files",
238-
inputSchema: { type: "object", properties: { query: { type: "string" } } },
239-
},
240-
],
241-
},
242-
},
243-
};
244-
const result = McpbManifestSchema.safeParse(manifest);
245-
expect(result.success).toBe(true);
246-
});
247-
248-
it("accepts primitive value in static_responses entry", () => {
249-
const manifest = {
250-
...base,
251-
static_responses: {
252-
ping: "pong",
253-
code: 200,
254-
ok: true,
255-
},
256-
};
257-
const result = McpbManifestSchema.safeParse(manifest);
258-
expect(result.success).toBe(true);
259-
});
260-
261-
it("accepts array value in static_responses entry", () => {
262-
const manifest = {
263-
...base,
264-
static_responses: {
265-
list: [1, 2, 3],
266-
},
267-
};
268-
const result = McpbManifestSchema.safeParse(manifest);
269-
expect(result.success).toBe(true);
270-
});
271-
272-
it("accepts null value in static_responses entry", () => {
273-
const manifest = {
274-
...base,
275-
static_responses: {
276-
initialize: null,
277-
},
278-
};
279-
const result = McpbManifestSchema.safeParse(manifest);
280-
expect(result.success).toBe(true);
281-
});
282-
283-
it("allows empty object for static_responses", () => {
284-
const manifest = { ...base, static_responses: {} };
285-
const result = McpbManifestSchema.safeParse(manifest);
286-
expect(result.success).toBe(true);
287-
});
288-
});
289210
});
290-

0 commit comments

Comments
 (0)