feat(mcp): zero-dependency MCP server + agent-integration guide - #71
Open
Chosen9115 wants to merge 1 commit into
Open
feat(mcp): zero-dependency MCP server + agent-integration guide#71Chosen9115 wants to merge 1 commit into
Chosen9115 wants to merge 1 commit into
Conversation
Make OpenSOP consumable by any agent through the interfaces they already speak, without coupling the standard to any one runtime. - mcp/opensop-mcp — a single bash file (deps: bash + jq only, same as the CLI; no pip, no npm, no build) that speaks MCP stdio (newline-delimited JSON-RPC 2.0) and shells out to the local CLI. Exposes the full agent loop as 9 tools: list, search, suggest, dry_run, run, status, submit, show, runs. Local-first (OPENSOP_SERVER opts into a remote). Trust boundary: run/submit execute local processes (arbitrary shell) — OPENSOP_MCP_READONLY hides and refuses them for untrusted corpora. - docs/CLAUDE-INTEGRATION.md — the canonical "how any agent consumes OpenSOP" guide (fixes the previously-dangling link from cli/README.md). Covers Path A (CLI as a plain shell tool — works with any terminal-backed agent) and Path B (the MCP server) with Claude Desktop / generic-client config snippets and a tool reference table. - mcp/README.md, plus discoverability pointers from README.md and docs/AGENTS.md. - mcp/test/test.sh — golden suite (17 cases): initialize handshake, tools/list + readonly filtering, every tool's happy path, error passthrough (isError), glob-safe search, notifications-get-no-reply, unknown method/tool, missing args. Security-reviewed before merge: no command injection (no eval anywhere); fixed glob expansion in search keyword splitting (read -ra, not unquoted $kws), JSON-RPC notification replies (HAS_ID guard), and an unguarded mktemp under set -e. All three are regression-covered. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Make OpenSOP consumable by any agent through the interfaces they already speak — without coupling the standard to a single runtime. OpenSOP is a neutral standard, so the right "play well with X" surface is generic (MCP + the CLI as a shell tool), and runtime-specific glue stays on the consumer's side.
Two ways for an agent to drive OpenSOP, both local-first:
opensop … --json. Zero integration code (works with terminal-backed agents, cron, a Makefile).mcp/opensop-mcpexposes the same agent loop as MCP tools over stdio.What's here
mcp/opensop-mcp— a single bash file. Deps:bash+jqonly (same as the CLI; no pip, no npm, no build step — the file is the binary). Speaks MCP stdio (newline-delimited JSON-RPC 2.0) and shells out to the local CLI. Exposes the full agent loop — discover → preview → run → resume → audit — as 9 tools:opensop_list,opensop_search,opensop_suggest,opensop_dry_run,opensop_run,opensop_status,opensop_submit,opensop_show,opensop_runs.OPENSOP_SERVERopts into a remote/sop/*server; unset = local.opensop_run/opensop_submitexecute local processes (arbitrary shell, same posture as a Makefile).OPENSOP_MCP_READONLYhides and refuses them — safe default for untrusted corpora.isError:truecarrying the{error,message,hint}envelope, so agents see structured failures.docs/CLAUDE-INTEGRATION.md— the canonical agent-integration guide (this also fixes the previously-dangling link fromcli/README.md). The agent loop with concrete commands, Path A vs Path B, Claude Desktop + generic-client config snippets, a 9-row tool reference table, and the trust/safety section.mcp/README.md+ discoverability pointers fromREADME.mdanddocs/AGENTS.md.mcp/test/test.sh— golden suite, 17 cases: initialize handshake (protocolVersion echo, serverInfo), tools/list + readonly filtering, every tool's happy path, run→status→runs→show loop, error passthrough, glob-safe search, notifications-get-no-reply, unknown method/tool, missing-arg.Security review (done before this PR)
An adversarial pass confirmed no command injection — there is no
evalanywhere in the server or the CLI; all tool arguments reach the CLI as array elements, never a shell string. Three issues were found and fixed, each now regression-covered:opensop_searchkeyword splitting used an unquotedkwarr=($kws), which glob-expanded*/foo*against the CWD → nowread -ra(word-split, no pathname expansion).HAS_IDguard so notifications never get a reply.errf=$(mktemp)was unguarded underset -e→ guarded, degrades to anisErrorresult.Test plan
bash -n mcp/opensop-mcp— clean.bash mcp/test/test.sh— 17 PASS,ALL PASS, exit 0.bash cli/test/test.sh— 289 PASS (this PR doesn't touch the CLI; confirms no disturbance).completed) → status → readonly hides run/submit. All correct.Self-rating
Correctness 9 · simplicity 9 (single-file, no new dependency class) · test coverage 9 (17 protocol/tool/error cases) · security 9 (adversarial review + fixes + regression tests) · spec fidelity 10 (no
/sop/*contract change; wraps existing CLI) · public-repo hygiene 10 (placeholders only; no PII/secrets/internal identifiers).🤖 Generated with Claude Code