@@ -238,9 +238,10 @@ Full Model Context Protocol support with external tool integration:
|
||||
- Stdio and HTTP transports for connecting to MCP servers
|
||||
- **OAuth support**: Explicit `clientId` and `callbackPort` in MCP server config, manual OAuth callbacks via slash commands
|
||||
- **Browser server filtering**: Automatically filters browser-type MCP servers to prevent conflicts with built-in browser tool
|
||||
- **Automatic Exa filtering**: Extracts Exa API keys and prefers the native Exa integration
|
||||
- **Config schema + setup guide**: [`docs/mcp-config.md`](./docs/mcp-config.md) and [`packages/coding-agent/src/config/mcp-schema.json`](./packages/coding-agent/src/config/mcp-schema.json)
|
||||
- Plugin CLI (`omp plugin install/enable/configure/doctor`)
|
||||
- Hot-loadable plugins from `~/.omp/plugins/` with npm/bun integration
|
||||
- Automatic Exa MCP server filtering with API key extraction
|
||||
- `disabledServers` works on both project-level and user-level third-party servers
|
||||
|
||||
### + Web Search & Fetch
|
||||
|
||||
@@ -0,0 +1,449 @@
|
||||
# MCP configuration in OMP
|
||||
|
||||
This guide explains how to add, edit, and validate MCP servers for the OMP coding agent.
|
||||
|
||||
Source of truth in code:
|
||||
|
||||
- Runtime config types: `packages/coding-agent/src/mcp/types.ts`
|
||||
- Config writer: `packages/coding-agent/src/mcp/config-writer.ts`
|
||||
- Loader + validation: `packages/coding-agent/src/mcp/config.ts`
|
||||
- Standalone `mcp.json` discovery: `packages/coding-agent/src/discovery/mcp-json.ts`
|
||||
- Schema: `packages/coding-agent/src/config/mcp-schema.json`
|
||||
|
||||
## Preferred config locations
|
||||
|
||||
OMP can discover MCP servers from multiple tools (`.claude/`, `.cursor/`, `.vscode/`, `opencode.json`, and more), but for OMP-native configuration you should usually use one of these files:
|
||||
|
||||
- Project: `.omp/mcp.json`
|
||||
- User: `~/.omp/mcp.json`
|
||||
|
||||
OMP also accepts fallback standalone files in the project root:
|
||||
|
||||
- `mcp.json`
|
||||
- `.mcp.json`
|
||||
|
||||
Use `.omp/mcp.json` when you want OMP to own the configuration. Use root `mcp.json` / `.mcp.json` only when you want a portable fallback file that other MCP clients may also read.
|
||||
|
||||
## Add a schema reference
|
||||
|
||||
Add this line at the top of the file for editor autocomplete and validation:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/config/mcp-schema.json",
|
||||
"mcpServers": {}
|
||||
}
|
||||
```
|
||||
|
||||
OMP now writes this automatically when `/mcp add`, `/mcp enable`, `/mcp disable`, `/mcp reauth`, or other config-writing flows create or update an OMP-managed MCP file.
|
||||
|
||||
## File shape
|
||||
|
||||
OMP supports this top-level structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/config/mcp-schema.json",
|
||||
"mcpServers": {
|
||||
"server-name": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "some-mcp-server"]
|
||||
}
|
||||
},
|
||||
"disabledServers": ["server-name"]
|
||||
}
|
||||
```
|
||||
|
||||
Top-level keys:
|
||||
|
||||
- `$schema` — optional JSON Schema URL for tooling
|
||||
- `mcpServers` — map of server name to server config
|
||||
- `disabledServers` — user-level denylist used to turn off discovered servers by name
|
||||
|
||||
Server names must match `^[a-zA-Z0-9_.-]{1,100}$`.
|
||||
|
||||
## Supported server fields
|
||||
|
||||
Shared fields for every transport:
|
||||
|
||||
- `enabled?: boolean` — skip this server when `false`
|
||||
- `timeout?: number` — connection timeout in milliseconds
|
||||
- `auth?: { ... }` — auth metadata used by OMP for OAuth/API-key flows
|
||||
- `oauth?: { ... }` — explicit OAuth client settings used during auth/reauth
|
||||
|
||||
### `stdio` transport
|
||||
|
||||
`stdio` is the default when `type` is omitted.
|
||||
|
||||
Required:
|
||||
|
||||
- `command: string`
|
||||
|
||||
Optional:
|
||||
|
||||
- `type?: "stdio"`
|
||||
- `args?: string[]`
|
||||
- `env?: Record<string, string>`
|
||||
- `cwd?: string`
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/config/mcp-schema.json",
|
||||
"mcpServers": {
|
||||
"filesystem": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"-y",
|
||||
"@modelcontextprotocol/server-filesystem",
|
||||
"/Users/alice/projects",
|
||||
"/Users/alice/Documents"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This follows the official Filesystem MCP server package (`@modelcontextprotocol/server-filesystem`).
|
||||
|
||||
### `http` transport
|
||||
|
||||
Required:
|
||||
|
||||
- `type: "http"`
|
||||
- `url: string`
|
||||
|
||||
Optional:
|
||||
|
||||
- `headers?: Record<string, string>`
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/config/mcp-schema.json",
|
||||
"mcpServers": {
|
||||
"github": {
|
||||
"type": "http",
|
||||
"url": "https://api.githubcopilot.com/mcp/"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This matches GitHub's hosted GitHub MCP server endpoint.
|
||||
|
||||
### `sse` transport
|
||||
|
||||
Required:
|
||||
|
||||
- `type: "sse"`
|
||||
- `url: string`
|
||||
|
||||
Optional:
|
||||
|
||||
- `headers?: Record<string, string>`
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/config/mcp-schema.json",
|
||||
"mcpServers": {
|
||||
"legacy-remote": {
|
||||
"type": "sse",
|
||||
"url": "https://example.com/mcp/sse"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`sse` is still supported for compatibility, but the MCP spec now prefers Streamable HTTP (`type: "http"`) for new servers.
|
||||
|
||||
## Auth fields
|
||||
|
||||
OMP understands two auth-related objects.
|
||||
|
||||
### `auth`
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "oauth" | "apikey",
|
||||
"credentialId": "optional-stored-credential-id",
|
||||
"tokenUrl": "optional-token-endpoint",
|
||||
"clientId": "optional-client-id",
|
||||
"clientSecret": "optional-client-secret"
|
||||
}
|
||||
```
|
||||
|
||||
Use this when OMP should remember how to rehydrate credentials for a server.
|
||||
|
||||
### `oauth`
|
||||
|
||||
```json
|
||||
{
|
||||
"clientId": "...",
|
||||
"clientSecret": "...",
|
||||
"redirectUri": "...",
|
||||
"callbackPort": 3334,
|
||||
"callbackPath": "/oauth/callback"
|
||||
}
|
||||
```
|
||||
|
||||
Use this when the MCP server requires explicit OAuth client settings.
|
||||
|
||||
Slack is the clearest current example. Slack's MCP server is hosted at `https://mcp.slack.com/mcp`, uses Streamable HTTP, and requires confidential OAuth with your Slack app's client credentials.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/config/mcp-schema.json",
|
||||
"mcpServers": {
|
||||
"slack": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.slack.com/mcp",
|
||||
"oauth": {
|
||||
"clientId": "YOUR_SLACK_CLIENT_ID",
|
||||
"clientSecret": "YOUR_SLACK_CLIENT_SECRET"
|
||||
},
|
||||
"auth": {
|
||||
"type": "oauth",
|
||||
"tokenUrl": "https://slack.com/api/oauth.v2.user.access",
|
||||
"clientId": "YOUR_SLACK_CLIENT_ID",
|
||||
"clientSecret": "YOUR_SLACK_CLIENT_SECRET"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Relevant Slack endpoints from Slack's docs:
|
||||
|
||||
- MCP endpoint: `https://mcp.slack.com/mcp`
|
||||
- Authorization endpoint: `https://slack.com/oauth/v2_user/authorize`
|
||||
- Token endpoint: `https://slack.com/api/oauth.v2.user.access`
|
||||
|
||||
## Common copy-paste examples
|
||||
|
||||
### Filesystem server via stdio
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/config/mcp-schema.json",
|
||||
"mcpServers": {
|
||||
"filesystem": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"-y",
|
||||
"@modelcontextprotocol/server-filesystem",
|
||||
"/absolute/path/one",
|
||||
"/absolute/path/two"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### GitHub hosted server via HTTP
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/config/mcp-schema.json",
|
||||
"mcpServers": {
|
||||
"github": {
|
||||
"type": "http",
|
||||
"url": "https://api.githubcopilot.com/mcp/"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### GitHub local server via Docker
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/config/mcp-schema.json",
|
||||
"mcpServers": {
|
||||
"github": {
|
||||
"command": "docker",
|
||||
"args": [
|
||||
"run",
|
||||
"-i",
|
||||
"--rm",
|
||||
"-e",
|
||||
"GITHUB_PERSONAL_ACCESS_TOKEN",
|
||||
"ghcr.io/github/github-mcp-server"
|
||||
],
|
||||
"env": {
|
||||
"GITHUB_PERSONAL_ACCESS_TOKEN": "GITHUB_PERSONAL_ACCESS_TOKEN"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This matches GitHub's official local Docker image `ghcr.io/github/github-mcp-server`.
|
||||
|
||||
### Slack hosted server via OAuth
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/config/mcp-schema.json",
|
||||
"mcpServers": {
|
||||
"slack": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.slack.com/mcp",
|
||||
"oauth": {
|
||||
"clientId": "YOUR_SLACK_CLIENT_ID",
|
||||
"clientSecret": "YOUR_SLACK_CLIENT_SECRET"
|
||||
},
|
||||
"auth": {
|
||||
"type": "oauth",
|
||||
"tokenUrl": "https://slack.com/api/oauth.v2.user.access",
|
||||
"clientId": "YOUR_SLACK_CLIENT_ID",
|
||||
"clientSecret": "YOUR_SLACK_CLIENT_SECRET"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Secrets and variable resolution
|
||||
|
||||
This is the part that usually trips people up.
|
||||
|
||||
### In `.omp/mcp.json` and `~/.omp/mcp.json`
|
||||
|
||||
Before OMP launches a server or makes an HTTP request, it resolves `env` and `headers` values like this:
|
||||
|
||||
1. If a value starts with `!`, OMP runs it as a shell command and uses trimmed stdout.
|
||||
2. Otherwise OMP first checks whether the value matches an environment variable name.
|
||||
3. If that environment variable is not set, OMP uses the string literally.
|
||||
|
||||
Examples:
|
||||
|
||||
```json
|
||||
{
|
||||
"env": {
|
||||
"GITHUB_PERSONAL_ACCESS_TOKEN": "GITHUB_PERSONAL_ACCESS_TOKEN"
|
||||
},
|
||||
"headers": {
|
||||
"X-MCP-Insiders": "true"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
That means this is valid and convenient for local secrets:
|
||||
|
||||
- `"GITHUB_PERSONAL_ACCESS_TOKEN": "GITHUB_PERSONAL_ACCESS_TOKEN"` → copy from the current shell environment
|
||||
- `"Authorization": "Bearer hardcoded-token"` → use the literal value
|
||||
- `"Authorization": "!printf 'Bearer %s' \"$GITHUB_TOKEN\""` → build the header from a command
|
||||
|
||||
### In root `mcp.json` and `.mcp.json`
|
||||
|
||||
The standalone fallback loader also expands `${VAR}` and `${VAR:-default}` inside strings during discovery.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"github": {
|
||||
"type": "http",
|
||||
"url": "https://api.githubcopilot.com/mcp/",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${GITHUB_TOKEN}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you want the least surprising OMP behavior, prefer `.omp/mcp.json` and use explicit env/header values.
|
||||
|
||||
## `disabledServers`
|
||||
|
||||
`disabledServers` is mainly useful in the user config file (`~/.omp/mcp.json`) when a server is discovered from some other source and you want OMP to ignore it without editing that other tool's config.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/config/mcp-schema.json",
|
||||
"disabledServers": ["github", "slack"]
|
||||
}
|
||||
```
|
||||
|
||||
## `/mcp add` vs editing JSON directly
|
||||
|
||||
Use `/mcp add` when you want guided setup.
|
||||
|
||||
Use direct JSON editing when:
|
||||
|
||||
- you need a transport or auth option the wizard does not prompt for yet
|
||||
- you want to paste a server definition from another MCP client
|
||||
- you want schema-backed validation in your editor
|
||||
|
||||
After editing, use:
|
||||
|
||||
- `/mcp reload` to rediscover and reconnect servers in the current session
|
||||
- `/mcp list` to see which config file a server came from
|
||||
- `/mcp test <name>` to test a single server
|
||||
|
||||
## Validation rules OMP enforces
|
||||
|
||||
From `validateServerConfig()` in `packages/coding-agent/src/mcp/config.ts`:
|
||||
|
||||
- `stdio` requires `command`
|
||||
- `http` and `sse` require `url`
|
||||
- a server cannot set both `command` and `url`
|
||||
- unknown `type` values are rejected
|
||||
|
||||
Practical implications:
|
||||
|
||||
- Omitting `type` means `stdio`
|
||||
- If you paste a remote server config and forget `"type": "http"`, OMP will treat it as `stdio` and complain that `command` is missing
|
||||
- `sse` remains valid for compatibility, but new hosted servers should usually be configured as `http`
|
||||
|
||||
## Discovery and precedence
|
||||
|
||||
OMP does not merge duplicate server definitions across files. Discovery providers are prioritized, and the higher-priority definition wins.
|
||||
|
||||
In practice:
|
||||
|
||||
- prefer `.omp/mcp.json` or `~/.omp/mcp.json` when you want an OMP-specific override
|
||||
- keep server names unique across tools when possible
|
||||
- use `disabledServers` in the user config when a third-party config keeps reintroducing a server you do not want
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### `Server "name": stdio server requires "command" field`
|
||||
|
||||
You probably omitted `type: "http"` on a remote server.
|
||||
|
||||
### `Server "name": both "command" and "url" are set`
|
||||
|
||||
Pick one transport. OMP treats `command` as stdio and `url` as http/sse.
|
||||
|
||||
### `/mcp add` worked but the server still does not connect
|
||||
|
||||
The JSON is valid, but the server may still be unreachable. Use `/mcp test <name>` and check whether:
|
||||
|
||||
- the binary or Docker image exists
|
||||
- required environment variables are set
|
||||
- the remote URL is reachable
|
||||
- the OAuth or API token is valid
|
||||
|
||||
### The server exists in another tool's config but not in OMP
|
||||
|
||||
Run `/mcp list`. OMP discovers many third-party MCP files, but project-level loading can also be disabled via the `mcp.enableProjectConfig` setting.
|
||||
|
||||
## References
|
||||
|
||||
- MCP transport spec: https://modelcontextprotocol.io/specification/2025-03-26/basic/transports
|
||||
- Filesystem server package: https://www.npmjs.com/package/@modelcontextprotocol/server-filesystem
|
||||
- GitHub MCP server: https://github.com/github/github-mcp-server
|
||||
- Slack MCP server docs: https://docs.slack.dev/ai/slack-mcp-server/
|
||||
@@ -8,5 +8,7 @@ For installation, setup, provider configuration, model roles, slash commands, an
|
||||
|
||||
Package-specific references:
|
||||
- [CHANGELOG](./CHANGELOG.md)
|
||||
- [Documentation](./docs/)
|
||||
- [MCP configuration guide](../../docs/mcp-config.md)
|
||||
- [MCP runtime lifecycle](../../docs/mcp-runtime-lifecycle.md)
|
||||
- [MCP server/tool authoring](../../docs/mcp-server-tool-authoring.md)
|
||||
- [DEVELOPMENT](./DEVELOPMENT.md)
|
||||
|
||||
@@ -23,6 +23,8 @@ export interface MCPServer {
|
||||
args?: string[];
|
||||
/** Environment variables */
|
||||
env?: Record<string, string>;
|
||||
/** Working directory for stdio transport */
|
||||
cwd?: string;
|
||||
/** URL (for HTTP/SSE transport) */
|
||||
url?: string;
|
||||
/** HTTP headers (for HTTP transport) */
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/config/mcp-schema.json",
|
||||
"title": "OMP MCP configuration",
|
||||
"description": "Schema for mcp.json, .mcp.json, .omp/mcp.json, and ~/.omp/mcp.json used by the OMP coding agent.",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"description": "Optional schema reference for editor autocomplete and validation."
|
||||
},
|
||||
"mcpServers": {
|
||||
"type": "object",
|
||||
"description": "Map of MCP server name to server configuration.",
|
||||
"propertyNames": {
|
||||
"pattern": "^[a-zA-Z0-9_.-]{1,100}$"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"$ref": "#/$defs/serverConfig"
|
||||
}
|
||||
},
|
||||
"disabledServers": {
|
||||
"type": "array",
|
||||
"description": "User-level denylist for disabling discovered servers by name.",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"stringMap": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"authConfig": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["type"],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["oauth", "apikey"],
|
||||
"description": "Auth strategy understood by OMP."
|
||||
},
|
||||
"credentialId": {
|
||||
"type": "string",
|
||||
"description": "Stored OAuth credential id from agent auth storage."
|
||||
},
|
||||
"tokenUrl": {
|
||||
"type": "string",
|
||||
"description": "Token endpoint persisted for refresh."
|
||||
},
|
||||
"clientId": {
|
||||
"type": "string",
|
||||
"description": "OAuth client id persisted for refresh."
|
||||
},
|
||||
"clientSecret": {
|
||||
"type": "string",
|
||||
"description": "OAuth client secret persisted for refresh."
|
||||
}
|
||||
}
|
||||
},
|
||||
"oauthConfig": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"clientId": {
|
||||
"type": "string"
|
||||
},
|
||||
"clientSecret": {
|
||||
"type": "string"
|
||||
},
|
||||
"redirectUri": {
|
||||
"type": "string"
|
||||
},
|
||||
"callbackPort": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 65535
|
||||
},
|
||||
"callbackPath": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"description": "Explicit OAuth client settings for servers that need them during /mcp reauth or initial connect."
|
||||
},
|
||||
"serverBase": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Whether OMP should try to connect this server."
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"exclusiveMinimum": 0,
|
||||
"description": "Connection timeout in milliseconds."
|
||||
},
|
||||
"auth": {
|
||||
"$ref": "#/$defs/authConfig"
|
||||
},
|
||||
"oauth": {
|
||||
"$ref": "#/$defs/oauthConfig"
|
||||
}
|
||||
}
|
||||
},
|
||||
"stdioServer": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/serverBase"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["command"],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["stdio"],
|
||||
"description": "Default transport when omitted."
|
||||
},
|
||||
"command": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "Executable to spawn."
|
||||
},
|
||||
"args": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Arguments passed to the stdio server process."
|
||||
},
|
||||
"env": {
|
||||
"$ref": "#/$defs/stringMap",
|
||||
"description": "Environment variables passed to the stdio process."
|
||||
},
|
||||
"cwd": {
|
||||
"type": "string",
|
||||
"description": "Working directory used when spawning the stdio process."
|
||||
}
|
||||
},
|
||||
"not": {
|
||||
"required": ["url"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"httpServer": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/serverBase"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["type", "url"],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["http"],
|
||||
"description": "Streamable HTTP transport."
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "MCP endpoint URL."
|
||||
},
|
||||
"headers": {
|
||||
"$ref": "#/$defs/stringMap",
|
||||
"description": "HTTP headers sent with MCP requests."
|
||||
}
|
||||
},
|
||||
"not": {
|
||||
"required": ["command"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"sseServer": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/serverBase"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["type", "url"],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["sse"],
|
||||
"description": "Legacy SSE transport kept for compatibility. Prefer http for new configs."
|
||||
},
|
||||
"url": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "Legacy SSE endpoint URL."
|
||||
},
|
||||
"headers": {
|
||||
"$ref": "#/$defs/stringMap",
|
||||
"description": "HTTP headers sent with the SSE transport."
|
||||
}
|
||||
},
|
||||
"not": {
|
||||
"required": ["command"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"serverConfig": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/$defs/stdioServer"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/httpServer"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/sseServer"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,6 +158,7 @@ async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>>
|
||||
command: serverConfig.command as string | undefined,
|
||||
args: serverConfig.args as string[] | undefined,
|
||||
env: serverConfig.env as Record<string, string> | undefined,
|
||||
cwd: serverConfig.cwd as string | undefined,
|
||||
url: serverConfig.url as string | undefined,
|
||||
headers: serverConfig.headers as Record<string, string> | undefined,
|
||||
auth: serverConfig.auth as
|
||||
|
||||
@@ -29,6 +29,7 @@ interface MCPConfigFile {
|
||||
command?: string;
|
||||
args?: string[];
|
||||
env?: Record<string, string>;
|
||||
cwd?: string;
|
||||
url?: string;
|
||||
headers?: Record<string, string>;
|
||||
auth?: {
|
||||
@@ -88,6 +89,7 @@ function transformMCPConfig(config: MCPConfigFile, source: SourceMeta): MCPServe
|
||||
command: serverConfig.command,
|
||||
args: serverConfig.args,
|
||||
env: serverConfig.env,
|
||||
cwd: serverConfig.cwd,
|
||||
url: serverConfig.url,
|
||||
headers: serverConfig.headers,
|
||||
auth: serverConfig.auth,
|
||||
@@ -100,6 +102,7 @@ function transformMCPConfig(config: MCPConfigFile, source: SourceMeta): MCPServe
|
||||
if (server.command) server.command = expandEnvVarsDeep(server.command);
|
||||
if (server.args) server.args = expandEnvVarsDeep(server.args);
|
||||
if (server.env) server.env = expandEnvVarsDeep(server.env);
|
||||
if (server.cwd) server.cwd = expandEnvVarsDeep(server.cwd);
|
||||
if (server.url) server.url = expandEnvVarsDeep(server.url);
|
||||
if (server.headers) server.headers = expandEnvVarsDeep(server.headers);
|
||||
if (server.auth) server.auth = expandEnvVarsDeep(server.auth);
|
||||
|
||||
@@ -9,7 +9,14 @@ import { isEnoent } from "@oh-my-pi/pi-utils";
|
||||
import { invalidate as invalidateFsCache } from "../capability/fs";
|
||||
|
||||
import { validateServerConfig } from "./config";
|
||||
import type { MCPConfigFile, MCPServerConfig } from "./types";
|
||||
import { MCP_CONFIG_SCHEMA_URL, type MCPConfigFile, type MCPServerConfig } from "./types";
|
||||
|
||||
function withSchema(config: MCPConfigFile): MCPConfigFile {
|
||||
return {
|
||||
$schema: config.$schema ?? MCP_CONFIG_SCHEMA_URL,
|
||||
...config,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an MCP config file.
|
||||
@@ -40,7 +47,7 @@ export async function writeMCPConfigFile(filePath: string, config: MCPConfigFile
|
||||
|
||||
// Write to temp file first (atomic write)
|
||||
const tmpPath = `${filePath}.tmp`;
|
||||
const content = JSON.stringify(config, null, 2);
|
||||
const content = JSON.stringify(withSchema(config), null, 2);
|
||||
await fs.promises.writeFile(tmpPath, content, { encoding: "utf-8", mode: 0o600 });
|
||||
|
||||
// Rename to final path (atomic on most systems)
|
||||
|
||||
@@ -53,6 +53,7 @@ function convertToLegacyConfig(server: MCPServer): MCPServerConfig {
|
||||
};
|
||||
if (server.args) config.args = server.args;
|
||||
if (server.env) config.env = server.env;
|
||||
if (server.cwd) config.cwd = server.cwd;
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
@@ -100,8 +100,12 @@ export interface MCPSseServerConfig extends MCPServerConfigBase {
|
||||
|
||||
export type MCPServerConfig = MCPStdioServerConfig | MCPHttpServerConfig | MCPSseServerConfig;
|
||||
|
||||
/** Root .mcp.json file structure */
|
||||
export const MCP_CONFIG_SCHEMA_URL =
|
||||
"https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/config/mcp-schema.json";
|
||||
|
||||
/** Root mcp.json/.mcp.json file structure */
|
||||
export interface MCPConfigFile {
|
||||
$schema?: string;
|
||||
mcpServers?: Record<string, MCPServerConfig>;
|
||||
disabledServers?: string[];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user