Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion docs/author-guide/collection-schema.md
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be good to add one HTTP mcp server as example and part of the scaffolding

Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,29 @@ items: # Required. List of resources (max 50)

mcp: # Optional. MCP server configurations
items:
# Stdio server (local process)
python-analyzer: # Server name
command: python # Required. Command to start server
type: stdio # Optional. Default: stdio
command: python # Required for stdio. Command to start server
args: # Optional. Command arguments
- "${bundlePath}/server.py" # ${bundlePath} = installed bundle path
env: # Optional. Environment variables
LOG_LEVEL: info
envFile: "${bundlePath}/.env" # Optional. Path to env file
disabled: false # Optional. Default: false
description: Python analyzer # Optional. Human-readable description

# Remote HTTP server
api-server:
type: http # Required for remote. One of: http, sse
url: "https://api.example.com/mcp" # Required for remote
headers: # Optional. Authentication headers
Authorization: "Bearer ${env:API_TOKEN}"

# Remote SSE server
streaming-server:
type: sse
url: "https://stream.example.com/mcp/events"

display: # Optional. UI preferences
color: "#3776AB" # Color theme
Expand All @@ -44,6 +60,24 @@ display: # Optional. UI preferences
show_badge: true # Show badge in UI
```

## MCP Server Duplicate Detection

When multiple collections define the same MCP server, Prompt Registry automatically detects and manages duplicates to prevent conflicts in VS Code's `mcp.json`.

### How It Works

**Server Identity** is computed based on server type:
- **Stdio servers**: `command` + `args` (e.g., `node server.js --port 3000`)
- **Remote servers**: `url` (e.g., `https://api.example.com/mcp`)

**Behavior**:
1. First installed server with a given identity remains **enabled**
2. Subsequent duplicates are **disabled** with a description noting the original
3. When the active server's bundle is uninstalled, a disabled duplicate is **re-enabled**
4. At least one instance stays active until all bundles with that server are removed

This allows multiple collections to safely share common MCP servers without conflicts.

## Validation

Run `Ctrl+Shift+P` → "Prompt Registry: Validate Collections"
Expand Down
76 changes: 75 additions & 1 deletion docs/contributor-guide/architecture/mcp-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,34 @@ graph TD
D --> E
```

## Bundle Manifest Schema
## Server Types

### Stdio Servers (Local Process)

```yaml
mcpServers:
server-name:
type: stdio # Optional (default)
command: string # Required
args: string[] # Optional
env: Record<string, string> # Optional
envFile: string # Optional - path to .env file
disabled: boolean # Optional (default: false)
description: string # Optional
```

### Remote Servers (HTTP/SSE)

```yaml
mcpServers:
api-server:
type: http # Required: 'http' or 'sse'
url: string # Required - supports http://, https://, unix://, pipe://
headers: Record<string, string> # Optional - for authentication
disabled: boolean # Optional
description: string # Optional
```

## Variable Substitution

| Variable | Description |
Expand Down Expand Up @@ -68,6 +84,64 @@ mcpServers:
3. Update tracking metadata
4. Atomic operations with backup/rollback

## Duplicate Detection Algorithm

When multiple bundles define the same MCP server, duplicates are automatically detected and disabled.

### Server Identity Computation

```typescript
computeServerIdentity(config: McpServerConfig): string {
if (isRemoteServerConfig(config)) {
return `remote:${config.url}`;
} else {
const argsStr = config.args?.join('|') || '';
return `stdio:${config.command}:${argsStr}`;
}
}
```

| Server Type | Identity Format | Example |
|-------------|-----------------|----------|
| Stdio | `stdio:{command}:{args joined by \|}` | `stdio:node:server.js\|--port\|3000` |
| Remote | `remote:{url}` | `remote:https://api.example.com/mcp` |

### Detection Flow

```mermaid
graph TD
A["After server installation"]
B["detectAndDisableDuplicates()"]
C["For each server in mcp.json"]
D{"Identity already seen?"}
E["Record identity → server mapping"]
F["Mark as disabled\nAdd description: 'Duplicate of X'"]
G["Write updated config"]

A --> B
B --> C
C --> D
D -->|No| E
D -->|Yes & enabled| F
E --> C
F --> C
C -->|Done| G
```

### Lifecycle Behavior

1. **Install**: First server with identity stays enabled; duplicates disabled
2. **Uninstall**: When active server's bundle is removed, remaining duplicates are re-evaluated
3. **Invariant**: At least one server per identity remains active until all bundles are removed

### Type Guards

```typescript
// Discriminate server types
isStdioServerConfig(config) // true if has 'command', no 'url'
isRemoteServerConfig(config) // true if has 'url' and type is 'http'|'sse'
```

## See Also

- [Installation Flow](./installation-flow.md) — Bundle installation
Expand Down
9 changes: 9 additions & 0 deletions lib/bin/generate-manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ try {
.replace(/^@[^/]+\//, '');
}

// Extract MCP servers from either 'mcp.items' or 'mcpServers' field (matching AwesomeCopilotAdapter)
const mcpServers = collection.mcpServers || (collection.mcp && collection.mcp.items);

// Create deployment manifest
const manifest = {
id: manifestId,
Expand All @@ -130,6 +133,7 @@ try {
repository: packageJson.repository?.url?.replace(/^git\+/, '').replace(/\.git$/, '') || '',
prompts: prompts,
dependencies: [],
...(mcpServers && Object.keys(mcpServers).length > 0 ? { mcpServers } : {}),
};

// Write deployment manifest
Expand All @@ -150,6 +154,11 @@ try {
const typeLabel = type.charAt(0).toUpperCase() + type.slice(1) + 's';
console.log(` ${typeLabel}: ${typesCounts[type]}`);
});

// Log MCP servers count if present
if (mcpServers && Object.keys(mcpServers).length > 0) {
console.log(` MCP Servers: ${Object.keys(mcpServers).length}`);
}
} catch (error) {
console.error('❌ Error generating deployment manifest:', error.message);
process.exit(1);
Expand Down
4 changes: 2 additions & 2 deletions lib/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@prompt-registry/collection-scripts",
"version": "1.0.0",
"version": "1.0.1",
"description": "Shared scripts for building, validating, and publishing Copilot prompt collections",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
Loading