mirror of
https://github.com/mksglu/context-mode.git
synced 2026-10-02 04:14:38 +08:00
The `omp plugin install context-mode` path wires the extension factory so routing hooks fire, but never creates an mcp.json entry — so the 11 ctx_* tools stay unreachable even though curl/wget are hard-blocked. Register the server on plugin load, only when absent (never clobbering a user's existing entry). Spawn via `node <plugin>/server.bundle.mjs` rather than the `context-mode` bin: under the plugin install the package lives in ~/.omp/plugins/node_modules and its bin is not on PATH. Takes effect on the next OMP restart, same as the manual mcp.json workaround the issue documents.
This commit is contained in:
@@ -1067,6 +1067,8 @@ Full configs: [`configs/kiro/mcp.json`](configs/kiro/mcp.json) | [`configs/kiro/
|
||||
|
||||
Both should show `context-mode` as `enabled`.
|
||||
|
||||
> The plugin self-registers its MCP server in `~/.omp/agent/mcp.json` on first load (spawned as `node <plugin>/server.bundle.mjs`, since the plugin-install package directory is not on `PATH`), so the 11 `ctx_*` tools become reachable after the restart in step 2 — no manual `mcp.json` edit needed ([#677](https://github.com/mksglu/context-mode/issues/677)). An existing `context-mode` entry is never overwritten; remove it if you want the plugin to re-register the bundled path.
|
||||
|
||||
**Install — manual plugin path (if `omp plugin install` is unavailable):**
|
||||
|
||||
OMP loads anything listed under `~/.omp/plugins/package.json` `dependencies` whose own `package.json` carries an `omp` (or `pi`) field. New plugins default to enabled — the lock file at `~/.omp/plugins/omp-plugins.lock.json` is only consulted when a plugin needs to be explicitly **disabled** (loader skips `runtimeState && !runtimeState.enabled` per [`extensibility/plugins/loader.ts:89-94`](https://github.com/can1357/oh-my-pi/blob/main/packages/coding-agent/src/extensibility/plugins/loader.ts)). So the manual install is two commands:
|
||||
|
||||
@@ -25,8 +25,9 @@
|
||||
*/
|
||||
|
||||
import { createHash } from "node:crypto";
|
||||
import { mkdirSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { existsSync, mkdirSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { resolveSessionDbPath, SessionDB } from "../../session/db.js";
|
||||
import { extractEvents } from "../../session/extract.js";
|
||||
@@ -74,6 +75,59 @@ let _sessionId = "";
|
||||
|
||||
const _ompAdapter = new OMPAdapter();
|
||||
|
||||
// ── MCP self-registration (issue #677) ───────────────────
|
||||
// The `omp plugin install context-mode` path wires THIS extension factory
|
||||
// (so routing hooks fire), but never creates the MCP config — so the 11
|
||||
// `ctx_*` tools stay unreachable even though curl/wget are blocked. Register
|
||||
// the server ourselves on plugin load, ONLY when absent (never clobber a
|
||||
// user's existing entry). Takes effect on the next OMP restart, same as the
|
||||
// manual mcp.json workaround the issue documents.
|
||||
const MCP_SERVER_NAME = "context-mode";
|
||||
// plugin.js ships at <pkg>/build/adapters/omp/plugin.js; the MCP server
|
||||
// bundle sits at the package root (<pkg>/server.bundle.mjs) — three up.
|
||||
const SERVER_BUNDLE_RELATIVE = "../../../server.bundle.mjs";
|
||||
|
||||
function resolveServerBundle(): string | null {
|
||||
try {
|
||||
const here = dirname(fileURLToPath(import.meta.url));
|
||||
const bundle = resolve(here, SERVER_BUNDLE_RELATIVE);
|
||||
return existsSync(bundle) ? bundle : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure `~/.omp/agent/mcp.json` registers the context-mode MCP server.
|
||||
*
|
||||
* Uses `node <abs>/server.bundle.mjs` rather than the `context-mode` bin:
|
||||
* under the plugin install the package lives in `~/.omp/plugins/node_modules`
|
||||
* and its bin is NOT on PATH, so the bare command would fail to spawn (the
|
||||
* exact symptom reported on issue #677). Best effort — never throws, never
|
||||
* breaks plugin load.
|
||||
*/
|
||||
function ensureMcpServerRegistered(): void {
|
||||
try {
|
||||
const bundle = resolveServerBundle();
|
||||
if (!bundle) return; // bundle missing → nothing safe to register
|
||||
|
||||
const settings = _ompAdapter.readSettings() ?? {};
|
||||
const mcpServers =
|
||||
(settings.mcpServers as Record<string, unknown> | undefined) ?? {};
|
||||
if (MCP_SERVER_NAME in mcpServers) return; // already present — don't clobber
|
||||
|
||||
mcpServers[MCP_SERVER_NAME] = {
|
||||
type: "stdio",
|
||||
command: "node",
|
||||
args: [bundle],
|
||||
};
|
||||
settings.mcpServers = mcpServers;
|
||||
_ompAdapter.writeSettings(settings as Record<string, unknown>);
|
||||
} catch {
|
||||
// best effort — a registration failure must never break plugin load
|
||||
}
|
||||
}
|
||||
|
||||
function getSessionDir(): string {
|
||||
const dir = _ompAdapter.getSessionDir();
|
||||
mkdirSync(dir, { recursive: true });
|
||||
@@ -189,6 +243,11 @@ export default function ompPlugin(pi: MinimalHookAPI): void {
|
||||
// earlier `OMP_PROJECT_DIR` read was an EM mistake — no upstream code
|
||||
// ever sets it. Drop it; fall through PI_PROJECT_DIR → cwd().
|
||||
const projectDir = process.env.PI_PROJECT_DIR || process.cwd();
|
||||
|
||||
// Self-register the MCP server so `ctx_*` tools are reachable under the
|
||||
// plugin install path, not just the manual MCP-only path (issue #677).
|
||||
ensureMcpServerRegistered();
|
||||
|
||||
const db = getOrCreateDB(projectDir);
|
||||
|
||||
// ── 1. session_start — initialize session row ─────────
|
||||
|
||||
@@ -447,4 +447,64 @@ describe("OMP plugin", () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// Slice 5: MCP self-registration (issue #677)
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
// The `omp plugin install` path wires the extension factory (routing
|
||||
// hooks fire) but never creates mcp.json, so the `ctx_*` tools stay
|
||||
// unreachable. The plugin must self-register the MCP server on load.
|
||||
|
||||
describe("Slice 5: MCP self-registration (issue #677)", () => {
|
||||
it("registers the context-mode MCP server in mcp.json when absent", async () => {
|
||||
const { OMPAdapter } = await import("../../src/adapters/omp/index.js");
|
||||
const { rmSync } = await import("node:fs");
|
||||
const adapter = new OMPAdapter();
|
||||
// Clean slate — drop any mcp.json a prior test in this suite wrote.
|
||||
try {
|
||||
rmSync(adapter.getSettingsPath(), { force: true });
|
||||
} catch {
|
||||
/* best effort */
|
||||
}
|
||||
|
||||
await registerOmpPlugin(api);
|
||||
|
||||
const settings = adapter.readSettings();
|
||||
const server = (settings?.mcpServers as Record<string, unknown> | undefined)?.[
|
||||
"context-mode"
|
||||
] as { command?: string; args?: unknown[] } | undefined;
|
||||
|
||||
expect(
|
||||
server,
|
||||
"context-mode must be registered in mcp.json after plugin load",
|
||||
).toBeDefined();
|
||||
// Plugin install puts the package in ~/.omp/plugins/node_modules where
|
||||
// the `context-mode` bin is NOT on PATH — must spawn via node + bundle.
|
||||
expect(server?.command).toBe("node");
|
||||
expect(Array.isArray(server?.args)).toBe(true);
|
||||
expect(String(server?.args?.[0])).toMatch(/server\.bundle\.mjs$/);
|
||||
});
|
||||
|
||||
it("does NOT clobber an existing context-mode mcp.json entry", async () => {
|
||||
const { OMPAdapter } = await import("../../src/adapters/omp/index.js");
|
||||
const adapter = new OMPAdapter();
|
||||
// User-supplied entry (e.g. the commenter's own working config) that
|
||||
// must survive plugin load untouched.
|
||||
adapter.writeSettings({
|
||||
mcpServers: {
|
||||
"context-mode": { command: "context-mode", args: ["--user-custom"] },
|
||||
},
|
||||
});
|
||||
|
||||
await registerOmpPlugin(api);
|
||||
|
||||
const settings = adapter.readSettings();
|
||||
const server = (settings?.mcpServers as Record<string, unknown>)["context-mode"] as {
|
||||
command?: string;
|
||||
args?: unknown[];
|
||||
};
|
||||
expect(server.command).toBe("context-mode");
|
||||
expect(server.args).toEqual(["--user-custom"]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user