@omnidev-ai/core 0.6.2 → 0.7.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/dist/index.d.ts +55 -4
- package/dist/index.js +150 -38
- package/package.json +1 -1
- package/src/capability/sources.ts +33 -9
- package/src/config/capabilities.ts +5 -3
- package/src/config/loader.ts +113 -32
- package/src/mcp-json/manager.ts +62 -3
- package/src/types/index.ts +26 -1
package/dist/index.d.ts
CHANGED
|
@@ -190,12 +190,37 @@ interface McpToolSchema {
|
|
|
190
190
|
description: string;
|
|
191
191
|
inputSchema: Record<string, unknown>;
|
|
192
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* MCP server configuration supporting multiple transport types:
|
|
195
|
+
*
|
|
196
|
+
* - **stdio**: Local process using stdin/stdout (default)
|
|
197
|
+
* - Requires: command
|
|
198
|
+
* - Optional: args, env, cwd
|
|
199
|
+
*
|
|
200
|
+
* - **http**: Remote HTTP server (recommended for remote servers)
|
|
201
|
+
* - Requires: url
|
|
202
|
+
* - Optional: headers (for authentication)
|
|
203
|
+
*
|
|
204
|
+
* - **sse**: Server-Sent Events (deprecated, use http instead)
|
|
205
|
+
* - Requires: url
|
|
206
|
+
* - Optional: headers (for authentication)
|
|
207
|
+
*/
|
|
193
208
|
interface McpConfig {
|
|
194
|
-
|
|
209
|
+
/** Executable to run (required for stdio transport) */
|
|
210
|
+
command?: string;
|
|
211
|
+
/** Command arguments (stdio transport only) */
|
|
195
212
|
args?: string[];
|
|
213
|
+
/** Environment variables (stdio transport only) */
|
|
196
214
|
env?: Record<string, string>;
|
|
215
|
+
/** Working directory (stdio transport only) */
|
|
197
216
|
cwd?: string;
|
|
217
|
+
/** Transport type: stdio (default), http, or sse */
|
|
198
218
|
transport?: McpTransport;
|
|
219
|
+
/** URL for remote servers (required for http/sse transport) */
|
|
220
|
+
url?: string;
|
|
221
|
+
/** HTTP headers for authentication (http/sse transport only) */
|
|
222
|
+
headers?: Record<string, string>;
|
|
223
|
+
/** Tool schemas (optional, for documentation) */
|
|
199
224
|
tools?: McpToolSchema[];
|
|
200
225
|
}
|
|
201
226
|
interface Skill {
|
|
@@ -585,6 +610,12 @@ declare function validateEnv(declarations: Record<string, EnvDeclaration | Recor
|
|
|
585
610
|
*/
|
|
586
611
|
declare function isSecretEnvVar(key: string, declarations: Record<string, EnvDeclaration | Record<string, never>>): boolean;
|
|
587
612
|
/**
|
|
613
|
+
* Load only the base config file (omni.toml) without merging local overrides.
|
|
614
|
+
* Use this when you need to modify and write back to omni.toml.
|
|
615
|
+
* @returns OmniConfig from omni.toml only
|
|
616
|
+
*/
|
|
617
|
+
declare function loadBaseConfig(): Promise<OmniConfig>;
|
|
618
|
+
/**
|
|
588
619
|
* Load and merge config and local configuration files
|
|
589
620
|
* @returns Merged OmniConfig object
|
|
590
621
|
*
|
|
@@ -697,14 +728,34 @@ declare function buildManifestFromCapabilities(capabilities: LoadedCapability[])
|
|
|
697
728
|
*/
|
|
698
729
|
declare function cleanupStaleResources(previousManifest: ResourceManifest, currentCapabilityIds: Set<string>): Promise<CleanupResult>;
|
|
699
730
|
/**
|
|
700
|
-
* MCP server configuration in .mcp.json
|
|
731
|
+
* MCP server configuration in .mcp.json for stdio transport
|
|
701
732
|
*/
|
|
702
|
-
interface
|
|
733
|
+
interface McpServerStdioConfig {
|
|
703
734
|
command: string;
|
|
704
735
|
args?: string[];
|
|
705
736
|
env?: Record<string, string>;
|
|
706
737
|
}
|
|
707
738
|
/**
|
|
739
|
+
* MCP server configuration in .mcp.json for HTTP transport
|
|
740
|
+
*/
|
|
741
|
+
interface McpServerHttpConfig {
|
|
742
|
+
type: "http";
|
|
743
|
+
url: string;
|
|
744
|
+
headers?: Record<string, string>;
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* MCP server configuration in .mcp.json for SSE transport
|
|
748
|
+
*/
|
|
749
|
+
interface McpServerSseConfig {
|
|
750
|
+
type: "sse";
|
|
751
|
+
url: string;
|
|
752
|
+
headers?: Record<string, string>;
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Union type for all MCP server configurations
|
|
756
|
+
*/
|
|
757
|
+
type McpServerConfig = McpServerStdioConfig | McpServerHttpConfig | McpServerSseConfig;
|
|
758
|
+
/**
|
|
708
759
|
* Structure of .mcp.json file
|
|
709
760
|
*/
|
|
710
761
|
interface McpJsonConfig {
|
|
@@ -822,4 +873,4 @@ declare function generateInstructionsTemplate(): string;
|
|
|
822
873
|
declare function debug(message: string, data?: unknown): void;
|
|
823
874
|
declare const version = "0.1.0";
|
|
824
875
|
declare function getVersion(): string;
|
|
825
|
-
export { writeRules, writeProviderConfig, writeMcpJson, writeEnabledProviders, writeConfig, writeActiveProfileState, version, validateEnv, syncMcpJson, syncAgentConfiguration, sourceToGitUrl, setProfile, setActiveProfile, saveManifest, saveLockFile, resolveEnabledCapabilities, readMcpJson, readEnabledProviders, readActiveProfileState, parseSourceConfig, parseProviderFlag, parseOmniConfig, parseCapabilityConfig, loadSubagents, loadSkills, loadRules, loadProviderConfig, loadProfileConfig, loadManifest, loadLockFile, loadEnvironment, loadDocs, loadConfig, loadCommands, loadCapabilityConfig, loadCapability, isSecretEnvVar, isProviderEnabled, installCapabilityDependencies, getVersion, getSourceCapabilityPath, getLockFilePath, getEnabledCapabilities, getActiveProviders, getActiveProfile, generateInstructionsTemplate, generateClaudeTemplate, generateAgentsTemplate, fetchCapabilitySource, fetchAllCapabilitySources, enableProvider, enableCapability, discoverCapabilities, disableProvider, disableCapability, debug, clearActiveProfileState, cleanupStaleResources, checkForUpdates, buildSyncBundle, buildRouteMap, buildManifestFromCapabilities, buildCommand, buildCapabilityRegistry, SyncResult, SyncOptions, SyncConfig, SyncBundle, SubagentPermissionMode, SubagentModel, SubagentHooks, SubagentHookConfig, SubagentExport, Subagent, SourceUpdateInfo, SkillExport, Skill, Rule, ResourceManifest, ProvidersState, ProviderSyncResult, ProviderManifest, ProviderInitResult, ProviderId, ProviderContext, ProviderConfig, ProviderAdapter, Provider, ProfileConfig, OmniConfig, McpTransport, McpToolSchema, McpServerConfig, McpJsonConfig, McpConfig, LoadedCapability, GitCapabilitySourceConfig, FileContent, FetchResult, EnvDeclaration, DocExport, Doc, DiscoveredContent, CommandExport, Command, CliConfig, CleanupResult, CapabilitySourceType, CapabilitySourceConfig, CapabilitySource, CapabilityResources, CapabilityRegistry, CapabilityMetadata, CapabilityLockEntry, CapabilityExports, CapabilityExport, CapabilityConfig, CapabilitiesLockFile, CapabilitiesConfig };
|
|
876
|
+
export { writeRules, writeProviderConfig, writeMcpJson, writeEnabledProviders, writeConfig, writeActiveProfileState, version, validateEnv, syncMcpJson, syncAgentConfiguration, sourceToGitUrl, setProfile, setActiveProfile, saveManifest, saveLockFile, resolveEnabledCapabilities, readMcpJson, readEnabledProviders, readActiveProfileState, parseSourceConfig, parseProviderFlag, parseOmniConfig, parseCapabilityConfig, loadSubagents, loadSkills, loadRules, loadProviderConfig, loadProfileConfig, loadManifest, loadLockFile, loadEnvironment, loadDocs, loadConfig, loadCommands, loadCapabilityConfig, loadCapability, loadBaseConfig, isSecretEnvVar, isProviderEnabled, installCapabilityDependencies, getVersion, getSourceCapabilityPath, getLockFilePath, getEnabledCapabilities, getActiveProviders, getActiveProfile, generateInstructionsTemplate, generateClaudeTemplate, generateAgentsTemplate, fetchCapabilitySource, fetchAllCapabilitySources, enableProvider, enableCapability, discoverCapabilities, disableProvider, disableCapability, debug, clearActiveProfileState, cleanupStaleResources, checkForUpdates, buildSyncBundle, buildRouteMap, buildManifestFromCapabilities, buildCommand, buildCapabilityRegistry, SyncResult, SyncOptions, SyncConfig, SyncBundle, SubagentPermissionMode, SubagentModel, SubagentHooks, SubagentHookConfig, SubagentExport, Subagent, SourceUpdateInfo, SkillExport, Skill, Rule, ResourceManifest, ProvidersState, ProviderSyncResult, ProviderManifest, ProviderInitResult, ProviderId, ProviderContext, ProviderConfig, ProviderAdapter, Provider, ProfileConfig, OmniConfig, McpTransport, McpToolSchema, McpServerStdioConfig, McpServerSseConfig, McpServerHttpConfig, McpServerConfig, McpJsonConfig, McpConfig, LoadedCapability, GitCapabilitySourceConfig, FileContent, FetchResult, EnvDeclaration, DocExport, Doc, DiscoveredContent, CommandExport, Command, CliConfig, CleanupResult, CapabilitySourceType, CapabilitySourceConfig, CapabilitySource, CapabilityResources, CapabilityRegistry, CapabilityMetadata, CapabilityLockEntry, CapabilityExports, CapabilityExport, CapabilityConfig, CapabilitiesLockFile, CapabilitiesConfig };
|
package/dist/index.js
CHANGED
|
@@ -691,13 +691,16 @@ function mergeConfigs(base, override) {
|
|
|
691
691
|
}
|
|
692
692
|
return merged;
|
|
693
693
|
}
|
|
694
|
-
async function
|
|
695
|
-
let baseConfig = {};
|
|
696
|
-
let localConfig = {};
|
|
694
|
+
async function loadBaseConfig() {
|
|
697
695
|
if (existsSync8(CONFIG_PATH)) {
|
|
698
696
|
const content = await readFile8(CONFIG_PATH, "utf-8");
|
|
699
|
-
|
|
697
|
+
return parseOmniConfig(content);
|
|
700
698
|
}
|
|
699
|
+
return {};
|
|
700
|
+
}
|
|
701
|
+
async function loadConfig() {
|
|
702
|
+
const baseConfig = await loadBaseConfig();
|
|
703
|
+
let localConfig = {};
|
|
701
704
|
if (existsSync8(LOCAL_CONFIG)) {
|
|
702
705
|
const content = await readFile8(LOCAL_CONFIG, "utf-8");
|
|
703
706
|
localConfig = parseOmniConfig(content);
|
|
@@ -759,16 +762,30 @@ function generateConfigToml(config) {
|
|
|
759
762
|
lines.push("# Fetch capabilities from Git repositories. On sync, these are");
|
|
760
763
|
lines.push("# cloned/updated and made available to your profiles.");
|
|
761
764
|
lines.push("#");
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
765
|
+
const sources = config.capabilities?.sources;
|
|
766
|
+
if (sources && Object.keys(sources).length > 0) {
|
|
767
|
+
lines.push("[capabilities.sources]");
|
|
768
|
+
for (const [name, sourceConfig] of Object.entries(sources)) {
|
|
769
|
+
if (typeof sourceConfig === "string") {
|
|
770
|
+
lines.push(`${name} = "${sourceConfig}"`);
|
|
771
|
+
} else if (sourceConfig.path) {
|
|
772
|
+
lines.push(`${name} = { source = "${sourceConfig.source}", path = "${sourceConfig.path}" }`);
|
|
773
|
+
} else {
|
|
774
|
+
lines.push(`${name} = "${sourceConfig.source}"`);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
} else {
|
|
778
|
+
lines.push("# [capabilities.sources]");
|
|
779
|
+
lines.push("# # GitHub shorthand (uses latest commit)");
|
|
780
|
+
lines.push('# tasks = "github:example-org/tasks-capability"');
|
|
781
|
+
lines.push("#");
|
|
782
|
+
lines.push("# # With subdirectory path");
|
|
783
|
+
lines.push('# ralph = { source = "github:example-org/ralph", path = "plugins/my-cap" }');
|
|
784
|
+
lines.push("#");
|
|
785
|
+
lines.push("# # Other Git sources");
|
|
786
|
+
lines.push('# private = "git@github.com:company/private-cap.git"');
|
|
787
|
+
lines.push('# gitlab = "https://gitlab.com/user/capability.git"');
|
|
788
|
+
}
|
|
772
789
|
lines.push("");
|
|
773
790
|
lines.push("# =============================================================================");
|
|
774
791
|
lines.push("# MCP Servers");
|
|
@@ -776,25 +793,66 @@ function generateConfigToml(config) {
|
|
|
776
793
|
lines.push("# Define MCP servers that automatically become capabilities.");
|
|
777
794
|
lines.push('# Reference in profiles using the MCP name directly, e.g. capabilities = ["filesystem"]');
|
|
778
795
|
lines.push("#");
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
796
|
+
const mcps = config.mcps;
|
|
797
|
+
if (mcps && Object.keys(mcps).length > 0) {
|
|
798
|
+
for (const [name, mcpConfig] of Object.entries(mcps)) {
|
|
799
|
+
lines.push(`[mcps.${name}]`);
|
|
800
|
+
if (mcpConfig.transport && mcpConfig.transport !== "stdio") {
|
|
801
|
+
lines.push(`transport = "${mcpConfig.transport}"`);
|
|
802
|
+
}
|
|
803
|
+
if (mcpConfig.command) {
|
|
804
|
+
lines.push(`command = "${mcpConfig.command}"`);
|
|
805
|
+
}
|
|
806
|
+
if (mcpConfig.args && mcpConfig.args.length > 0) {
|
|
807
|
+
const argsStr = mcpConfig.args.map((a) => `"${a}"`).join(", ");
|
|
808
|
+
lines.push(`args = [${argsStr}]`);
|
|
809
|
+
}
|
|
810
|
+
if (mcpConfig.cwd) {
|
|
811
|
+
lines.push(`cwd = "${mcpConfig.cwd}"`);
|
|
812
|
+
}
|
|
813
|
+
if (mcpConfig.url) {
|
|
814
|
+
lines.push(`url = "${mcpConfig.url}"`);
|
|
815
|
+
}
|
|
816
|
+
if (mcpConfig.env && Object.keys(mcpConfig.env).length > 0) {
|
|
817
|
+
lines.push(`[mcps.${name}.env]`);
|
|
818
|
+
for (const [key, value] of Object.entries(mcpConfig.env)) {
|
|
819
|
+
lines.push(`${key} = "${value}"`);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
if (mcpConfig.headers && Object.keys(mcpConfig.headers).length > 0) {
|
|
823
|
+
lines.push(`[mcps.${name}.headers]`);
|
|
824
|
+
for (const [key, value] of Object.entries(mcpConfig.headers)) {
|
|
825
|
+
lines.push(`${key} = "${value}"`);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
lines.push("");
|
|
829
|
+
}
|
|
830
|
+
} else {
|
|
831
|
+
lines.push("# [mcps.filesystem]");
|
|
832
|
+
lines.push('# command = "npx"');
|
|
833
|
+
lines.push('# args = ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"]');
|
|
834
|
+
lines.push('# transport = "stdio" # stdio (default), sse, or http');
|
|
835
|
+
lines.push("#");
|
|
836
|
+
lines.push("# [mcps.database]");
|
|
837
|
+
lines.push('# command = "node"');
|
|
838
|
+
lines.push('# args = ["./servers/database.js"]');
|
|
839
|
+
lines.push('# cwd = "./mcp-servers"');
|
|
840
|
+
lines.push("# [mcps.database.env]");
|
|
841
|
+
lines.push('# DB_URL = "${DATABASE_URL}"');
|
|
842
|
+
lines.push("");
|
|
843
|
+
}
|
|
791
844
|
lines.push("# =============================================================================");
|
|
792
845
|
lines.push("# Always Enabled Capabilities");
|
|
793
846
|
lines.push("# =============================================================================");
|
|
794
847
|
lines.push("# Capabilities that load in ALL profiles, regardless of profile config.");
|
|
795
848
|
lines.push("# Useful for essential tools needed everywhere.");
|
|
796
849
|
lines.push("#");
|
|
797
|
-
|
|
850
|
+
if (config.always_enabled_capabilities && config.always_enabled_capabilities.length > 0) {
|
|
851
|
+
const caps = config.always_enabled_capabilities.map((c) => `"${c}"`).join(", ");
|
|
852
|
+
lines.push(`always_enabled_capabilities = [${caps}]`);
|
|
853
|
+
} else {
|
|
854
|
+
lines.push('# always_enabled_capabilities = ["git-tools", "linting"]');
|
|
855
|
+
}
|
|
798
856
|
lines.push("");
|
|
799
857
|
lines.push("# =============================================================================");
|
|
800
858
|
lines.push("# Profiles");
|
|
@@ -883,7 +941,7 @@ async function getEnabledCapabilities() {
|
|
|
883
941
|
return resolveEnabledCapabilities(config, activeProfile);
|
|
884
942
|
}
|
|
885
943
|
async function enableCapability(capabilityId) {
|
|
886
|
-
const config = await
|
|
944
|
+
const config = await loadBaseConfig();
|
|
887
945
|
const activeProfile = await getActiveProfile() ?? config.active_profile ?? "default";
|
|
888
946
|
if (!config.profiles) {
|
|
889
947
|
config.profiles = {};
|
|
@@ -897,7 +955,7 @@ async function enableCapability(capabilityId) {
|
|
|
897
955
|
await writeConfig(config);
|
|
898
956
|
}
|
|
899
957
|
async function disableCapability(capabilityId) {
|
|
900
|
-
const config = await
|
|
958
|
+
const config = await loadBaseConfig();
|
|
901
959
|
const activeProfile = await getActiveProfile() ?? config.active_profile ?? "default";
|
|
902
960
|
if (!config.profiles?.[activeProfile]) {
|
|
903
961
|
return;
|
|
@@ -1389,6 +1447,8 @@ async function fetchCapabilitySource(id, sourceConfig, options) {
|
|
|
1389
1447
|
return fetchGitCapabilitySource(id, config, options);
|
|
1390
1448
|
}
|
|
1391
1449
|
function generateMcpCapabilityTomlContent(id, mcpConfig) {
|
|
1450
|
+
const transport = mcpConfig.transport ?? "stdio";
|
|
1451
|
+
const isRemote = transport === "http" || transport === "sse";
|
|
1392
1452
|
let tomlContent = `# Auto-generated by OmniDev from omni.toml [mcps] section - DO NOT EDIT
|
|
1393
1453
|
|
|
1394
1454
|
[capability]
|
|
@@ -1402,21 +1462,42 @@ wrapped = true
|
|
|
1402
1462
|
generated_from_omni_toml = true
|
|
1403
1463
|
|
|
1404
1464
|
[mcp]
|
|
1405
|
-
command = "${mcpConfig.command}"
|
|
1406
1465
|
`;
|
|
1407
|
-
if (
|
|
1408
|
-
tomlContent += `
|
|
1466
|
+
if (isRemote) {
|
|
1467
|
+
tomlContent += `transport = "${transport}"
|
|
1409
1468
|
`;
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1469
|
+
if (mcpConfig.url) {
|
|
1470
|
+
tomlContent += `url = "${mcpConfig.url}"
|
|
1471
|
+
`;
|
|
1472
|
+
}
|
|
1473
|
+
} else {
|
|
1474
|
+
if (mcpConfig.command) {
|
|
1475
|
+
tomlContent += `command = "${mcpConfig.command}"
|
|
1476
|
+
`;
|
|
1477
|
+
}
|
|
1478
|
+
if (mcpConfig.args && mcpConfig.args.length > 0) {
|
|
1479
|
+
tomlContent += `args = ${JSON.stringify(mcpConfig.args)}
|
|
1413
1480
|
`;
|
|
1481
|
+
}
|
|
1482
|
+
if (mcpConfig.transport) {
|
|
1483
|
+
tomlContent += `transport = "${mcpConfig.transport}"
|
|
1484
|
+
`;
|
|
1485
|
+
}
|
|
1486
|
+
if (mcpConfig.cwd) {
|
|
1487
|
+
tomlContent += `cwd = "${mcpConfig.cwd}"
|
|
1488
|
+
`;
|
|
1489
|
+
}
|
|
1414
1490
|
}
|
|
1415
|
-
if (mcpConfig.
|
|
1416
|
-
tomlContent += `
|
|
1491
|
+
if (isRemote && mcpConfig.headers && Object.keys(mcpConfig.headers).length > 0) {
|
|
1492
|
+
tomlContent += `
|
|
1493
|
+
[mcp.headers]
|
|
1494
|
+
`;
|
|
1495
|
+
for (const [key, value] of Object.entries(mcpConfig.headers)) {
|
|
1496
|
+
tomlContent += `"${key}" = "${value}"
|
|
1417
1497
|
`;
|
|
1498
|
+
}
|
|
1418
1499
|
}
|
|
1419
|
-
if (mcpConfig.env && Object.keys(mcpConfig.env).length > 0) {
|
|
1500
|
+
if (!isRemote && mcpConfig.env && Object.keys(mcpConfig.env).length > 0) {
|
|
1420
1501
|
tomlContent += `
|
|
1421
1502
|
[mcp.env]
|
|
1422
1503
|
`;
|
|
@@ -1653,6 +1734,36 @@ async function writeMcpJson(config) {
|
|
|
1653
1734
|
`, "utf-8");
|
|
1654
1735
|
}
|
|
1655
1736
|
function buildMcpServerConfig(mcp) {
|
|
1737
|
+
const transport = mcp.transport ?? "stdio";
|
|
1738
|
+
if (transport === "http") {
|
|
1739
|
+
if (!mcp.url) {
|
|
1740
|
+
throw new Error("HTTP transport requires a URL");
|
|
1741
|
+
}
|
|
1742
|
+
const config2 = {
|
|
1743
|
+
type: "http",
|
|
1744
|
+
url: mcp.url
|
|
1745
|
+
};
|
|
1746
|
+
if (mcp.headers && Object.keys(mcp.headers).length > 0) {
|
|
1747
|
+
config2.headers = mcp.headers;
|
|
1748
|
+
}
|
|
1749
|
+
return config2;
|
|
1750
|
+
}
|
|
1751
|
+
if (transport === "sse") {
|
|
1752
|
+
if (!mcp.url) {
|
|
1753
|
+
throw new Error("SSE transport requires a URL");
|
|
1754
|
+
}
|
|
1755
|
+
const config2 = {
|
|
1756
|
+
type: "sse",
|
|
1757
|
+
url: mcp.url
|
|
1758
|
+
};
|
|
1759
|
+
if (mcp.headers && Object.keys(mcp.headers).length > 0) {
|
|
1760
|
+
config2.headers = mcp.headers;
|
|
1761
|
+
}
|
|
1762
|
+
return config2;
|
|
1763
|
+
}
|
|
1764
|
+
if (!mcp.command) {
|
|
1765
|
+
throw new Error("stdio transport requires a command");
|
|
1766
|
+
}
|
|
1656
1767
|
const config = {
|
|
1657
1768
|
command: mcp.command
|
|
1658
1769
|
};
|
|
@@ -2111,6 +2222,7 @@ export {
|
|
|
2111
2222
|
loadCommands,
|
|
2112
2223
|
loadCapabilityConfig,
|
|
2113
2224
|
loadCapability,
|
|
2225
|
+
loadBaseConfig,
|
|
2114
2226
|
isSecretEnvVar,
|
|
2115
2227
|
isProviderEnabled,
|
|
2116
2228
|
installCapabilityDependencies,
|
package/package.json
CHANGED
|
@@ -752,6 +752,9 @@ function generateMcpCapabilityTomlContent(
|
|
|
752
752
|
id: string,
|
|
753
753
|
mcpConfig: import("../types/index.js").McpConfig,
|
|
754
754
|
): string {
|
|
755
|
+
const transport = mcpConfig.transport ?? "stdio";
|
|
756
|
+
const isRemote = transport === "http" || transport === "sse";
|
|
757
|
+
|
|
755
758
|
let tomlContent = `# Auto-generated by OmniDev from omni.toml [mcps] section - DO NOT EDIT
|
|
756
759
|
|
|
757
760
|
[capability]
|
|
@@ -765,22 +768,43 @@ wrapped = true
|
|
|
765
768
|
generated_from_omni_toml = true
|
|
766
769
|
|
|
767
770
|
[mcp]
|
|
768
|
-
command = "${mcpConfig.command}"
|
|
769
771
|
`;
|
|
770
772
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
773
|
+
// For remote transports (http/sse), url is required
|
|
774
|
+
if (isRemote) {
|
|
775
|
+
tomlContent += `transport = "${transport}"\n`;
|
|
776
|
+
if (mcpConfig.url) {
|
|
777
|
+
tomlContent += `url = "${mcpConfig.url}"\n`;
|
|
778
|
+
}
|
|
779
|
+
} else {
|
|
780
|
+
// For stdio transport, command is required
|
|
781
|
+
if (mcpConfig.command) {
|
|
782
|
+
tomlContent += `command = "${mcpConfig.command}"\n`;
|
|
783
|
+
}
|
|
774
784
|
|
|
775
|
-
|
|
776
|
-
|
|
785
|
+
if (mcpConfig.args && mcpConfig.args.length > 0) {
|
|
786
|
+
tomlContent += `args = ${JSON.stringify(mcpConfig.args)}\n`;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
if (mcpConfig.transport) {
|
|
790
|
+
tomlContent += `transport = "${mcpConfig.transport}"\n`;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
if (mcpConfig.cwd) {
|
|
794
|
+
tomlContent += `cwd = "${mcpConfig.cwd}"\n`;
|
|
795
|
+
}
|
|
777
796
|
}
|
|
778
797
|
|
|
779
|
-
|
|
780
|
-
|
|
798
|
+
// Headers for remote transports
|
|
799
|
+
if (isRemote && mcpConfig.headers && Object.keys(mcpConfig.headers).length > 0) {
|
|
800
|
+
tomlContent += `\n[mcp.headers]\n`;
|
|
801
|
+
for (const [key, value] of Object.entries(mcpConfig.headers)) {
|
|
802
|
+
tomlContent += `"${key}" = "${value}"\n`;
|
|
803
|
+
}
|
|
781
804
|
}
|
|
782
805
|
|
|
783
|
-
|
|
806
|
+
// Env variables for stdio transport
|
|
807
|
+
if (!isRemote && mcpConfig.env && Object.keys(mcpConfig.env).length > 0) {
|
|
784
808
|
tomlContent += `\n[mcp.env]\n`;
|
|
785
809
|
for (const [key, value] of Object.entries(mcpConfig.env)) {
|
|
786
810
|
tomlContent += `${key} = "${value}"\n`;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { loadConfig, writeConfig } from "./loader.js";
|
|
1
|
+
import { loadBaseConfig, loadConfig, writeConfig } from "./loader.js";
|
|
2
2
|
import { getActiveProfile, resolveEnabledCapabilities } from "./profiles.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -17,7 +17,8 @@ export async function getEnabledCapabilities(): Promise<string[]> {
|
|
|
17
17
|
* @param capabilityId - The ID of the capability to enable
|
|
18
18
|
*/
|
|
19
19
|
export async function enableCapability(capabilityId: string): Promise<void> {
|
|
20
|
-
|
|
20
|
+
// Use loadBaseConfig to avoid writing local overrides to omni.toml
|
|
21
|
+
const config = await loadBaseConfig();
|
|
21
22
|
const activeProfile = (await getActiveProfile()) ?? config.active_profile ?? "default";
|
|
22
23
|
|
|
23
24
|
if (!config.profiles) {
|
|
@@ -39,7 +40,8 @@ export async function enableCapability(capabilityId: string): Promise<void> {
|
|
|
39
40
|
* @param capabilityId - The ID of the capability to disable
|
|
40
41
|
*/
|
|
41
42
|
export async function disableCapability(capabilityId: string): Promise<void> {
|
|
42
|
-
|
|
43
|
+
// Use loadBaseConfig to avoid writing local overrides to omni.toml
|
|
44
|
+
const config = await loadBaseConfig();
|
|
43
45
|
const activeProfile = (await getActiveProfile()) ?? config.active_profile ?? "default";
|
|
44
46
|
|
|
45
47
|
if (!config.profiles?.[activeProfile]) {
|
package/src/config/loader.ts
CHANGED
|
@@ -35,6 +35,19 @@ function mergeConfigs(base: OmniConfig, override: OmniConfig): OmniConfig {
|
|
|
35
35
|
return merged;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Load only the base config file (omni.toml) without merging local overrides.
|
|
40
|
+
* Use this when you need to modify and write back to omni.toml.
|
|
41
|
+
* @returns OmniConfig from omni.toml only
|
|
42
|
+
*/
|
|
43
|
+
export async function loadBaseConfig(): Promise<OmniConfig> {
|
|
44
|
+
if (existsSync(CONFIG_PATH)) {
|
|
45
|
+
const content = await readFile(CONFIG_PATH, "utf-8");
|
|
46
|
+
return parseOmniConfig(content);
|
|
47
|
+
}
|
|
48
|
+
return {};
|
|
49
|
+
}
|
|
50
|
+
|
|
38
51
|
/**
|
|
39
52
|
* Load and merge config and local configuration files
|
|
40
53
|
* @returns Merged OmniConfig object
|
|
@@ -43,14 +56,9 @@ function mergeConfigs(base: OmniConfig, override: OmniConfig): OmniConfig {
|
|
|
43
56
|
* Local config takes precedence over main config. Missing files are treated as empty configs.
|
|
44
57
|
*/
|
|
45
58
|
export async function loadConfig(): Promise<OmniConfig> {
|
|
46
|
-
|
|
59
|
+
const baseConfig = await loadBaseConfig();
|
|
47
60
|
let localConfig: OmniConfig = {};
|
|
48
61
|
|
|
49
|
-
if (existsSync(CONFIG_PATH)) {
|
|
50
|
-
const content = await readFile(CONFIG_PATH, "utf-8");
|
|
51
|
-
baseConfig = parseOmniConfig(content);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
62
|
if (existsSync(LOCAL_CONFIG)) {
|
|
55
63
|
const content = await readFile(LOCAL_CONFIG, "utf-8");
|
|
56
64
|
localConfig = parseOmniConfig(content);
|
|
@@ -132,26 +140,46 @@ function generateConfigToml(config: OmniConfig): string {
|
|
|
132
140
|
}
|
|
133
141
|
lines.push("");
|
|
134
142
|
|
|
135
|
-
// Capability sources
|
|
143
|
+
// Capability sources
|
|
136
144
|
lines.push("# =============================================================================");
|
|
137
145
|
lines.push("# Capability Sources");
|
|
138
146
|
lines.push("# =============================================================================");
|
|
139
147
|
lines.push("# Fetch capabilities from Git repositories. On sync, these are");
|
|
140
148
|
lines.push("# cloned/updated and made available to your profiles.");
|
|
141
149
|
lines.push("#");
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
150
|
+
|
|
151
|
+
const sources = config.capabilities?.sources;
|
|
152
|
+
if (sources && Object.keys(sources).length > 0) {
|
|
153
|
+
lines.push("[capabilities.sources]");
|
|
154
|
+
for (const [name, sourceConfig] of Object.entries(sources)) {
|
|
155
|
+
if (typeof sourceConfig === "string") {
|
|
156
|
+
// Simple string source
|
|
157
|
+
lines.push(`${name} = "${sourceConfig}"`);
|
|
158
|
+
} else if (sourceConfig.path) {
|
|
159
|
+
// Full config object with path
|
|
160
|
+
lines.push(
|
|
161
|
+
`${name} = { source = "${sourceConfig.source}", path = "${sourceConfig.path}" }`,
|
|
162
|
+
);
|
|
163
|
+
} else {
|
|
164
|
+
// Full config object without path - just write the source
|
|
165
|
+
lines.push(`${name} = "${sourceConfig.source}"`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
} else {
|
|
169
|
+
lines.push("# [capabilities.sources]");
|
|
170
|
+
lines.push("# # GitHub shorthand (uses latest commit)");
|
|
171
|
+
lines.push('# tasks = "github:example-org/tasks-capability"');
|
|
172
|
+
lines.push("#");
|
|
173
|
+
lines.push("# # With subdirectory path");
|
|
174
|
+
lines.push('# ralph = { source = "github:example-org/ralph", path = "plugins/my-cap" }');
|
|
175
|
+
lines.push("#");
|
|
176
|
+
lines.push("# # Other Git sources");
|
|
177
|
+
lines.push('# private = "git@github.com:company/private-cap.git"');
|
|
178
|
+
lines.push('# gitlab = "https://gitlab.com/user/capability.git"');
|
|
179
|
+
}
|
|
152
180
|
lines.push("");
|
|
153
181
|
|
|
154
|
-
// MCP servers
|
|
182
|
+
// MCP servers
|
|
155
183
|
lines.push("# =============================================================================");
|
|
156
184
|
lines.push("# MCP Servers");
|
|
157
185
|
lines.push("# =============================================================================");
|
|
@@ -160,19 +188,67 @@ function generateConfigToml(config: OmniConfig): string {
|
|
|
160
188
|
'# Reference in profiles using the MCP name directly, e.g. capabilities = ["filesystem"]',
|
|
161
189
|
);
|
|
162
190
|
lines.push("#");
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
191
|
+
|
|
192
|
+
const mcps = config.mcps;
|
|
193
|
+
if (mcps && Object.keys(mcps).length > 0) {
|
|
194
|
+
for (const [name, mcpConfig] of Object.entries(mcps)) {
|
|
195
|
+
lines.push(`[mcps.${name}]`);
|
|
196
|
+
|
|
197
|
+
// Transport type (default is stdio)
|
|
198
|
+
if (mcpConfig.transport && mcpConfig.transport !== "stdio") {
|
|
199
|
+
lines.push(`transport = "${mcpConfig.transport}"`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// For stdio transport
|
|
203
|
+
if (mcpConfig.command) {
|
|
204
|
+
lines.push(`command = "${mcpConfig.command}"`);
|
|
205
|
+
}
|
|
206
|
+
if (mcpConfig.args && mcpConfig.args.length > 0) {
|
|
207
|
+
const argsStr = mcpConfig.args.map((a) => `"${a}"`).join(", ");
|
|
208
|
+
lines.push(`args = [${argsStr}]`);
|
|
209
|
+
}
|
|
210
|
+
if (mcpConfig.cwd) {
|
|
211
|
+
lines.push(`cwd = "${mcpConfig.cwd}"`);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// For http/sse transport
|
|
215
|
+
if (mcpConfig.url) {
|
|
216
|
+
lines.push(`url = "${mcpConfig.url}"`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Environment variables (sub-table)
|
|
220
|
+
if (mcpConfig.env && Object.keys(mcpConfig.env).length > 0) {
|
|
221
|
+
lines.push(`[mcps.${name}.env]`);
|
|
222
|
+
for (const [key, value] of Object.entries(mcpConfig.env)) {
|
|
223
|
+
lines.push(`${key} = "${value}"`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Headers (sub-table)
|
|
228
|
+
if (mcpConfig.headers && Object.keys(mcpConfig.headers).length > 0) {
|
|
229
|
+
lines.push(`[mcps.${name}.headers]`);
|
|
230
|
+
for (const [key, value] of Object.entries(mcpConfig.headers)) {
|
|
231
|
+
lines.push(`${key} = "${value}"`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
lines.push("");
|
|
236
|
+
}
|
|
237
|
+
} else {
|
|
238
|
+
lines.push("# [mcps.filesystem]");
|
|
239
|
+
lines.push('# command = "npx"');
|
|
240
|
+
lines.push('# args = ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"]');
|
|
241
|
+
lines.push('# transport = "stdio" # stdio (default), sse, or http');
|
|
242
|
+
lines.push("#");
|
|
243
|
+
lines.push("# [mcps.database]");
|
|
244
|
+
lines.push('# command = "node"');
|
|
245
|
+
lines.push('# args = ["./servers/database.js"]');
|
|
246
|
+
lines.push('# cwd = "./mcp-servers"');
|
|
247
|
+
lines.push("# [mcps.database.env]");
|
|
248
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: Example of env var syntax
|
|
249
|
+
lines.push('# DB_URL = "${DATABASE_URL}"');
|
|
250
|
+
lines.push("");
|
|
251
|
+
}
|
|
176
252
|
|
|
177
253
|
// Always enabled capabilities
|
|
178
254
|
lines.push("# =============================================================================");
|
|
@@ -181,7 +257,12 @@ function generateConfigToml(config: OmniConfig): string {
|
|
|
181
257
|
lines.push("# Capabilities that load in ALL profiles, regardless of profile config.");
|
|
182
258
|
lines.push("# Useful for essential tools needed everywhere.");
|
|
183
259
|
lines.push("#");
|
|
184
|
-
|
|
260
|
+
if (config.always_enabled_capabilities && config.always_enabled_capabilities.length > 0) {
|
|
261
|
+
const caps = config.always_enabled_capabilities.map((c) => `"${c}"`).join(", ");
|
|
262
|
+
lines.push(`always_enabled_capabilities = [${caps}]`);
|
|
263
|
+
} else {
|
|
264
|
+
lines.push('# always_enabled_capabilities = ["git-tools", "linting"]');
|
|
265
|
+
}
|
|
185
266
|
lines.push("");
|
|
186
267
|
|
|
187
268
|
// Profiles
|
package/src/mcp-json/manager.ts
CHANGED
|
@@ -4,14 +4,37 @@ import type { LoadedCapability, McpConfig } from "../types";
|
|
|
4
4
|
import type { ResourceManifest } from "../state/manifest";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* MCP server configuration in .mcp.json
|
|
7
|
+
* MCP server configuration in .mcp.json for stdio transport
|
|
8
8
|
*/
|
|
9
|
-
export interface
|
|
9
|
+
export interface McpServerStdioConfig {
|
|
10
10
|
command: string;
|
|
11
11
|
args?: string[];
|
|
12
12
|
env?: Record<string, string>;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* MCP server configuration in .mcp.json for HTTP transport
|
|
17
|
+
*/
|
|
18
|
+
export interface McpServerHttpConfig {
|
|
19
|
+
type: "http";
|
|
20
|
+
url: string;
|
|
21
|
+
headers?: Record<string, string>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* MCP server configuration in .mcp.json for SSE transport
|
|
26
|
+
*/
|
|
27
|
+
export interface McpServerSseConfig {
|
|
28
|
+
type: "sse";
|
|
29
|
+
url: string;
|
|
30
|
+
headers?: Record<string, string>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Union type for all MCP server configurations
|
|
35
|
+
*/
|
|
36
|
+
export type McpServerConfig = McpServerStdioConfig | McpServerHttpConfig | McpServerSseConfig;
|
|
37
|
+
|
|
15
38
|
/**
|
|
16
39
|
* Structure of .mcp.json file
|
|
17
40
|
*/
|
|
@@ -52,7 +75,43 @@ export async function writeMcpJson(config: McpJsonConfig): Promise<void> {
|
|
|
52
75
|
* Build MCP server config from capability's mcp section
|
|
53
76
|
*/
|
|
54
77
|
function buildMcpServerConfig(mcp: McpConfig): McpServerConfig {
|
|
55
|
-
const
|
|
78
|
+
const transport = mcp.transport ?? "stdio";
|
|
79
|
+
|
|
80
|
+
// HTTP transport - remote server
|
|
81
|
+
if (transport === "http") {
|
|
82
|
+
if (!mcp.url) {
|
|
83
|
+
throw new Error("HTTP transport requires a URL");
|
|
84
|
+
}
|
|
85
|
+
const config: McpServerHttpConfig = {
|
|
86
|
+
type: "http",
|
|
87
|
+
url: mcp.url,
|
|
88
|
+
};
|
|
89
|
+
if (mcp.headers && Object.keys(mcp.headers).length > 0) {
|
|
90
|
+
config.headers = mcp.headers;
|
|
91
|
+
}
|
|
92
|
+
return config;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// SSE transport - remote server (deprecated)
|
|
96
|
+
if (transport === "sse") {
|
|
97
|
+
if (!mcp.url) {
|
|
98
|
+
throw new Error("SSE transport requires a URL");
|
|
99
|
+
}
|
|
100
|
+
const config: McpServerSseConfig = {
|
|
101
|
+
type: "sse",
|
|
102
|
+
url: mcp.url,
|
|
103
|
+
};
|
|
104
|
+
if (mcp.headers && Object.keys(mcp.headers).length > 0) {
|
|
105
|
+
config.headers = mcp.headers;
|
|
106
|
+
}
|
|
107
|
+
return config;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// stdio transport - local process (default)
|
|
111
|
+
if (!mcp.command) {
|
|
112
|
+
throw new Error("stdio transport requires a command");
|
|
113
|
+
}
|
|
114
|
+
const config: McpServerStdioConfig = {
|
|
56
115
|
command: mcp.command,
|
|
57
116
|
};
|
|
58
117
|
if (mcp.args) {
|
package/src/types/index.ts
CHANGED
|
@@ -62,12 +62,37 @@ export interface McpToolSchema {
|
|
|
62
62
|
inputSchema: Record<string, unknown>;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/**
|
|
66
|
+
* MCP server configuration supporting multiple transport types:
|
|
67
|
+
*
|
|
68
|
+
* - **stdio**: Local process using stdin/stdout (default)
|
|
69
|
+
* - Requires: command
|
|
70
|
+
* - Optional: args, env, cwd
|
|
71
|
+
*
|
|
72
|
+
* - **http**: Remote HTTP server (recommended for remote servers)
|
|
73
|
+
* - Requires: url
|
|
74
|
+
* - Optional: headers (for authentication)
|
|
75
|
+
*
|
|
76
|
+
* - **sse**: Server-Sent Events (deprecated, use http instead)
|
|
77
|
+
* - Requires: url
|
|
78
|
+
* - Optional: headers (for authentication)
|
|
79
|
+
*/
|
|
65
80
|
export interface McpConfig {
|
|
66
|
-
|
|
81
|
+
/** Executable to run (required for stdio transport) */
|
|
82
|
+
command?: string;
|
|
83
|
+
/** Command arguments (stdio transport only) */
|
|
67
84
|
args?: string[];
|
|
85
|
+
/** Environment variables (stdio transport only) */
|
|
68
86
|
env?: Record<string, string>;
|
|
87
|
+
/** Working directory (stdio transport only) */
|
|
69
88
|
cwd?: string;
|
|
89
|
+
/** Transport type: stdio (default), http, or sse */
|
|
70
90
|
transport?: McpTransport;
|
|
91
|
+
/** URL for remote servers (required for http/sse transport) */
|
|
92
|
+
url?: string;
|
|
93
|
+
/** HTTP headers for authentication (http/sse transport only) */
|
|
94
|
+
headers?: Record<string, string>;
|
|
95
|
+
/** Tool schemas (optional, for documentation) */
|
|
71
96
|
tools?: McpToolSchema[];
|
|
72
97
|
}
|
|
73
98
|
|