fix: #4 — feat(core): stdio MCP server + CLI subcommands#14
Merged
Conversation
Wires the full v0.1 binary surface from issue #4. One executable now exposes serve / lookup / lookup-from-prompt / recap / init, plus a stdio MCP server with four tools (lookup, search, random, list_traditions). - internal/mcp/server.go — minimal JSON-RPC 2.0 stdio server (NDJSON framing). Tool errors are returned as result-with-isError per MCP convention; transport-level failures use JSON-RPC error objects. Zero non-stdlib dependencies. - internal/cli/{lookup,recap,init}.go — subcommand handlers. lookup-from-prompt accepts both Claude (`/bible John 3:16`) and Codex (`[[bible:John 3:16]]`) marker styles and emits an additionalContext envelope suitable for both UserPromptExpansion and UserPromptSubmit hooks. init splices marker-fenced snippets into ~/.claude/settings.json and ~/.codex/config.toml; rerunning is a no-op once installed, --uninstall strips the block, and user content outside the markers is preserved. - internal/injector/envelope.go — renders the §6.3 reflection envelope wrapping the verse text in <scripture_card> tags. - internal/packs/lookup.go — shared `LookupReference` and `ReferenceID` so both CLI and MCP map resolver.Reference → pack id from a single source. Tests cover the four MCP methods (initialize, notifications, tools/list, tools/call), all CLI subcommands, marker scanning for both formats with trailing prompt text, init's install/uninstall/idempotent/dry-run paths, and a p50 lookup latency check (<5ms budget per the issue). The empty internal/{cli,injector,mcp}/doc.go placeholders from #1 are removed; each package now carries its docstring on the file that defines its API.
This was referenced May 2, 2026
MiaoDX
added a commit
that referenced
this pull request
May 2, 2026
…#18) Closes #7. - install.sh detects OS (darwin/linux) and arch (arm64/x86_64), downloads the matching scripture-mcp release tarball, and installs the binary to ~/.local/bin (overridable with --prefix). Re-running overwrites the binary in place — that's the upgrade path. - After install, it detects which coding agents are on $PATH (claude, codex), prompts (via /dev/tty so the prompt still works under curl|bash), and calls 'scripture-mcp init --target=<agent>' for each. init is already idempotent on configs (PR #14's marker-fenced splice). - --uninstall reverses both halves: 'init --uninstall' for each detected agent, then removes the binary. - --no-wire installs the binary only; --yes auto-confirms; --version pins a release tag; --from-archive bypasses the network and unpacks a local tarball (used by the test harness, also handy for air- gapped installs). Tests live in internal/installer/install_test.go: they build the real scripture-mcp, pack it into a tarball, then drive install.sh through fresh-install, idempotent re-run, and uninstall against a sandboxed HOME and a stubbed PATH. Covered: --help surface, unknown-flag exit, no-agents case, --no-wire, missing archive. README's Install section is rewritten around install.sh (was 'planned'); the brew line is removed per the issue's note. Co-authored-by: Claude <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.
Closes #4.
Wires the full v0.1 binary surface. One executable now exposes
serve,lookup,lookup-from-prompt,recap, andinit, with astdio MCP server that publishes four tools.
What's in this PR
internal/mcp/server.go— minimal JSON-RPC 2.0 stdio MCP server(NDJSON framing). Implements
initialize,tools/list,tools/call,ping,shutdown, and thenotifications/initializednotification.Tool-level failures are returned as
result-with-isErrorper MCPconvention; transport-level failures become JSON-RPC error objects.
Zero non-stdlib dependencies — the surface is small enough (3 methods,
4 tools, no resources/prompts) that pulling in an MCP SDK would
double the project's dependency footprint for very little gain.
internal/cli/{cli,lookup,recap,init}.go— subcommand handlers. Theflag parser tolerates flags placed after positional args
(
lookup "John 3:16" --format=json) per the issue spec. JSON is thedefault
lookupoutput.lookup-from-promptaccepts both Claude (/bible John 3:16) andCodex (
[[bible:John 3:16]]) marker styles. It reads either a rawprompt or a JSON object with a
prompt/user_promptfield on stdin(the latter is the form Claude Code / Codex hooks send) and emits an
additionalContextenvelope on stdout suitable for bothUserPromptExpansionandUserPromptSubmit. With no marker itexits 0 silently so hooks add nothing to the prompt. Trailing user
prompt text after the reference is handled by progressively trimming
tokens from the right until the resolver accepts what remains.
recap— Mode B terminal print.--tradition=filters,--first-lettermasks all-but-first character of each word/Hanglyph for memory mode,
--seed=Nmakes selection deterministic.Recap output goes to stdout only; the contract that it never enters
a
model_callinput is enforced by where the agents invoke it(Claude Stop hook, Codex
cdxshell wrapper) — both land in feat(claude): Claude Code adapter (Mode A + Mode B + skill) #5/feat(codex): Codex adapter (Mode A + Mode B) #6.init --target={claude-code,codex} [--recap=on|off] [--uninstall] [--dry-run]— splices a marker-fenced snippet into~/.claude/settings.jsonor~/.codex/config.toml. Idempotent(a second run reports "already up to date");
--uninstallstripsthe block and collapses the surrounding blank line; user content
outside the markers is preserved. The Claude snippet matches
plan.md§5.1; the Codex snippet matches §5.2.internal/injector/envelope.go— renders the §6.3 reflectionenvelope wrapping verse text in
<scripture_card>tags with the"do not preach / quote verbatim" framing.
DisplayRefformats theverse's reference for human display, switching by tradition.
internal/packs/lookup.go— sharedLookupReference(r resolver.Reference)and
ReferenceID(r)helpers, used by bothinternal/cliandinternal/mcpso the resolver→pack-id mapping has a singleauthoritative implementation. Returns
packs.ErrNotBundledwhen thereference resolves to a tradition shipped api-only in this build
(heart-sutra, quran), distinct from a hard "id miss" error.
Acceptance-criteria mapping
scripture-mcp servelaunches a stdio MCP serverlookup,search,random,list_traditions—all return verses with
checksum_sha256andsourceplan.md§5.1 (Claude Code) and §5.2(Codex) —
initwrites those snippets verbatimscripture-mcp lookup "<ref>" --format=json→ JSON Verse onstdout (default;
--format=textfor terminal-pretty)scripture-mcp lookup-from-promptreads stdin, emitsadditionalContextJSON suitable for both ClaudeUserPromptExpansionand CodexUserPromptSubmitscripture-mcp recap [--tradition=<t>] [--terminal]printspretty terminal output, exit 0
scripture-mcp recap --first-letterprints first-letter patternscripture-mcp init --target={claude-code,codex}merges configsnippet without overwriting; idempotent; supports
--uninstalland
--dry-runlookup< 5 ms — verified byTestLookupLatency(200 trials over the in-memory registry)Tests
internal/mcp/server_test.go—initialize, notifications-have-no-reply,
tools/listexposes all four,tools/callfor each toolincluding the missing-verse → tool-error path, parse-error framing,
unknown-method → method-not-found, and the p50 latency check.
internal/cli/lookup_test.go— JSON default, JSON/text formats,ambiguous → exit 3, unrecognized → exit 1, sutra → "not bundled"
message, plus marker-scan tests for slash and inline forms (with
trailing prompt text), JSON input form, and silent-on-no-marker.
internal/cli/recap_test.go— deterministic with seed, traditionfilter, first-letter mask (ASCII + Han), unknown-tradition exit.
internal/cli/init_test.go— file creation, recap-on/off variant,idempotent rerun, user-content preservation, uninstall, codex TOML
output, missing-target → exit 2, dry-run.
internal/injector/envelope_test.go— envelope wrapper structureand per-tradition
DisplayRefcases.internal/packs/lookup_test.go—ReferenceIDmapping incl.multi-word and numbered books, error cases,
LookupReferencehappy/api-only/miss paths.
Verification
make all(lint + verify-packs + test + build) clean./bin/scripture-mcp lookup "John 3:16" --format=jsonresolves, returns canonical verse with checksum
8473c0b1c7664945528317faf77351258eb79f8b11ba821ef76d7e916cde711a(
initialize/tools/list/tools/call lookup John 3:16) intoscripture-mcp servereturns protocolVersion2024-11-05, thefull tool list, and the verse with matching checksum
Generated by Claude Code